[layer23] Fixed call reference for incomming calls. (mobile terminated)
[osmocom-bb.git] / src / host / layer23 / src / mnccms.c
index bf1690b..ec2625d 100644 (file)
 #include <osmocom/logging.h>
 #include <osmocom/osmocom_data.h>
 #include <osmocom/mncc.h>
-#include <osmocom/telnet_interface.h>
+#include <osmocom/vty.h>
 
 void *l23_ctx;
-static int new_callref = 1;
+static uint32_t new_callref = 1;
 static LLIST_HEAD(call_list);
 
 /*
@@ -96,15 +96,15 @@ int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg)
        struct gsm_call *call = get_call_ref(data->callref);
        struct gsm_mncc mncc;
        uint8_t cause;
+       int first_call = 0;
 
        /* call does not exist */
        if (!call && msg_type != MNCC_SETUP_IND) {
                LOGP(DMNCC, LOGL_INFO, "Rejecting incomming call "
-                       "(callref %d)\n", data->callref);
+                       "(callref %x)\n", data->callref);
                if (msg_type == MNCC_REL_IND || msg_type == MNCC_REL_CNF)
                        return 0;
                cause = GSM48_CC_CAUSE_INCOMPAT_DEST;
-
                release:
                memset(&mncc, 0, sizeof(struct gsm_mncc));
                mncc.callref = data->callref;
@@ -114,10 +114,12 @@ int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg)
 
        /* setup without call */
        if (!call) {
+               if (llist_empty(&call_list))
+                       first_call = 1;
                call = talloc_zero(l23_ctx, struct gsm_call);
                if (!call)
                        return -ENOMEM;
-               call->callref = new_callref++;
+               call->callref = data->callref;
                llist_add_tail(&call->entry, &call_list);
        }
 
@@ -211,32 +213,68 @@ int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg)
                break;
        case MNCC_SETUP_IND:
                vty_notify(ms, NULL);
