From a5a5cd1eff13e8079c6f0058d5e5d94e8dd99ba5 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Thu, 1 Dec 2011 11:50:33 +0100 Subject: layer23/mobile Fixes and improvements of built in call control (mnccms) The list of calls are now per MS instance, as they should be. Added init and exit function. Pending call instances are removed on exit. Added call state to call instances for easier state handling. Call functions now have optional indexes to handle explicitly given calls. gsm_call structure and function prototypes are now moved to a new header file (mnccms.h). --- .../include/osmocom/bb/common/osmocom_data.h | 1 + .../layer23/include/osmocom/bb/mobile/Makefile.am | 2 +- src/host/layer23/include/osmocom/bb/mobile/mncc.h | 30 -- .../layer23/include/osmocom/bb/mobile/mnccms.h | 59 ++++ src/host/layer23/src/mobile/app_mobile.c | 5 +- src/host/layer23/src/mobile/main.c | 3 +- src/host/layer23/src/mobile/mnccms.c | 365 +++++++++++++++------ src/host/layer23/src/mobile/vty_interface.c | 80 +++-- 8 files changed, 393 insertions(+), 152 deletions(-) create mode 100644 src/host/layer23/include/osmocom/bb/mobile/mnccms.h diff --git a/src/host/layer23/include/osmocom/bb/common/osmocom_data.h b/src/host/layer23/include/osmocom/bb/common/osmocom_data.h index 7e8faa88..d42cf09b 100644 --- a/src/host/layer23/include/osmocom/bb/common/osmocom_data.h +++ b/src/host/layer23/include/osmocom/bb/common/osmocom_data.h @@ -36,6 +36,7 @@ struct osmomncc_entity { int (*mncc_recv)(struct osmocom_ms *ms, int msg_type, void *arg); struct mncc_sock_state *sock_state; uint32_t ref; + struct llist_head call_list; }; diff --git a/src/host/layer23/include/osmocom/bb/mobile/Makefile.am b/src/host/layer23/include/osmocom/bb/mobile/Makefile.am index b58b9529..6adb6991 100644 --- a/src/host/layer23/include/osmocom/bb/mobile/Makefile.am +++ b/src/host/layer23/include/osmocom/bb/mobile/Makefile.am @@ -1,3 +1,3 @@ noinst_HEADERS = gsm322.h gsm480_ss.h gsm411_sms.h gsm48_cc.h gsm48_mm.h \ gsm48_rr.h mncc.h settings.h subscriber.h support.h \ - transaction.h vty.h mncc_sock.h + transaction.h vty.h mncc_sock.h mnccms.h diff --git a/src/host/layer23/include/osmocom/bb/mobile/mncc.h b/src/host/layer23/include/osmocom/bb/mobile/mncc.h index a73a882c..5431d696 100644 --- a/src/host/layer23/include/osmocom/bb/mobile/mncc.h +++ b/src/host/layer23/include/osmocom/bb/mobile/mncc.h @@ -26,38 +26,8 @@ #ifndef _MNCC_H #define _MNCC_H -#include #include -struct gsm_call { - struct llist_head entry; - - struct osmocom_ms *ms; - - uint32_t callref; - - uint8_t init; /* call initiated, no response yet */ - uint8_t hold; /* call on hold */ - uint8_t ring; /* call ringing/knocking */ - - struct osmo_timer_list dtmf_timer; - uint8_t dtmf_state; - uint8_t dtmf_index; - char dtmf[32]; /* dtmf sequence */ - - struct osmo_timer_list ringer_timer; - uint8_t ringer_state; -}; - -#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 RINGER_MARK 0, 500000 -#define RINGER_SPACE 0, 250000 - #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/mnccms.h b/src/host/layer23/include/osmocom/bb/mobile/mnccms.h new file mode 100644 index 00000000..929bc8c1 --- /dev/null +++ b/src/host/layer23/include/osmocom/bb/mobile/mnccms.h @@ -0,0 +1,59 @@ +#ifndef _MNCCMS_H +#define _MNCCMS_H + +#include + +#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 RINGER_MARK 0, 500000 +#define RINGER_SPACE 0, 250000 + +#define CALL_ST_IDLE 0 /* no state */ +#define CALL_ST_MO_INIT 1 /* call initiated, no response yet */ +#define CALL_ST_MO_PROC 2 /* call proceeding */ +#define CALL_ST_MO_ALERT 3 /* call alerting */ +#define CALL_ST_MT_RING 4 /* call ringing */ +#define CALL_ST_MT_KNOCK 5 /* call knocking */ +#define CALL_ST_ACTIVE 6 /* call connected and active */ +#define CALL_ST_HOLD 7 /* call connected, but on hold */ +#define CALL_ST_DISC_RX 8 /* call disconnected (disc received) */ +#define CALL_ST_DISC_TX 9 /* call disconnected (disc sent) */ + +struct gsm_call { + struct llist_head entry; + + struct osmocom_ms *ms; + + uint32_t callref; + + uint8_t call_state; + + char number[33]; /* remote number */ + + struct osmo_timer_list dtmf_timer; + uint8_t dtmf_state; + uint8_t dtmf_index; + char dtmf[32]; /* dtmf sequence */ + + struct osmo_timer_list ringer_timer; + uint8_t ringer_state; +}; + +int mncc_recv_dummy(struct osmocom_ms *ms, int msg_type, void *arg); +int mncc_recv_socket(struct osmocom_ms *ms, int msg_type, void *arg); +int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg); +int mnccms_init(struct osmocom_ms *ms); +void mnccms_exit(struct osmocom_ms *ms); +int mncc_call(struct osmocom_ms *ms, char *number); +int mncc_hangup(struct osmocom_ms *ms, int index); +int mncc_answer(struct osmocom_ms *ms, int index); +int mncc_hold(struct osmocom_ms *ms, int index); +int mncc_retrieve(struct osmocom_ms *ms, int index); +int mncc_dtmf(struct osmocom_ms *ms, int index, char *dtmf); +int mncc_list(struct osmocom_ms *ms); + +#endif /* _MNCCMS_H */ diff --git a/src/host/layer23/src/mobile/app_mobile.c b/src/host/layer23/src/mobile/app_mobile.c index e0767416..59284e9d 100644 --- a/src/host/layer23/src/mobile/app_mobile.c +++ b/src/host/layer23/src/mobile/app_mobile.c @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -51,8 +52,6 @@ extern void *l23_ctx; extern struct llist_head ms_list; extern int vty_reading; -int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg); -int mncc_recv_dummy(struct osmocom_ms *ms, int msg_type, void *arg); int (*mncc_recv_app)(struct osmocom_ms *ms, int, void *); static int quit; @@ -160,6 +159,7 @@ int mobile_exit(struct osmocom_ms *ms, int force) gsm48_mm_exit(ms); gsm48_rr_exit(ms); gsm_subscr_exit(ms); + mnccms_exit(ms); gsm48_cc_exit(ms); gsm480_ss_exit(ms); gsm411_sms_exit(ms); @@ -199,6 +199,7 @@ int mobile_init(struct osmocom_ms *ms) osmosap_init(ms); gsm_sim_init(ms); + mnccms_init(ms); gsm48_cc_init(ms); gsm480_ss_init(ms); gsm411_sms_init(ms); diff --git a/src/host/layer23/src/mobile/main.c b/src/host/layer23/src/mobile/main.c index a6dd082a..0eadda27 100644 --- a/src/host/layer23/src/mobile/main.c +++ b/src/host/layer23/src/mobile/main.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -59,8 +60,6 @@ char *config_dir = NULL; int use_mncc_sock = 0; int daemonize = 0; -int mncc_recv_socket(struct osmocom_ms *ms, int msg_type, void *arg); - int mobile_delete(struct osmocom_ms *ms, int force); int mobile_signal_cb(unsigned int subsys, unsigned int signal, void *handler_data, void *signal_data); diff --git a/src/host/layer23/src/mobile/mnccms.c b/src/host/layer23/src/mobile/mnccms.c index 4fc08853..c61ddc03 100644 --- a/src/host/layer23/src/mobile/mnccms.c +++ b/src/host/layer23/src/mobile/mnccms.c @@ -30,17 +30,16 @@ #include #include #include +#include #include 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); static void timeout_ringer(void *arg); -int mncc_answer(struct osmocom_ms *ms); /* * support functions @@ -68,7 +67,8 @@ static void update_ringer(struct gsm_call *call) { struct osmocom_ms *ms = call->ms; - if (call->ring) { + if (call->call_state == CALL_ST_MT_RING + || call->call_state == CALL_ST_MT_KNOCK) { struct gsm_settings *set = &ms->settings; /* ringer on */ @@ -119,7 +119,7 @@ static void free_call(struct gsm_call *call) { stop_dtmf_timer(call); - call->ring = 0; + call->call_state = CALL_ST_IDLE; update_ringer(call); llist_del(&call->entry); @@ -127,12 +127,11 @@ static void free_call(struct gsm_call *call) talloc_free(call); } - -struct gsm_call *get_call_ref(uint32_t callref) +static struct gsm_call *get_call_ref(struct osmocom_ms *ms, uint32_t callref) { struct gsm_call *callt; - llist_for_each_entry(callt, &call_list, entry) { + llist_for_each_entry(callt, &ms->mncc_entity.call_list, entry) { if (callt->callref == callref) return callt; } @@ -318,7 +317,7 @@ int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg) { struct gsm_settings *set = &ms->settings; struct gsm_mncc *data = arg; - struct gsm_call *call = get_call_ref(data->callref); + struct gsm_call *call = get_call_ref(ms, data->callref); struct gsm_mncc mncc; uint8_t cause; int8_t speech_ver = -1, speech_ver_half = -1, temp; @@ -340,19 +339,16 @@ int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg) /* setup without call */ if (!call) { - if (llist_empty(&call_list)) + if (llist_empty(&ms->mncc_entity.call_list)) first_call = 1; 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); + llist_add_tail(&call->entry, &ms->mncc_entity.call_list); } - /* not in initiated state anymore */ - call->init = 0; - switch (msg_type) { case MNCC_DISC_IND: vty_notify(ms, NULL); @@ -411,6 +407,7 @@ int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg) if ((data->fields & MNCC_F_PROGRESS) && data->progress.descr == 8) { vty_notify(ms, "Please hang up!\n"); + call->call_state = CALL_ST_DISC_RX; break; } free_call(call); @@ -430,6 +427,7 @@ int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg) case MNCC_CALL_PROC_IND: vty_notify(ms, NULL); vty_notify(ms, "Call is proceeding\n"); + call->call_state = CALL_ST_MO_PROC; LOGP(DMNCC, LOGL_INFO, "Call is proceeding\n"); if ((data->fields & MNCC_F_BEARER_CAP) && data->bearer_cap.speech_ver[0] >= 0) { @@ -439,11 +437,13 @@ int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg) case MNCC_ALERT_IND: vty_notify(ms, NULL); vty_notify(ms, "Call is alerting\n"); + call->call_state = CALL_ST_MO_ALERT; LOGP(DMNCC, LOGL_INFO, "Call is alerting\n"); break; case MNCC_SETUP_CNF: vty_notify(ms, NULL); vty_notify(ms, "Call is answered\n"); + call->call_state = CALL_ST_ACTIVE; LOGP(DMNCC, LOGL_INFO, "Call is answered\n"); break; case MNCC_SETUP_IND: @@ -502,17 +502,28 @@ int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg) /* presentation allowed if present == 0 */ if (data->calling.present || !data->calling.number[0]) vty_notify(ms, "Incoming call (anonymous)\n"); - else if (data->calling.type == 1) + else if (data->calling.type == 1) { vty_notify(ms, "Incoming call (from +%s)\n", data->calling.number); - else if (data->calling.type == 2) + call->number[0] = '+'; + strncpy(call->number + 1, data->calling.number, + sizeof(call->number) - 2); + } else if (data->calling.type == 2) { vty_notify(ms, "Incoming call (from 0-%s)\n", data->calling.number); - else + call->number[0] = '0'; + call->number[1] = '-'; + strncpy(call->number + 2, data->calling.number, + sizeof(call->number) - 3); + } else { vty_notify(ms, "Incoming call (from %s)\n", data->calling.number); + strncpy(call->number, data->calling.number, + sizeof(call->number) - 1); + } + call->number[sizeof(call->number) - 1] = '\0'; LOGP(DMNCC, LOGL_INFO, "Incoming call (from %s callref %x)\n", - data->calling.number, call->callref); + call->number, call->callref); memset(&mncc, 0, sizeof(struct gsm_mncc)); mncc.callref = call->callref; /* only include bearer cap, if not given in setup @@ -531,20 +542,20 @@ int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg) mncc.cccap.dtmf = 1; } mncc_tx_to_cc(ms, MNCC_CALL_CONF_REQ, &mncc); - if (first_call) + if (first_call) { LOGP(DMNCC, LOGL_INFO, "Ring!\n"); - else { + call->call_state = CALL_ST_MT_RING; + } else { LOGP(DMNCC, LOGL_INFO, "Knock!\n"); - call->hold = 1; + call->call_state = CALL_ST_MT_KNOCK; } - call->ring = 1; update_ringer(call); memset(&mncc, 0, sizeof(struct gsm_mncc)); mncc.callref = call->callref; mncc_tx_to_cc(ms, MNCC_ALERT_REQ, &mncc); if (ms->settings.auto_answer) { LOGP(DMNCC, LOGL_INFO, "Auto-answering call\n"); - mncc_answer(ms); + mncc_answer(ms, 0); } break; case MNCC_SETUP_COMPL_IND: @@ -556,28 +567,34 @@ int mncc_recv_mobile(struct osmocom_ms *ms, int msg_type, void *arg) vty_notify(ms, NULL); vty_notify(ms, "Call is on hold\n"); LOGP(DMNCC, LOGL_INFO, "Call is on hold\n"); - call->hold = 1; + call->call_state = CALL_ST_HOLD; 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"); + call->call_state = CALL_ST_ACTIVE; 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; + call->call_state = CALL_ST_ACTIVE; 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"); + call->call_state = CALL_ST_HOLD; break; case MNCC_FACILITY_IND: LOGP(DMNCC, LOGL_INFO, "Facility info not displayed, " "unsupported\n"); break; + case MNCC_NOTIFY_IND: + LOGP(DMNCC, LOGL_INFO, "Notify info not displayed, " + "unsupported\n"); + break; case MNCC_START_DTMF_RSP: case MNCC_START_DTMF_REJ: case MNCC_STOP_DTMF_RSP: @@ -597,8 +614,8 @@ int mncc_call(struct osmocom_ms *ms, char *number) struct gsm_call *call; struct gsm_mncc setup; - llist_for_each_entry(call, &call_list, entry) { - if (!call->hold) { + llist_for_each_entry(call, &ms->mncc_entity.call_list, entry) { + if (call->call_state != CALL_ST_HOLD) { vty_notify(ms, NULL); vty_notify(ms, "Please put active call on hold " "first!\n"); @@ -612,18 +629,20 @@ int mncc_call(struct osmocom_ms *ms, char *number) return -ENOMEM; call->ms = ms; call->callref = new_callref++; - call->init = 1; - llist_add_tail(&call->entry, &call_list); + llist_add_tail(&call->entry, &ms->mncc_entity.call_list); memset(&setup, 0, sizeof(struct gsm_mncc)); setup.callref = call->callref; if (!strncasecmp(number, "emerg", 5)) { LOGP(DMNCC, LOGL_INFO, "Make emergency call\n"); + strcpy(call->number, "emergency"); /* emergency */ setup.emergency = 1; } else { LOGP(DMNCC, LOGL_INFO, "Make call to %s\n", number); + strncpy(call->number, number, sizeof(call->number) - 1); + call->number[sizeof(call->number) - 1] = '\0'; /* called number */ setup.fields |= MNCC_F_CALLED; if (number[0] == '+') { @@ -651,135 +670,195 @@ int mncc_call(struct osmocom_ms *ms, char *number) setup.cccap.dtmf = 1; } } + call->call_state = CALL_ST_MO_INIT; return mncc_tx_to_cc(ms, MNCC_SETUP_REQ, &setup); } -int mncc_hangup(struct osmocom_ms *ms) +int mncc_hangup(struct osmocom_ms *ms, int index) { - struct gsm_call *call, *found = NULL; + struct gsm_call *search, *first = NULL, *call = NULL; + int calls = 0; struct gsm_mncc disc; - llist_for_each_entry(call, &call_list, entry) { - if (!call->hold) { - found = call; - break; - } + llist_for_each_entry(search, &ms->mncc_entity.call_list, entry) { + calls++; + if (calls == 1) + first = search; + if (calls == index) + call = search; } - if (!found) { + if (calls == 0) { LOGP(DMNCC, LOGL_INFO, "No active call to hangup\n"); vty_notify(ms, NULL); vty_notify(ms, "No active call\n"); return -EINVAL; } + if (calls == 1 && index == 0) + call = first; + if (calls > 1 && index == 0) { + vty_notify(ms, NULL); + vty_notify(ms, "Select call 1..%d\n", calls); + return -EINVAL; + } + if (!call) { + vty_notify(ms, NULL); + vty_notify(ms, "Given number %d out of range!\n", index); + vty_notify(ms, "Select call 1..%d\n", calls); + return -EINVAL; + } + + call->call_state = CALL_ST_DISC_TX; memset(&disc, 0, sizeof(struct gsm_mncc)); - disc.callref = found->callref; + disc.callref = call->callref; mncc_set_cause(&disc, GSM48_CAUSE_LOC_USER, GSM48_CC_CAUSE_NORM_CALL_CLEAR); - return mncc_tx_to_cc(ms, (call->init) ? MNCC_REL_REQ : MNCC_DISC_REQ, - &disc); + return mncc_tx_to_cc(ms, (call->call_state == CALL_ST_MO_INIT) ? + MNCC_REL_REQ : MNCC_DISC_REQ, &disc); } -int mncc_answer(struct osmocom_ms *ms) +int mncc_answer(struct osmocom_ms *ms, int index) { - struct gsm_call *call, *alerting = NULL; + struct gsm_call *search, *first = NULL, *call = NULL; + int calls = 0, alerting = 0, active = 0; struct gsm_mncc rsp; - int active = 0; - llist_for_each_entry(call, &call_list, entry) { - if (call->ring) - alerting = call; - else if (!call->hold) - active = 1; + llist_for_each_entry(search, &ms->mncc_entity.call_list, entry) { + calls++; + if (search->call_state == CALL_ST_MT_RING + || search->call_state == CALL_ST_MT_KNOCK) { + alerting++; + if (alerting == 1) + first = search; + if (calls == index) + call = search; + } else + if (search->call_state != CALL_ST_HOLD) + active = calls; + } + 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 %d on hold first!\n", + active); + return -EBUSY; } - if (!alerting) { + if (alerting == 0) { 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"); + if (alerting == 1 && index == 0) + call = first; + if (alerting > 1 && index == 0) { vty_notify(ms, NULL); - vty_notify(ms, "Please put active call on hold first!\n"); - return -EBUSY; + vty_notify(ms, "Select call 1..%d\n", calls); + return -EINVAL; } - alerting->ring = 0; - update_ringer(alerting); - alerting->hold = 0; + if (!call) { + vty_notify(ms, NULL); + vty_notify(ms, "Given number %d out of range!\n", index); + vty_notify(ms, "Select call 1..%d\n", calls); + return -EINVAL; + } + call->call_state = CALL_ST_ACTIVE; + update_ringer(call); memset(&rsp, 0, sizeof(struct gsm_mncc)); - rsp.callref = alerting->callref; + rsp.callref = call->callref; return mncc_tx_to_cc(ms, MNCC_SETUP_RSP, &rsp); } -int mncc_hold(struct osmocom_ms *ms) +int mncc_hold(struct osmocom_ms *ms, int index) { - struct gsm_call *call, *found = NULL; + struct gsm_call *search, *first = NULL, *call = NULL; + int calls = 0, active = 0; struct gsm_mncc hold; - llist_for_each_entry(call, &call_list, entry) { - if (!call->hold) { - found = call; - break; + /* normally the selection should not happen, because only one call can + * be active. + */ + llist_for_each_entry(search, &ms->mncc_entity.call_list, entry) { + calls++; + if (search->call_state == CALL_ST_ACTIVE) { + active++; + if (active == 1) + first = search; + if (calls == index) + call = search; } } - if (!found) { - LOGP(DMNCC, LOGL_INFO, "No active call to hold\n"); + if (active == 0) { + LOGP(DMNCC, LOGL_INFO, "No call to hold\n"); vty_notify(ms, NULL); vty_notify(ms, "No active call\n"); + return -EBUSY; + } + if (active == 1 && index == 0) + call = first; + if (active > 1 && index == 0) { + vty_notify(ms, NULL); + vty_notify(ms, "Select call 1..%d\n", calls); + return -EINVAL; + } + if (!call) { + vty_notify(ms, NULL); + vty_notify(ms, "Given number %d out of range!\n", index); + vty_notify(ms, "Select call 1..%d\n", calls); return -EINVAL; } memset(&hold, 0, sizeof(struct gsm_mncc)); - hold.callref = found->callref; + hold.callref = call->callref; return mncc_tx_to_cc(ms, MNCC_HOLD_REQ, &hold); } -int mncc_retrieve(struct osmocom_ms *ms, int number) +int mncc_retrieve(struct osmocom_ms *ms, int index) { - struct gsm_call *call; + struct gsm_call *search, *first = NULL, *call = NULL; + int calls = 0, hold = 0, active = 0; 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; + llist_for_each_entry(search, &ms->mncc_entity.call_list, entry) { + calls++; + if (search->call_state == CALL_ST_HOLD) { + hold++; + if (hold == 1) + first = search; + if (calls == index) + call = search; + } else + if (search->call_state != CALL_ST_MT_KNOCK) + active = calls; } if (active) { LOGP(DMNCC, LOGL_INFO, "Cannot retrieve during active call\n"); vty_notify(ms, NULL); - vty_notify(ms, "Hold active call first!\n"); + vty_notify(ms, "Hold or release active call first!\n"); return -EINVAL; } - if (holdnum == 0) { + if (hold == 0) { + LOGP(DMNCC, LOGL_INFO, "No call to hold\n"); vty_notify(ms, NULL); vty_notify(ms, "No call on hold!\n"); return -EINVAL; } - if (holdnum > 1 && number <= 0) { + if (hold == 1 && index == 0) + call = first; + if (hold > 1 && index == 0) { vty_notify(ms, NULL); - vty_notify(ms, "Select call 1..%d\n", holdnum); + vty_notify(ms, "Select call 1..%d\n", calls); return -EINVAL; } - if (holdnum == 1 && number <= 0) - number = 1; - if (number > holdnum) { + if (!call) { vty_notify(ms, NULL); - vty_notify(ms, "Given number %d out of range!\n", number); - vty_notify(ms, "Select call 1..%d\n", holdnum); + vty_notify(ms, "Given number %d out of range!\n", index); + vty_notify(ms, "Select call 1..%d\n", calls); 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_tx_to_cc(ms, MNCC_RETRIEVE_REQ, &retr); @@ -843,20 +922,41 @@ static void timeout_dtmf(void *arg) dtmf_statemachine(call, NULL); } -int mncc_dtmf(struct osmocom_ms *ms, char *dtmf) +int mncc_dtmf(struct osmocom_ms *ms, int index, char *dtmf) { - struct gsm_call *call, *found = NULL; - - llist_for_each_entry(call, &call_list, entry) { - if (!call->hold) { - found = call; - break; + struct gsm_call *search, *first = NULL, *call = NULL; + int calls = 0, active = 0; + + /* normally the selection should not happen, because only one call can + * be active. + */ + llist_for_each_entry(search, &ms->mncc_entity.call_list, entry) { + calls++; + if (search->call_state == CALL_ST_ACTIVE) { + active++; + if (active == 1) + first = search; + if (calls == index) + call = search; } } - if (!found) { - LOGP(DMNCC, LOGL_INFO, "No active call to send DTMF\n"); + if (active == 0) { + LOGP(DMNCC, LOGL_INFO, "No call to send dtmf to\n"); vty_notify(ms, NULL); vty_notify(ms, "No active call\n"); + return -EBUSY; + } + if (active == 1 && index == 0) + call = first; + if (active > 1 && index == 0) { + vty_notify(ms, NULL); + vty_notify(ms, "Select call 1..%d\n", calls); + return -EINVAL; + } + if (!call) { + vty_notify(ms, NULL); + vty_notify(ms, "Given number %d out of range!\n", index); + vty_notify(ms, "Select call 1..%d\n", calls); return -EINVAL; } @@ -870,3 +970,74 @@ int mncc_dtmf(struct osmocom_ms *ms, char *dtmf) return dtmf_statemachine(call, NULL); } +int mncc_list(struct osmocom_ms *ms) +{ + struct gsm_call *call; + int calls = 0; + const char *state; + + vty_notify(ms, NULL); + llist_for_each_entry(call, &ms->mncc_entity.call_list, entry) { + calls++; + switch (call->call_state) { + case CALL_ST_MO_INIT: + state = "Dialing"; + break; + case CALL_ST_MO_PROC: + state = "Proceeding"; + break; + case CALL_ST_MO_ALERT: + state = "Ringing"; + break; + case CALL_ST_MT_RING: + state = "Incomming"; + break; + case CALL_ST_MT_KNOCK: + state = "Knocking"; + break; + case CALL_ST_ACTIVE: + state = "Connected"; + break; + case CALL_ST_HOLD: + state = "On Hold"; + break; + case CALL_ST_DISC_TX: + state = "Releasing"; + break; + case CALL_ST_DISC_RX: + state = "Hung Up"; + break; + default: + continue; + } + if (call->number[0]) + vty_notify(ms, "%s (%s)\n", state, call->number); + else + vty_notify(ms, "%s\n", state); + + } + if (calls == 0) + vty_notify(ms, "No call\n"); + + return 0; +} + +/* + * init / exit + */ + +int mnccms_init(struct osmocom_ms *ms) +{ + INIT_LLIST_HEAD(&ms->mncc_entity.call_list); + + return 0; +} + +void mnccms_exit(struct osmocom_ms *ms) +{ + struct gsm_call *c, *c2; + + llist_for_each_entry_safe(c, c2, &ms->mncc_entity.call_list, entry) + free_call(c); +} + diff --git a/src/host/layer23/src/mobile/vty_interface.c b/src/host/layer23/src/mobile/vty_interface.c index 975b0d7d..c95ed85d 100644 --- a/src/host/layer23/src/mobile/vty_interface.c +++ b/src/host/layer23/src/mobile/vty_interface.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -44,13 +45,6 @@ void *l23_ctx; -int mncc_call(struct osmocom_ms *ms, char *number); -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; @@ -808,11 +802,14 @@ DEFUN(network_select, network_select_cmd, return CMD_SUCCESS; } -DEFUN(call, call_cmd, "call MS_NAME (NUMBER|emergency|answer|hangup|hold)", +/* + * call functions + */ + +DEFUN(call, call_cmd, "call MS_NAME (NUMBER|emergency|list)", "Make a call\nName of MS (see \"show ms\")\nPhone number to call " "(Use digits '0123456789*#abc', and '+' to dial international)\n" - "Make an emergency call\nAnswer an incomming call\nHangup a call\n" - "Hold current active call\n") + "Make an emergency call\nList all calls") { struct osmocom_ms *ms; struct gsm_settings *set; @@ -833,12 +830,8 @@ DEFUN(call, call_cmd, "call MS_NAME (NUMBER|emergency|answer|hangup|hold)", number = (char *)argv[1]; if (!strcmp(number, "emergency")) mncc_call(ms, number); - else if (!strcmp(number, "answer")) - mncc_answer(ms); - else if (!strcmp(number, "hangup")) - mncc_hangup(ms); - else if (!strcmp(number, "hold")) - mncc_hold(ms); + else if (!strcmp(number, "list")) + mncc_list(ms); else { llist_for_each_entry(abbrev, &set->abbrev, list) { if (!strcmp(number, abbrev->abbrev)) { @@ -856,6 +849,51 @@ DEFUN(call, call_cmd, "call MS_NAME (NUMBER|emergency|answer|hangup|hold)", return CMD_SUCCESS; } +DEFUN(call_answer, call_answer_cmd, "call MS_NAME answer [NUMBER]", + "Make a call\nName of MS (see \"show ms\")\n" + "Answer incomming call\nNumber of call to answer") +{ + struct osmocom_ms *ms; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + mncc_answer(ms, (argc > 1) ? atoi(argv[1]) : 0); + + return CMD_SUCCESS; +} + +DEFUN(call_hangup, call_hangup_cmd, "call MS_NAME hangup [NUMBER]", + "Make a call\nName of MS (see \"show ms\")\n" + "Hangup call\nNumber of call to hangup") +{ + struct osmocom_ms *ms; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + mncc_hangup(ms, (argc > 1) ? atoi(argv[1]) : 0); + + return CMD_SUCCESS; +} + +DEFUN(call_hold, call_hold_cmd, "call MS_NAME hold [NUMBER]", + "Make a call\nName of MS (see \"show ms\")\n" + "Hold an active call\nNumber of call to hold") +{ + struct osmocom_ms *ms; + + ms = get_ms(argv[0], vty); + if (!ms) + return CMD_WARNING; + + mncc_hold(ms, (argc > 1) ? atoi(argv[1]) : 0); + + return CMD_SUCCESS; +} + DEFUN(call_retr, call_retr_cmd, "call MS_NAME retrieve [NUMBER]", "Make a call\nName of MS (see \"show ms\")\n" "Retrieve call on hold\nNumber of call to retrieve") @@ -871,9 +909,9 @@ 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", +DEFUN(call_dtmf, call_dtmf_cmd, "call MS_NAME dtmf DIGITS [NUMBER]", "Make a call\nName of MS (see \"show ms\")\n" - "One or more DTMF digits to transmit") + "One or more DTMF digits to transmit\nNumber of call") { struct osmocom_ms *ms; struct gsm_settings *set; @@ -889,7 +927,7 @@ DEFUN(call_dtmf, call_dtmf_cmd, "call MS_NAME dtmf DIGITS", return CMD_WARNING; } - mncc_dtmf(ms, (char *)argv[1]); + mncc_dtmf(ms, (argc > 2) ? atoi(argv[2]) : 0, (char *)argv[1]); return CMD_SUCCESS; } @@ -2950,6 +2988,9 @@ int ms_vty_init(void) install_element(ENABLE_NODE, &network_show_cmd); install_element(ENABLE_NODE, &network_select_cmd); install_element(ENABLE_NODE, &call_cmd); + install_element(ENABLE_NODE, &call_answer_cmd); + install_element(ENABLE_NODE, &call_hangup_cmd); + install_element(ENABLE_NODE, &call_hold_cmd); install_element(ENABLE_NODE, &call_retr_cmd); install_element(ENABLE_NODE, &call_dtmf_cmd); install_element(ENABLE_NODE, &sms_cmd); @@ -3015,7 +3056,6 @@ int ms_vty_init(void) install_element(MS_NODE, &cfg_ms_no_codec_half_cmd); install_element(MS_NODE, &cfg_ms_abbrev_cmd); install_element(MS_NODE, &cfg_ms_no_abbrev_cmd); - install_element(MS_NODE, &cfg_ms_testsim_cmd); install_element(MS_NODE, &cfg_ms_nb_idle_cmd); install_element(MS_NODE, &cfg_ms_no_nb_idle_cmd); install_element(MS_NODE, &cfg_ms_nb_dedicated_cmd); -- cgit v1.2.3