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/.

Harald Welte gerrit-no-reply at lists.osmocom.org
Sat Feb 23 15:14:04 UTC 2019


Harald Welte has submitted this change and it was merged. ( 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 tries 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/assignment_fsm.c
M src/osmo-bsc/bsc_vty.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, 321 insertions(+), 113 deletions(-)

Approvals:
  Jenkins Builder: Verified
  tnt: Looks good to me, but someone else must approve
  Harald Welte: Looks good to me, approved



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 f6c5129..4d27a2e 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;
+};
+
 /* Information retrieved during an Assignment Request from the MSC. This is storage of the Assignment instructions
  * parsed from the Assignment Request message, to pass on until the gscon and assignment FSMs have decided whether an
  * Assignment is actually going to be carried out. Should remain unchanged after initial decoding. */
@@ -110,9 +116,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;
 };
 
 /* State of an ongoing Assignment, while the assignment_fsm is still busy. This serves as state separation to keep the
@@ -629,6 +638,13 @@
 	struct gsm48_req_ref *rqd_ref;
 
 	struct gsm_subscriber_connection *conn;
+
+	/* 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;
 };
 
 /* One Timeslot in a TRX */
diff --git a/src/osmo-bsc/assignment_fsm.c b/src/osmo-bsc/assignment_fsm.c
index 2ad3959..67937d6 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->lchan->ch_mode_rate.s15_s0;
 			sc_ptr = ≻
 		}
 	}
@@ -248,9 +248,11 @@
 				  new_lchan->nr);
 }
 