-               if (!llist_empty(&call_list)) {
-                       vty_notify(ms, "Incomming call rejected\n");
+               if (!first_call && !ms->settings.cw) {
+                       vty_notify(ms, "Incomming call rejected while busy\n");
                        LOGP(DMNCC, LOGL_INFO, "Incomming call but busy\n");
                        cause = GSM48_CC_CAUSE_NORM_CALL_CLEAR;
                        goto release;
                }
-               if (!data->calling.present || !data->calling.number[0])
-                       vty_notify(ms, "Incomming call\n");
+               /* presentation allowed if present == 0 */
+               if (data->calling.present || !data->calling.number[0])
+                       vty_notify(ms, "Incomming call (callref %x)\n",
+                               call->callref);
                else if (data->calling.type == 1)
-                       vty_notify(ms, "Incomming call from +%s\n",
-                               data->calling.number);
+                       vty_notify(ms, "Incomming call from +%s (callref %x)\n",
+                               data->calling.number, call->callref);
                else if (data->calling.type == 2)
-                       vty_notify(ms, "Incomming call from 0-%s\n",
-                               data->calling.number);
+                       vty_notify(ms, "Incomming call from 0-%s (callref "
+                               "%x)\n", data->calling.number, call->callref);
                else
-                       vty_notify(ms, "Incomming call from %s\n",
-                               data->calling.number);
-               LOGP(DMNCC, LOGL_INFO, "Incomming call\n");
+                       vty_notify(ms, "Incomming call from %s (callref %x)\n",
+                               data->calling.number, call->callref);
+               LOGP(DMNCC, LOGL_INFO, "Incomming call (callref %x)\n",
+                       call->callref);
                memset(&mncc, 0, sizeof(struct gsm_mncc));
                mncc.callref = call->callref;
                mncc_send(ms, MNCC_CALL_CONF_REQ, &mncc);
-               LOGP(DMNCC, LOGL_INFO, "Ring!\n");
+               if (first_call)
+                       LOGP(DMNCC, LOGL_INFO, "Ring!\n");
+               else {
+                       LOGP(DMNCC, LOGL_INFO, "Knock!\n");
+                       call->hold = 1;
+               }
+               call->ring = 1;
                memset(&mncc, 0, sizeof(struct gsm_mncc));
                mncc.callref = call->callref;
                mncc_send(ms, MNCC_ALERT_REQ, &mncc);
                break;
+       case MNCC_SETUP_COMPL_IND:
+               vty_notify(ms, NULL);
+               vty_notify(ms, "Call is connected\n");
+               LOGP(DMNCC, LOGL_INFO, "Call is connected\n");
+               break;
+       case MNCC_HOLD_CNF:
+               vty_notify(ms, NULL);
+               vty_notify(ms, "Call is on hold\n");
+               LOGP(DMNCC, LOGL_INFO, "Call is on hold\n");
+               call->hold = 1;
+               break;
+       case MNCC_HOLD_REJ:
+               vty_notify(ms, NULL);
+               vty_notify(ms, "Call hold was rejected\n");
+               LOGP(DMNCC, LOGL_INFO, "Call hold was rejected\n");
+               break;
+       case MNCC_RETRIEVE_CNF:
+               vty_notify(ms, NULL);
+               vty_notify(ms, "Call is retrieved\n");
+               LOGP(DMNCC, LOGL_INFO, "Call is retrieved\n");
+               call->hold = 0;
+               break;
+       case MNCC_RETRIEVE_REJ:
+               vty_notify(ms, NULL);
+               vty_notify(ms, "Call retrieve was rejected\n");
+               LOGP(DMNCC, LOGL_INFO, "Call retrieve was rejected\n");
+               break;
        default:
                LOGP(DMNCC, LOGL_INFO, "Message 0x%02x unsupported\n",
                        msg_type);
@@ -251,9 +289,14 @@ int mncc_call(struct osmocom_ms *ms, char *number)
        struct gsm_call *call;
        struct gsm_mncc setup;
 
-       if (!llist_empty(&call_list)) {
-               LOGP(DMNCC, LOGL_INFO, "Cannot make a call, busy!\n");
-               return -EBUSY;
+       llist_for_each_entry(call, &call_list, entry) {
+               if (!call->hold) {
+                       vty_notify(ms, NULL);
+                       vty_notify(ms, "Please put active call on hold "
+                               "first!\n");
+                       LOGP(DMNCC, LOGL_INFO, "Cannot make a call, busy!\n");
+                       return -EBUSY;
+               }
        }
 
        call = talloc_zero(l23_ctx, struct gsm_call);
@@ -285,6 +328,10 @@ int mncc_call(struct osmocom_ms *ms, char *number)
                setup.bearer_cap.speech_ver[1] = -1; /* end of list */
                setup.bearer_cap.transfer = 0;
                setup.bearer_cap.mode = 0;
+               if (ms->settings.clir)
+                       setup.clir.sup = 1;
+               else if (ms->settings.clip)
+                       setup.clir.inv = 1;
        }
 
        return mncc_send(ms, MNCC_SETUP_REQ, &setup);
@@ -292,17 +339,24 @@ int mncc_call(struct osmocom_ms *ms, char *number)
 
 int mncc_hangup(struct osmocom_ms *ms)
 {
-       struct gsm_call *call;
+       struct gsm_call *call, *found = NULL;
        struct gsm_mncc disc;
 
-       if (llist_empty(&call_list)) {
+       llist_for_each_entry(call, &call_list, entry) {
+               if (!call->hold) {
+                       found = call;
+                       break;
+               }
+       }
+       if (!found) {
                LOGP(DMNCC, LOGL_INFO, "No active call to hangup\n");
-               return -EBUSY;
+               vty_notify(ms, NULL);
+               vty_notify(ms, "No active call\n");
+               return -EINVAL;
        }
-       call = llist_entry(call_list.next, struct gsm_call, entry);
 
        memset(&disc, 0, sizeof(struct gsm_mncc));
-       disc.callref = call->callref;
+       disc.callref = found->callref;
        mncc_set_cause(&disc, GSM48_CAUSE_LOC_USER,
                GSM48_CC_CAUSE_NORM_CALL_CLEAR);
        return mncc_send(ms, MNCC_DISC_REQ, &disc);
@@ -310,22 +364,107 @@ int mncc_hangup(struct osmocom_ms *ms)
 
 int mncc_answer(struct osmocom_ms *ms)
 {
-       struct gsm_call *call;
+       struct gsm_call *call, *alerting = NULL;
        struct gsm_mncc rsp;
+       int active = 0;
 
-       if (llist_empty(&call_list)) {
-               LOGP(DMNCC, LOGL_INFO, "No call to answer\n");
+       llist_for_each_entry(call, &call_list, entry) {
+               if (call->ring)
+                       alerting = call;
+               else if (!call->hold)
+                       active = 1;
+       }
+       if (!alerting) {
+               LOGP(DMNCC, LOGL_INFO, "No call alerting\n");
+               vty_notify(ms, NULL);
+               vty_notify(ms, "No alerting call\n");
+               return -EBUSY;
+       }
+       if (active) {
+               LOGP(DMNCC, LOGL_INFO, "Answer but we have an active call\n");
+               vty_notify(ms, NULL);
+               vty_notify(ms, "Please put active call on hold first!\n");
                return -EBUSY;
        }
-       call = llist_entry(call_list.next, struct gsm_call, entry);
+       alerting->ring = 0;
+       alerting->hold = 0;
 
        memset(&rsp, 0, sizeof(struct gsm_mncc));
-       rsp.callref = call->callref;
-       mncc_set_cause(&rsp, GSM48_CAUSE_LOC_USER,
-               GSM48_CC_CAUSE_NORM_CALL_CLEAR);
+       rsp.callref = alerting->callref;
        return mncc_send(ms, MNCC_SETUP_RSP, &rsp);
 }
 
+int mncc_hold(struct osmocom_ms *ms)
+{
+       struct gsm_call *call, *found = NULL;
+       struct gsm_mncc hold;
+
+       llist_for_each_entry(call, &call_list, entry) {
+               if (!call->hold) {
+                       found = call;
+                       break;
+               }
+       }
+       if (!found) {
+               LOGP(DMNCC, LOGL_INFO, "No active call to hold\n");
+               vty_notify(ms, NULL);
+               vty_notify(ms, "No active call\n");
+               return -EINVAL;
+       }
+
+       memset(&hold, 0, sizeof(struct gsm_mncc));
+       hold.callref = found->callref;
+       return mncc_send(ms, MNCC_HOLD_REQ, &hold);
+}
+
+int mncc_retrieve(struct osmocom_ms *ms, int number)
+{
+       struct gsm_call *call;
+       struct gsm_mncc retr;
+       int holdnum = 0, active = 0, i = 0;
+
+       llist_for_each_entry(call, &call_list, entry) {
+               if (call->hold)
+                       holdnum++;
+               if (!call->hold)
+                       active = 1;
+       }
+       if (active) {
+               LOGP(DMNCC, LOGL_INFO, "Cannot retrieve during active call\n");
+               vty_notify(ms, NULL);
+               vty_notify(ms, "Hold active call first!\n");
+               return -EINVAL;
+       }
+       if (holdnum == 0) {
+               vty_notify(ms, NULL);
+               vty_notify(ms, "No call on hold!\n");
+               return -EINVAL;
+       }
+       if (holdnum > 1 && number <= 0) {
+               vty_notify(ms, NULL);
+               vty_notify(ms, "Select call 1..%d\n", holdnum);
+               return -EINVAL;
+       }
+       if (holdnum == 1 && number <= 0)
+               number = 1;
+       if (number > holdnum) {
+               vty_notify(ms, NULL);
+               vty_notify(ms, "Given number %d out of range!\n", number);
+               vty_notify(ms, "Select call 1..%d\n", holdnum);
+               return -EINVAL;
+       }
+
+       llist_for_each_entry(call, &call_list, entry) {
+               i++;
+               if (i == number)
+                       break;
+       }
+
+       memset(&retr, 0, sizeof(struct gsm_mncc));
+       retr.callref = call->callref;
+       return mncc_send(ms, MNCC_RETRIEVE_REQ, &retr);
+}
+