2 * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 #include <osmocore/talloc.h>
30 #include <osmocom/bb/common/logging.h>
31 #include <osmocom/bb/common/osmocom_data.h>
32 #include <osmocom/bb/mobile/mncc.h>
33 #include <osmocom/bb/mobile/vty.h>
36 static uint32_t new_callref = 1;
37 static LLIST_HEAD(call_list);
43 void mncc_set_cause(struct gsm_mncc *data, int loc, int val);
45 static void free_call(struct gsm_call *call)
47 llist_del(&call->entry);
48 DEBUGP(DMNCC, "(call %x) Call removed.\n", call->callref);
53 struct gsm_call *get_call_ref(uint32_t callref)
55 struct gsm_call *callt;
57 llist_for_each_entry(callt, &call_list, entry) {
58 if (callt->callref == callref)
64 static int8_t mncc_get_bearer(struct gsm_support *sup, uint8_t speech_ver)
69 LOGP(DMNCC, LOGL_INFO, " net suggests full rate v3\n");
71 LOGP(DMNCC, LOGL_INFO, " full rate v3 not supported\n");
77 LOGP(DMNCC, LOGL_INFO, " net suggests full rate v2\n");
79 LOGP(DMNCC, LOGL_INFO, " full rate v2 not supported\n");
83 case 0: /* mandatory */
85 LOGP(DMNCC, LOGL_INFO, " net suggests full rate v1\n");
87 LOGP(DMNCC, LOGL_INFO, " full rate v1 not supported\n");
93 LOGP(DMNCC, LOGL_INFO, " net suggests half rate v3\n");
95 LOGP(DMNCC, LOGL_INFO, " half rate v3 not supported\n");
101 LOGP(DMNCC, LOGL_INFO, " net suggests half rate v1\n");
103 LOGP(DMNCC, LOGL_INFO, " half rate v1 not supported\n");
108 LOGP(DMNCC, LOGL_INFO, " net suggests unknown speech version "
116 static void mncc_set_bearer(struct osmocom_ms *ms, int8_t speech_ver,
117 struct gsm_mncc *mncc)
119 struct gsm_support *sup = &ms->support;
120 struct gsm_settings *set = &ms->settings;
123 mncc->fields |= MNCC_F_BEARER_CAP;
124 mncc->bearer_cap.coding = 0;
125 if (sup->ch_cap == GSM_CAP_SDCCH_TCHF_TCHH
126 && (sup->half_v1 || sup->half_v3)) {
127 mncc->bearer_cap.radio = 3;
128 LOGP(DMNCC, LOGL_INFO, " support TCH/H also\n");
130 mncc->bearer_cap.radio = 1;
131 LOGP(DMNCC, LOGL_INFO, " support TCH/F only\n");
133 mncc->bearer_cap.speech_ctm = 0;
134 /* if no specific speech_ver is given */
135 if (speech_ver < 0) {
136 /* if half rate is supported and prefered */
137 if (sup->half_v3 && set->half && set->half_prefer) {
138 mncc->bearer_cap.speech_ver[i++] = 5;
139 LOGP(DMNCC, LOGL_INFO, " support half rate v3\n");
141 if (sup->half_v1 && set->half && set->half_prefer) {
142 mncc->bearer_cap.speech_ver[i++] = 1;
143 LOGP(DMNCC, LOGL_INFO, " support half rate v1\n");
145 /* if full rate is supported */
147 mncc->bearer_cap.speech_ver[i++] = 4;
148 LOGP(DMNCC, LOGL_INFO, " support full rate v3\n");
151 mncc->bearer_cap.speech_ver[i++] = 2;
152 LOGP(DMNCC, LOGL_INFO, " support full rate v2\n");
154 if (sup->full_v1) { /* mandatory, so it's always true */
155 mncc->bearer_cap.speech_ver[i++] = 0;
156 LOGP(DMNCC, LOGL_INFO, " support full rate v1\n");
158 /* if half rate is supported and not prefered */
159 if (sup->half_v3 && set->half && !set->half_prefer) {
160 mncc->bearer_cap.speech_ver[i++] = 5;
161 LOGP(DMNCC, LOGL_INFO, " support half rate v3\n");
163 if (sup->half_v1 && set->half && !set->half_prefer) {
164 mncc->bearer_cap.speech_ver[i++] = 1;
165 LOGP(DMNCC, LOGL_INFO, " support half rate v1\n");
167 /* if specific speech_ver is given (it must be supported) */
169 mncc->bearer_cap.speech_ver[i++] = speech_ver;
170 mncc->bearer_cap.speech_ver[i] = -1; /* end of list */
171 mncc->bearer_cap.transfer = 0;
172 mncc->bearer_cap.mode = 0;
176 * MNCCms dummy application
179 /* this is a minimal implementation as required by GSM 04.08 */
180 int mncc_recv_dummy(struct osmocom_ms *ms, int msg_type, void *arg)
182 struct gsm_mncc *data = arg;
183 uint32_t callref = data->callref;
186 if (msg_type == MNCC_REL_IND || msg_type == MNCC_REL_CNF)
189 LOGP(DMNCC, LOGL_INFO, "Rejecting incomming call\n");
191 /* reject, as we don't support Calls */
192 memset(&rel, 0, sizeof(struct gsm_mncc));
193 rel.callref = callref;
194 mncc_set_cause(&rel, GSM48_CAUSE_LOC_USER,
195 GSM48_CC_CAUSE_INCOMPAT_DEST);
197 return mncc_send(ms, MNCC_REL_REQ, &rel);
201 * MNCCms basic call application
204 int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg)
206 struct gsm_settings *set = &ms->settings;
207 struct gsm_support *sup = &ms->support;
208 struct gsm_mncc *data = arg;
209 struct gsm_call *call = get_call_ref(data->callref);
210 struct gsm_mncc mncc;
212 int8_t speech_ver = -1, speech_ver_half = -1, temp;
215 /* call does not exist */
216 if (!call && msg_type != MNCC_SETUP_IND) {
217 LOGP(DMNCC, LOGL_INFO, "Rejecting incomming call "
218 "(callref %x)\n", data->callref);
219 if (msg_type == MNCC_REL_IND || msg_type == MNCC_REL_CNF)
221 cause = GSM48_CC_CAUSE_INCOMPAT_DEST;
223 memset(&mncc, 0, sizeof(struct gsm_mncc));
224 mncc.callref = data->callref;
225 mncc_set_cause(&mncc, GSM48_CAUSE_LOC_USER, cause);
226 return mncc_send(ms, MNCC_REL_REQ, &mncc);
229 /* setup without call */
231 if (llist_empty(&call_list))
233 call = talloc_zero(l23_ctx, struct gsm_call);
236 call->callref = data->callref;
237 llist_add_tail(&call->entry, &call_list);
240 /* not in initiated state anymore */
245 vty_notify(ms, NULL);
246 switch (data->cause.value) {
247 case GSM48_CC_CAUSE_UNASSIGNED_NR:
248 vty_notify(ms, "Call: Number not assigned\n");
250 case GSM48_CC_CAUSE_NO_ROUTE:
251 vty_notify(ms, "Call: Destination unreachable\n");
253 case GSM48_CC_CAUSE_NORM_CALL_CLEAR:
254 vty_notify(ms, "Call: Remote hangs up\n");
256 case GSM48_CC_CAUSE_USER_BUSY:
257 vty_notify(ms, "Call: Remote busy\n");
259 case GSM48_CC_CAUSE_USER_NOTRESPOND:
260 vty_notify(ms, "Call: Remote not responding\n");
262 case GSM48_CC_CAUSE_USER_ALERTING_NA:
263 vty_notify(ms, "Call: Remote not answering\n");
265 case GSM48_CC_CAUSE_CALL_REJECTED:
266 vty_notify(ms, "Call has been rejected\n");
268 case GSM48_CC_CAUSE_NUMBER_CHANGED:
269 vty_notify(ms, "Call: Number changed\n");
271 case GSM48_CC_CAUSE_PRE_EMPTION:
272 vty_notify(ms, "Call: Cleared due to pre-emption\n");
274 case GSM48_CC_CAUSE_DEST_OOO:
275 vty_notify(ms, "Call: Remote out of order\n");
277 case GSM48_CC_CAUSE_INV_NR_FORMAT:
278 vty_notify(ms, "Call: Number invalid or imcomplete\n");
280 case GSM48_CC_CAUSE_NO_CIRCUIT_CHAN:
281 vty_notify(ms, "Call: No channel available\n");
283 case GSM48_CC_CAUSE_NETWORK_OOO:
284 vty_notify(ms, "Call: Network out of order\n");
286 case GSM48_CC_CAUSE_TEMP_FAILURE:
287 vty_notify(ms, "Call: Temporary failure\n");
289 case GSM48_CC_CAUSE_SWITCH_CONG:
290 vty_notify(ms, "Congestion\n");
293 vty_notify(ms, "Call has been disconnected\n");
295 LOGP(DMNCC, LOGL_INFO, "Call has been disconnected "
296 "(cause %d)\n", data->cause.value);
297 if ((data->fields & MNCC_F_PROGRESS)
298 && data->progress.descr == 8) {
299 vty_notify(ms, "Please hang up!\n");
303 cause = GSM48_CC_CAUSE_NORM_CALL_CLEAR;
307 vty_notify(ms, NULL);
308 if (data->cause.value == GSM48_CC_CAUSE_CALL_REJECTED)
309 vty_notify(ms, "Call has been rejected\n");
311 vty_notify(ms, "Call has been released\n");
312 LOGP(DMNCC, LOGL_INFO, "Call has been released (cause %d)\n",
316 case MNCC_CALL_PROC_IND:
317 vty_notify(ms, NULL);
318 vty_notify(ms, "Call is proceeding\n");
319 LOGP(DMNCC, LOGL_INFO, "Call is proceeding\n");
320 if ((data->fields & MNCC_F_BEARER_CAP)
321 && data->bearer_cap.speech_ver[0] >= 0) {
322 mncc_get_bearer(sup, data->bearer_cap.speech_ver[0]);
326 vty_notify(ms, NULL);
327 vty_notify(ms, "Call is aleriting\n");
328 LOGP(DMNCC, LOGL_INFO, "Call is alerting\n");
331 vty_notify(ms, NULL);
332 vty_notify(ms, "Call is answered\n");
333 LOGP(DMNCC, LOGL_INFO, "Call is answered\n");
336 vty_notify(ms, NULL);
337 if (!first_call && !ms->settings.cw) {
338 vty_notify(ms, "Incomming call rejected while busy\n");
339 LOGP(DMNCC, LOGL_INFO, "Incomming call but busy\n");
340 cause = GSM48_CC_CAUSE_USER_BUSY;
343 /* select first supported speech_ver */
344 if ((data->fields & MNCC_F_BEARER_CAP)) {
347 for (i = 0; data->bearer_cap.speech_ver[i] >= 0; i++) {
349 temp = mncc_get_bearer(sup,
350 data->bearer_cap.speech_ver[i]);
353 if (temp == 5 || temp == 1) { /* half */
354 /* only the first half rate */
355 if (speech_ver_half < 0)
356 speech_ver_half = temp;
358 /* only the first full rate */
363 /* half and full given */
364 if (speech_ver_half >= 0 && speech_ver >= 0) {
365 if (set->half_prefer) {
366 LOGP(DMNCC, LOGL_INFO, " both supported"
367 " codec rates are given, using "
368 "preferred half rate\n");
369 speech_ver = speech_ver_half;
371 LOGP(DMNCC, LOGL_INFO, " both supported"
372 " codec rates are given, using "
373 "preferred full rate\n");
374 } else if (speech_ver_half < 0 && speech_ver < 0) {
375 LOGP(DMNCC, LOGL_INFO, " no supported codec "
377 /* only half rate is given, use it */
378 } else if (speech_ver_half >= 0) {
379 LOGP(DMNCC, LOGL_INFO, " only supported half "
380 "rate codec is given, using it\n");
381 speech_ver = speech_ver_half;
382 /* only full rate is given, use it */
384 LOGP(DMNCC, LOGL_INFO, " only supported full "
385 "rate codec is given, using it\n");
388 /* presentation allowed if present == 0 */
389 if (data->calling.present || !data->calling.number[0])
390 vty_notify(ms, "Incomming call (anonymous)\n");
391 else if (data->calling.type == 1)
392 vty_notify(ms, "Incomming call (from +%s)\n",
393 data->calling.number);
394 else if (data->calling.type == 2)
395 vty_notify(ms, "Incomming call (from 0-%s)\n",
396 data->calling.number);
398 vty_notify(ms, "Incomming call (from %s)\n",
399 data->calling.number);
400 LOGP(DMNCC, LOGL_INFO, "Incomming call (from %s callref %x)\n",
401 data->calling.number, call->callref);
402 memset(&mncc, 0, sizeof(struct gsm_mncc));
403 mncc.callref = call->callref;
404 /* only include bearer cap, if not given in setup
405 * or if multiple codecs are given
406 * or if not only full rate
407 * or if given codec is unimplemented
409 if (!(data->fields & MNCC_F_BEARER_CAP) || speech_ver < 0)
410 mncc_set_bearer(ms, -1, &mncc);
411 else if (data->bearer_cap.speech_ver[1] >= 0
413 mncc_set_bearer(ms, speech_ver, &mncc);
414 mncc_send(ms, MNCC_CALL_CONF_REQ, &mncc);
416 LOGP(DMNCC, LOGL_INFO, "Ring!\n");
418 LOGP(DMNCC, LOGL_INFO, "Knock!\n");
422 memset(&mncc, 0, sizeof(struct gsm_mncc));
423 mncc.callref = call->callref;
424 mncc_send(ms, MNCC_ALERT_REQ, &mncc);
426 case MNCC_SETUP_COMPL_IND:
427 vty_notify(ms, NULL);
428 vty_notify(ms, "Call is connected\n");
429 LOGP(DMNCC, LOGL_INFO, "Call is connected\n");
432 vty_notify(ms, NULL);
433 vty_notify(ms, "Call is on hold\n");
434 LOGP(DMNCC, LOGL_INFO, "Call is on hold\n");
438 vty_notify(ms, NULL);
439 vty_notify(ms, "Call hold was rejected\n");
440 LOGP(DMNCC, LOGL_INFO, "Call hold was rejected\n");
442 case MNCC_RETRIEVE_CNF:
443 vty_notify(ms, NULL);
444 vty_notify(ms, "Call is retrieved\n");
445 LOGP(DMNCC, LOGL_INFO, "Call is retrieved\n");
448 case MNCC_RETRIEVE_REJ:
449 vty_notify(ms, NULL);
450 vty_notify(ms, "Call retrieve was rejected\n");
451 LOGP(DMNCC, LOGL_INFO, "Call retrieve was rejected\n");
454 LOGP(DMNCC, LOGL_INFO, "Message 0x%02x unsupported\n",
462 int mncc_call(struct osmocom_ms *ms, char *number)
464 struct gsm_call *call;
465 struct gsm_mncc setup;
467 llist_for_each_entry(call, &call_list, entry) {
469 vty_notify(ms, NULL);
470 vty_notify(ms, "Please put active call on hold "
472 LOGP(DMNCC, LOGL_INFO, "Cannot make a call, busy!\n");
477 call = talloc_zero(l23_ctx, struct gsm_call);
480 call->callref = new_callref++;
482 llist_add_tail(&call->entry, &call_list);
484 memset(&setup, 0, sizeof(struct gsm_mncc));
485 setup.callref = call->callref;
487 if (!strncasecmp(number, "emerg", 5)) {
488 LOGP(DMNCC, LOGL_INFO, "Make emergency call\n");
492 LOGP(DMNCC, LOGL_INFO, "Make call to %s\n", number);
494 setup.fields |= MNCC_F_CALLED;
495 if (number[0] == '+') {
497 setup.called.type = 1; /* international */
499 setup.called.type = 0; /* auto/unknown - prefix must be
501 setup.called.plan = 1; /* ISDN */
502 strncpy(setup.called.number, number,
503 sizeof(setup.called.number) - 1);
505 /* bearer capability (mandatory) */
506 mncc_set_bearer(ms, -1, &setup);
507 if (ms->settings.clir)
509 else if (ms->settings.clip)
513 return mncc_send(ms, MNCC_SETUP_REQ, &setup);
516 int mncc_hangup(struct osmocom_ms *ms)
518 struct gsm_call *call, *found = NULL;
519 struct gsm_mncc disc;
521 llist_for_each_entry(call, &call_list, entry) {
528 LOGP(DMNCC, LOGL_INFO, "No active call to hangup\n");
529 vty_notify(ms, NULL);
530 vty_notify(ms, "No active call\n");
534 memset(&disc, 0, sizeof(struct gsm_mncc));
535 disc.callref = found->callref;
536 mncc_set_cause(&disc, GSM48_CAUSE_LOC_USER,
537 GSM48_CC_CAUSE_NORM_CALL_CLEAR);
538 return mncc_send(ms, (call->init) ? MNCC_REL_REQ : MNCC_DISC_REQ,
542 int mncc_answer(struct osmocom_ms *ms)
544 struct gsm_call *call, *alerting = NULL;
548 llist_for_each_entry(call, &call_list, entry) {
551 else if (!call->hold)
555 LOGP(DMNCC, LOGL_INFO, "No call alerting\n");
556 vty_notify(ms, NULL);
557 vty_notify(ms, "No alerting call\n");
561 LOGP(DMNCC, LOGL_INFO, "Answer but we have an active call\n");
562 vty_notify(ms, NULL);
563 vty_notify(ms, "Please put active call on hold first!\n");
569 memset(&rsp, 0, sizeof(struct gsm_mncc));
570 rsp.callref = alerting->callref;
571 return mncc_send(ms, MNCC_SETUP_RSP, &rsp);
574 int mncc_hold(struct osmocom_ms *ms)
576 struct gsm_call *call, *found = NULL;
577 struct gsm_mncc hold;
579 llist_for_each_entry(call, &call_list, entry) {
586 LOGP(DMNCC, LOGL_INFO, "No active call to hold\n");
587 vty_notify(ms, NULL);
588 vty_notify(ms, "No active call\n");
592 memset(&hold, 0, sizeof(struct gsm_mncc));
593 hold.callref = found->callref;
594 return mncc_send(ms, MNCC_HOLD_REQ, &hold);
597 int mncc_retrieve(struct osmocom_ms *ms, int number)
599 struct gsm_call *call;
600 struct gsm_mncc retr;
601 int holdnum = 0, active = 0, i = 0;
603 llist_for_each_entry(call, &call_list, entry) {
610 LOGP(DMNCC, LOGL_INFO, "Cannot retrieve during active call\n");
611 vty_notify(ms, NULL);
612 vty_notify(ms, "Hold active call first!\n");
616 vty_notify(ms, NULL);
617 vty_notify(ms, "No call on hold!\n");
620 if (holdnum > 1 && number <= 0) {
621 vty_notify(ms, NULL);
622 vty_notify(ms, "Select call 1..%d\n", holdnum);
625 if (holdnum == 1 && number <= 0)
627 if (number > holdnum) {
628 vty_notify(ms, NULL);
629 vty_notify(ms, "Given number %d out of range!\n", number);
630 vty_notify(ms, "Select call 1..%d\n", holdnum);
634 llist_for_each_entry(call, &call_list, entry) {
640 memset(&retr, 0, sizeof(struct gsm_mncc));
641 retr.callref = call->callref;
642 return mncc_send(ms, MNCC_RETRIEVE_REQ, &retr);