[layer23] DTMF support
authorAndreas.Eversberg <jolly@eversberg.eu>
Fri, 15 Oct 2010 18:54:57 +0000 (18:54 +0000)
committerAndreas.Eversberg <jolly@eversberg.eu>
Fri, 15 Oct 2010 18:54:57 +0000 (18:54 +0000)
src/host/layer23/include/osmocom/bb/mobile/mncc.h
src/host/layer23/include/osmocom/bb/mobile/settings.h
src/host/layer23/src/mobile/mnccms.c
src/host/layer23/src/mobile/settings.c
src/host/layer23/src/mobile/vty_interface.c

index 37e1aff..1f8e909 100644 (file)
 #include <osmocore/mncc.h>
 
 struct gsm_call {
-       struct llist_head entry;
+       struct llist_head       entry;
 
-       void *ms;
+       struct osmocom_ms       *ms;
 
-       uint32_t callref;
+       uint32_t                callref;
 
-       uint8_t init; /* call has been initiated, no response yet */
-       uint8_t hold; /* call on hold */
-       uint8_t ring; /* call is ringing/knocking */
+       uint8_t                 init; /* call initiated, no response yet */
+       uint8_t                 hold; /* call on hold */
+       uint8_t                 ring; /* call ringing/knocking */
+
+       struct timer_list       dtmf_timer;
+       uint8_t                 dtmf_state;
+       uint8_t                 dtmf_index;
+       char                    dtmf[32]; /* dtmf sequence */
 };
 
+#define DTMF_ST_IDLE           0       /* no DTMF active */
+#define DTMF_ST_START          1       /* DTMF started, waiting for resp. */
+#define DTMF_ST_MARK           2       /* wait tone duration */
+#define DTMF_ST_STOP           3       /* DTMF stopped, waiting for resp. */
+#define DTMF_ST_SPACE          4       /* wait space between tones */
+
 #define MNCC_SETUP_REQ         0x0101
 #define MNCC_SETUP_IND         0x0102
 #define MNCC_SETUP_RSP         0x0103
index 88649e3..a6708e6 100644 (file)
@@ -43,6 +43,7 @@ struct gsm_settings {
        uint8_t                 no_lupd;
 
        /* supported by configuration */
+       uint8_t                 cc_dtmf;
        uint8_t                 sms_ptp;
        uint8_t                 a5_1;
        uint8_t                 a5_2;
index 84081e8..4e44bf9 100644 (file)
@@ -36,14 +36,36 @@ void *l23_ctx;
 static uint32_t new_callref = 1;
 static LLIST_HEAD(call_list);
 
+void mncc_set_cause(struct gsm_mncc *data, int loc, int val);
+static int dtmf_statemachine(struct gsm_call *call, struct gsm_mncc *mncc);
+static void timeout_dtmf(void *arg);
+
 /*
  * support functions
  */
 
-void mncc_set_cause(struct gsm_mncc *data, int loc, int val);
+/* DTMF timer */
+static void start_dtmf_timer(struct gsm_call *call, uint16_t ms)
+{
+       LOGP(DCC, LOGL_INFO, "starting DTMF timer %d ms\n", ms);
+       call->dtmf_timer.cb = timeout_dtmf;
+       call->dtmf_timer.data = call;
+       bsc_schedule_timer(&call->dtmf_timer, 0, ms * 1000);
+}
+
+static void stop_dtmf_timer(struct gsm_call *call)
+{
+       if (bsc_timer_pending(&call->dtmf_timer)) {
+               LOGP(DCC, LOGL_INFO, "stopping pending DTMF timer\n");
+               bsc_del_timer(&call->dtmf_timer);
+       }
+}
 
+/* free call instance */
 static void free_call(struct gsm_call *call)
 {
+       stop_dtmf_timer(call);
+
        llist_del(&call->entry);
        DEBUGP(DMNCC, "(call %x) Call removed.\n", call->callref);
        talloc_free(call);
@@ -231,6 +253,7 @@ int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg)
                call = talloc_zero(l23_ctx, struct gsm_call);
                if (!call)
                        return -ENOMEM;
+               call->ms = ms;
                call->callref = data->callref;
                llist_add_tail(&call->entry, &call_list);
        }
@@ -288,7 +311,8 @@ int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg)
                        vty_notify(ms, "Congestion\n");
                        break;
                default:
-                       vty_notify(ms, "Call has been disconnected\n");
+                       vty_notify(ms, "Call has been disconnected "
+                               "(clear cause %d)\n", data->cause.value);
                }
                LOGP(DMNCC, LOGL_INFO, "Call has been disconnected "
                        "(cause %d)\n", data->cause.value);
@@ -452,6 +476,11 @@ int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg)
                LOGP(DMNCC, LOGL_INFO, "Facility info not displayed, "
                        "unsupported\n");
                break;