-static bool lchan_type_compat_with_mode(enum gsm_chan_t type,
-					enum gsm48_chan_mode chan_mode, int full_rate)
+static bool lchan_type_compat_with_mode(enum gsm_chan_t type, const struct channel_mode_and_rate *ch_mode_rate)
 {
+	enum gsm48_chan_mode chan_mode = ch_mode_rate->chan_mode;
+	bool full_rate = ch_mode_rate->full_rate;
+
 	switch (chan_mode) {
 	case GSM48_CMODE_SIGN:
 		switch (type) {
@@ -293,6 +295,107 @@
 	OSMO_ASSERT(osmo_fsm_register(&assignment_fsm) == 0);
 }
 
+static int check_requires_voice(bool *requires_voice, enum gsm48_chan_mode chan_mode)
+{
+	*requires_voice = false;
+
+	switch (chan_mode) {
+	case GSM48_CMODE_SPEECH_V1:
+	case GSM48_CMODE_SPEECH_EFR:
+	case GSM48_CMODE_SPEECH_AMR:
+		*requires_voice = true;
+		break;
+	case GSM48_CMODE_SIGN:
+		*requires_voice = false;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 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_requires_voice_stream(struct gsm_subscriber_connection *conn)
+{
+	bool result_requires_voice_alt;
+	bool result_requires_voice_pref;
+	struct assignment_request *req = &conn->assignment.req;
+	struct osmo_fsm_inst *fi = conn->fi;
+	int rc;
+
+	/* 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 */
+	rc = check_requires_voice(&result_requires_voice_pref, req->ch_mode_rate_pref.chan_mode);
+	if (rc < 0) {
+		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_requires_voice_pref;
+
+	/* If there is an alternate setting, check that one as well */
+	if (!req->ch_mode_rate_alt_present)
+		return 0;
+	rc = check_requires_voice(&result_requires_voice_alt, req->ch_mode_rate_alt.chan_mode);
+	if (rc < 0) {
+		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_requires_voice_pref != result_requires_voice_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 bool reuse_existing_lchan(struct gsm_subscriber_connection *conn)
+{
+	struct assignment_request *req = &conn->assignment.req;
+
+	if (!conn->lchan)
+		return false;
+
+	/* 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))
+		conn->lchan->ch_mode_rate = req->ch_mode_rate_pref;
+	else if (req->ch_mode_rate_alt_present
+		 && lchan_type_compat_with_mode(conn->lchan->type, &req->ch_mode_rate_alt))
+		conn->lchan->ch_mode_rate = req->ch_mode_rate_alt;
+	else
+		return false;
+
+	if (conn->lchan->tch_mode != conn->lchan->ch_mode_rate.chan_mode) {
+		/* 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_DEBUG,
+			       "Current lchan would be compatible, but Channel Mode Modify is not implemented\n");
+		return false;
+	}
+
+	return true;
+}
+
 void assignment_fsm_start(struct gsm_subscriber_connection *conn, struct gsm_bts *bts,
 			  struct assignment_request *req)
 {
@@ -311,75 +414,77 @@
 	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) {
+	/* Check if we need a voice stream. If yes, set the approriate struct
+	 * members in conn */
+	if (check_requires_voice_stream(conn) < 0)
+		return;
 
-	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;
+	/* There may be an already existing lchan, if yes, try to work with
+	 * the existing lchan */
+	if (reuse_existing_lchan(conn)) {
+		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(conn->lchan->ch_mode_rate.chan_mode),
+			       gsm_lchan_name(conn->lchan));
 
-	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));
+		send_assignment_complete(conn);
+		/* If something went wrong during send_assignment_complete(), the fi will be gone from
+		 * error handling in there. */
+		if (conn->assignment.fi) {
+			assignment_count_result(BSC_CTR_ASSIGNMENT_COMPLETED);
+			osmo_fsm_inst_term(conn->assignment.fi, OSMO_FSM_TERM_REGULAR, 0);
+		}
 		return;
 	}
 
-	if (conn->lchan
-	    && lchan_type_compat_with_mode(conn->lchan->type, req->chan_mode, req->full_rate)) {
+	/* 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->lchan->ch_mode_rate = req->ch_mode_rate_pref;
 
-		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");
+	/* 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->lchan->ch_mode_rate = req->ch_mode_rate_alt;
 	}
 
-	conn->assignment.new_lchan = lchan_select_by_chan_mode(bts, req->chan_mode, req->full_rate);
-
+	/* Check whether the lchan allocation was successful or not and tear
+	 * down the assignment in case of failure. */
 	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);
+				" No lchan available for: preferred=%s%s / alternate=%s%s\n",
+				gsm48_chan_mode_name(req->ch_mode_rate_pref.chan_mode),
+				req->ch_mode_rate_pref.full_rate ? ",FR" : ",HR",
+				req->ch_mode_rate_alt_present ?
+					gsm48_chan_mode_name(req->ch_mode_rate_alt.chan_mode) : "none",
+				req->ch_mode_rate_alt_present ?
+					(req->ch_mode_rate_alt.full_rate ? ",FR" : ",HR") : "");
 		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->lchan->ch_mode_rate.chan_mode),
+		       conn->lchan->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->lchan->ch_mode_rate.chan_mode,
+		.s15_s0 = conn->lchan->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/bsc_vty.c b/src/osmo-bsc/bsc_vty.c
index 357ee9e..9413d36 100644
--- a/src/osmo-bsc/bsc_vty.c
+++ b/src/osmo-bsc/bsc_vty.c
@@ -4802,6 +4802,7 @@
 	ts = vty_get_ts(vty, argv[0], argv[1], argv[2]);
 	if (!ts)
 		return CMD_WARNING;
+
 	lchan = &ts->lchan[ss_nr];
 	if (!lchan->fi)
 		return CMD_WARNING;
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 578cff3..30297f6 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->lchan->s15_s0;
+			sc.cfg = conn->lchan->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 ab7f79c..85aab22 100644
--- a/src/osmo-bsc/osmo_bsc_bssap.c
+++ b/src/osmo-bsc/osmo_bsc_bssap.c
@@ -616,6 +616,101 @@
 	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_codecs(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) {
+	case GSM0808_SPEECH_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_SPEECH_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_SPEECH_PERM:
+	case GSM0808_SPEECH_PERM_NO_CHANGE:
+	case GSM0808_SPEECH_FULL_PREF_NO_CHANGE:
+	case GSM0808_SPEECH_FULL_PREF:
+		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_HR);
+			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_SPEECH_HALF_PREF_NO_CHANGE:
+	case GSM0808_SPEECH_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_FR);
+			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.
  *
@@ -625,18 +720,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,
@@ -644,7 +736,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);
@@ -732,33 +823,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_codecs(&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),
@@ -772,9 +849,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: merged
Gerrit-Change-Id: I5239e05c1cfbcb8af28f43373a58fa6c2d216c51
Gerrit-Change-Number: 12625
Gerrit-PatchSet: 12
Gerrit-Owner: dexter <pmaier at sysmocom.de>
Gerrit-Assignee: Neels Hofmeyr <nhofmeyr at sysmocom.de>
Gerrit-Reviewer: Harald Welte <laforge at gnumonks.org>
Gerrit-Reviewer: Jenkins Builder (1000002)
Gerrit-Reviewer: Max <msuraev at sysmocom.de>
Gerrit-Reviewer: Neels Hofmeyr <nhofmeyr at sysmocom.de>
Gerrit-Reviewer: dexter <pmaier at sysmocom.de>
Gerrit-Reviewer: tnt <tnt at 246tNt.com>
Gerrit-CC: Vadim Yanitskiy <axilirator at gmail.com>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20190223/c3b1e3ae/attachment.htm>


More information about the gerrit-log mailing list