summaryrefslogtreecommitdiffstats
path: root/openbsc/src/libmsc/gsm_04_08.c
diff options
context:
space:
mode:
Diffstat (limited to 'openbsc/src/libmsc/gsm_04_08.c')
-rw-r--r--openbsc/src/libmsc/gsm_04_08.c243
1 files changed, 239 insertions, 4 deletions
diff --git a/openbsc/src/libmsc/gsm_04_08.c b/openbsc/src/libmsc/gsm_04_08.c
index 02ffe58..f71d43c 100644
--- a/openbsc/src/libmsc/gsm_04_08.c
+++ b/openbsc/src/libmsc/gsm_04_08.c
@@ -67,6 +67,8 @@
void *tall_locop_ctx;
void *tall_authciphop_ctx;
+static int tch_rtp_signal(struct gsm_lchan *lchan, int signal);
+
static int gsm0408_loc_upd_acc(struct gsm_subscriber_connection *conn);
static int gsm48_tx_simple(struct gsm_subscriber_connection *conn,
uint8_t pdisc, uint8_t msg_type);
@@ -1463,6 +1465,15 @@ static int switch_for_handover(struct gsm_lchan *old_lchan,
{
struct rtp_socket *old_rs, *new_rs, *other_rs;
+ /* Ask the new socket to send to the already known port. */
+ if (new_lchan->conn->mncc_rtp_bridge) {
+ LOGP(DHO, LOGL_DEBUG, "Forwarding RTP\n");
+ rsl_ipacc_mdcx(new_lchan,
+ old_lchan->abis_ip.connect_ip,
+ old_lchan->abis_ip.connect_port, 0);
+ return 0;
+ }
+
if (ipacc_rtp_direct) {
LOGP(DHO, LOGL_ERROR, "unable to handover in direct RTP mode\n");
return 0;
@@ -1507,11 +1518,19 @@ static int switch_for_handover(struct gsm_lchan *old_lchan,
return 0;
}
+static void maybe_switch_for_handover(struct gsm_lchan *lchan)
+{
+ struct gsm_lchan *old_lchan;
+ old_lchan = bsc_handover_pending(lchan);
+ if (old_lchan)
+ switch_for_handover(old_lchan, lchan);
+}
+
/* some other part of the code sends us a signal */
static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
void *handler_data, void *signal_data)
{
- struct gsm_lchan *lchan = signal_data, *old_lchan;
+ struct gsm_lchan *lchan = signal_data;
int rc;
struct gsm_network *net;
struct gsm_trans *trans;
@@ -1519,6 +1538,10 @@ static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
if (subsys != SS_ABISIP)
return 0;
+ /* RTP bridge handling */
+ if (lchan->conn && lchan->conn->mncc_rtp_bridge)
+ return tch_rtp_signal(lchan, signal);
+
/* in case we use direct BTS-to-BTS RTP */
if (ipacc_rtp_direct)
return 0;
@@ -1560,9 +1583,7 @@ static int handle_abisip_signal(unsigned int subsys, unsigned int signal,
* Do we have a handover pending for this new lchan? In that
* case re-route the audio from the old channel to the new one.
*/
- old_lchan = bsc_handover_pending(lchan);
- if (old_lchan)
- switch_for_handover(old_lchan, lchan);
+ maybe_switch_for_handover(lchan);
break;
case S_ABISIP_DLCX_IND:
/* the BTS tells us a RTP stream has been disconnected */
@@ -2905,11 +2926,218 @@ static int gsm48_cc_rx_userinfo(struct gsm_trans *trans, struct msgb *msg)
static int _gsm48_lchan_modify(struct gsm_trans *trans, void *arg)
{
struct gsm_mncc *mode = arg;
+ struct gsm_lchan *lchan = trans->conn->lchan;
+
+ /*
+ * We were forced to make an assignment a lot earlier and
+ * we should avoid sending another assignment that might
+ * even lead to a different kind of lchan (TCH/F vs. TCH/H).
+ * In case of rtp-bridge it is too late to change things
+ * here.
+ */
+ if (trans->conn->mncc_rtp_bridge && lchan->tch_mode != GSM48_CMODE_SIGN)
+ return 0;
return gsm0808_assign_req(trans->conn, mode->lchan_mode,
trans->conn->lchan->type != GSM_LCHAN_TCH_H);
}
+static void mncc_recv_rtp(struct gsm_network *net, uint32_t callref,
+ int cmd, uint32_t addr, uint16_t port, uint32_t payload_type,
+ uint32_t payload_msg_type)
+{
+ uint8_t data[sizeof(struct gsm_mncc)];
+ struct gsm_mncc_rtp *rtp;
+
+ memset(&data, 0, sizeof(data));
+ rtp = (struct gsm_mncc_rtp *) &data[0];
+
+ rtp->callref = callref;
+ rtp->msg_type = cmd;
+ rtp->ip = addr;
+ rtp->port = port;
+ rtp->payload_type = payload_type;
+ rtp->payload_msg_type = payload_msg_type;
+ mncc_recvmsg(net, NULL, cmd, (struct gsm_mncc *)data);
+}
+
+static void mncc_recv_rtp_sock(struct gsm_network *net, struct gsm_trans *trans, int cmd)
+{
+ struct gsm_lchan *lchan;
+ int msg_type;
+
+ lchan = trans->conn->lchan;
+ switch (lchan->abis_ip.rtp_payload) {
+ case RTP_PT_GSM_FULL:
+ msg_type = GSM_TCHF_FRAME;
+ break;
+ case RTP_PT_GSM_EFR:
+ msg_type = GSM_TCHF_FRAME_EFR;
+ break;
+ case RTP_PT_GSM_HALF:
+ msg_type = GSM_TCHH_FRAME;
+ break;
+ case RTP_PT_AMR:
+ msg_type = GSM_TCH_FRAME_AMR;
+ break;
+ default:
+ LOGP(DMNCC, LOGL_ERROR, "%s unknown payload type %d\n",
+ gsm_lchan_name(lchan), lchan->abis_ip.rtp_payload);
+ msg_type = 0;
+ break;
+ }
+
+ return mncc_recv_rtp(net, trans->callref, cmd,
+ lchan->abis_ip.bound_ip,
+ lchan->abis_ip.bound_port,
+ lchan->abis_ip.rtp_payload,
+ msg_type);
+}
+
+static void mncc_recv_rtp_err(struct gsm_network *net, uint32_t callref, int cmd)
+{
+ return mncc_recv_rtp(net, callref, cmd, 0, 0, 0, 0);
+}
+
+static int tch_rtp_create(struct gsm_network *net, uint32_t callref)
+{
+ struct gsm_bts *bts;
+ struct gsm_lchan *lchan;
+ struct gsm_trans *trans;
+
+ /* Find callref */
+ trans = trans_find_by_callref(net, callref);
+ if (!trans) {
+ LOGP(DMNCC, LOGL_ERROR, "RTP create for non-existing trans\n");
+ mncc_recv_rtp_err(net, callref, MNCC_RTP_CREATE);
+ return -EIO;
+ }
+ log_set_context(BSC_CTX_SUBSCR, trans->subscr);
+ if (!trans->conn) {
+ LOGP(DMNCC, LOGL_NOTICE, "RTP create for trans without conn\n");
+ mncc_recv_rtp_err(net, callref, MNCC_RTP_CREATE);
+ return 0;
+ }
+
+ lchan = trans->conn->lchan;
+ bts = lchan->ts->trx->bts;
+ if (!is_ipaccess_bts(bts)) {
+ /*
+ * I want this to be straight forward and have no audio flow
+ * through the nitb/osmo-mss system. This currently means that
+ * this will not work with BS11/Nokia type BTS. We would need
+ * to have a trau<->rtp bridge for these but still preferable
+ * in another process.
+ */
+ LOGP(DMNCC, LOGL_ERROR, "RTP create only works with IP systems\n");
+ mncc_recv_rtp_err(net, callref, MNCC_RTP_CREATE);
+ return -EINVAL;
+ }
+
+ trans->conn->mncc_rtp_bridge = 1;
+ /*
+ * *sigh* we need to pick a codec now. Pick the most generic one
+ * right now and hope we could fix that later on. This is very
+ * similiar to the above routine.
+ * TODO: Use the default codec version...
+ */
+ if (lchan->tch_mode == GSM48_CMODE_SIGN) {
+ trans->conn->mncc_rtp_create_pending = 1;
+ /* TODO... transport or fix the default type... */
+ return gsm0808_assign_req(trans->conn, GSM48_CMODE_SPEECH_V1,
+ lchan->type != GSM_LCHAN_TCH_H);
+ }
+
+ mncc_recv_rtp_sock(trans->net, trans, MNCC_RTP_CREATE);
+ return 0;
+}
+
+static int tch_rtp_connect(struct gsm_network *net, void *arg)
+{
+ struct gsm_lchan *lchan;
+ struct gsm_trans *trans;
+ struct gsm_mncc_rtp *rtp = arg;
+
+ /* Find callref */
+ trans = trans_find_by_callref(net, rtp->callref);
+ if (!trans) {
+ LOGP(DMNCC, LOGL_ERROR, "RTP connect for non-existing trans\n");
+ mncc_recv_rtp_err(net, rtp->callref, MNCC_RTP_CONNECT);
+ return -EIO;
+ }
+ log_set_context(BSC_CTX_SUBSCR, trans->subscr);
+ if (!trans->conn) {
+ LOGP(DMNCC, LOGL_ERROR, "RTP connect for trans without conn\n");
+ mncc_recv_rtp_err(net, rtp->callref, MNCC_RTP_CONNECT);
+ return 0;
+ }
+
+ lchan = trans->conn->lchan;
+
+ /* TODO: Check if payload_msg_type is compatible with what we have */
+ if (rtp->payload_type != lchan->abis_ip.rtp_payload) {
+ LOGP(DMNCC, LOGL_ERROR, "RTP connect with different RTP payload\n");
+ mncc_recv_rtp_err(net, rtp->callref, MNCC_RTP_CONNECT);
+ }
+
+ /*
+ * FIXME: payload2 can't be sent with MDCX as the osmo-bts code
+ * complains about both rtp and rtp payload2 being present in the
+ * same package!
+ */
+ trans->conn->mncc_rtp_connect_pending = 1;
+ return rsl_ipacc_mdcx(lchan, rtp->ip, rtp->port, 0);
+}
+
+static int tch_rtp_signal(struct gsm_lchan *lchan, int signal)
+{
+ struct gsm_network *net;
+ struct gsm_trans *tmp, *trans = NULL;
+
+ net = lchan->ts->trx->bts->network;
+ llist_for_each_entry(tmp, &net->trans_list, entry) {
+ if (!tmp->conn)
+ continue;
+ if (tmp->conn->lchan != lchan && tmp->conn->ho_lchan != lchan)
+ continue;
+ trans = tmp;
+ break;
+ }
+
+ if (!trans) {
+ LOGP(DMNCC, LOGL_ERROR, "%s IPA abis signal but no transaction.\n",
+ gsm_lchan_name(lchan));
+ return 0;
+ }
+
+ switch (signal) {
+ case S_ABISIP_CRCX_ACK:
+ if (lchan->conn->mncc_rtp_create_pending) {
+ lchan->conn->mncc_rtp_create_pending = 0;
+ LOGP(DMNCC, LOGL_NOTICE, "%s sending pending RTP create ind.\n",
+ gsm_lchan_name(lchan));
+ mncc_recv_rtp_sock(net, trans, MNCC_RTP_CREATE);
+ }
+ /*
+ * TODO: this appears to be too early? Why not until after
+ * the handover detect or the handover complete?
+ */
+ maybe_switch_for_handover(lchan);
+ break;
+ case S_ABISIP_MDCX_ACK:
+ if (lchan->conn->mncc_rtp_connect_pending) {
+ lchan->conn->mncc_rtp_connect_pending = 0;
+ LOGP(DMNCC, LOGL_NOTICE, "%s sending pending RTP connect ind.\n",
+ gsm_lchan_name(lchan));
+ mncc_recv_rtp_sock(net, trans, MNCC_RTP_CONNECT);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+
static struct downstate {
uint32_t states;
int type;
@@ -2990,6 +3218,13 @@ int mncc_tx_to_cc(struct gsm_network *net, int msg_type, void *arg)
return tch_recv_mncc(net, data->callref, 0);
case MNCC_FRAME_RECV:
return tch_recv_mncc(net, data->callref, 1);
+ case MNCC_RTP_CREATE:
+ return tch_rtp_create(net, data->callref);
+ case MNCC_RTP_CONNECT:
+ return tch_rtp_connect(net, arg);
+ case MNCC_RTP_FREE:
+ /* unused right now */
+ return -EIO;
case GSM_TCHF_FRAME:
case GSM_TCHF_FRAME_EFR:
case GSM_TCHH_FRAME: