aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSylvain Munaut <tnt@246tNt.com>2019-02-14 20:13:08 +0100
committerSylvain Munaut <tnt@246tNt.com>2019-03-13 18:13:13 +0100
commite3c6e0d1cc97dac09cc7d838bcaed8aca697a38c (patch)
treebef31e9acc5afef495ab38926c51007450941872
parenta3a8821167b7b62c17a3002ddbd992eb05e92ab2 (diff)
libmsc: Allow different channel types to be requested as silent callstnt/silent-call
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
-rw-r--r--include/osmocom/msc/silent_call.h5
-rw-r--r--src/libmsc/msc_vty.c72
-rw-r--r--src/libmsc/silent_call.c94
3 files changed, 151 insertions, 20 deletions
diff --git a/include/osmocom/msc/silent_call.h b/include/osmocom/msc/silent_call.h
index 70324e5..dbd7dcc 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);
+ 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 2adb2a4..4ec2e5b 100644
--- a/src/libmsc/msc_vty.c
+++ b/src/libmsc/msc_vty.c
@@ -1042,21 +1042,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-65536>]",
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 = NULL;
+ uint16_t port = 0;
+ int rc, speech;
if (!vsub) {
vty_out(vty, "%% No subscriber found for %s %s%s",
@@ -1064,16 +1078,52 @@ DEFUN(subscriber_silent_call_start,
return CMD_WARNING;
}
+ memset(&ct, 0x00, sizeof(ct));
+
+ 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"))
- type = RSL_CHANNEED_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"))
- type = RSL_CHANNEED_TCH_ForH;
- else if (!strcmp(argv[2], "sdcch"))
- type = RSL_CHANNEED_SDCCH;
- else
- type = RSL_CHANNEED_ANY; /* Defaults to 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, vty, type);
+ 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 2a9fa9c..cadd17d 100644
--- a/src/libmsc/silent_call.c
+++ b/src/libmsc/silent_call.c
@@ -24,6 +24,7 @@
#include <unistd.h>
#include <errno.h>
+#include <osmocom/core/byteswap.h>
#include <osmocom/core/msgb.h>
#include <osmocom/msc/signal.h>
#include <osmocom/msc/debug.h>
@@ -31,13 +32,37 @@
#include <osmocom/msc/gsm_subscriber.h>
#include <osmocom/msc/vlr.h>
+#include <osmocom/sigtran/sccp_helpers.h>
+
+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,20 +81,58 @@ 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 */
+ 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;
+
case GSM_PAGING_EXPIRED:
case GSM_PAGING_BUSY:
DEBUGP(DLSMS, "expired\n");
osmo_signal_dispatch(SS_SCALL, S_SCALL_EXPIRED, &sigdata);
break;
+
default:
rc = -EINVAL;
break;
}
+ if (rc)
+ talloc_free(scd);
+
return rc;
}
@@ -120,18 +183,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,
+ 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;
+
+ scd = talloc_zero(vsub, struct silent_call_data);
+
+ memcpy(&scd->ct, ct, sizeof(struct gsm0808_channel_type));
- /* 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,
+ 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;
}