[layer23] Do not reject mobile terminated calls with no codec given
[osmocom-bb.git] / src / host / layer23 / src / mobile / mnccms.c
1 /*
2  * (C) 2010 by Andreas Eversberg <jolly@eversberg.eu>
3  *
4  * All Rights Reserved
5  *
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.
10  *
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.
15  *
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.
19  *
20  */
21
22 #include <stdint.h>
23 #include <errno.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <stdlib.h>
27
28 #include <osmocore/talloc.h>
29
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>
34
35 void *l23_ctx;
36 static uint32_t new_callref = 1;
37 static LLIST_HEAD(call_list);
38
39 /*
40  * support functions
41  */
42
43 void mncc_set_cause(struct gsm_mncc *data, int loc, int val);
44
45 static void free_call(struct gsm_call *call)
46 {
47         llist_del(&call->entry);
48         DEBUGP(DMNCC, "(call %x) Call removed.\n", call->callref);
49         talloc_free(call);
50 }
51
52
53 struct gsm_call *get_call_ref(uint32_t callref)
54 {
55         struct gsm_call *callt;
56
57         llist_for_each_entry(callt, &call_list, entry) {
58                 if (callt->callref == callref)
59                         return callt;
60         }
61         return NULL;
62 }
63
64 static int8_t mncc_get_bearer(struct gsm_support *sup, uint8_t speech_ver)
65 {
66         switch (speech_ver) {
67         case 4:
68                 if (sup->full_v3)
69                         LOGP(DMNCC, LOGL_INFO, " net suggests full rate v3\n");
70                 else {
71                         LOGP(DMNCC, LOGL_INFO, " full rate v3 not supported\n");
72                         speech_ver = -1;
73                 }
74                 break;
75         case 2:
76                 if (sup->full_v2)
77                         LOGP(DMNCC, LOGL_INFO, " net suggests full rate v2\n");
78                 else {
79                         LOGP(DMNCC, LOGL_INFO, " full rate v2 not supported\n");
80                         speech_ver = -1;
81                 }
82                 break;
83         case 0: /* mandatory */
84                 if (sup->full_v1)
85                         LOGP(DMNCC, LOGL_INFO, " net suggests full rate v1\n");
86                 else {
87                         LOGP(DMNCC, LOGL_INFO, " full rate v1 not supported\n");
88                         speech_ver = -1;
89                 }
90                 break;
91         case 5:
92                 if (sup->half_v3)
93                         LOGP(DMNCC, LOGL_INFO, " net suggests half rate v3\n");
94                 else {
95                         LOGP(DMNCC, LOGL_INFO, " half rate v3 not supported\n");
96                         speech_ver = -1;
97                 }
98                 break;
99         case 1:
100                 if (sup->half_v1)
101                         LOGP(DMNCC, LOGL_INFO, " net suggests half rate v1\n");
102                 else {
103                         LOGP(DMNCC, LOGL_INFO, " half rate v1 not supported\n");
104                         speech_ver = -1;
105                 }
106                 break;
107         default:
108                 LOGP(DMNCC, LOGL_INFO, " net suggests unknown speech version "
109                         "%d\n", speech_ver);
110                 speech_ver = -1;
111         }
112
113         return speech_ver;
114 }
115
116 static void mncc_set_bearer(struct osmocom_ms *ms, int8_t speech_ver,
117         struct gsm_mncc *mncc)
118 {
119         struct gsm_support *sup = &ms->support;
120         struct gsm_settings *set = &ms->settings;
121         int i = 0;
122
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");
129         } else {
130                 mncc->bearer_cap.radio = 1;
131                 LOGP(DMNCC, LOGL_INFO, " support TCH/F only\n");
132         }
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");
140                 }
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");
144                 }
145                 /* if full rate is supported */
146                 if (sup->full_v3) {
147                         mncc->bearer_cap.speech_ver[i++] = 4;
148                         LOGP(DMNCC, LOGL_INFO, " support full rate v3\n");
149                 }
150                 if (sup->full_v2) {
151                         mncc->bearer_cap.speech_ver[i++] = 2;
152                         LOGP(DMNCC, LOGL_INFO, " support full rate v2\n");
153                 }
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");
157                 }
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");
162                 }
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");
166                 }
167         /* if specific speech_ver is given (it must be supported) */
168         } else 
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;
173 }
174
175 /*
176  * MNCCms dummy application
177  */
178
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)
181 {
182         struct gsm_mncc *data = arg;
183         uint32_t callref = data->callref;
184         struct gsm_mncc rel;
185
186         if (msg_type == MNCC_REL_IND || msg_type == MNCC_REL_CNF)
187                 return 0;
188
189         LOGP(DMNCC, LOGL_INFO, "Rejecting incomming call\n");
190
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);
196
197         return mncc_send(ms, MNCC_REL_REQ, &rel);
198 }
199
200 /*
201  * MNCCms basic call application
202  */
203
204 int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg)
205 {
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;
211         uint8_t cause;
212         int8_t  speech_ver = -1, speech_ver_half = -1, temp;
213         int first_call = 0;
214
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)
220                         return 0;
221                 cause = GSM48_CC_CAUSE_INCOMPAT_DEST;
222                 release:
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);
227         }
228
229         /* setup without call */
230         if (!call) {
231                 if (llist_empty(&call_list))
232                         first_call = 1;
233                 call = talloc_zero(l23_ctx, struct gsm_call);
234                 if (!call)
235                         return -ENOMEM;
236                 call->callref = data->callref;
237                 llist_add_tail(&call->entry, &call_list);
238         }
239
240         /* not in initiated state anymore */
241         call->init = 0;
242
243         switch (msg_type) {
244         case MNCC_DISC_IND:
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");
249                         break;
250                 case GSM48_CC_CAUSE_NO_ROUTE:
251                         vty_notify(ms, "Call: Destination unreachable\n");
252                         break;
253                 case GSM48_CC_CAUSE_NORM_CALL_CLEAR:
254                         vty_notify(ms, "Call: Remote hangs up\n");
255                         break;
256                 case GSM48_CC_CAUSE_USER_BUSY:
257                         vty_notify(ms, "Call: Remote busy\n");
258                         break;
259                 case GSM48_CC_CAUSE_USER_NOTRESPOND:
260                         vty_notify(ms, "Call: Remote not responding\n");
261                         break;
262                 case GSM48_CC_CAUSE_USER_ALERTING_NA:
263                         vty_notify(ms, "Call: Remote not answering\n");
264                         break;
265                 case GSM48_CC_CAUSE_CALL_REJECTED:
266                         vty_notify(ms, "Call has been rejected\n");
267                         break;
268                 case GSM48_CC_CAUSE_NUMBER_CHANGED:
269                         vty_notify(ms, "Call: Number changed\n");
270                         break;
271                 case GSM48_CC_CAUSE_PRE_EMPTION:
272                         vty_notify(ms, "Call: Cleared due to pre-emption\n");
273                         break;
274                 case GSM48_CC_CAUSE_DEST_OOO:
275                         vty_notify(ms, "Call: Remote out of order\n");
276                         break;
277                 case GSM48_CC_CAUSE_INV_NR_FORMAT:
278                         vty_notify(ms, "Call: Number invalid or imcomplete\n");
279                         break;
280                 case GSM48_CC_CAUSE_NO_CIRCUIT_CHAN:
281                         vty_notify(ms, "Call: No channel available\n");
282                         break;
283                 case GSM48_CC_CAUSE_NETWORK_OOO:
284                         vty_notify(ms, "Call: Network out of order\n");
285                         break;
286                 case GSM48_CC_CAUSE_TEMP_FAILURE:
287                         vty_notify(ms, "Call: Temporary failure\n");
288                         break;
289                 case GSM48_CC_CAUSE_SWITCH_CONG:
290                         vty_notify(ms, "Congestion\n");
291                         break;
292                 default:
293                         vty_notify(ms, "Call has been disconnected\n");
294                 }
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");
300                         break;
301                 }
302                 free_call(call);
303                 cause = GSM48_CC_CAUSE_NORM_CALL_CLEAR;
304                 goto release;
305         case MNCC_REL_IND:
306         case MNCC_REL_CNF:
307                 vty_notify(ms, NULL);
308                 if (data->cause.value == GSM48_CC_CAUSE_CALL_REJECTED)
309                         vty_notify(ms, "Call has been rejected\n");
310                 else
311                         vty_notify(ms, "Call has been released\n");
312                 LOGP(DMNCC, LOGL_INFO, "Call has been released (cause %d)\n",
313                         data->cause.value);
314                 free_call(call);
315                 break;
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]);
323                 }
324                 break;
325         case MNCC_ALERT_IND:
326                 vty_notify(ms, NULL);
327                 vty_notify(ms, "Call is aleriting\n");
328                 LOGP(DMNCC, LOGL_INFO, "Call is alerting\n");
329                 break;
330         case MNCC_SETUP_CNF:
331                 vty_notify(ms, NULL);
332                 vty_notify(ms, "Call is answered\n");
333                 LOGP(DMNCC, LOGL_INFO, "Call is answered\n");
334                 break;
335         case MNCC_SETUP_IND:
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;
341                         goto release;
342                 }
343                 /* select first supported speech_ver */
344                 if ((data->fields & MNCC_F_BEARER_CAP)) {
345                         int i;
346
347                         for (i = 0; data->bearer_cap.speech_ver[i] >= 0; i++) {
348
349                                 temp = mncc_get_bearer(sup,
350                                         data->bearer_cap.speech_ver[i]);
351                                 if (temp < 0)
352                                         continue;
353                                 if (temp == 5 || temp == 1) { /* half */
354                                         /* only the first half rate */
355                                         if (speech_ver_half < 0)
356                                                 speech_ver_half = temp;
357                                 } else {
358                                         /* only the first full rate */
359                                         if (speech_ver < 0)
360                                                 speech_ver = temp;
361                                 }
362                         }
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;
370                                 } else
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 "
376                                         "rate is given\n");
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 */
383                         } else {
384                                 LOGP(DMNCC, LOGL_INFO, " only supported full "
385                                         "rate codec is given, using it\n");
386                         }
387                 }
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);
397                 else
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
408                  */
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
412                       || speech_ver != 0)
413                         mncc_set_bearer(ms, speech_ver, &mncc);
414                 mncc_send(ms, MNCC_CALL_CONF_REQ, &mncc);
415                 if (first_call)
416                         LOGP(DMNCC, LOGL_INFO, "Ring!\n");
417                 else {
418                         LOGP(DMNCC, LOGL_INFO, "Knock!\n");
419                         call->hold = 1;
420                 }
421                 call->ring = 1;
422                 memset(&mncc, 0, sizeof(struct gsm_mncc));
423                 mncc.callref = call->callref;
424                 mncc_send(ms, MNCC_ALERT_REQ, &mncc);
425                 break;
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");
430                 break;
431         case MNCC_HOLD_CNF:
432                 vty_notify(ms, NULL);
433                 vty_notify(ms, "Call is on hold\n");
434                 LOGP(DMNCC, LOGL_INFO, "Call is on hold\n");
435                 call->hold = 1;
436                 break;
437         case MNCC_HOLD_REJ:
438                 vty_notify(ms, NULL);
439                 vty_notify(ms, "Call hold was rejected\n");
440                 LOGP(DMNCC, LOGL_INFO, "Call hold was rejected\n");
441                 break;
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");
446                 call->hold = 0;
447                 break;
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");
452                 break;
453         default:
454                 LOGP(DMNCC, LOGL_INFO, "Message 0x%02x unsupported\n",
455                         msg_type);
456                 return -EINVAL;
457         }
458
459         return 0;
460 }
461
462 int mncc_call(struct osmocom_ms *ms, char *number)
463 {
464         struct gsm_call *call;
465         struct gsm_mncc setup;
466
467         llist_for_each_entry(call, &call_list, entry) {
468                 if (!call->hold) {
469                         vty_notify(ms, NULL);
470                         vty_notify(ms, "Please put active call on hold "
471                                 "first!\n");
472                         LOGP(DMNCC, LOGL_INFO, "Cannot make a call, busy!\n");
473                         return -EBUSY;
474                 }
475         }
476
477         call = talloc_zero(l23_ctx, struct gsm_call);
478         if (!call)
479                 return -ENOMEM;
480         call->callref = new_callref++;
481         call->init = 1;
482         llist_add_tail(&call->entry, &call_list);
483
484         memset(&setup, 0, sizeof(struct gsm_mncc));
485         setup.callref = call->callref;
486
487         if (!strncasecmp(number, "emerg", 5)) {
488                 LOGP(DMNCC, LOGL_INFO, "Make emergency call\n");
489                 /* emergency */
490                 setup.emergency = 1;
491         } else {
492                 LOGP(DMNCC, LOGL_INFO, "Make call to %s\n", number);
493                 /* called number */
494                 setup.fields |= MNCC_F_CALLED;
495                 if (number[0] == '+') {
496                         number++;
497                         setup.called.type = 1; /* international */
498                 } else
499                         setup.called.type = 0; /* auto/unknown - prefix must be
500                                                   used */
501                 setup.called.plan = 1; /* ISDN */
502                 strncpy(setup.called.number, number,
503                         sizeof(setup.called.number) - 1);
504                 
505                 /* bearer capability (mandatory) */
506                 mncc_set_bearer(ms, -1, &setup);
507                 if (ms->settings.clir)
508                         setup.clir.sup = 1;
509                 else if (ms->settings.clip)
510                         setup.clir.inv = 1;
511         }
512
513         return mncc_send(ms, MNCC_SETUP_REQ, &setup);
514 }
515
516 int mncc_hangup(struct osmocom_ms *ms)
517 {
518         struct gsm_call *call, *found = NULL;
519         struct gsm_mncc disc;
520
521         llist_for_each_entry(call, &call_list, entry) {
522                 if (!call->hold) {
523                         found = call;
524                         break;
525                 }
526         }
527         if (!found) {
528                 LOGP(DMNCC, LOGL_INFO, "No active call to hangup\n");
529                 vty_notify(ms, NULL);
530                 vty_notify(ms, "No active call\n");
531                 return -EINVAL;
532         }
533
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,
539                 &disc);
540 }
541
542 int mncc_answer(struct osmocom_ms *ms)
543 {
544         struct gsm_call *call, *alerting = NULL;
545         struct gsm_mncc rsp;
546         int active = 0;
547
548         llist_for_each_entry(call, &call_list, entry) {
549                 if (call->ring)
550                         alerting = call;
551                 else if (!call->hold)
552                         active = 1;
553         }
554         if (!alerting) {
555                 LOGP(DMNCC, LOGL_INFO, "No call alerting\n");
556                 vty_notify(ms, NULL);
557                 vty_notify(ms, "No alerting call\n");
558                 return -EBUSY;
559         }
560         if (active) {
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");
564                 return -EBUSY;
565         }
566         alerting->ring = 0;
567         alerting->hold = 0;
568
569         memset(&rsp, 0, sizeof(struct gsm_mncc));
570         rsp.callref = alerting->callref;
571         return mncc_send(ms, MNCC_SETUP_RSP, &rsp);
572 }
573
574 int mncc_hold(struct osmocom_ms *ms)
575 {
576         struct gsm_call *call, *found = NULL;
577         struct gsm_mncc hold;
578
579         llist_for_each_entry(call, &call_list, entry) {
580                 if (!call->hold) {
581                         found = call;
582                         break;
583                 }
584         }
585         if (!found) {
586                 LOGP(DMNCC, LOGL_INFO, "No active call to hold\n");
587                 vty_notify(ms, NULL);
588                 vty_notify(ms, "No active call\n");
589                 return -EINVAL;
590         }
591
592         memset(&hold, 0, sizeof(struct gsm_mncc));
593         hold.callref = found->callref;
594         return mncc_send(ms, MNCC_HOLD_REQ, &hold);
595 }
596
597 int mncc_retrieve(struct osmocom_ms *ms, int number)
598 {
599         struct gsm_call *call;
600         struct gsm_mncc retr;
601         int holdnum = 0, active = 0, i = 0;
602
603         llist_for_each_entry(call, &call_list, entry) {
604                 if (call->hold)
605                         holdnum++;
606                 if (!call->hold)
607                         active = 1;
608         }
609         if (active) {
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");
613                 return -EINVAL;
614         }
615         if (holdnum == 0) {
616                 vty_notify(ms, NULL);
617                 vty_notify(ms, "No call on hold!\n");
618                 return -EINVAL;
619         }
620         if (holdnum > 1 && number <= 0) {
621                 vty_notify(ms, NULL);
622                 vty_notify(ms, "Select call 1..%d\n", holdnum);
623                 return -EINVAL;
624         }
625         if (holdnum == 1 && number <= 0)
626                 number = 1;
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);
631                 return -EINVAL;
632         }
633
634         llist_for_each_entry(call, &call_list, entry) {
635                 i++;
636                 if (i == number)
637                         break;
638         }
639
640         memset(&retr, 0, sizeof(struct gsm_mncc));
641         retr.callref = call->callref;
642         return mncc_send(ms, MNCC_RETRIEVE_REQ, &retr);
643 }
644
645
646
647