+       case MNCC_START_DTMF_RSP:
+       case MNCC_START_DTMF_REJ:
+       case MNCC_STOP_DTMF_RSP:
+               dtmf_statemachine(call, data);
+               break;
        default:
                LOGP(DMNCC, LOGL_INFO, "Message 0x%02x unsupported\n",
                        msg_type);
@@ -479,6 +508,7 @@ int mncc_call(struct osmocom_ms *ms, char *number)
        call = talloc_zero(l23_ctx, struct gsm_call);
        if (!call)
                return -ENOMEM;
+       call->ms = ms;
        call->callref = new_callref++;
        call->init = 1;
        llist_add_tail(&call->entry, &call_list);
@@ -510,6 +540,12 @@ int mncc_call(struct osmocom_ms *ms, char *number)
                        setup.clir.sup = 1;
                else if (ms->settings.clip)
                        setup.clir.inv = 1;
+
+               /* CC capabilities (optional) */
+               if (ms->settings.cc_dtmf) {
+                       setup.fields |= MNCC_F_CCCAP;
+                       setup.cccap.dtmf = 1;
+               }
        }
 
        return mncc_send(ms, MNCC_SETUP_REQ, &setup);
@@ -644,6 +680,88 @@ int mncc_retrieve(struct osmocom_ms *ms, int number)
        return mncc_send(ms, MNCC_RETRIEVE_REQ, &retr);
 }
 
+/*
+ * DTMF
+ */
+
+static int dtmf_statemachine(struct gsm_call *call, struct gsm_mncc *mncc)
+{
+       struct osmocom_ms *ms = call->ms;
+       struct gsm_mncc dtmf;
+
+       switch (call->dtmf_state) {
+       case DTMF_ST_SPACE:
+       case DTMF_ST_IDLE:
+               /* end of string */
+               if (!call->dtmf[call->dtmf_index]) {
+                       LOGP(DMNCC, LOGL_INFO, "done with DTMF\n");
+                       call->dtmf_state = DTMF_ST_IDLE;
+                       return -EOF;
+               }
+               memset(&dtmf, 0, sizeof(struct gsm_mncc));
+               dtmf.callref = call->callref;
+               dtmf.keypad = call->dtmf[call->dtmf_index++];
+               call->dtmf_state = DTMF_ST_START;
+               LOGP(DMNCC, LOGL_INFO, "start DTMF (keypad %c)\n",
+                       dtmf.keypad);
+               return mncc_send(ms, MNCC_START_DTMF_REQ, &dtmf);
+       case DTMF_ST_START:
+               if (mncc->msg_type != MNCC_START_DTMF_RSP) {
+                       LOGP(DMNCC, LOGL_INFO, "DTMF was rejected\n");
+                       return -ENOTSUP;
+               }
+               start_dtmf_timer(call, 70);
+               call->dtmf_state = DTMF_ST_MARK;
+               LOGP(DMNCC, LOGL_INFO, "DTMF is on\n");
+               break;
+       case DTMF_ST_MARK:
+               memset(&dtmf, 0, sizeof(struct gsm_mncc));
+               dtmf.callref = call->callref;
+               call->dtmf_state = DTMF_ST_STOP;
+               LOGP(DMNCC, LOGL_INFO, "stop DTMF\n");
+               return mncc_send(ms, MNCC_STOP_DTMF_REQ, &dtmf);
+       case DTMF_ST_STOP:
+               start_dtmf_timer(call, 120);
+               call->dtmf_state = DTMF_ST_SPACE;
+               LOGP(DMNCC, LOGL_INFO, "DTMF is off\n");
+               break;
+       }
 
+       return 0;
+}
 
+static void timeout_dtmf(void *arg)
+{
+       struct gsm_call *call = arg;
+
+       LOGP(DCC, LOGL_INFO, "DTMF timer has fired\n");
+       dtmf_statemachine(call, NULL);
+}
+
+int mncc_dtmf(struct osmocom_ms *ms, char *dtmf)
+{
+       struct gsm_call *call, *found = NULL;
+
+       llist_for_each_entry(call, &call_list, entry) {
+               if (!call->hold) {
+                       found = call;
+                       break;
+               }
+       }
+       if (!found) {
+               LOGP(DMNCC, LOGL_INFO, "No active call to send DTMF\n");
+               vty_notify(ms, NULL);
+               vty_notify(ms, "No active call\n");
+               return -EINVAL;
+       }
+
+       if (call->dtmf_state != DTMF_ST_IDLE) {
+               LOGP(DMNCC, LOGL_INFO, "sending DTMF already\n");
+               return -EINVAL;
+       }
+
+       call->dtmf_index = 0;
+       strncpy(call->dtmf, dtmf, sizeof(call->dtmf) - 1);
+       return dtmf_statemachine(call, NULL);
+}
 
