diff options
Diffstat (limited to 'src/libmsc/silent_call.c')
-rw-r--r-- | src/libmsc/silent_call.c | 102 |
1 files changed, 93 insertions, 9 deletions
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 <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,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; + + scd = talloc_zero(vsub, struct silent_call_data); - /* 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, + 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; } |