Change in osmo-bsc[master]: assignment_fsm: fix channel allocator preferences

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

dexter gerrit-no-reply at lists.osmocom.org
Thu Jan 17 17:28:58 UTC 2019


dexter has uploaded this change for review. ( https://gerrit.osmocom.org/12625


Change subject: assignment_fsm: fix channel allocator preferences
......................................................................

assignment_fsm: fix channel allocator preferences

When the MSC allocates a channel through the ASSIGNMENT REQUEST, it may
ask for a TCH/H and a TCH/F at the same time and tell which of the two
types it prefers.

The process of channel allocation currently selects, based on the BTS,
MSC and MS capabilites exactly one apropriate codec/rate (e.g. TCH/H)
and then trys to allocate it. If that allocation fails, there is no way
to try the second choice and the assignment fails.

For example: The MSC asks for TCH/F and TCH/H, prefering TCH/F, then the
channel allocator will try TCH/F and if it fails (all TCH/F are
currently in use), then TCH/H is never tried.

Since the BSC currently only trys the first best codec/rate that is
supported it also ignores the preference.

Lets fix those problems by including the preference information and both
possible codec/rate settings into the channel allocation decision.

Change-Id: I5239e05c1cfbcb8af28f43373a58fa6c2d216c51
Related: OS#3503
---
M include/osmocom/bsc/codec_pref.h
M include/osmocom/bsc/gsm_data.h
M src/osmo-bsc/abis_rsl.c
M src/osmo-bsc/assignment_fsm.c
M src/osmo-bsc/codec_pref.c
M src/osmo-bsc/handover_fsm.c
M src/osmo-bsc/osmo_bsc_bssap.c
M tests/codec_pref/codec_pref_test.c
8 files changed, 366 insertions(+), 136 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/osmo-bsc refs/changes/25/12625/1

diff --git a/include/osmocom/bsc/codec_pref.h b/include/osmocom/bsc/codec_pref.h
index 51340c1..adefe04 100644
--- a/include/osmocom/bsc/codec_pref.h
+++ b/include/osmocom/bsc/codec_pref.h
@@ -9,14 +9,19 @@
 struct bts_codec_conf;
 struct bsc_msc_data;
 struct gsm_bts;
+struct channel_mode_and_rate;
 
-int match_codec_pref(enum gsm48_chan_mode *chan_mode,
-		     bool *full_rate,
-		     uint16_t *s15_s0,
+enum rate_pref {
+	RATE_PREF_NONE,
+	RATE_PREF_HR,
+	RATE_PREF_FR,
+};
+
+int match_codec_pref(struct channel_mode_and_rate *ch_mode_rate,
 		     const struct gsm0808_channel_type *ct,
 		     const struct gsm0808_speech_codec_list *scl,
 		     const struct bsc_msc_data *msc,
-		     const struct gsm_bts *bts);
+		     const struct gsm_bts *bts, enum rate_pref rate_pref);
 
 void gen_bss_supported_codec_list(struct gsm0808_speech_codec_list *scl,
 				  const struct bsc_msc_data *msc,
diff --git a/include/osmocom/bsc/gsm_data.h b/include/osmocom/bsc/gsm_data.h
index 22d80df..a161051 100644
--- a/include/osmocom/bsc/gsm_data.h
+++ b/include/osmocom/bsc/gsm_data.h
@@ -99,6 +99,12 @@
 	SUBSCR_SCCP_ST_CONNECTED
 };
 
+struct channel_mode_and_rate {
+	enum gsm48_chan_mode chan_mode;
+	bool full_rate;
+	uint16_t s15_s0;
+};
+
 struct assignment_request {
 	bool aoip;
 
@@ -107,9 +113,12 @@
 	char msc_rtp_addr[INET_ADDRSTRLEN];
 	uint16_t msc_rtp_port;
 
-	enum gsm48_chan_mode chan_mode;
-	bool full_rate;
-	uint16_t s15_s0;
+	/* Prefered rate/codec setting (mandatory) */
+	struct channel_mode_and_rate ch_mode_rate_pref;
+
+	/* Alternate rate/codec setting (optional) */
+	bool ch_mode_rate_alt_present;
+	struct channel_mode_and_rate ch_mode_rate_alt;
 };
 
 struct assignment_fsm_data {
@@ -282,6 +291,13 @@
 		/* pointer to "other" connection, if Call Leg Relocation was successful */
 		struct gsm_subscriber_connection *other;
 	} lcls;
+
+	/* Depending on the preferences that where submitted together with
+	 * the assignment and the current channel load, the BSC has to select
+	 * one of the offered codec/rates. The final selection by the BSC is
+	 * stored here and is used when sending the assignment complete or
+	 * when performing a handover procedure. */
+	struct channel_mode_and_rate ch_mode_rate;
 };
 
 
