From adf65e29fd0723cc2550be9c37668cfaf27858f9 Mon Sep 17 00:00:00 2001 From: "Andreas.Eversberg" Date: Fri, 15 Oct 2010 18:54:57 +0000 Subject: [PATCH 1/1] [layer23] DTMF support --- .../layer23/include/osmocom/bb/mobile/mncc.h | 23 +++- .../include/osmocom/bb/mobile/settings.h | 1 + src/host/layer23/src/mobile/mnccms.c | 122 +++++++++++++++++- src/host/layer23/src/mobile/settings.c | 3 + src/host/layer23/src/mobile/vty_interface.c | 51 ++++++++ 5 files changed, 192 insertions(+), 8 deletions(-) diff --git a/src/host/layer23/include/osmocom/bb/mobile/mncc.h b/src/host/layer23/include/osmocom/bb/mobile/mncc.h index 37e1aff..1f8e909 100644 --- a/src/host/layer23/include/osmocom/bb/mobile/mncc.h +++ b/src/host/layer23/include/osmocom/bb/mobile/mncc.h @@ -30,17 +30,28 @@ #include 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 diff --git a/src/host/layer23/include/osmocom/bb/mobile/settings.h b/src/host/layer23/include/osmocom/bb/mobile/settings.h index 88649e3..a6708e6 100644 --- a/src/host/layer23/include/osmocom/bb/mobile/settings.h +++ b/src/host/layer23/include/osmocom/bb/mobile/settings.h @@ -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; diff --git a/src/host/layer23/src/mobile/mnccms.c b/src/host/layer23/src/mobile/mnccms.c index 84081e8..4e44bf9 100644 --- a/src/host/layer23/src/mobile/mnccms.c +++ b/src/host/layer23/src/mobile/mnccms.c @@ -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); +} diff --git a/src/host/layer23/src/mobile/settings.c b/src/host/layer23/src/mobile/settings.c index 1f08cc2..6294ec6 100644 --- a/src/host/layer23/src/mobile/settings.c +++ b/src/host/layer23/src/mobile/settings.c @@ -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; diff --git a/src/host/layer23/src/mobile/vty_interface.c b/src/host/layer23/src/mobile/vty_interface.c index 35cab31..57046ac 100644 --- a/src/host/layer23/src/mobile/vty_interface.c +++ b/src/host/layer23/src/mobile/vty_interface.c @@ -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); -- 2.20.1