index 1f08cc2..6294ec6 100644 (file)
@@ -68,6 +68,9 @@ int gsm_settings_init(struct osmocom_ms *ms)
        if (sup->half_v1 || sup->half_v3)
                set->half = 1;
 
+       /* software features */
+       set->cc_dtmf = 1;
+
        INIT_LLIST_HEAD(&set->abbrev);
 
        return 0;
index 35cab31..57046ac 100644 (file)
@@ -44,6 +44,7 @@ int mncc_hangup(struct osmocom_ms *ms);
 int mncc_answer(struct osmocom_ms *ms);
 int mncc_hold(struct osmocom_ms *ms);
 int mncc_retrieve(struct osmocom_ms *ms, int number);
+int mncc_dtmf(struct osmocom_ms *ms, char *dtmf);
 
 extern struct llist_head ms_list;
 extern struct llist_head active_connections;
@@ -716,6 +717,29 @@ DEFUN(call_retr, call_retr_cmd, "call MS_NAME retrieve [number]",
        return CMD_SUCCESS;
 }
 
+DEFUN(call_dtmf, call_dtmf_cmd, "call MS_NAME dtmf DIGITS",
+       "Make a call\nName of MS (see \"show ms\")\n"
+       "One or more DTMF digits to transmit")
+{
+       struct osmocom_ms *ms;
+       struct gsm_settings *set;
+
+       ms = get_ms(argv[0], vty);
+       if (!ms)
+               return CMD_WARNING;
+       set = &ms->settings;
+
+       if (!set->cc_dtmf) {
+               vty_out(vty, "DTMF not supported, please enable!%s",
+                       VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+
+       mncc_dtmf(ms, (char *)argv[1]);
+
+       return CMD_SUCCESS;
+}
+
 DEFUN(network_show, network_show_cmd, "network show MS_NAME",
        "Network ...\nShow results of network search (again)\n"
        "Name of MS (see \"show ms\")")
@@ -1514,6 +1538,30 @@ DEFUN(cfg, cfg_cmd, "no " cmd, NO_STR "Disable " desc " support") \
        return CMD_SUCCESS; \
 }
 
+#define SET_EN(cfg, cfg_cmd, item, cmd, desc, restart) \
+DEFUN(cfg, cfg_cmd, cmd, "Enable " desc "support") \
+{ \
+       struct osmocom_ms *ms = vty->index; \
+       struct gsm_settings *set = &ms->settings; \
+       if (restart) \
+               vty_restart(vty); \
+       set->item = 1; \
+       return CMD_SUCCESS; \
+}
+
+#define SET_DI(cfg, cfg_cmd, item, cmd, desc, restart) \
+DEFUN(cfg, cfg_cmd, "no " cmd, NO_STR "Disable " desc " support") \
+{ \
+       struct osmocom_ms *ms = vty->index; \
+       struct gsm_settings *set = &ms->settings; \
+       if (restart) \
+               vty_restart(vty); \
+       set->item = 0; \
+       return CMD_SUCCESS; \
+}
+
+SET_EN(cfg_ms_sup_dtmf, cfg_ms_sup_dtmf_cmd, cc_dtmf, "dtmf", "DTMF", 0);
+SET_DI(cfg_ms_sup_no_dtmf, cfg_ms_sup_no_dtmf_cmd, cc_dtmf, "dtmf", "DTMF", 0);
 SUP_EN(cfg_ms_sup_sms, cfg_ms_sup_sms_cmd, sms_ptp, "sms", "SMS", 0);
 SUP_DI(cfg_ms_sup_no_sms, cfg_ms_sup_no_sms_cmd, sms_ptp, "sms", "SMS", 0);
 SUP_EN(cfg_ms_sup_a5_1, cfg_ms_sup_a5_1_cmd, a5_1, "a5/1", "A5/1", 0);
@@ -1921,6 +1969,7 @@ int ms_vty_init(void)
        install_element(ENABLE_NODE, &network_select_cmd);
        install_element(ENABLE_NODE, &call_cmd);
        install_element(ENABLE_NODE, &call_retr_cmd);
+       install_element(ENABLE_NODE, &call_dtmf_cmd);
 
        install_element(CONFIG_NODE, &cfg_gps_device_cmd);
        install_element(CONFIG_NODE, &cfg_gps_baud_cmd);
@@ -1967,6 +2016,8 @@ int ms_vty_init(void)
        install_default(SUPPORT_NODE);
        install_element(SUPPORT_NODE, &ournode_exit_cmd);
        install_element(SUPPORT_NODE, &ournode_end_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_dtmf_cmd);
+       install_element(SUPPORT_NODE, &cfg_ms_sup_no_dtmf_cmd);
        install_element(SUPPORT_NODE, &cfg_ms_sup_sms_cmd);
        install_element(SUPPORT_NODE, &cfg_ms_sup_no_sms_cmd);
        install_element(SUPPORT_NODE, &cfg_ms_sup_a5_1_cmd);