do CN CRCX first
In order to send the MSC's RTP endpoint IP address+port in the initial
SDP, move the MGCP CRCX up to an earlier point in the sequence of
establishing a voice call.
Update the voice call sequence chart to show the effects.
Though the semantic change is rather simple, the patch is rather huge --
things have to happen in a different order, and async waits have to
happen at different times.
The new codec filter helps to carry codec resolution information across
the newly arranged code paths.
Related: SYS#5066
Change-Id: Ie433db1ba0c46d4b97538a969233c155cefac21c
diff --git a/src/libmsc/msc_a.c b/src/libmsc/msc_a.c
index a7424e0..c022ee2 100644
--- a/src/libmsc/msc_a.c
+++ b/src/libmsc/msc_a.c
@@ -564,6 +564,66 @@
}
}
+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);
+
+ 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);
+}
+
/* 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)
{
@@ -617,15 +677,6 @@
}
}
-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;
@@ -719,10 +770,7 @@
msc_a_call_leg_ran_local_addr_available(msc_a);
return;
case RTP_TO_CN:
- /* The rtp_stream has gotten the new RTP address and port from the MGW. Also update the codecs
- * filter result to convey this RTP address and port towards the remote call leg. */
- codec_filter_set_local_rtp(&msc_a->cc.active_trans->cc.codecs, &rtps->local);
- msc_a_call_leg_cn_local_addr_available(msc_a, rtps->for_trans);
+ cc_on_cn_local_rtp_port_known(rtps->for_trans);
return;
default:
LOG_MSC_A(msc_a, LOGL_ERROR, "Invalid data for %s\n", osmo_fsm_event_name(fi->fsm, event));
@@ -1341,7 +1389,7 @@
struct rtp_stream *rtps_to_ran = msc_a->cc.call_leg ? msc_a->cc.call_leg->rtp[RTP_TO_RAN] : NULL;
const struct gsm0808_speech_codec *codec_if_known = ac->assignment_complete.codec_present ?
&ac->assignment_complete.codec : NULL;
- const struct codec_mapping *codec_cn = NULL;
+ const struct codec_mapping *codec_cn;
if (!rtps_to_ran) {
LOG_MSC_A(msc_a, LOGL_ERROR, "Rx Assignment Complete, but no RTP stream is set up\n");
@@ -1400,23 +1448,19 @@
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);
/* Remember the Codec List (BSS Supported) */
if (ac->assignment_complete.codec_list_bss_supported)
codec_filter_set_bss(&cc_trans->cc.codecs, ac->assignment_complete.codec_list_bss_supported);
- /* 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. */
codec_filter_run(&cc_trans->cc.codecs);
- if (call_leg_ensure_ci(msc_a->cc.call_leg, RTP_TO_CN, cc_trans->callref, cc_trans,
- &cc_trans->cc.codecs.result.audio_codecs, NULL)) {
- LOG_MSC_A_CAT(msc_a, DCC, LOGL_ERROR, "Error creating MGW CI towards CN\n");
+ LOG_TRANS(cc_trans, LOGL_INFO, "Assignment Complete: RAN: %s, CN: %s\n",
+ sdp_audio_codecs_to_str(&rtps_to_ran->codecs),
+ sdp_audio_codecs_to_str(&cc_trans->cc.codecs.result.audio_codecs));
+
+ if (cc_on_assignment_done(cc_trans)) {
+ /* If an error occurred, it was logged in cc_assignment_done() */
call_leg_release(msc_a->cc.call_leg);
return;
}
@@ -1807,10 +1851,9 @@
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);
- struct sdp_audio_codecs *codecs;
+ struct call_leg *cl;
+ bool cn_rtp_available;
+ bool ran_rtp_available;
OSMO_ASSERT(!msc_a->cc.active_trans);
msc_a->cc.active_trans = cc_trans;
@@ -1818,36 +1861,39 @@
cc_trans->cc.codecs.assignment = (struct sdp_audio_codec){};
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);
- }
-
- 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;
- }
- }
-
- /* Make sure an MGW endpoint towards RAN is present. If it is already set up, "skip" to
- * MSC_EV_CALL_LEG_LOCAL_ADDR_AVAILABLE immediately. If not, set it up. */
- if (call_leg_local_ip(cl, RTP_TO_RAN))
- return osmo_fsm_inst_dispatch(msc_a->c.fi, MSC_EV_CALL_LEG_RTP_LOCAL_ADDR_AVAILABLE, cl->rtp[RTP_TO_RAN]);
-
+ /* See if we can set a preliminary codec. If not, pass none for the time being. */
codec_filter_run(&cc_trans->cc.codecs);
- if (msc_a->c.ran->force_mgw_codecs_to_ran.count)
- codecs = &msc_a->c.ran->force_mgw_codecs_to_ran;
- else
- codecs = &cc_trans->cc.codecs.result.audio_codecs;
- return call_leg_ensure_ci(msc_a->cc.call_leg, RTP_TO_RAN, cc_trans->callref, cc_trans, codecs, NULL);
+
+ cn_rtp_available = call_leg_local_ip(cl, RTP_TO_CN);
+ ran_rtp_available = call_leg_local_ip(cl, RTP_TO_RAN);
+
+ /* 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,
+ &cc_trans->cc.codecs.result.audio_codecs, NULL);
+ if (!ran_rtp_available) {
+ struct sdp_audio_codecs *codecs;
+ if (msc_a->c.ran->force_mgw_codecs_to_ran.count)
+ codecs = &msc_a->c.ran->force_mgw_codecs_to_ran;
+ else
+ codecs = &cc_trans->cc.codecs.result.audio_codecs;
+ return call_leg_ensure_ci(cl, RTP_TO_RAN, cc_trans->callref, cc_trans, codecs, NULL);
+ }
+
+ /* 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]);
+ 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)