From 935583069d3d79ba93640df2e9287acdf8c50fe1 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Thu, 14 Feb 2019 20:13:08 +0100 Subject: [PATCH] libmsc: Allow different channel types to be requested as silent calls Change-Id: I82645708dd27864cf33ea9cc993ead0983415602 --- include/osmocom/msc/silent_call.h | 5 +- src/libmsc/msc_vty.c | 76 ++++++++++++++++++---- src/libmsc/silent_call.c | 102 +++++++++++++++++++++++++++--- 3 files changed, 160 insertions(+), 23 deletions(-) diff --git a/include/osmocom/msc/silent_call.h b/include/osmocom/msc/silent_call.h index 70324e5f3..ca36052ff 100644 --- a/include/osmocom/msc/silent_call.h +++ b/include/osmocom/msc/silent_call.h @@ -2,9 +2,12 @@ #define _SILENT_CALL_H struct ran_conn; +struct gsm0808_channel_type; extern int gsm_silent_call_start(struct vlr_subscr *vsub, - void *data, int type); + const struct gsm0808_channel_type *ct, + const char *traffic_dst_ip, uint16_t traffic_dst_port, + void *data); extern int gsm_silent_call_stop(struct vlr_subscr *vsub); #if 0 diff --git a/src/libmsc/msc_vty.c b/src/libmsc/msc_vty.c index ac3946afb..6404ee94a 100644 --- a/src/libmsc/msc_vty.c +++ b/src/libmsc/msc_vty.c @@ -1066,21 +1066,35 @@ err: return rc; } -#define CHAN_TYPES "(any|tch/f|tch/any|sdcch)" +#define CHAN_TYPES "(any|tch/f|tch/h|tch/any|sdcch)" #define CHAN_TYPE_HELP \ "Any channel\n" \ "TCH/F channel\n" \ + "TCH/H channel\n" \ "Any TCH channel\n" \ "SDCCH channel\n" +#define CHAN_MODES "(signalling|speech-hr|speech-fr|speech-efr|speech-amr)" +#define CHAN_MODE_HELP \ + "Signalling only\n" \ + "Speech with HR codec\n" \ + "Speech with FR codec\n" \ + "Speech with EFR codec\n" \ + "Speech with AMR codec\n" + DEFUN(subscriber_silent_call_start, subscriber_silent_call_start_cmd, - "subscriber " SUBSCR_TYPES " ID silent-call start (any|tch/f|tch/any|sdcch)", + "subscriber " SUBSCR_TYPES " ID silent-call start " CHAN_TYPES " " CHAN_MODES " [IP] [<0-65535>]", SUBSCR_HELP "Silent call operation\n" "Start silent call\n" - CHAN_TYPE_HELP) + CHAN_TYPE_HELP CHAN_MODE_HELP + "Target IP for RTP traffic (default 127.0.0.1)\n" + "Target port for RTP traffic (default: 4000)\n") { struct vlr_subscr *vsub = get_vsub_by_argv(gsmnet, argv[0], argv[1]); - int rc, type; + struct gsm0808_channel_type ct; + const char *ip; + uint16_t port; + int rc, speech; if (!vsub) { vty_out(vty, "%% No subscriber found for %s %s%s", @@ -1088,16 +1102,52 @@ DEFUN(subscriber_silent_call_start, return CMD_WARNING; } - if (!strcmp(argv[2], "tch/f")) - type = RSL_CHANNEED_TCH_F; - else if (!strcmp(argv[2], "tch/any")) - type = RSL_CHANNEED_TCH_ForH; - else if (!strcmp(argv[2], "sdcch")) - type = RSL_CHANNEED_SDCCH; - else - type = RSL_CHANNEED_ANY; /* Defaults to ANY */ + memset(&ct, 0x00, sizeof(ct)); - rc = gsm_silent_call_start(vsub, vty, type); + if (!strcmp(argv[3], "signalling")) { + ct.ch_indctr = GSM0808_CHAN_SIGN; + ct.perm_spch[0] = 0; /* Spare but required */ + ct.perm_spch_len = 1; + } else if (!strcmp(argv[3], "speech-hr")) { + ct.ch_indctr = GSM0808_CHAN_SPEECH; + ct.perm_spch[0] = GSM0808_PERM_HR1; + ct.perm_spch_len = 1; + } else if (!strcmp(argv[3], "speech-fr")) { + ct.ch_indctr = GSM0808_CHAN_SPEECH; + ct.perm_spch[0] = GSM0808_PERM_FR1; + ct.perm_spch_len = 1; + } else if (!strcmp(argv[3], "speech-efr")) { + ct.ch_indctr = GSM0808_CHAN_SPEECH; + ct.perm_spch[0] = GSM0808_PERM_FR2; + ct.perm_spch_len = 1; + } else if (!strcmp(argv[3], "speech-amr")) { + ct.ch_indctr = GSM0808_CHAN_SPEECH; + ct.perm_spch[0] = GSM0808_PERM_FR3; + ct.perm_spch[1] = GSM0808_PERM_HR3; + ct.perm_spch_len = 2; + } + + speech = ct.ch_indctr == GSM0808_CHAN_SPEECH; + + if (!strcmp(argv[2], "tch/f")) + ct.ch_rate_type = speech ? GSM0808_SPEECH_FULL_BM : GSM0808_SIGN_FULL_BM; + else if (!strcmp(argv[2], "tch/h")) + ct.ch_rate_type = speech ? GSM0808_SPEECH_HALF_LM : GSM0808_SIGN_HALF_LM; + else if (!strcmp(argv[2], "tch/any")) + ct.ch_rate_type = speech ? GSM0808_SPEECH_FULL_PREF : GSM0808_SIGN_FULL_PREF; + else if (!strcmp(argv[2], "sdcch")) { + if (speech) { + vty_out(vty, "Can't request speech on SDCCH%s", VTY_NEWLINE); + return CMD_WARNING; + } + ct.ch_rate_type = GSM0808_SIGN_SDCCH; + } else + ct.ch_rate_type = speech ? GSM0808_SPEECH_FULL_PREF : GSM0808_SIGN_ANY; + + ip = argc >= 5 ? argv[4] : "127.0.0.1"; + port = argc >= 6 ? atoi(argv[5]) : 4000; + + rc = gsm_silent_call_start(vsub, &ct, ip, port, vty); switch (rc) { case -ENODEV: vty_out(vty, "%% Subscriber not attached%s", VTY_NEWLINE); diff --git a/src/libmsc/silent_call.c b/src/libmsc/silent_call.c index 2a9fa9cd2..7effba3e7 100644 --- a/src/libmsc/silent_call.c +++ b/src/libmsc/silent_call.c @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -31,13 +32,37 @@ #include #include +#include + +struct silent_call_data { + struct gsm0808_channel_type ct; + + char traffic_ip[INET_ADDRSTRLEN]; + uint16_t traffic_port; + + void *data; + + struct osmo_timer_list timer; + struct ran_conn *conn; +}; + +static void timer_cb(void *data) +{ + struct silent_call_data *scd = (struct silent_call_data *)data; + ran_conn_communicating(scd->conn); + talloc_free(scd); +} + /* paging of the requested subscriber has completed */ static int paging_cb_silent(unsigned int hooknum, unsigned int event, struct msgb *msg, void *_conn, void *_data) { + struct silent_call_data *scd = (struct silent_call_data *)_data; struct ran_conn *conn = _conn; struct scall_signal_data sigdata; + struct msgb *msg_ass; int rc = 0; + int i; if (hooknum != GSM_HOOK_RR_PAGING) return -EINVAL; @@ -45,7 +70,7 @@ static int paging_cb_silent(unsigned int hooknum, unsigned int event, DEBUGP(DLSMS, "paging_cb_silent: "); sigdata.conn = conn; - sigdata.data = _data; + sigdata.data = scd->data; switch (event) { case GSM_PAGING_SUCCEEDED: @@ -56,10 +81,52 @@ static int paging_cb_silent(unsigned int hooknum, unsigned int event, conn->lchan->ts->nr, conn->lchan->ts->trx->arfcn); #endif conn->silent_call = 1; + + /* Increment lchan reference count and mark as active*/ ran_conn_get(conn, RAN_CONN_USE_SILENT_CALL); - /* increment lchan reference count */ + + /* Schedule a timer to mark it as active */ + /* This is a hack we we can't call ran_conn_communicating + * from here because we're in the call back context of + * a RAN FSM event but before it actually changes its own + * state and it's not ready to accept this. + * Of all alternatives considered, making the call in an + * 'immediate timer' is the least disruptive and least ugly + * way to do it I could find. + */ + scd->conn = conn; + osmo_timer_setup(&scd->timer, timer_cb, scd); + osmo_timer_schedule(&scd->timer, 0, 0); + + /* Manually craft an assignement message with requested mode */ + if (scd->ct.ch_indctr == GSM0808_CHAN_SPEECH) { + struct gsm0808_speech_codec_list scl; + union { + struct sockaddr_storage st; + struct sockaddr_in in; + } rtp_addr; + + memset(&rtp_addr, 0, sizeof(rtp_addr)); + rtp_addr.in.sin_family = AF_INET; + rtp_addr.in.sin_port = osmo_htons(scd->traffic_port); + rtp_addr.in.sin_addr.s_addr = inet_addr(scd->traffic_ip); + + for (i = 0; i < scd->ct.perm_spch_len; i++) + gsm0808_speech_codec_from_chan_type(&scl.codec[i], scd->ct.perm_spch[i]); + scl.len = scd->ct.perm_spch_len; + + msg_ass = gsm0808_create_ass(&scd->ct, NULL, &rtp_addr.st, &scl, NULL); + } else { + msg_ass = gsm0808_create_ass(&scd->ct, NULL, NULL, NULL, NULL); + } + + /* Send assignement message, hoping it will work */ + osmo_sccp_tx_data_msg(conn->a.scu, conn->a.conn_id, msg_ass); + + /* Signal completion */ osmo_signal_dispatch(SS_SCALL, S_SCALL_SUCCESS, &sigdata); - break; + return 0; + case GSM_PAGING_EXPIRED: case GSM_PAGING_BUSY: DEBUGP(DLSMS, "expired\n"); @@ -70,6 +137,8 @@ static int paging_cb_silent(unsigned int hooknum, unsigned int event, break; } + talloc_free(scd); + return rc; } @@ -120,18 +189,33 @@ int silent_call_reroute(struct ran_conn *conn, struct msgb *msg) /* initiate a silent call with a given subscriber */ -int gsm_silent_call_start(struct vlr_subscr *vsub, void *data, int type) +int gsm_silent_call_start(struct vlr_subscr *vsub, + const struct gsm0808_channel_type *ct, + const char *traffic_dst_ip, uint16_t traffic_dst_port, + void *data) { struct subscr_request *req; + struct silent_call_data *scd; - /* FIXME the VTY command allows selecting a silent call channel type. - * This doesn't apply to the situation after MSCSPLIT with an - * A-interface. */ - req = subscr_request_conn(vsub, paging_cb_silent, data, + scd = talloc_zero(vsub, struct silent_call_data); + + memcpy(&scd->ct, ct, sizeof(scd->ct)); + + if (traffic_dst_ip) { + strncpy(scd->traffic_ip, traffic_dst_ip, sizeof(scd->traffic_ip)); + scd->traffic_port = traffic_dst_port; + } + + scd->data = data; + + req = subscr_request_conn(vsub, paging_cb_silent, scd, "establish silent call", SGSAP_SERV_IND_CS_CALL); - if (!req) + if (!req) { + talloc_free(scd); return -ENODEV; + } + return 0; }