diff options
Diffstat (limited to 'src/libmsc/msc_a.c')
-rw-r--r-- | src/libmsc/msc_a.c | 209 |
1 files changed, 148 insertions, 61 deletions
diff --git a/src/libmsc/msc_a.c b/src/libmsc/msc_a.c index e2bf975ca..0fa189282 100644 --- a/src/libmsc/msc_a.c +++ b/src/libmsc/msc_a.c @@ -46,6 +46,7 @@ #include <osmocom/msc/call_leg.h> #include <osmocom/msc/rtp_stream.h> #include <osmocom/msc/msc_ho.h> +#include <osmocom/msc/codec_sdp_cc_t9n.h> #define MSC_A_USE_WAIT_CLEAR_COMPLETE "wait-Clear-Complete" @@ -550,12 +551,87 @@ static void msc_a_fsm_authenticated(struct osmo_fsm_inst *fi, uint32_t event, vo } } +static struct call_leg *msc_a_ensure_call_leg(struct msc_a *msc_a, struct gsm_trans *for_cc_trans) +{ + struct call_leg *cl = msc_a->cc.call_leg; + struct gsm_network *net = msc_a_net(msc_a); + + /* Ensure that events about RTP endpoints coming from the msc_a->cc.call_leg know which gsm_trans to abort on + * error */ + if (!msc_a->cc.active_trans) + msc_a->cc.active_trans = for_cc_trans; + if (msc_a->cc.active_trans != for_cc_trans) { + LOG_TRANS(for_cc_trans, LOGL_ERROR, + "Cannot create call leg, another trans is already active for this conn\n"); + return NULL; + } + + if (!cl) { + cl = msc_a->cc.call_leg = call_leg_alloc(msc_a->c.fi, + MSC_EV_CALL_LEG_TERM, + MSC_EV_CALL_LEG_RTP_LOCAL_ADDR_AVAILABLE, + MSC_EV_CALL_LEG_RTP_COMPLETE); + OSMO_ASSERT(cl); + + /* HACK: We put the connection in loopback mode at the beginnig to + * trick the hNodeB into doing the IuUP negotiation with itself. + * This is a hack we need because osmo-mgw does not support IuUP yet, see OS#2459. */ + if (msc_a->c.ran->type == OSMO_RAT_UTRAN_IU) + cl->crcx_conn_mode[RTP_TO_RAN] = MGCP_CONN_LOOPBACK; + + if (net->use_osmux != OSMUX_USAGE_OFF) { + struct msc_i *msc_i = msc_a_msc_i(msc_a); + if (msc_i->c.remote_to) { + /* TODO: investigate what to do in this case */ + LOG_MSC_A(msc_a, LOGL_ERROR, "Osmux not yet supported for inter-MSC"); + } else { + cl->ran_peer_supports_osmux = msc_i->ran_conn->ran_peer->remote_supports_osmux; + } + } + + } + return cl; +} + +int msc_a_ensure_cn_local_rtp(struct msc_a *msc_a, struct gsm_trans *cc_trans) +{ + struct call_leg *cl; + struct rtp_stream *rtp_to_ran; + + cl = msc_a_ensure_call_leg(msc_a, cc_trans); + if (!cl) + return -EINVAL; + rtp_to_ran = cl->rtp[RTP_TO_RAN]; + + if (call_leg_local_ip(cl, RTP_TO_CN)) { + /* Already has an RTP address and port towards the CN, continue right away. */ + return osmo_fsm_inst_dispatch(msc_a->c.fi, MSC_EV_CALL_LEG_RTP_LOCAL_ADDR_AVAILABLE, cl->rtp[RTP_TO_CN]); + } + + /* No CN RTP address available yet, ask the MGW to create one. + * Set a codec to be used: if Assignment on the RAN side is already done, take the same codec as the RTP_TO_RAN. + * If no RAN side RTP is established, try to guess a preliminary codec from SDP -- before Assignment, picking a + * codec from the SDP is more politeness/avoiding confusion than necessity. The actual codec to be used would be + * determined later. If no codec could be determined, pass none for the time being. */ + return call_leg_ensure_ci(cl, RTP_TO_CN, cc_trans->callref, cc_trans, + rtp_to_ran->codecs_known ? &rtp_to_ran->codecs : NULL, NULL); +} + +static void msc_a_call_leg_cn_local_addr_available(struct msc_a *msc_a, struct gsm_trans *cc_trans) +{ + cc_cn_local_rtp_port_known(cc_trans); +} + + /* The MGW has given us a local IP address for the RAN side. Ready to start the Assignment of a voice channel. */ static void msc_a_call_leg_ran_local_addr_available(struct msc_a *msc_a) { struct ran_msg msg; struct gsm_trans *cc_trans = msc_a->cc.active_trans; - struct gsm0808_channel_type channel_type; + struct gsm0808_channel_type channel_type = { + .ch_indctr = GSM0808_CHAN_SPEECH, + .ch_rate_type = GSM0808_SPEECH_FULL_PREF, + }; if (!cc_trans) { LOG_MSC_A(msc_a, LOGL_ERROR, "No CC transaction active\n"); @@ -565,9 +641,21 @@ static void msc_a_call_leg_ran_local_addr_available(struct msc_a *msc_a) /* Once a CI is known, we could also CRCX the CN side of the MGW endpoint, but it makes sense to wait for the * codec to be determined by the Assignment Complete message, first. */ + cc_sdp_filter(&cc_trans->cc.sdp); + LOG_TRANS(cc_trans, LOGL_DEBUG, "Sending Assignment Command with codecs: %s\n", cc_sdp_name(&cc_trans->cc.sdp)); - if (mncc_bearer_cap_to_channel_type(&channel_type, &cc_trans->bearer_cap)) { - LOG_MSC_A(msc_a, LOGL_ERROR, "Cannot compose Channel Type from bearer capabilities\n"); + if (!cc_trans->cc.sdp.result.audio_codecs.count) { + LOG_TRANS(cc_trans, LOGL_ERROR, "Assignment not possible, no matching codec: %s\n", + cc_sdp_name(&cc_trans->cc.sdp)); + call_leg_release(msc_a->cc.call_leg); + return; + } + + /* Compose 48.008 Channel Type from the current set of codecs determined from both local and remote codec + * capabilities. */ + if (sdp_audio_codecs_to_gsm0808_channel_type(&channel_type, &cc_trans->cc.sdp.result.audio_codecs)) { + LOG_MSC_A(msc_a, LOGL_ERROR, "Cannot compose Channel Type (Permitted Speech) from codecs: %s\n", + cc_sdp_name(&cc_trans->cc.sdp)); trans_free(cc_trans); return; } @@ -592,15 +680,6 @@ static void msc_a_call_leg_ran_local_addr_available(struct msc_a *msc_a) } } -static void msc_a_call_leg_cn_local_addr_available(struct msc_a *msc_a, struct gsm_trans *cc_trans) -{ - if (gsm48_tch_rtp_create(cc_trans)) { - LOG_MSC_A(msc_a, LOGL_ERROR, "Cannot inform MNCC of RTP address\n"); - trans_free(cc_trans); - return; - } -} - static struct gsm_trans *find_waiting_call(struct msc_a *msc_a) { struct gsm_trans *trans; @@ -1313,6 +1392,7 @@ static void msc_a_up_call_assignment_complete(struct msc_a *msc_a, const struct struct rtp_stream *rtps_to_ran = msc_a->cc.call_leg ? msc_a->cc.call_leg->rtp[RTP_TO_RAN] : NULL; const enum mgcp_codecs *codec_if_known = ac->assignment_complete.codec_present ? &ac->assignment_complete.codec : NULL; + const struct codec_mapping *m; if (!rtps_to_ran) { LOG_MSC_A(msc_a, LOGL_ERROR, "Rx Assignment Complete, but no RTP stream is set up\n"); @@ -1330,25 +1410,33 @@ static void msc_a_up_call_assignment_complete(struct msc_a *msc_a, const struct return; } - /* Update RAN-side endpoint CI: */ - if (codec_if_known) - rtp_stream_set_codec(rtps_to_ran, *codec_if_known); + if (codec_if_known) { + m = codec_mapping_by_mgcp_codec(*codec_if_known); + if (!m) { + LOG_TRANS(cc_trans, LOGL_ERROR, "Unknown codec in Assignment Complete: %s\n", + osmo_mgcpc_codec_name(ac->assignment_complete.codec)); + call_leg_release(msc_a->cc.call_leg); + return; + } + + /* Update RAN-side endpoint CI: */ + rtp_stream_set_one_codec(rtps_to_ran, &m->sdp); + } + rtp_stream_set_remote_addr(rtps_to_ran, &ac->assignment_complete.remote_rtp); if (rtps_to_ran->use_osmux) rtp_stream_set_remote_osmux_cid(rtps_to_ran, ac->assignment_complete.osmux_cid); - rtp_stream_commit(rtps_to_ran); - /* Setup CN side endpoint CI: - * Now that - * - the first CI has been created and a definitive endpoint name is assigned to the call_leg's MGW - * endpoint, - * - the Assignment has chosen a speech codec - * go on to create the CN side RTP stream's CI. */ - if (call_leg_ensure_ci(msc_a->cc.call_leg, RTP_TO_CN, cc_trans->callref, cc_trans, - codec_if_known, NULL)) { - LOG_MSC_A_CAT(msc_a, DCC, LOGL_ERROR, "Error creating MGW CI towards CN\n"); + /* Remember the Codec List (BSS Supported) */ + if (ac->assignment_complete.codec_list_bss_supported) + cc_sdp_set_cell(&cc_trans->cc.sdp, ac->assignment_complete.codec_list_bss_supported); + + cc_trans->cc.sdp.assignment = m->sdp; + + if (cc_assignment_done(cc_trans)) { + /* If an error occured, it was logged in cc_assignment_done() */ call_leg_release(msc_a->cc.call_leg); return; } @@ -1430,6 +1518,15 @@ int msc_a_ran_dec_from_msc_i(struct msc_a *msc_a, struct msc_a_ran_dec_data *d) .lai.plmn = msc_a_net(msc_a)->plmn, }; gsm0808_cell_id_to_cgi(&msc_a->via_cell, msg->compl_l3.cell_id); + if (msg->compl_l3.codec_list_bss_supported) { + msc_a->cc.codec_list_bss_supported = *msg->compl_l3.codec_list_bss_supported; + if (log_check_level(msc_a->c.ran->log_subsys, LOGL_DEBUG)) { + struct sdp_audio_codecs ac = {}; + sdp_audio_codecs_from_speech_codec_list(&ac, &msc_a->cc.codec_list_bss_supported); + LOG_MSC_A(msc_a, LOGL_DEBUG, "Complete Layer 3: Codec List (BSS Supported): %s\n", + sdp_audio_codecs_name(&ac)); + } + } rc = msc_a_up_l3(msc_a, msg->compl_l3.msg); if (!rc) { struct ran_conn *conn = msub_ran_conn(msc_a->c.msub); @@ -1725,52 +1822,42 @@ int msc_tx_common_id(struct msc_a *msc_a, enum msc_role to_role) static int msc_a_start_assignment(struct msc_a *msc_a, struct gsm_trans *cc_trans) { - struct call_leg *cl = msc_a->cc.call_leg; - struct msc_i *msc_i = msc_a_msc_i(msc_a); - struct gsm_network *net = msc_a_net(msc_a); - enum mgcp_codecs codec, *codec_ptr; + struct call_leg *cl; + bool cn_rtp_available; + bool ran_rtp_available; + struct sdp_audio_codecs *codecs; OSMO_ASSERT(!msc_a->cc.active_trans); msc_a->cc.active_trans = cc_trans; OSMO_ASSERT(cc_trans && cc_trans->type == TRANS_CC); + cl = msc_a_ensure_call_leg(msc_a, cc_trans); + if (!cl) + return -EINVAL; - if (!cl) { - cl = msc_a->cc.call_leg = call_leg_alloc(msc_a->c.fi, - MSC_EV_CALL_LEG_TERM, - MSC_EV_CALL_LEG_RTP_LOCAL_ADDR_AVAILABLE, - MSC_EV_CALL_LEG_RTP_COMPLETE); - OSMO_ASSERT(cl); + /* See if we can set a preliminary codec. If not, pass none for the time being. */ + cc_sdp_filter(&cc_trans->cc.sdp); + codecs = cc_trans->cc.sdp.result.audio_codecs.count ? &cc_trans->cc.sdp.result.audio_codecs : NULL; - /* HACK: We put the connection in loopback mode at the beginning to - * trick the hNodeB into doing the IuUP negotiation with itself. - * This is a hack we need because osmo-mgw does not support IuUP yet, see OS#2459. */ - if (msc_a->c.ran->type == OSMO_RAT_UTRAN_IU) - cl->crcx_conn_mode[RTP_TO_RAN] = MGCP_CONN_LOOPBACK; - } + cn_rtp_available = call_leg_local_ip(cl, RTP_TO_CN); + ran_rtp_available = call_leg_local_ip(cl, RTP_TO_RAN); - if (net->use_osmux != OSMUX_USAGE_OFF) { - msc_i = msc_a_msc_i(msc_a); - if (msc_i->c.remote_to) { - /* TODO: investigate what to do in this case */ - LOG_MSC_A(msc_a, LOGL_ERROR, "Osmux not yet supported for inter-MSC"); - } else { - cl->ran_peer_supports_osmux = msc_i->ran_conn->ran_peer->remote_supports_osmux; - } - } + /* Set up RTP ports for both RAN and CN side. Even though we ask for both at the same time, the + * osmo_mgcpc_ep_fsm automagically waits for the first CRCX to complete before firing the second CRCX. The one + * issued first here will also be the first CRCX sent to the MGW. Usually both still need to be set up. */ + if (!cn_rtp_available) + call_leg_ensure_ci(cl, RTP_TO_CN, cc_trans->callref, cc_trans, codecs, NULL); + if (!ran_rtp_available) + call_leg_ensure_ci(cl, RTP_TO_RAN, cc_trans->callref, cc_trans, codecs, NULL); - /* This will lead to either MSC_EV_CALL_LEG_LOCAL_ADDR_AVAILABLE or MSC_EV_CALL_LEG_TERM. - * If the local address is already known, then immediately trigger. */ - if (call_leg_local_ip(cl, RTP_TO_RAN)) + /* Should these already be set up, immediately continue by retriggering the events signalling that the RTP + * ports are available. The ordering is: first CN, then RAN. */ + if (cn_rtp_available && ran_rtp_available) return osmo_fsm_inst_dispatch(msc_a->c.fi, MSC_EV_CALL_LEG_RTP_LOCAL_ADDR_AVAILABLE, cl->rtp[RTP_TO_RAN]); - - if (msc_a->c.ran->type == OSMO_RAT_UTRAN_IU) { - codec = CODEC_IUFP; - codec_ptr = &codec; - } else { - codec_ptr = NULL; - } - return call_leg_ensure_ci(msc_a->cc.call_leg, RTP_TO_RAN, cc_trans->callref, cc_trans, codec_ptr, NULL); + else if (cn_rtp_available) + return osmo_fsm_inst_dispatch(msc_a->c.fi, MSC_EV_CALL_LEG_RTP_LOCAL_ADDR_AVAILABLE, cl->rtp[RTP_TO_CN]); + /* Otherwise wait for MGCP response and continue from there. */ + return 0; } int msc_a_try_call_assignment(struct gsm_trans *cc_trans) |