diff --git a/src/osmo-bsc/abis_rsl.c b/src/osmo-bsc/abis_rsl.c
index d67573f..c1e6bf0 100644
--- a/src/osmo-bsc/abis_rsl.c
+++ b/src/osmo-bsc/abis_rsl.c
@@ -1375,22 +1375,12 @@
 	/* check availability / allocate channel
 	 *
 	 * - First try to allocate SDCCH.
-	 * - If SDCCH is not available, try whatever MS requested, if not SDCCH.
-	 * - If there is still no channel available, reject channel request.
+	 * - If SDCCH is not available, try to a TCH/H (less bandwith).
+	 * - If there is still no channel available, try a TCH/F.
 	 *
-	 * Note: If the MS requests not TCH/H, we don't know if the phone
-	 *       supports TCH/H, so we must assign TCH/F or SDCCH.
 	 */
 	lchan = lchan_select_by_type(bts, GSM_LCHAN_SDCCH);
-	if (!lchan && lctype != GSM_LCHAN_SDCCH) {
-		LOGP(DRSL, LOGL_NOTICE, "(bts=%d) CHAN RQD: no resources for %s "
-			"0x%x, retrying with %s\n",
-			msg->lchan->ts->trx->bts->nr,
-			gsm_lchant_name(GSM_LCHAN_SDCCH), rqd_ref->ra,
-			gsm_lchant_name(lctype));
-		lchan = lchan_select_by_type(bts, lctype);
-	}
-	if (!lchan && lctype == GSM_LCHAN_SDCCH) {
+	if (!lchan) {
 		LOGP(DRSL, LOGL_NOTICE, "(bts=%d) CHAN RQD: no resources for %s "
 			"0x%x, retrying with %s\n",
 			msg->lchan->ts->trx->bts->nr,
@@ -1398,7 +1388,7 @@
 			gsm_lchant_name(GSM_LCHAN_TCH_H));
 		lchan = lchan_select_by_type(bts, GSM_LCHAN_TCH_H);
 	}
-	if (!lchan && lctype == GSM_LCHAN_SDCCH) {
+	if (!lchan) {
 		LOGP(DRSL, LOGL_NOTICE, "(bts=%d) CHAN RQD: no resources for %s "
 			"0x%x, retrying with %s\n",
 			msg->lchan->ts->trx->bts->nr,
diff --git a/src/osmo-bsc/assignment_fsm.c b/src/osmo-bsc/assignment_fsm.c
index a24f7f9..d61b4bb 100644
--- a/src/osmo-bsc/assignment_fsm.c
+++ b/src/osmo-bsc/assignment_fsm.c
@@ -170,7 +170,7 @@
 		if (gscon_is_aoip(conn)) {
 			/* Extrapolate speech codec from speech mode */
 			gsm0808_speech_codec_from_chan_type(&sc, perm_spch);
-			sc.cfg = conn->assignment.req.s15_s0;
+			sc.cfg = conn->ch_mode_rate.s15_s0;
 			sc_ptr = ≻
 		}
 	}
@@ -252,8 +252,11 @@
 }
 
 static bool lchan_type_compat_with_mode(enum gsm_chan_t type,
-					enum gsm48_chan_mode chan_mode, int full_rate)
+					struct channel_mode_and_rate *ch_mode_rate)
 {
+	enum gsm48_chan_mode chan_mode = ch_mode_rate->chan_mode;
+	int full_rate = ch_mode_rate->full_rate;
+
 	switch (chan_mode) {
 	case GSM48_CMODE_SIGN:
 		switch (type) {
@@ -296,6 +299,170 @@
 	OSMO_ASSERT(osmo_fsm_register(&assignment_fsm) == 0);
 }
 
+/* Check if the incoming assignment requests requires a voice stream or not,
+ * we will look at the preferred and the alternate channel mode and also make
+ * sure that both are consistent. */
+static int check_voice_stream_needed(struct gsm_subscriber_connection *conn, struct osmo_fsm_inst *fi)
+{
+	bool result_alt = false;
+	bool result_pref = false;
+	struct assignment_request *req = &conn->assignment.req;
+
+	/* When the assignment request indicates that there is an alternate
+	 * rate available (e.g. "Full or Half rate channel, Half rate
+	 * preferred..."), then both must be either voice or either signalling,
+	 * a mismatch is not permitted */
+
+	/* Check the prefered setting */
+	switch (req->ch_mode_rate_pref.chan_mode) {
+	case GSM48_CMODE_SPEECH_V1:
+	case GSM48_CMODE_SPEECH_EFR:
+	case GSM48_CMODE_SPEECH_AMR:
+		result_pref = true;
+		break;
+	case GSM48_CMODE_SIGN:
+		result_pref = false;
+		break;
+	default:
+		assignment_fail(GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_NOT_SUPP,
+				"Prefered channel mode not supported: %s",
+				gsm48_chan_mode_name(req->ch_mode_rate_pref.chan_mode));
+		return -EINVAL;
+	}
+	conn->assignment.requires_voice_stream = result_pref;
+
+	/* If there is an alternate setting, check that one as well */
+	if (!req->ch_mode_rate_alt_present)
+		return 0;
+	switch (req->ch_mode_rate_alt.chan_mode) {
+	case GSM48_CMODE_SPEECH_V1:
+	case GSM48_CMODE_SPEECH_EFR:
+	case GSM48_CMODE_SPEECH_AMR:
+		result_alt = true;
+		break;
+	case GSM48_CMODE_SIGN:
+		result_alt = false;
+		break;
+	default:
+		assignment_fail(GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_NOT_SUPP,
+				"Alternate channel mode not supported: %s",
+				gsm48_chan_mode_name(req->ch_mode_rate_alt.chan_mode));
+		return -EINVAL;
+	}
+
+	/* Make sure both settings match */
+	if (result_pref != result_alt) {
+		assignment_fail(GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_NOT_SUPP,
+				"Inconsistent channel modes: %s != %s",
+				gsm48_chan_mode_name(req->ch_mode_rate_pref.chan_mode),
+				gsm48_chan_mode_name(req->ch_mode_rate_alt.chan_mode));
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* Check if the conn is already associated with an lchan. If yes, we will check
+ * if that lchan is compatible with the preferred rate/codec. If the lchan
+ * turns out to be incompatible we try with the alternate rate/codec. */
+static int check_for_existing_lchan(struct gsm_subscriber_connection *conn)
+{
+	struct assignment_request *req = &conn->assignment.req;
+
+	if (!conn->lchan)
+		return 0;
+
+	/* Check if the currently existing lchan is compatible with the
+	 * preferred rate/codec. */
+	if (lchan_type_compat_with_mode(conn->lchan->type, &req->ch_mode_rate_pref)) {
+		if (conn->lchan->tch_mode == req->ch_mode_rate_pref.chan_mode) {
+			/* current lchan suffices and already is in the right mode. We're done. */
+			LOG_ASSIGNMENT(conn, LOGL_DEBUG,
+				       "Current lchan is compatible with requested chan_mode,"
+				       " sending BSSMAP Assignment Complete directly."
+				       " requested chan_mode=%s; current lchan is %s\n",
+				       gsm48_chan_mode_name(req->ch_mode_rate_pref.chan_mode),
+				       gsm_lchan_name(conn->lchan));
+
+			conn->ch_mode_rate = req->ch_mode_rate_pref;
+
+			send_assignment_complete(conn);
+			return 1;
+		}
+
+		if (!req->ch_mode_rate_alt_present) {
+			/* FIXME: send Channel Mode Modify to put the current lchan in the right mode, and kick
+			 * off its RTP stream setup code path. See gsm48_lchan_modify() and
+			 * gsm48_rx_rr_modif_ack(), and see lchan_fsm.h LCHAN_EV_CHAN_MODE_MODIF_* */
+			LOG_ASSIGNMENT(conn, LOGL_ERROR,
+				       "NOT IMPLEMENTED:"
+				       " Current lchan would be compatible, we should send Channel Mode Modify\n");
+			return 0;
+		}
+	}
+
+	/* Exit early when no alternate choice for a rate/codec exists. */
+	if (!req->ch_mode_rate_alt_present)
+		return 0;
+
+	/* In cases where the prefered rate/codec does is not compatible,
+	 * try the alternate rate/codec. */
+	if (lchan_type_compat_with_mode(conn->lchan->type, &req->ch_mode_rate_alt)) {
+		if (conn->lchan->tch_mode == req->ch_mode_rate_alt.chan_mode) {
+			/* current lchan suffices and already is in the right mode. We're done. */
+			LOG_ASSIGNMENT(conn, LOGL_DEBUG,
+				       "Current lchan is compatible with requested chan_mode,"
+				       " sending BSSMAP Assignment Complete directly."
+				       " requested chan_mode=%s; current lchan is %s\n",
+				       gsm48_chan_mode_name(req->ch_mode_rate_alt.chan_mode),
+				       gsm_lchan_name(conn->lchan));
+
+			conn->ch_mode_rate = req->ch_mode_rate_alt;
+
+			send_assignment_complete(conn);
+			return 1;
+		}
+
+		/* FIXME: send Channel Mode Modify to put the current lchan in the right mode, and kick
+		 * off its RTP stream setup code path. See gsm48_lchan_modify() and
+		 * gsm48_rx_rr_modif_ack(), and see lchan_fsm.h LCHAN_EV_CHAN_MODE_MODIF_* */
+		LOG_ASSIGNMENT(conn, LOGL_ERROR,
+			       "NOT IMPLEMENTED:"
+			       " Current lchan would be compatible, we should send Channel Mode Modify\n");
+	}
+
+	return 0;
+}
+
+/* Check if a new lchan finally exists, if not log the problem and inform the
+ * MSC by sending an BSSMAP assignment failure */
+static int check_new_lchan(struct gsm_subscriber_connection *conn, struct osmo_fsm_inst *fi)
+{
+	struct assignment_request *req = &conn->assignment.req;
+
+	if (!conn->assignment.new_lchan && req->ch_mode_rate_alt_present) {
+		assignment_count_result(BSC_CTR_ASSIGNMENT_NO_CHANNEL);
+		assignment_fail(GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE,
+				"BSSMAP Assignment Command:"
+				" No lchan available for: chan_mode_pref=%s, full_rate_pref=%i / chan_mode_alt=%s, full_rate_alt=%i\n",
+				get_value_string(gsm48_chan_mode_names, req->ch_mode_rate_pref.chan_mode),
+				req->ch_mode_rate_pref.full_rate, get_value_string(gsm48_chan_mode_names,
+										   req->ch_mode_rate_alt.chan_mode),
+				req->ch_mode_rate_alt.full_rate);
+		return -1;
+	} else if (!conn->assignment.new_lchan) {
+		assignment_count_result(BSC_CTR_ASSIGNMENT_NO_CHANNEL);
+		assignment_fail(GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE,
+				"BSSMAP Assignment Command:"
+				" No lchan available for: chan_mode=%s, full_rate=%i\n",
+				get_value_string(gsm48_chan_mode_names, req->ch_mode_rate_pref.chan_mode),
+				req->ch_mode_rate_pref.full_rate);
+		return -1;
+	}
+
+	return 0;
+}
+
 void assignment_fsm_start(struct gsm_subscriber_connection *conn, struct gsm_bts *bts,
 			  struct assignment_request *req)
 {
@@ -314,75 +481,50 @@
 	conn->assignment.fi = fi;
 	fi->priv = conn;
 
+	/* Create a copy of the request data and use that copy from now on. */
 	conn->assignment.req = *req;
+	req = &conn->assignment.req;
 
-	switch (req->chan_mode) {
-
-	case GSM48_CMODE_SPEECH_V1:
-	case GSM48_CMODE_SPEECH_EFR:
-	case GSM48_CMODE_SPEECH_AMR:
-		conn->assignment.requires_voice_stream = true;
-		/* Select an lchan below. */
-		break;
-
-	case GSM48_CMODE_SIGN:
-		conn->assignment.requires_voice_stream = false;
-		/* Select an lchan below. */
-		break;
-
-	default:
-		assignment_fail(GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_NOT_SUPP,
-				"Channel mode not supported: %s",
-				gsm48_chan_mode_name(req->chan_mode));
+	/* Check if we need a voice stream. If yes, set the approriate struct
+	 * members in conn */
+	if (check_voice_stream_needed(conn, fi) < 0)
 		return;
-	}
 
-	if (conn->lchan
-	    && lchan_type_compat_with_mode(conn->lchan->type, req->chan_mode, req->full_rate)) {
-
-		if (conn->lchan->tch_mode == req->chan_mode) {
-			/* current lchan suffices and already is in the right mode. We're done. */
-			LOG_ASSIGNMENT(conn, LOGL_DEBUG,
-				       "Current lchan is compatible with requested chan_mode,"
-				       " sending BSSMAP Assignment Complete directly."
-				       " requested chan_mode=%s; current lchan is %s\n",
-				       gsm48_chan_mode_name(req->chan_mode),
-				       gsm_lchan_name(conn->lchan));
-			send_assignment_complete(conn);
-			return;
-		}
-
-		/* FIXME: send Channel Mode Modify to put the current lchan in the right mode, and kick
-		 * off its RTP stream setup code path. See gsm48_lchan_modify() and
-		 * gsm48_rx_rr_modif_ack(), and see lchan_fsm.h LCHAN_EV_CHAN_MODE_MODIF_* */
-		LOG_ASSIGNMENT(conn, LOGL_ERROR,
-			       "NOT IMPLEMENTED:"
-			       " Current lchan would be compatible, we should send Channel Mode Modify\n");
-	}
-
-	conn->assignment.new_lchan = lchan_select_by_chan_mode(bts, req->chan_mode, req->full_rate);
-
-	if (!conn->assignment.new_lchan) {
-		assignment_count_result(BSC_CTR_ASSIGNMENT_NO_CHANNEL);
-		assignment_fail(GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE,
-				"BSSMAP Assignment Command:"
-				" No lchan available for: chan_mode=%s, full_rate=%i\n",
-				get_value_string(gsm48_chan_mode_names, req->chan_mode), req->full_rate);
+	/* There may be an already existing lchan, if yes, try to work with
+	 * the existing lchan */
+	if (check_for_existing_lchan(conn))
 		return;
+
+	/* Try to allocate a new lchan with the preferred codec/rate choice */
+	conn->assignment.new_lchan =
+	    lchan_select_by_chan_mode(bts, req->ch_mode_rate_pref.chan_mode, req->ch_mode_rate_pref.full_rate);
+	conn->ch_mode_rate = req->ch_mode_rate_pref;
+
+	/* In case the lchan allocation fails, we try with the alternat
+	 * codec/rate choice (if possible) */
+	if (!conn->assignment.new_lchan && req->ch_mode_rate_alt_present) {
+		conn->assignment.new_lchan =
+		    lchan_select_by_chan_mode(bts, req->ch_mode_rate_alt.chan_mode, req->ch_mode_rate_alt.full_rate);
+		conn->ch_mode_rate = req->ch_mode_rate_alt;
 	}
 
+	/* Check whether the lchan allocation was successful or not and tear
+	 * down the assignment in case of failure. */
+	if (check_new_lchan(conn, fi) < 0)
+		return;
+
 	assignment_fsm_update_id(conn);
 	LOG_ASSIGNMENT(conn, LOGL_INFO, "Starting Assignment: chan_mode=%s, full_rate=%d,"
 		       " aoip=%s MSC-rtp=%s:%u\n",
-		       gsm48_chan_mode_name(req->chan_mode), req->full_rate,
+		       gsm48_chan_mode_name(conn->ch_mode_rate.chan_mode), conn->ch_mode_rate.full_rate,
 		       req->aoip ? "yes" : "no", req->msc_rtp_addr, req->msc_rtp_port);
 
 	assignment_fsm_state_chg(ASSIGNMENT_ST_WAIT_LCHAN_ACTIVE);
 	info = (struct lchan_activate_info){
 		.activ_for = FOR_ASSIGNMENT,
 		.for_conn = conn,
-		.chan_mode = req->chan_mode,
-		.s15_s0 = req->s15_s0,
+		.chan_mode = conn->ch_mode_rate.chan_mode,
+		.s15_s0 = conn->ch_mode_rate.s15_s0,
 		.requires_voice_stream = conn->assignment.requires_voice_stream,
 		.msc_assigned_cic = req->msc_assigned_cic,
 		.re_use_mgw_endpoint_from_lchan = conn->lchan,
diff --git a/src/osmo-bsc/codec_pref.c b/src/osmo-bsc/codec_pref.c
index c99c383..a94d6a8 100644
--- a/src/osmo-bsc/codec_pref.c
+++ b/src/osmo-bsc/codec_pref.c
@@ -266,21 +266,18 @@
 
 /*! Match the codec preferences from local config with a received codec preferences IEs received from the
  * MSC and the BTS' codec configuration.
- *  \param[out] chan_mode GSM 04.08 channel mode.
- *  \param[out] full_rate true if full-rate.
- *  \param[out] s15_s0 codec configuration bits S15-S0 (AMR)
+ *  \param[out] ch_mode_rate resulting codec and rate information
  *  \param[in] ct GSM 08.08 channel type received from MSC.
  *  \param[in] scl GSM 08.08 speech codec list received from MSC (optional).
  *  \param[in] msc associated msc (current codec settings).
  *  \param[in] bts associated bts (current codec settings).
+ *  \param[in] pref selected rate preference (full, half or none).
  *  \returns 0 on success, -1 in case no match was found */
-int match_codec_pref(enum gsm48_chan_mode *chan_mode,
-		     bool *full_rate,
-		     uint16_t *s15_s0,
+int match_codec_pref(struct channel_mode_and_rate *ch_mode_rate,
 		     const struct gsm0808_channel_type *ct,
 		     const struct gsm0808_speech_codec_list *scl,
 		     const struct bsc_msc_data *msc,
-		     const struct gsm_bts *bts)
+		     const struct gsm_bts *bts, enum rate_pref rate_pref)
 {
 	unsigned int i;
 	uint8_t perm_spch;
@@ -297,6 +294,18 @@
 		/* Pick a permitted speech value from the global codec configuration list */
 		perm_spch = audio_support_to_gsm88(msc->audio_support[i]);
 
+		/* Determine if the result is a half or full rate codec */
+		rc = full_rate_from_perm_spch(&ch_mode_rate->full_rate, perm_spch);
+		if (rc < 0)
+			return -EINVAL;
+
+		/* If we have a preference for FR or HR in our request, we
+		 * discard the potential match */
+		if (rate_pref == RATE_PREF_HR && ch_mode_rate->full_rate)
+			continue;
+		if (rate_pref == RATE_PREF_FR && !ch_mode_rate->full_rate)
+			continue;
+
 		/* Check this permitted speech value against the BTS specific parameters.
 		 * if the BTS does not support the codec, try the next one */
 		if (!test_codec_support_bts(bts, perm_spch))
@@ -312,19 +321,14 @@
 
 	/* Exit without result, in case no match can be deteched */
 	if (!match) {
-		*full_rate = false;
-		*chan_mode = GSM48_CMODE_SIGN;
-		*s15_s0 = 0;
+		ch_mode_rate->full_rate = false;
+		ch_mode_rate->chan_mode = GSM48_CMODE_SIGN;
+		ch_mode_rate->s15_s0 = 0;
 		return -1;
 	}
 
-	/* Determine if the result is a half or full rate codec */
-	rc = full_rate_from_perm_spch(full_rate, perm_spch);
-	if (rc < 0)
-		return -EINVAL;
-
 	/* Lookup a channel mode for the selected codec */
-	*chan_mode = gsm88_to_chan_mode(perm_spch);
+	ch_mode_rate->chan_mode = gsm88_to_chan_mode(perm_spch);
 
 	/* Special handling for AMR */
 	if (perm_spch == GSM0808_PERM_HR3 || perm_spch == GSM0808_PERM_FR3) {
@@ -337,9 +341,9 @@
 		 * result */
 		amr_s15_s0_supported = gen_bss_supported_amr_s15_s0(msc, bts, (perm_spch == GSM0808_PERM_HR3));
 		if (sc_match)
-			*s15_s0 = sc_match->cfg & amr_s15_s0_supported;
+			ch_mode_rate->s15_s0 = sc_match->cfg & amr_s15_s0_supported;
 		else
-			*s15_s0 = amr_s15_s0_supported;
+			ch_mode_rate->s15_s0 = amr_s15_s0_supported;
 
 		/* NOTE: The function test_codec_pref() will populate the
 		 * sc_match pointer from the searched speech codec list. For
@@ -349,7 +353,7 @@
 		 * However s15_s0 is always populated with a meaningful value,
 		 * regardless if AoIP is in use or not. */
 	} else
-		*s15_s0 = 0;
+		ch_mode_rate->s15_s0 = 0;
 
 	return 0;
 }
diff --git a/src/osmo-bsc/handover_fsm.c b/src/osmo-bsc/handover_fsm.c
index 35f2e55..dc89ca4 100644
--- a/src/osmo-bsc/handover_fsm.c
+++ b/src/osmo-bsc/handover_fsm.c
@@ -521,10 +521,8 @@
 	struct bsc_msc_data *msc = conn->sccp.msc;
 	struct handover_in_req *req = &ho->inter_bsc_in;
 	int match_idx;
-	enum gsm48_chan_mode mode;
-	bool full_rate = false;
-	uint16_t s15_s0;
 	struct osmo_fsm_inst *fi;
+	struct channel_mode_and_rate ch_mode_rate = {};
 
 	handover_fsm_alloc(conn);
 
@@ -562,7 +560,7 @@
 		       bts->nr, req->cell_id_target_name);
 
 		/* Figure out channel type */
-		if (match_codec_pref(&mode, &full_rate, &s15_s0, &req->ct, &req->scl, msc, bts)) {
+		if (match_codec_pref(&ch_mode_rate, &req->ct, &req->scl, msc, bts, RATE_PREF_NONE)) {
 			LOG_HO(conn, LOGL_DEBUG,
 			       "BTS %u has no matching channel codec (%s, speech codec list len = %u)\n",
 			       bts->nr, gsm0808_channel_type_name(&req->ct), req->scl.len);
@@ -570,10 +568,10 @@
 		}
 
 		LOG_HO(conn, LOGL_DEBUG, "BTS %u: Found matching audio type: %s %s (for %s)\n",
-		       bts->nr, gsm48_chan_mode_name(mode), full_rate? "full-rate" : "half-rate",
+		       bts->nr, gsm48_chan_mode_name(ch_mode_rate.chan_mode), ch_mode_rate.full_rate? "full-rate" : "half-rate",
 		       gsm0808_channel_type_name(&req->ct));
 
-		lchan = lchan_select_by_chan_mode(bts, mode, full_rate);
+		lchan = lchan_select_by_chan_mode(bts, ch_mode_rate.chan_mode, ch_mode_rate.full_rate);
 		if (!lchan) {
 			LOG_HO(conn, LOGL_DEBUG, "BTS %u has no matching free channels\n", bts->nr);
 			continue;
@@ -605,9 +603,9 @@
 	info = (struct lchan_activate_info){
 		.activ_for = FOR_HANDOVER,
 		.for_conn = conn,
-		.chan_mode = mode,
-		.s15_s0 = s15_s0,
-		.requires_voice_stream = chan_mode_is_tch(mode),
+		.chan_mode = ch_mode_rate.chan_mode,
+		.s15_s0 = ch_mode_rate.s15_s0,
+		.requires_voice_stream = chan_mode_is_tch(ch_mode_rate.chan_mode),
 		.msc_assigned_cic = req->msc_assigned_cic,
 	};
 
@@ -715,7 +713,7 @@
 		if (gscon_is_aoip(conn)) {
 			/* Extrapolate speech codec from speech mode */
 			gsm0808_speech_codec_from_chan_type(&sc, ho_perf_params.speech_version_chosen);
-			sc.cfg = conn->assignment.req.s15_s0;
+			sc.cfg = conn->ch_mode_rate.s15_s0;
 			memcpy(&ho_perf_params.speech_codec_chosen, &sc, sizeof(sc));
 			ho_perf_params.speech_codec_chosen_present = true;
 		}
diff --git a/src/osmo-bsc/osmo_bsc_bssap.c b/src/osmo-bsc/osmo_bsc_bssap.c
index f2ccf2c..7b3cd25 100644
--- a/src/osmo-bsc/osmo_bsc_bssap.c
+++ b/src/osmo-bsc/osmo_bsc_bssap.c
@@ -599,6 +599,97 @@
 	return 0;
 }
 
+/* Select a prefered and an alternative codec rate depending on the available
+ * capabilities. This decision does not include the actual channel load yet,
+ * this is also the reason why the result is a prefered and an alternate
+ * setting. The final decision is made in assignment_fsm.c when the actual
+ * lchan is requested. The preferred lchan will be requested first. If we
+ * find an alternate setting here, this one will be tried secondly if our
+ * primary coice fails. */
+static int select_codec(struct assignment_request *req, struct gsm0808_channel_type *ct,
+			struct gsm_subscriber_connection *conn)
+{
+	int rc;
+	struct bsc_msc_data *msc;
+
+	msc = conn->sccp.msc;
+	req->ch_mode_rate_alt_present = false;
+
+	switch (ct->ch_rate_type & GSM0808_SPEECH_PERM) {
+	case GSM0808_DATA_FULL_BM:
+		rc = match_codec_pref(&req->ch_mode_rate_pref, ct, &conn->codec_list, msc, conn_get_bts(conn),
+				      RATE_PREF_FR);
+		break;
+	case GSM0808_DATA_HALF_LM:
+		rc = match_codec_pref(&req->ch_mode_rate_pref, ct, &conn->codec_list, msc, conn_get_bts(conn),
+				      RATE_PREF_HR);
+		break;
+	case GSM0808_DATA_FULL_RPREF:
+		rc = match_codec_pref(&req->ch_mode_rate_pref, ct, &conn->codec_list, msc, conn_get_bts(conn),
+				      RATE_PREF_FR);
+		if (rc < 0) {
+			rc = match_codec_pref(&req->ch_mode_rate_pref, ct, &conn->codec_list, msc, conn_get_bts(conn),
+					      RATE_PREF_NONE);
+			break;
+		}
+		rc = match_codec_pref(&req->ch_mode_rate_alt, ct, &conn->codec_list, msc, conn_get_bts(conn),
+				      RATE_PREF_HR);
+		if (rc == 0)
+			req->ch_mode_rate_alt_present = true;
+		rc = 0;
+		break;
+	case GSM0808_DATA_HALF_PREF:
+		rc = match_codec_pref(&req->ch_mode_rate_pref, ct, &conn->codec_list, msc, conn_get_bts(conn),
+				      RATE_PREF_HR);
+
+		if (rc < 0) {
+			rc = match_codec_pref(&req->ch_mode_rate_pref, ct, &conn->codec_list, msc, conn_get_bts(conn),
+					      RATE_PREF_NONE);
+			break;
+		}
+		rc = match_codec_pref(&req->ch_mode_rate_alt, ct, &conn->codec_list, msc, conn_get_bts(conn),
+				      RATE_PREF_FR);
+		if (rc == 0)
+			req->ch_mode_rate_alt_present = true;
+		rc = 0;
+		break;
+	default:
+		rc = -EINVAL;
+		break;
+	}
+
+	if (rc < 0) {
+		LOGP(DMSC, LOGL_ERROR, "No supported audio type found for channel_type ="
+		     " { ch_indctr=0x%x, ch_rate_type=0x%x, perm_spch=[%s] }\n",
+		     ct->ch_indctr, ct->ch_rate_type, osmo_hexdump(ct->perm_spch, ct->perm_spch_len));
+		/* TODO: actually output codec names, e.g. implement
+		 * gsm0808_permitted_speech_names[] and iterate perm_spch. */
+		return -EINVAL;
+	}
+
+	if (req->ch_mode_rate_alt_present) {
+		DEBUGP(DMSC, "Found matching audio type (preferred): %s %s for channel_type ="
+		       " { ch_indctr=0x%x, ch_rate_type=0x%x, perm_spch=[ %s] }\n",
+		       req->ch_mode_rate_pref.full_rate ? "full rate" : "half rate",
+		       get_value_string(gsm48_chan_mode_names, req->ch_mode_rate_pref.chan_mode),
+		       ct->ch_indctr, ct->ch_rate_type, osmo_hexdump(ct->perm_spch, ct->perm_spch_len));
+		DEBUGP(DMSC, "Found matching audio type (alternative): %s %s for channel_type ="
+		       " { ch_indctr=0x%x, ch_rate_type=0x%x, perm_spch=[ %s] }\n",
+		       req->ch_mode_rate_alt.full_rate ? "full rate" : "half rate",
+		       get_value_string(gsm48_chan_mode_names, req->ch_mode_rate_alt.chan_mode),
+		       ct->ch_indctr, ct->ch_rate_type, osmo_hexdump(ct->perm_spch, ct->perm_spch_len));
+	} else {
+		DEBUGP(DMSC, "Found matching audio type: %s %s for channel_type ="
+		       " { ch_indctr=0x%x, ch_rate_type=0x%x, perm_spch=[ %s] }\n",
+		       req->ch_mode_rate_pref.full_rate ? "full rate" : "half rate",
+		       get_value_string(gsm48_chan_mode_names, req->ch_mode_rate_pref.chan_mode),
+		       ct->ch_indctr, ct->ch_rate_type, osmo_hexdump(ct->perm_spch, ct->perm_spch_len));
+
+	}
+
+	return 0;
+}
+
 /*
  * Handle the assignment request message.
  *
@@ -608,18 +699,15 @@
 				     struct msgb *msg, unsigned int length)
 {
 	struct msgb *resp;
-	struct bsc_msc_data *msc;
 	struct tlv_parsed tp;
 	uint16_t cic = 0;
-	enum gsm48_chan_mode chan_mode = GSM48_CMODE_SIGN;
-	bool full_rate = false;
-	uint16_t s15_s0 = 0;
 	bool aoip = false;
 	struct sockaddr_storage rtp_addr;
 	struct gsm0808_channel_type ct;
 	uint8_t cause;
 	int rc;
 	struct assignment_request req = {};
+	struct channel_mode_and_rate ch_mode_rate_pref = {};
 
 	if (!conn) {
 		LOGP(DMSC, LOGL_ERROR,
@@ -627,7 +715,6 @@
 		return -1;
 	}
 
-	msc = conn->sccp.msc;
 	aoip = gscon_is_aoip(conn);
 
 	tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, length - 1, 0, 0);
@@ -715,33 +802,19 @@
 			goto reject;
 		}
 
+		req = (struct assignment_request){
+			.aoip = aoip,
+			.msc_assigned_cic = cic,
+		};
+
 		/* Match codec information from the assignment command against the
 		 * local preferences of the BSC and BTS */
-		rc = match_codec_pref(&chan_mode, &full_rate, &s15_s0, &ct, &conn->codec_list,
-				      msc, conn_get_bts(conn));
+		rc = select_codec(&req, &ct, conn);
 		if (rc < 0) {
-			LOGP(DMSC, LOGL_ERROR, "No supported audio type found for channel_type ="
-			     " { ch_indctr=0x%x, ch_rate_type=0x%x, perm_spch=[ %s] }\n",
-			     ct.ch_indctr, ct.ch_rate_type, osmo_hexdump(ct.perm_spch, ct.perm_spch_len));
-			/* TODO: actually output codec names, e.g. implement
-			 * gsm0808_permitted_speech_names[] and iterate perm_spch. */
 			cause = GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_UNAVAIL;
 			goto reject;
 		}
 
-		DEBUGP(DMSC, "Found matching audio type: %s %s for channel_type ="
-		       " { ch_indctr=0x%x, ch_rate_type=0x%x, perm_spch=[ %s] }\n",
-		       full_rate? "full rate" : "half rate",
-		       get_value_string(gsm48_chan_mode_names, chan_mode),
-		       ct.ch_indctr, ct.ch_rate_type, osmo_hexdump(ct.perm_spch, ct.perm_spch_len));
-
-		req = (struct assignment_request){
-			.aoip = aoip,
-			.msc_assigned_cic = cic,
-			.chan_mode = chan_mode,
-			.full_rate = full_rate,
-			.s15_s0 = s15_s0
-		};
 		if (aoip) {
 			unsigned int rc = osmo_sockaddr_to_str_and_uint(req.msc_rtp_addr,
 									sizeof(req.msc_rtp_addr),
@@ -755,9 +828,13 @@
 		}
 		break;
 	case GSM0808_CHAN_SIGN:
+		ch_mode_rate_pref = (struct channel_mode_and_rate) {
+			.chan_mode = GSM48_CMODE_SIGN,
+		};
+
 		req = (struct assignment_request){
 			.aoip = aoip,
-			.chan_mode = chan_mode,
+			.ch_mode_rate_pref = ch_mode_rate_pref,
 		};
 		break;
 	default:
diff --git a/tests/codec_pref/codec_pref_test.c b/tests/codec_pref/codec_pref_test.c
index 534b99e..bb5468a 100644
--- a/tests/codec_pref/codec_pref_test.c
+++ b/tests/codec_pref/codec_pref_test.c
@@ -380,9 +380,7 @@
 {
 	int rc;
 	unsigned int i;
-	bool full_rate;
-	enum gsm48_chan_mode chan_mode;
-	uint16_t s15_s0;
+	struct channel_mode_and_rate ch_mode_rate = { };
 
 	printf("Determining channel mode and rate:\n");
 
@@ -407,9 +405,9 @@
 	printf("   codec->efr=%u\n", bts->codec.efr);
 	printf("   codec->amr=%u\n", bts->codec.amr);
 
-	rc = match_codec_pref(&chan_mode, &full_rate, &s15_s0, ct, scl, msc, bts);
+	rc = match_codec_pref(&ch_mode_rate, ct, scl, msc, bts, RATE_PREF_NONE);
 	printf(" * result: rc=%i, full_rate=%i, s15_s0=%04x, chan_mode=%s\n",
-	       rc, full_rate, s15_s0, gsm48_chan_mode_name(chan_mode));
+	       rc, ch_mode_rate.full_rate, ch_mode_rate.s15_s0, gsm48_chan_mode_name(ch_mode_rate.chan_mode));
 
 	printf("\n");
 

-- 
To view, visit https://gerrit.osmocom.org/12625
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-bsc
Gerrit-Branch: master
Gerrit-MessageType: newchange
Gerrit-Change-Id: I5239e05c1cfbcb8af28f43373a58fa6c2d216c51
Gerrit-Change-Number: 12625
Gerrit-PatchSet: 1
Gerrit-Owner: dexter <pmaier at sysmocom.de>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20190117/9ff7f0f0/attachment.htm>


More information about the gerrit-log mailing list