summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas.Eversberg <jolly@eversberg.eu>2010-10-15 18:54:57 +0000
committerAndreas.Eversberg <jolly@eversberg.eu>2010-10-15 18:54:57 +0000
commitadf65e29fd0723cc2550be9c37668cfaf27858f9 (patch)
tree89891ad47443a9b9d3cf191817da98569e17cca7
parent19807191b6b7407900971b3af533611661ff623d (diff)
[layer23] DTMF support
-rw-r--r--src/host/layer23/include/osmocom/bb/mobile/mncc.h23
-rw-r--r--src/host/layer23/include/osmocom/bb/mobile/settings.h1
-rw-r--r--src/host/layer23/src/mobile/mnccms.c122
-rw-r--r--src/host/layer23/src/mobile/settings.c3
-rw-r--r--src/host/layer23/src/mobile/vty_interface.c51
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 37e1affc..1f8e9099 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 <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
diff --git a/src/host/layer23/include/osmocom/bb/mobile/settings.h b/src/host/layer23/include/osmocom/bb/mobile/settings.h
index 88649e36..a6708e6d 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 84081e82..4e44bf97 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 1f08cc2d..6294ec65 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 35cab315..57046ac3 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);