Change in osmo-bsc[master]: make sure channel mode and s15_s0 are updated only after an ACK

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

neels gerrit-no-reply at lists.osmocom.org
Tue Jun 1 17:29:34 UTC 2021


neels has submitted this change. ( https://gerrit.osmocom.org/c/osmo-bsc/+/24352 )

Change subject: make sure channel mode and s15_s0 are updated only after an ACK
......................................................................

make sure channel mode and s15_s0 are updated only after an ACK

I noticed during testing that an lchan used as TCH/F in fact still had
its channel mode set to Signalling -- because on Assignment, the Speech
mode used to be placed in the *previous* lchan and the new lchan was
never updated after the Activ ACK. This is unbearable confusion which I
complained about numerous times, so far mostly for cosmetic reasons. But
implementing re-assignment properly actually requires this to be cleaned
up.

Keep all volatile chan mode settings in lchan->activate.* or
lchan->modify.*, and only update lchan->* members when an ACK has been
received for those settings. So a failed request keeps a sane state.

Make sure that those settings are in fact updated in the proper lchan,
upon an ACK, so that subsequent re-assignment or mode-modify know the
accurate lchan state.

Related are upcoming patches that sort out the AMR multirate
configuration in a similar fashion, see
Iebac2dc26412d877e5364f90d6f2ed7a7952351e
Ia7519d2fa9e7f0b61b222d27d077bde4660c40b9
Ie57f9d0e3912632903d9740291225bfd1634ed47.

Related: SYS#5315 OS#4940 OS#3787 OS#3833
Change-Id: Ie0da36124d73efc28a8809b63d7c96e2167fc412
---
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/bsc_vty.c
M src/osmo-bsc/gsm_04_08_rr.c
M src/osmo-bsc/gsm_data.c
M src/osmo-bsc/handover_decision_2.c
M src/osmo-bsc/handover_fsm.c
M src/osmo-bsc/lchan_fsm.c
M src/osmo-bsc/lchan_rtp_fsm.c
M src/osmo-bsc/osmo_bsc_bssap.c
M src/osmo-bsc/osmo_bsc_lcls.c
M tests/handover/handover_test.c
13 files changed, 186 insertions(+), 163 deletions(-)

Approvals:
  Jenkins Builder: Verified
  laforge: Looks good to me, approved
  fixeria: Looks good to me, but someone else must approve



diff --git a/include/osmocom/bsc/gsm_data.h b/include/osmocom/bsc/gsm_data.h
index 4b175a4..68c3074 100644
--- a/include/osmocom/bsc/gsm_data.h
+++ b/include/osmocom/bsc/gsm_data.h
@@ -105,6 +105,8 @@
 	CH_RATE_FULL,
 };
 
+enum channel_rate chan_t_to_chan_rate(enum gsm_chan_t chan_t);
+
 struct channel_mode_and_rate {
 	enum gsm48_chan_mode chan_mode;
 	enum channel_rate chan_rate;
@@ -137,7 +139,7 @@
 
 	/* Rate/codec setting in preference order (need at least 1 !) */
 	int n_ch_mode_rate;
-	struct channel_mode_and_rate ch_mode_rate[3];
+	struct channel_mode_and_rate ch_mode_rate_list[3];
 };
 
 /* State of an ongoing Assignment, while the assignment_fsm is still busy. This serves as state separation to keep the
@@ -146,6 +148,7 @@
 struct assignment_fsm_data {
 	struct assignment_request req;
 	bool requires_voice_stream;
+	struct channel_mode_and_rate selected_ch_mode_rate;
 
 	struct osmo_fsm_inst *fi;
 	struct gsm_lchan *new_lchan;
@@ -585,12 +588,8 @@
 struct lchan_activate_info {
 	enum lchan_activate_for activ_for;
 	struct gsm_subscriber_connection *for_conn;
-	/* This always is for a specific lchan, so its lchan->type indicates full or half rate.
-	 * When a dyn TS was selected, the lchan->type has been set to the desired rate. */
-	enum gsm48_chan_mode chan_mode;
+	struct channel_mode_and_rate ch_mode_rate;
 	struct gsm_encr encr;
-	/* AMR config */
-	uint16_t s15_s0;
 	bool requires_voice_stream;
 	bool wait_before_switching_rtp; /*< true = requires LCHAN_EV_READY_TO_SWITCH_RTP */
 	uint16_t msc_assigned_cic;
@@ -613,11 +612,9 @@
 
 struct lchan_modify_info {
 	enum lchan_modify_for modify_for;
-	enum gsm48_chan_mode chan_mode;
+	struct channel_mode_and_rate ch_mode_rate;
 	bool requires_voice_stream;
 	uint16_t msc_assigned_cic;
-	/* AMR config */
-	uint16_t s15_s0;
 };
 
 struct gsm_lchan {
@@ -672,8 +669,6 @@
 	enum gsm_chan_t type;
 	/* RSL channel mode */
 	enum rsl_cmod_spd rsl_cmode;
-	/* If TCH, traffic channel mode */
-	enum gsm48_chan_mode tch_mode;
 	enum lchan_csd_mode csd_mode;
 	/* Power levels for MS and BTS */
 	uint8_t bs_power;
@@ -684,8 +679,6 @@
 	/* AMR bits */
 	uint8_t mr_ms_lv[7];
 	uint8_t mr_bts_lv[7];
-	/* AMR bits were based on these rate bits: */
-	uint16_t s15_s0;
 
 	/* Established data link layer services */
 	uint8_t sapis[8];
@@ -727,12 +720,9 @@
 
 	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;
+	/* After the Channel Activation ACK or RSL Mode Modify ACK is received, this reflects the actually used
+	 * channel_mode_and_rate. */
+	struct channel_mode_and_rate current_ch_mode_rate;
 };
 
 /* One Timeslot in a TRX */
diff --git a/src/osmo-bsc/abis_rsl.c b/src/osmo-bsc/abis_rsl.c
index eb30345..1aaebe3 100644
--- a/src/osmo-bsc/abis_rsl.c
+++ b/src/osmo-bsc/abis_rsl.c
@@ -70,7 +70,7 @@
 	OSMO_ASSERT(bts);
 
 	if (lchan->type == GSM_LCHAN_TCH_H) {
-		switch (lchan->tch_mode) {
+		switch (lchan->current_ch_mode_rate.chan_mode) {
 		case GSM48_CMODE_SPEECH_AMR:
 			rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_CODEC_AMR_H]);
 			break;
@@ -81,7 +81,7 @@
 			break;
 		}
 	} else if (lchan->type == GSM_LCHAN_TCH_F) {
-		switch (lchan->tch_mode) {
+		switch (lchan->current_ch_mode_rate.chan_mode) {
 		case GSM48_CMODE_SPEECH_AMR:
 			rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_CODEC_AMR_F]);
 			break;
@@ -351,7 +351,8 @@
 }
 
 static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm,
-				   struct gsm_lchan *lchan)
+				   struct gsm_lchan *lchan,
+				   const struct channel_mode_and_rate *ch_mode_rate)
 {
 	memset(cm, 0, sizeof(*cm));
 
@@ -366,7 +367,7 @@
 	cm->spd_ind = lchan->rsl_cmode;
 
 	if (lchan->rsl_cmode == RSL_CMOD_SPD_SIGN &&
-	    lchan->tch_mode != GSM48_CMODE_SIGN)
+	    ch_mode_rate->chan_mode != GSM48_CMODE_SIGN)
 		LOGP(DRSL, LOGL_ERROR, "unsupported: rsl_mode == signalling, "
 			"but tch_mode != signalling\n");
 
@@ -389,7 +390,7 @@
 		return -EINVAL;
 	}
 
-	switch (lchan->tch_mode) {
+	switch (ch_mode_rate->chan_mode) {
 	case GSM48_CMODE_SIGN:
 		cm->chan_rate = 0;
 		break;
@@ -408,7 +409,7 @@
 		switch (lchan->csd_mode) {
 		case LCHAN_CSD_M_NT:
 			/* non-transparent CSD with RLP */
-			switch (lchan->tch_mode) {
+			switch (ch_mode_rate->chan_mode) {
 			case GSM48_CMODE_DATA_14k5:
 				cm->chan_rate = RSL_CMOD_SP_NT_14k5;
 				break;
@@ -421,7 +422,7 @@
 			default:
 				LOGP(DRSL, LOGL_ERROR,
 				     "unsupported lchan->tch_mode %u\n",
-				     lchan->tch_mode);
+				     ch_mode_rate->chan_mode);
 				return -EINVAL;
 			}
 			break;
@@ -458,20 +459,19 @@
 		}
 		break;
 	default:
-		LOGP(DRSL, LOGL_ERROR,
-		     "unsupported lchan->tch_mode %u\n",
-		     lchan->tch_mode);
+		LOGP(DRSL, LOGL_ERROR, "unsupported channel mode %u\n", ch_mode_rate->chan_mode);
 		return -EINVAL;
 	}
 
 	return 0;
 }
 
-static void mr_config_for_bts(struct gsm_lchan *lchan, struct msgb *msg)
+static void mr_config_for_bts(struct gsm_lchan *lchan, struct msgb *msg,
+			      const struct channel_mode_and_rate *ch_mode_rate)
 {
 	uint8_t len;
 
-	if (lchan->tch_mode != GSM48_CMODE_SPEECH_AMR)
+	if (ch_mode_rate->chan_mode != GSM48_CMODE_SPEECH_AMR)
 		return;
 
 	len = lchan->mr_bts_lv[0];
@@ -532,7 +532,7 @@
 	/* PDCH activation is a job for rsl_tx_dyn_ts_pdch_act_deact(); */
 	OSMO_ASSERT(act_type != RSL_ACT_OSMO_PDCH);
 
-	rc = channel_mode_from_lchan(&cm, lchan);
+	rc = channel_mode_from_lchan(&cm, lchan, &lchan->activate.info.ch_mode_rate);
 	if (rc < 0) {
 		LOGP(DRSL, LOGL_ERROR,
 		     "%s Cannot find channel mode from lchan type\n",
@@ -606,7 +606,7 @@
 	add_power_control_params(msg, RSL_IE_BS_POWER_PARAM, lchan);
 	add_power_control_params(msg, RSL_IE_MS_POWER_PARAM, lchan);
 
-	mr_config_for_bts(lchan, msg);
+	mr_config_for_bts(lchan, msg, &lchan->activate.info.ch_mode_rate);
 	rep_acch_cap_for_bts(lchan, msg);
 
 	msg->dst = trx->rsl_link;
@@ -637,7 +637,7 @@
 	uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
 	struct rsl_ie_chan_mode cm;
 
-	rc = channel_mode_from_lchan(&cm, lchan);
+	rc = channel_mode_from_lchan(&cm, lchan, &lchan->modify.info.ch_mode_rate);
 	if (rc < 0)
 		return rc;
 
@@ -656,7 +656,7 @@
 			msgb_tlv_put(msg, RSL_IE_ENCR_INFO, rc, encr_info);
 	}
 
-	mr_config_for_bts(lchan, msg);
+	mr_config_for_bts(lchan, msg, &lchan->modify.info.ch_mode_rate);
         rep_acch_cap_for_bts(lchan, msg);
 
 	msg->dst = lchan->ts->trx->rsl_link;
@@ -1768,7 +1768,10 @@
 		  gsm_chreq_name(rqd->reason), rqd->ref.ra, rqd->ta);
 	info = (struct lchan_activate_info){
 		.activ_for = ACTIVATE_FOR_MS_CHANNEL_REQUEST,
-		.chan_mode = GSM48_CMODE_SIGN,
+		.ch_mode_rate = {
+			.chan_mode = GSM48_CMODE_SIGN,
+			.chan_rate = CH_RATE_SDCCH,
+		},
 		.ta = rqd->ta,
 		.ta_known = true,
 	};
diff --git a/src/osmo-bsc/assignment_fsm.c b/src/osmo-bsc/assignment_fsm.c
index 60e3342..aeb9a24 100644
--- a/src/osmo-bsc/assignment_fsm.c
+++ b/src/osmo-bsc/assignment_fsm.c
@@ -85,7 +85,7 @@
 		if (bts) { \
 			rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_##counter]); \
 			if (BTS_##counter != BTS_CTR_ASSIGNMENT_NO_CHANNEL) { \
-				switch (conn->lchan->ch_mode_rate.chan_mode) { \
+				switch (conn->assignment.req.ch_mode_rate_list[0].chan_mode) { \
 				case GSM48_CMODE_SIGN: \
 					rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_##counter##_SIGN]); \
 					break; \
@@ -175,18 +175,18 @@
 	struct gsm_lchan *lchan = conn->lchan;
 	struct osmo_fsm_inst *fi = conn->fi;
 
-	chosen_channel = gsm0808_chosen_channel(lchan->type, lchan->tch_mode);
+	chosen_channel = gsm0808_chosen_channel(lchan->type, lchan->current_ch_mode_rate.chan_mode);
 	if (!chosen_channel) {
 		assignment_fail(GSM0808_CAUSE_EQUIPMENT_FAILURE,
 				"Unable to compose Chosen Channel for mode=%s type=%s",
-				get_value_string(gsm48_chan_mode_names, lchan->tch_mode),
+				get_value_string(gsm48_chan_mode_names, lchan->current_ch_mode_rate.chan_mode),
 				gsm_lchant_name(lchan->type));
 		return;
 	}
 
 	/* Generate voice related fields */
 	if (conn->assignment.requires_voice_stream) {
-		perm_spch = gsm0808_permitted_speech(lchan->type, lchan->tch_mode);
+		perm_spch = gsm0808_permitted_speech(lchan->type, lchan->current_ch_mode_rate.chan_mode);
 
 		if (gscon_is_aoip(conn)) {
 			if (!osmo_mgcpc_ep_ci_get_crcx_info_to_sockaddr(conn->user_plane.mgw_endpoint_ci_msc,
@@ -212,7 +212,7 @@
 		if (gscon_is_aoip(conn)) {
 			/* Extrapolate speech codec from speech mode */
 			gsm0808_speech_codec_from_chan_type(&sc, perm_spch);
-			sc.cfg = conn->lchan->activate.info.s15_s0;
+			sc.cfg = conn->lchan->activate.info.ch_mode_rate.s15_s0;
 			sc_ptr = ≻
 		}
 	}
@@ -395,11 +395,11 @@
 	 * a mismatch is not permitted */
 
 	for (i = 0; i < req->n_ch_mode_rate; i++) {
-		rc = check_requires_voice(&requires_voice_alt, req->ch_mode_rate[i].chan_mode);
+		rc = check_requires_voice(&requires_voice_alt, req->ch_mode_rate_list[i].chan_mode);
 		if (rc < 0) {
 			assignment_fail(GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_NOT_SUPP,
 					"Channel mode not supported (prev level %d): %s", i,
-					gsm48_chan_mode_name(req->ch_mode_rate[i].chan_mode));
+					gsm48_chan_mode_name(req->ch_mode_rate_list[i].chan_mode));
 			return -EINVAL;
 		}
 
@@ -408,8 +408,8 @@
 		else if (requires_voice_alt != requires_voice_pref) {
 			assignment_fail(GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_NOT_SUPP,
 					"Requested a mix of Signalling and non-Signalling channel modes: %s != %s",
-					gsm48_chan_mode_name(req->ch_mode_rate[0].chan_mode),
-					gsm48_chan_mode_name(req->ch_mode_rate[i].chan_mode));
+					gsm48_chan_mode_name(req->ch_mode_rate_list[0].chan_mode),
+					gsm48_chan_mode_name(req->ch_mode_rate_list[i].chan_mode));
 			return -EINVAL;
 		}
 	}
@@ -431,9 +431,9 @@
 	/* Check if the currently existing lchan is compatible with the
 	 * preferred rate/codec. */
 	for (i = 0; i < req->n_ch_mode_rate; i++) {
-		if (!lchan_type_compat_with_mode(conn->lchan->type, &req->ch_mode_rate[i]))
+		if (!lchan_type_compat_with_mode(conn->lchan->type, &req->ch_mode_rate_list[i]))
 			continue;
-		conn->lchan->ch_mode_rate = req->ch_mode_rate[i];
+		conn->assignment.selected_ch_mode_rate = req->ch_mode_rate_list[i];
 		return true;
 	}
 
@@ -485,12 +485,12 @@
 
 		/* If the requested mode and the current TCH mode matches up, just send the
 		 * assignment complete directly and be done with the assignment procedure. */
-		if (conn->lchan->tch_mode == conn->lchan->ch_mode_rate.chan_mode) {
+		if (conn->lchan->current_ch_mode_rate.chan_mode == conn->assignment.selected_ch_mode_rate.chan_mode) {
 			LOG_ASSIGNMENT(conn, LOGL_DEBUG,
 				       "Current lchan mode 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),
+				       gsm48_chan_mode_name(conn->assignment.selected_ch_mode_rate.chan_mode),
 				       gsm_lchan_name(conn->lchan));
 
 			if (req->assign_for == ASSIGN_FOR_BSSMAP_REQ)
@@ -509,14 +509,13 @@
 		LOG_ASSIGNMENT(conn, LOGL_DEBUG,
 			       "Current lchan mode is not compatible with requested chan_mode,"
 			       " so we will modify it. requested chan_mode=%s; current lchan is %s\n",
-			       gsm48_chan_mode_name(conn->lchan->ch_mode_rate.chan_mode),
+			       gsm48_chan_mode_name(conn->assignment.selected_ch_mode_rate.chan_mode),
 			       gsm_lchan_name(conn->lchan));
 
 		modif_info = (struct lchan_modify_info){
 			.modify_for = MODIFY_FOR_ASSIGNMENT,
-			.chan_mode = conn->lchan->ch_mode_rate.chan_mode,
+			.ch_mode_rate = conn->assignment.selected_ch_mode_rate,
 			.requires_voice_stream = conn->assignment.requires_voice_stream,
-			.s15_s0 = conn->lchan->ch_mode_rate.s15_s0,
 			.msc_assigned_cic = req->msc_assigned_cic,
 		};
 
@@ -529,22 +528,23 @@
 	/* Try to allocate a new lchan in order of preference */
 	for (i = 0; i < req->n_ch_mode_rate; i++) {
 		conn->assignment.new_lchan = lchan_select_by_chan_mode(bts,
-		    req->ch_mode_rate[i].chan_mode, req->ch_mode_rate[i].chan_rate);
-		/* FIXME: at this point there is merely an assignment request with a given ch_mode_rate. Writing this to
-		 * conn->lchan->ch_mode_rate is a violation of scopes: the lchan->* state should only be modified
-		 * *after* the assignment is confirmed to be completed. Before that, this data should live in
-		 * conn->assignment or the lchan_activate_info, the designated places for not-yet-confirmed data. See
-		 * OS#3833 */
-		conn->lchan->ch_mode_rate = req->ch_mode_rate[i];
-		if (conn->assignment.new_lchan)
-			break;
+		    req->ch_mode_rate_list[i].chan_mode, req->ch_mode_rate_list[i].chan_rate);
+		if (!conn->assignment.new_lchan)
+			continue;
+		LOG_ASSIGNMENT(conn, LOGL_DEBUG, "selected new lchan %s for mode[%d] = %s channel_rate=%d\n",
+			       gsm_lchan_name(conn->assignment.new_lchan),
+			       i, gsm48_chan_mode_name(req->ch_mode_rate_list[i].chan_mode),
+			       req->ch_mode_rate_list[i].chan_rate);
+
+		conn->assignment.selected_ch_mode_rate = req->ch_mode_rate_list[i];
+		break;
 	}
 
 	/* 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(CTR_ASSIGNMENT_NO_CHANNEL);
-		switch (req->ch_mode_rate[0].chan_mode) {
+		switch (req->ch_mode_rate_list[0].chan_mode) {
 		case GSM48_CMODE_SIGN:
 			rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_ASSIGNMENT_NO_CHANNEL_SIGN]);
 			break;
@@ -559,12 +559,12 @@
 		assignment_fail(GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE,
 				"BSSMAP Assignment Command:"
 				" No lchan available for: pref=%s:%s / alt1=%s:%s / alt2=%s:%s\n",
-				gsm48_chan_mode_name(req->ch_mode_rate[0].chan_mode),
-				rate_names[req->ch_mode_rate[0].chan_rate],
-				req->n_ch_mode_rate > 1 ? gsm48_chan_mode_name(req->ch_mode_rate[1].chan_mode) : "",
-				req->n_ch_mode_rate > 1 ? rate_names[req->ch_mode_rate[1].chan_rate] : "",
-				req->n_ch_mode_rate > 2 ? gsm48_chan_mode_name(req->ch_mode_rate[2].chan_mode) : "",
-				req->n_ch_mode_rate > 2 ? rate_names[req->ch_mode_rate[2].chan_rate] : ""
+				gsm48_chan_mode_name(req->ch_mode_rate_list[0].chan_mode),
+				rate_names[req->ch_mode_rate_list[0].chan_rate],
+				req->n_ch_mode_rate > 1 ? gsm48_chan_mode_name(req->ch_mode_rate_list[1].chan_mode) : "",
+				req->n_ch_mode_rate > 1 ? rate_names[req->ch_mode_rate_list[1].chan_rate] : "",
+				req->n_ch_mode_rate > 2 ? gsm48_chan_mode_name(req->ch_mode_rate_list[2].chan_mode) : "",
+				req->n_ch_mode_rate > 2 ? rate_names[req->ch_mode_rate_list[2].chan_rate] : ""
 		);
 		return;
 	}
@@ -572,8 +572,8 @@
 	assignment_fsm_update_id(conn);
 	LOG_ASSIGNMENT(conn, LOGL_INFO, "Starting Assignment: chan_mode=%s, chan_type=%s,"
 		       " aoip=%s MSC-rtp=%s:%u (osmux=%s)\n",
-		       gsm48_chan_mode_name(conn->lchan->ch_mode_rate.chan_mode),
-		       rate_names[conn->lchan->ch_mode_rate.chan_rate],
+		       gsm48_chan_mode_name(conn->assignment.selected_ch_mode_rate.chan_mode),
+		       rate_names[conn->assignment.selected_ch_mode_rate.chan_rate],
 		       req->aoip ? "yes" : "no", req->msc_rtp_addr, req->msc_rtp_port,
 		       req->use_osmux ? "yes" : "no");
 
@@ -581,9 +581,8 @@
 	activ_info = (struct lchan_activate_info){
 		.activ_for = ACTIVATE_FOR_ASSIGNMENT,
 		.for_conn = conn,
-		.chan_mode = conn->lchan->ch_mode_rate.chan_mode,
+		.ch_mode_rate = conn->assignment.selected_ch_mode_rate,
 		.encr = conn->lchan->encr,
-		.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 b964dbc..7ede51a 100644
--- a/src/osmo-bsc/bsc_vty.c
+++ b/src/osmo-bsc/bsc_vty.c
@@ -1624,7 +1624,7 @@
 		ms_pwr_dbm(lchan->ts->trx->bts->band, lchan->ms_power),
 		VTY_NEWLINE);
 	vty_out(vty, "  Channel Mode / Codec: %s%s",
-		gsm48_chan_mode_name(lchan->tch_mode),
+		gsm48_chan_mode_name(lchan->current_ch_mode_rate.chan_mode),
 		VTY_NEWLINE);
 	if (lchan->conn && lchan->conn->bsub) {
 		vty_out(vty, "  Subscriber:%s", VTY_NEWLINE);
@@ -6071,13 +6071,17 @@
 		if (!strcmp(codec_str, "hr") || !strcmp(codec_str, "fr")) {
 			info = (struct lchan_activate_info) {
 				.activ_for = ACTIVATE_FOR_VTY,
-				.chan_mode = GSM48_CMODE_SPEECH_V1,
+				.ch_mode_rate = {
+					.chan_mode = GSM48_CMODE_SPEECH_V1,
+				},
 				.requires_voice_stream = false,
 			};
 		} else if (!strcmp(codec_str, "efr")) {
 			info = (struct lchan_activate_info) {
 				.activ_for = ACTIVATE_FOR_VTY,
-				.chan_mode = GSM48_CMODE_SPEECH_EFR,
+				.ch_mode_rate = {
+					.chan_mode = GSM48_CMODE_SPEECH_EFR,
+				},
 				.requires_voice_stream = false,
 			};
 		} else if (!strcmp(codec_str, "amr")) {
@@ -6087,14 +6091,18 @@
 			}
 			info = (struct lchan_activate_info) {
 				.activ_for = ACTIVATE_FOR_VTY,
-				.chan_mode = GSM48_CMODE_SPEECH_AMR,
-				.s15_s0 = amr_modes[amr_mode],
+				.ch_mode_rate = {
+					.chan_mode = GSM48_CMODE_SPEECH_AMR,
+					.s15_s0 = amr_modes[amr_mode],
+				},
 				.requires_voice_stream = false,
 			};
 		} else if (!strcmp(codec_str, "sig")) {
 			info = (struct lchan_activate_info) {
 				.activ_for = ACTIVATE_FOR_VTY,
-				.chan_mode = GSM48_CMODE_SIGN,
+				.ch_mode_rate = {
+					.chan_mode = GSM48_CMODE_SIGN,
+				},
 				.requires_voice_stream = false,
 			};
 		} else {
@@ -6102,6 +6110,8 @@
 			return CMD_WARNING;
 		}
 
+		info.ch_mode_rate.chan_rate = chan_t_to_chan_rate(lchan_t);
+
 		vty_out(vty, "%% activating lchan %s as %s%s", gsm_lchan_name(lchan), gsm_chan_t_name(lchan->type),
 			VTY_NEWLINE);
 		lchan_activate(lchan, &info);
diff --git a/src/osmo-bsc/gsm_04_08_rr.c b/src/osmo-bsc/gsm_04_08_rr.c
index 764af72..8a375cb 100644
--- a/src/osmo-bsc/gsm_04_08_rr.c
+++ b/src/osmo-bsc/gsm_04_08_rr.c
@@ -234,7 +234,7 @@
 
 static void mr_config_for_ms(struct gsm_lchan *lchan, struct msgb *msg)
 {
-	if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR)
+	if (lchan->current_ch_mode_rate.chan_mode == GSM48_CMODE_SPEECH_AMR)
 		msgb_tlv_put(msg, GSM48_IE_MUL_RATE_CFG, lchan->mr_ms_lv[0],
 			lchan->mr_ms_lv + 1);
 }
@@ -538,7 +538,7 @@
 	}
 	/* FIXME: optional bits for type of synchronization? */
 
-	msgb_tv_put(msg, GSM48_IE_CHANMODE_1, new_lchan->tch_mode);
+	msgb_tv_put(msg, GSM48_IE_CHANMODE_1, new_lchan->current_ch_mode_rate.chan_mode);
 
 	/* Mobile Allocation (after time), TLV (see 3GPP TS 44.018, 10.5.2.21) */
 	if (new_lchan->ts->hopping.enabled) {
@@ -548,7 +548,7 @@
 	}
 
 	/* in case of multi rate we need to attach a config */
-	if (new_lchan->tch_mode == GSM48_CMODE_SPEECH_AMR)
+	if (new_lchan->current_ch_mode_rate.chan_mode == GSM48_CMODE_SPEECH_AMR)
 		msgb_tlv_put(msg, GSM48_IE_MUL_RATE_CFG, new_lchan->mr_ms_lv[0],
 			new_lchan->mr_ms_lv + 1);
 
@@ -573,7 +573,8 @@
 	struct gsm48_ass_cmd *ass =
 		(struct gsm48_ass_cmd *) msgb_put(msg, sizeof(*ass));
 
-	DEBUGP(DRR, "-> ASSIGNMENT COMMAND tch_mode=0x%02x\n", new_lchan->tch_mode);
+	DEBUGP(DRR, "-> ASSIGNMENT COMMAND tch_mode=0x%02x\n",
+	       current_lchan->conn->assignment.selected_ch_mode_rate.chan_mode);
 
 	msg->lchan = current_lchan;
 	gh->proto_discr = GSM48_PDISC_RR;
@@ -599,7 +600,7 @@
 				  si1->cell_channel_description);
 	}
 
-	msgb_tv_put(msg, GSM48_IE_CHANMODE_1, new_lchan->tch_mode);
+	msgb_tv_put(msg, GSM48_IE_CHANMODE_1, current_lchan->conn->assignment.selected_ch_mode_rate.chan_mode);
 
 	/* Mobile Allocation (freq. hopping), TLV (see 3GPP TS 44.018, 10.5.2.21) */
 	if (new_lchan->ts->hopping.enabled) {
@@ -645,7 +646,6 @@
 
 	DEBUGP(DRR, "-> CHANNEL MODE MODIFY mode=0x%02x\n", mode);
 
-	lchan->tch_mode = mode;
 	msg->lchan = lchan;
 	gh->proto_discr = GSM48_PDISC_RR;
 	gh->msg_type = GSM48_MT_RR_CHAN_MODE_MODIF;
@@ -670,10 +670,10 @@
 	LOG_LCHAN(msg->lchan, LOGL_DEBUG, "CHANNEL MODE MODIFY ACK for %s\n",
 		  gsm48_chan_mode_name(mod->mode));
 
-	if (mod->mode != msg->lchan->tch_mode) {
+	if (mod->mode != msg->lchan->modify.info.ch_mode_rate.chan_mode) {
 		LOG_LCHAN(msg->lchan, LOGL_ERROR,
 			  "CHANNEL MODE MODIFY ACK has wrong mode: Wanted: %s Got: %s\n",
-			  gsm48_chan_mode_name(msg->lchan->tch_mode),
+			  gsm48_chan_mode_name(msg->lchan->modify.info.ch_mode_rate.chan_mode),
 			  gsm48_chan_mode_name(mod->mode));
 		return -1;
 	}
diff --git a/src/osmo-bsc/gsm_data.c b/src/osmo-bsc/gsm_data.c
index 781db7c..a371064 100644
--- a/src/osmo-bsc/gsm_data.c
+++ b/src/osmo-bsc/gsm_data.c
@@ -685,6 +685,22 @@
 	}
 }
 
+enum channel_rate chan_t_to_chan_rate(enum gsm_chan_t chan_t)
+{
+	switch (chan_t) {
+	case GSM_LCHAN_SDCCH:
+		return CH_RATE_SDCCH;
+	case GSM_LCHAN_TCH_F:
+		return CH_RATE_FULL;
+	case GSM_LCHAN_TCH_H:
+		return CH_RATE_HALF;
+	default:
+		/* For other channel types, the channel_rate value is never used. It is fine to return an invalid value,
+		 * and callers don't actually need to check for this. */
+		return -1;
+	}
+}
+
 /* Can the timeslot in principle be used as this PCHAN kind? */
 bool ts_is_capable_of_pchan(struct gsm_bts_trx_ts *ts, enum gsm_phys_chan_config pchan)
 {
diff --git a/src/osmo-bsc/handover_decision_2.c b/src/osmo-bsc/handover_decision_2.c
index 3326c0b..5b7d4b1 100644
--- a/src/osmo-bsc/handover_decision_2.c
+++ b/src/osmo-bsc/handover_decision_2.c
@@ -52,7 +52,7 @@
 	     lchan->ts->nr, \
 	     lchan->nr, \
 	     gsm_lchant_name(lchan->type), \
-	     gsm48_chan_mode_name(lchan->tch_mode), \
+	     gsm48_chan_mode_name(lchan->current_ch_mode_rate.chan_mode), \
 	     bsc_subscr_name(lchan->conn? lchan->conn->bsub : NULL), \
 	     ## args)
 
@@ -63,7 +63,7 @@
 	     lchan->ts->nr, \
 	     lchan->nr, \
 	     gsm_lchant_name(lchan->type), \
-	     gsm48_chan_mode_name(lchan->tch_mode), \
+	     gsm48_chan_mode_name(lchan->current_ch_mode_rate.chan_mode), \
 	     new_bts->nr, \
 	     bsc_subscr_name(lchan->conn? lchan->conn->bsub : NULL), \
 	     ## args)
@@ -75,7 +75,7 @@
 	     lchan->ts->nr, \
 	     lchan->nr, \
 	     gsm_lchant_name(lchan->type), \
-	     gsm48_chan_mode_name(lchan->tch_mode), \
+	     gsm48_chan_mode_name(lchan->current_ch_mode_rate.chan_mode), \
 	     gsm0808_cell_id_list_name(remote_cil), \
 	     bsc_subscr_name(lchan->conn? lchan->conn->bsub : NULL), \
 	     ## args)
@@ -478,7 +478,7 @@
 
 	/* compatibility check for codecs.
 	 * if so, the candidates for full rate and half rate are selected */
-	switch (c->current.lchan->tch_mode) {
+	switch (c->current.lchan->current_ch_mode_rate.chan_mode) {
 	case GSM48_CMODE_SPEECH_V1:
 		switch (c->current.lchan->type) {
 		case GSM_LCHAN_TCH_F: /* mandatory */
@@ -489,7 +489,7 @@
 				LOGPHOLCHANTOBTS(c->current.lchan, c->target.bts, LOGL_DEBUG,
 						 "tch_mode='%s' type='%s' not supported\n",
 						 get_value_string(gsm48_chan_mode_names,
-								  c->current.lchan->tch_mode),
+								  c->current.lchan->current_ch_mode_rate.chan_mode),
 						 gsm_lchant_name(c->current.lchan->type));
 				break;
 			}
@@ -498,7 +498,8 @@
 			break;
 		default:
 			LOGPHOLCHAN(c->current.lchan, LOGL_ERROR, "Unexpected channel type: neither TCH/F nor TCH/H for %s\n",
-				    get_value_string(gsm48_chan_mode_names, c->current.lchan->tch_mode));
+				    get_value_string(gsm48_chan_mode_names,
+						     c->current.lchan->current_ch_mode_rate.chan_mode));
 			return;
 		}
 		break;
@@ -757,7 +758,7 @@
 
 	/* compatibility check for codecs -- we have no notion of what the remote BSS supports. We can
 	 * only assume that a handover would work, and use only the local requirements. */
-	switch (c->current.lchan->tch_mode) {
+	switch (c->current.lchan->current_ch_mode_rate.chan_mode) {
 	case GSM48_CMODE_SPEECH_V1:
 		switch (c->current.lchan->type) {
 		case GSM_LCHAN_TCH_F: /* mandatory */
@@ -769,7 +770,8 @@
 			break;
 		default:
 			LOGPHOLCHAN(c->current.lchan, LOGL_ERROR, "Unexpected channel type: neither TCH/F nor TCH/H for %s\n",
-				    get_value_string(gsm48_chan_mode_names, c->current.lchan->tch_mode));
+				    get_value_string(gsm48_chan_mode_names,
+						     c->current.lchan->current_ch_mode_rate.chan_mode));
 			return;
 		}
 		break;
@@ -815,7 +817,7 @@
 	bool full_rate = false;
 
 	/* afs_bias becomes > 0, if AFS is used and is improved */
-	if (c->current.lchan->tch_mode == GSM48_CMODE_SPEECH_AMR)
+	if (c->current.lchan->current_ch_mode_rate.chan_mode == GSM48_CMODE_SPEECH_AMR)
 		afs_bias = ho_get_hodec2_afs_bias_rxlev(c->target.bts->ho);
 
 	/* select TCH rate, prefer TCH/F if AFS is improved */
@@ -1243,7 +1245,7 @@
 static int find_alternative_lchan(struct gsm_lchan *lchan, bool include_weaker_rxlev)
 {
 	struct gsm_bts *bts = lchan->ts->trx->bts;
-	int ahs = (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR
+	int ahs = (lchan->current_ch_mode_rate.chan_mode == GSM48_CMODE_SPEECH_AMR
 		   && lchan->type == GSM_LCHAN_TCH_H);
 	int rxlev_current;
 	struct ho_candidate clist[1 + ARRAY_SIZE(lchan->neigh_meas)];
@@ -1451,7 +1453,7 @@
 
 	/* improve levels in case of AFS, if defined */
 	if (lchan->type == GSM_LCHAN_TCH_F
-	 && lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) {
+	 && lchan->current_ch_mode_rate.chan_mode == GSM48_CMODE_SPEECH_AMR) {
 		int av_rxlev_was = av_rxlev;
 		int av_rxqual_was = av_rxqual;
 		int rxlev_bias = ho_get_hodec2_afs_bias_rxlev(bts->ho);
diff --git a/src/osmo-bsc/handover_fsm.c b/src/osmo-bsc/handover_fsm.c
index 0b5a8be..b7658c5 100644
--- a/src/osmo-bsc/handover_fsm.c
+++ b/src/osmo-bsc/handover_fsm.c
@@ -61,7 +61,7 @@
 		lchan ? lchan->ts->nr : 0, \
 		lchan ? gsm_lchant_name(lchan->type) : "?", \
 		lchan ? lchan->nr : 0, \
-		lchan ? gsm48_chan_mode_name(lchan->tch_mode) : "?"
+		lchan ? gsm48_chan_mode_name(lchan->current_ch_mode_rate.chan_mode) : "?"
 
 #define LOG_FMT_TO_LCHAN "%u-%u-%u-%s%s%s-%u"
 #define LOG_ARGS_TO_LCHAN(lchan) \
@@ -398,14 +398,14 @@
 	info = (struct lchan_activate_info){
 		.activ_for = ACTIVATE_FOR_HANDOVER,
 		.for_conn = conn,
-		.chan_mode = conn->lchan->tch_mode,
+		.ch_mode_rate = conn->lchan->current_ch_mode_rate,
 		.encr = conn->lchan->encr,
 		.requires_voice_stream = conn->lchan->mgw_endpoint_ci_bts ? true : false,
 		.msc_assigned_cic = conn->ho.inter_bsc_in.msc_assigned_cic,
 		.re_use_mgw_endpoint_from_lchan = conn->lchan,
 		.wait_before_switching_rtp = true,
-		.s15_s0 = conn->lchan->activate.info.s15_s0,
 	};
+	info.ch_mode_rate.chan_rate = chan_t_to_chan_rate(ho->new_lchan->type);
 
 	/* For intra-cell handover, we know the accurate Timing Advance from the previous lchan. For inter-cell
 	 * handover, no Timing Advance for the new cell is known, so leave it unset. */
@@ -696,8 +696,7 @@
 	info = (struct lchan_activate_info){
 		.activ_for = ACTIVATE_FOR_HANDOVER,
 		.for_conn = conn,
-		.chan_mode = ch_mode_rate.chan_mode,
-		.s15_s0 = ch_mode_rate.s15_s0,
+		.ch_mode_rate = ch_mode_rate,
 		.requires_voice_stream = chan_mode_is_tch(ch_mode_rate.chan_mode),
 		.msc_assigned_cic = req->msc_assigned_cic,
 	};
@@ -849,7 +848,7 @@
 	};
 
 	/* Chosen Channel 3.2.2.33 */
-	ho_perf_params.chosen_channel = gsm0808_chosen_channel(lchan->type, lchan->tch_mode);
+	ho_perf_params.chosen_channel = gsm0808_chosen_channel(lchan->type, lchan->current_ch_mode_rate.chan_mode);
 	if (!ho_perf_params.chosen_channel) {
 		LOG_HO(conn, LOGL_ERROR, "Failed to generate Chosen Channel IE, can't send HANDOVER PERFORMED!\n");
 		return;
@@ -862,14 +861,15 @@
 
 	if (ho->new_lchan->activate.info.requires_voice_stream) {
 		/* Speech Version (chosen) 3.2.2.51 */
-		ho_perf_params.speech_version_chosen = gsm0808_permitted_speech(lchan->type, lchan->tch_mode);
+		ho_perf_params.speech_version_chosen = gsm0808_permitted_speech(lchan->type,
+										lchan->current_ch_mode_rate.chan_mode);
 		ho_perf_params.speech_version_chosen_present = true;
 
 		/* Speech Codec (chosen) 3.2.2.104 */
 		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->ch_mode_rate.s15_s0;
+			sc.cfg = conn->lchan->current_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/lchan_fsm.c b/src/osmo-bsc/lchan_fsm.c
index 1735346..6144ccf 100644
--- a/src/osmo-bsc/lchan_fsm.c
+++ b/src/osmo-bsc/lchan_fsm.c
@@ -569,6 +569,8 @@
 
 	/* Proceed with the generation of the multirate configuration IE
 	 * (MS and BTS) */
+	/* FIXME: this actually modifies the lchan->mr_ms_lv and ->mr_bts_lv before an ACK for these AMR bits has been
+	 * received. Until an ACK is received, all state should live in lchan->activate.* or lchan->modify.* ONLY. */
 	rc = gsm48_multirate_config(lchan->mr_ms_lv, &mr_conf_filtered, mr->ms_mode, mr->num_modes);
 	if (rc != 0) {
 		LOG_LCHAN(lchan, LOGL_ERROR, "can not encode multirate configuration (MS)\n");
@@ -580,7 +582,6 @@
 		return -EINVAL;
 	}
 
-	lchan->s15_s0 = s15_s0;
 	return 0;
 }
 
@@ -645,30 +646,28 @@
 			lchan->bs_power = bts->bs_power_ctrl.bs_power_val_db / 2;
 	}
 
-	if (info->chan_mode == GSM48_CMODE_SPEECH_AMR) {
-		if (lchan_mr_config(lchan, info->s15_s0) < 0) {
+	if (info->ch_mode_rate.chan_mode == GSM48_CMODE_SPEECH_AMR) {
+		if (lchan_mr_config(lchan, info->ch_mode_rate.s15_s0) < 0) {
 			lchan_fail("Can not generate multirate configuration IE\n");
 			return;
 		}
 	}
 
-	switch (info->chan_mode) {
+	switch (info->ch_mode_rate.chan_mode) {
 
 	case GSM48_CMODE_SIGN:
 		lchan->rsl_cmode = RSL_CMOD_SPD_SIGN;
-		lchan->tch_mode = GSM48_CMODE_SIGN;
 		break;
 
 	case GSM48_CMODE_SPEECH_V1:
 	case GSM48_CMODE_SPEECH_EFR:
 	case GSM48_CMODE_SPEECH_AMR:
 		lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH;
-		lchan->tch_mode = info->chan_mode;
 		break;
 
 	default:
 		lchan_fail("Not implemented: cannot activate for chan mode %s",
-			   gsm48_chan_mode_name(info->chan_mode));
+			   gsm48_chan_mode_name(info->ch_mode_rate.chan_mode));
 		return;
 	}
 
@@ -682,7 +681,7 @@
 			(use_mgwep_ci ? osmo_mgcpc_ep_ci_name(use_mgwep_ci) : "new")
 			: "none",
 		  gsm_lchant_name(lchan->type),
-		  gsm48_chan_mode_name(lchan->tch_mode),
+		  gsm48_chan_mode_name(lchan->activate.info.ch_mode_rate.chan_mode),
 		  (lchan->activate.info.encr.alg_id ? : 1)-1,
 		  lchan->activate.info.encr.key_len ? osmo_hexdump_nospc(lchan->activate.info.encr.key,
 									 lchan->activate.info.encr.key_len) : "none");
@@ -822,6 +821,11 @@
 {
 	int rc;
 	struct gsm_lchan *lchan = lchan_fi_lchan(fi);
+
+	lchan->current_ch_mode_rate = lchan->activate.info.ch_mode_rate;
+	LOG_LCHAN(lchan, LOGL_INFO, "Rx Activ ACK %s\n",
+		  gsm48_chan_mode_name(lchan->current_ch_mode_rate.chan_mode));
+
 	if (lchan->release.requested) {
 		lchan_fail_to(LCHAN_ST_WAIT_RF_RELEASE_ACK, "Release requested while activating");
 		return;
@@ -948,7 +952,7 @@
 static void lchan_fsm_wait_rr_chan_mode_modify_ack_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
 {
 	struct gsm_lchan *lchan = lchan_fi_lchan(fi);
-	gsm48_lchan_modify(lchan, lchan->modify.info.chan_mode);
+	gsm48_lchan_modify(lchan, lchan->modify.info.ch_mode_rate.chan_mode);
 }
 
 static void lchan_fsm_wait_rr_chan_mode_modify_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -989,8 +993,7 @@
 
 	case LCHAN_EV_RSL_CHAN_MODE_MODIFY_ACK:
 		/* The Channel Mode Modify was ACKed, now the requested values become the accepted and used values. */
-		lchan->tch_mode = lchan->modify.info.chan_mode;
-		lchan->s15_s0 = lchan->modify.info.s15_s0;
+		lchan->current_ch_mode_rate = lchan->modify.info.ch_mode_rate;
 
 		if (lchan->modify.info.requires_voice_stream
 		    && !lchan->fi_rtp) {
@@ -999,7 +1002,7 @@
 			lchan->activate.info = (struct lchan_activate_info){
 				.activ_for = ACTIVATE_FOR_MODE_MODIFY_RTP,
 				.for_conn = lchan->conn,
-				.s15_s0 = lchan->modify.info.s15_s0,
+				.ch_mode_rate = lchan->modify.info.ch_mode_rate,
 				.requires_voice_stream = true,
 				.msc_assigned_cic = lchan->modify.info.msc_assigned_cic,
 			};
@@ -1142,8 +1145,8 @@
 
 		use_mgwep_ci = lchan_use_mgw_endpoint_ci_bts(lchan);
 
-		if (modif_info->chan_mode == GSM48_CMODE_SPEECH_AMR) {
-			if (lchan_mr_config(lchan, modif_info->s15_s0) < 0) {
+		if (modif_info->ch_mode_rate.chan_mode == GSM48_CMODE_SPEECH_AMR) {
+			if (lchan_mr_config(lchan, modif_info->ch_mode_rate.s15_s0) < 0) {
 				lchan_fail("Can not generate multirate configuration IE\n");
 				return;
 			}
@@ -1157,7 +1160,7 @@
 			  (use_mgwep_ci ? osmo_mgcpc_ep_ci_name(use_mgwep_ci) : "new")
 			  : "none",
 			  gsm_lchant_name(lchan->type),
-			  gsm48_chan_mode_name(lchan->tch_mode));
+			  gsm48_chan_mode_name(lchan->modify.info.ch_mode_rate.chan_mode));
 
 		lchan_fsm_state_chg(LCHAN_ST_WAIT_RR_CHAN_MODE_MODIFY_ACK);
 		return;
diff --git a/src/osmo-bsc/lchan_rtp_fsm.c b/src/osmo-bsc/lchan_rtp_fsm.c
index 8991acc..366bd1b 100644
--- a/src/osmo-bsc/lchan_rtp_fsm.c
+++ b/src/osmo-bsc/lchan_rtp_fsm.c
@@ -268,19 +268,19 @@
 		return;
 	}
 
-	val = ipacc_speech_mode(lchan->tch_mode, lchan->type);
+	val = ipacc_speech_mode(lchan->activate.info.ch_mode_rate.chan_mode, lchan->type);
 	if (val < 0) {
 		lchan_rtp_fail("Cannot determine Abis/IP speech mode for tch_mode=%s type=%s\n",
-			   get_value_string(gsm48_chan_mode_names, lchan->tch_mode),
+			   get_value_string(gsm48_chan_mode_names, lchan->activate.info.ch_mode_rate.chan_mode),
 			   gsm_lchant_name(lchan->type));
 		return;
 	}
 	lchan->abis_ip.speech_mode = val;
 
-	val = ipacc_payload_type(lchan->tch_mode, lchan->type);
+	val = ipacc_payload_type(lchan->activate.info.ch_mode_rate.chan_mode, lchan->type);
 	if (val < 0) {
 		lchan_rtp_fail("Cannot determine Abis/IP payload type for tch_mode=%s type=%s\n",
-			   get_value_string(gsm48_chan_mode_names, lchan->tch_mode),
+			   get_value_string(gsm48_chan_mode_names, lchan->activate.info.ch_mode_rate.chan_mode),
 			   gsm_lchant_name(lchan->type));
 		return;
 	}
@@ -834,14 +834,14 @@
 
 void mgcp_pick_codec(struct mgcp_conn_peer *verb_info, const struct gsm_lchan *lchan, bool bss_side)
 {
-	enum mgcp_codecs codec = chan_mode_to_mgcp_codec(lchan->tch_mode,
+	enum mgcp_codecs codec = chan_mode_to_mgcp_codec(lchan->activate.info.ch_mode_rate.chan_mode,
 							 lchan->type == GSM_LCHAN_TCH_H? false : true);
 	int custom_pt;
 
 	if (codec < 0) {
 		LOG_LCHAN(lchan, LOGL_ERROR,
 			  "Unable to determine MGCP codec type for %s in chan-mode %s\n",
-			  gsm_lchant_name(lchan->type), gsm48_chan_mode_name(lchan->tch_mode));
+			  gsm_lchant_name(lchan->type), gsm48_chan_mode_name(lchan->activate.info.ch_mode_rate.chan_mode));
 		verb_info->codecs_len = 0;
 		return;
 	}
diff --git a/src/osmo-bsc/osmo_bsc_bssap.c b/src/osmo-bsc/osmo_bsc_bssap.c
index fff5fbb..239c2ca 100644
--- a/src/osmo-bsc/osmo_bsc_bssap.c
+++ b/src/osmo-bsc/osmo_bsc_bssap.c
@@ -679,12 +679,12 @@
 
 	switch (ct->ch_rate_type) {
 	case GSM0808_SPEECH_FULL_BM:
-		rc = match_codec_pref(&req->ch_mode_rate[nc], ct, &conn->codec_list, msc, bts,
+		rc = match_codec_pref(&req->ch_mode_rate_list[nc], ct, &conn->codec_list, msc, bts,
 				      RATE_PREF_FR);
 		nc += (rc == 0) ? 1 : 0;
 		break;
 	case GSM0808_SPEECH_HALF_LM:
-		rc = match_codec_pref(&req->ch_mode_rate[nc], ct, &conn->codec_list, msc, bts,
+		rc = match_codec_pref(&req->ch_mode_rate_list[nc], ct, &conn->codec_list, msc, bts,
 				      RATE_PREF_HR);
 		nc += (rc == 0) ? 1 : 0;
 		break;
@@ -692,19 +692,19 @@
 	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[nc], ct, &conn->codec_list, msc, bts,
+		rc = match_codec_pref(&req->ch_mode_rate_list[nc], ct, &conn->codec_list, msc, bts,
 				      RATE_PREF_FR);
 		nc += (rc == 0) ? 1 : 0;
-		rc = match_codec_pref(&req->ch_mode_rate[nc], ct, &conn->codec_list, msc, bts,
+		rc = match_codec_pref(&req->ch_mode_rate_list[nc], ct, &conn->codec_list, msc, bts,
 				      RATE_PREF_HR);
 		nc += (rc == 0) ? 1 : 0;
 		break;
 	case GSM0808_SPEECH_HALF_PREF_NO_CHANGE:
 	case GSM0808_SPEECH_HALF_PREF:
-		rc = match_codec_pref(&req->ch_mode_rate[nc], ct, &conn->codec_list, msc, bts,
+		rc = match_codec_pref(&req->ch_mode_rate_list[nc], ct, &conn->codec_list, msc, bts,
 				      RATE_PREF_HR);
 		nc += (rc == 0) ? 1 : 0;
-		rc = match_codec_pref(&req->ch_mode_rate[nc], ct, &conn->codec_list, msc, bts,
+		rc = match_codec_pref(&req->ch_mode_rate_list[nc], ct, &conn->codec_list, msc, bts,
 				      RATE_PREF_FR);
 		nc += (rc == 0) ? 1 : 0;
 		break;
@@ -726,8 +726,8 @@
 		DEBUGP(DMSC, "Found matching audio type (pref=%d): %s %s for channel_type ="
 		       " { ch_indctr=0x%x, ch_rate_type=0x%x, perm_spch=[ %s] }\n",
 		       i,
-		       req->ch_mode_rate[i].chan_rate == CH_RATE_FULL ? "full rate" : "half rate",
-		       get_value_string(gsm48_chan_mode_names, req->ch_mode_rate[i].chan_mode),
+		       req->ch_mode_rate_list[i].chan_rate == CH_RATE_FULL ? "full rate" : "half rate",
+		       get_value_string(gsm48_chan_mode_names, req->ch_mode_rate_list[i].chan_mode),
 		       ct->ch_indctr, ct->ch_rate_type, osmo_hexdump(ct->perm_spch, ct->perm_spch_len));
 	}
 
@@ -742,43 +742,43 @@
 
 	switch (ct->ch_rate_type) {
 	case GSM0808_SIGN_ANY:
-		req->ch_mode_rate[nc++].chan_rate = CH_RATE_SDCCH;
-		req->ch_mode_rate[nc++].chan_rate = CH_RATE_HALF;
-		req->ch_mode_rate[nc++].chan_rate = CH_RATE_FULL;
+		req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_SDCCH;
+		req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_HALF;
+		req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_FULL;
 		break;
 	case GSM0808_SIGN_SDCCH:
-		req->ch_mode_rate[nc++].chan_rate = CH_RATE_SDCCH;
+		req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_SDCCH;
 		break;
 	case GSM0808_SIGN_SDCCH_FULL_BM:
-		req->ch_mode_rate[nc++].chan_rate = CH_RATE_SDCCH;
-		req->ch_mode_rate[nc++].chan_rate = CH_RATE_FULL;
+		req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_SDCCH;
+		req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_FULL;
 		break;
 	case GSM0808_SIGN_SDCCH_HALF_LM:
-		req->ch_mode_rate[nc++].chan_rate = CH_RATE_SDCCH;
-		req->ch_mode_rate[nc++].chan_rate = CH_RATE_HALF;
+		req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_SDCCH;
+		req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_HALF;
 		break;
 	case GSM0808_SIGN_FULL_BM:
-		req->ch_mode_rate[nc++].chan_rate = CH_RATE_FULL;
+		req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_FULL;
 		break;
 	case GSM0808_SIGN_HALF_LM:
-		req->ch_mode_rate[nc++].chan_rate = CH_RATE_HALF;
+		req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_HALF;
 		break;
 	case GSM0808_SIGN_FULL_PREF:
 	case GSM0808_SIGN_FULL_PREF_NO_CHANGE:
-		req->ch_mode_rate[nc++].chan_rate = CH_RATE_FULL;
-		req->ch_mode_rate[nc++].chan_rate = CH_RATE_HALF;
+		req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_FULL;
+		req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_HALF;
 		break;
 	case GSM0808_SIGN_HALF_PREF:
 	case GSM0808_SIGN_HALF_PREF_NO_CHANGE:
-		req->ch_mode_rate[nc++].chan_rate = CH_RATE_HALF;
-		req->ch_mode_rate[nc++].chan_rate = CH_RATE_FULL;
+		req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_HALF;
+		req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_FULL;
 		break;
 	default:
 		break;
 	}
 
 	for (i = 0; i < nc; i++)
-		req->ch_mode_rate[i].chan_mode = GSM48_CMODE_SIGN;
+		req->ch_mode_rate_list[i].chan_mode = GSM48_CMODE_SIGN;
 
 	req->n_ch_mode_rate = nc;
 
@@ -1379,7 +1379,7 @@
 	case GSM_LCHAN_TCH_H:
 		params.speech_version_used_present = true;
 		params.speech_version_used = gsm0808_permitted_speech(lchan->type,
-								      lchan->tch_mode);
+								      lchan->current_ch_mode_rate.chan_mode);
 		if (!params.speech_version_used) {
 			LOG_HO(lchan->conn, LOGL_ERROR, "Cannot encode Speech Version (Used)"
 			       " for BSSMAP Handover Required message\n");
@@ -1417,9 +1417,10 @@
 		.l3_info = rr_ho_command->data,
 		.l3_info_len = rr_ho_command->len,
 		.chosen_channel_present = true,
-		.chosen_channel = gsm0808_chosen_channel(new_lchan->type, new_lchan->tch_mode),
+		.chosen_channel = gsm0808_chosen_channel(new_lchan->type, new_lchan->current_ch_mode_rate.chan_mode),
 		.chosen_encr_alg = new_lchan->encr.alg_id,
-		.chosen_speech_version = gsm0808_permitted_speech(new_lchan->type, new_lchan->tch_mode),
+		.chosen_speech_version = gsm0808_permitted_speech(new_lchan->type,
+								  new_lchan->current_ch_mode_rate.chan_mode),
 	};
 
 	if (gscon_is_aoip(conn)) {
@@ -1475,7 +1476,7 @@
 		.chosen_encr_alg = lchan->encr.alg_id,
 
 		.chosen_channel_present = true,
-		.chosen_channel = gsm0808_chosen_channel(lchan->type, lchan->tch_mode),
+		.chosen_channel = gsm0808_chosen_channel(lchan->type, lchan->current_ch_mode_rate.chan_mode),
 
 		.lcls_bss_status_present = (lcls_status != 0xff),
 		.lcls_bss_status = lcls_status,
@@ -1483,7 +1484,7 @@
 
 	/* speech_codec_chosen */
 	if (ho->new_lchan->activate.info.requires_voice_stream && gscon_is_aoip(conn)) {
-		int perm_spch = gsm0808_permitted_speech(lchan->type, lchan->tch_mode);
+		int perm_spch = gsm0808_permitted_speech(lchan->type, lchan->current_ch_mode_rate.chan_mode);
 		params.speech_codec_chosen_present = true;
 		rc = gsm0808_speech_codec_from_chan_type(&params.speech_codec_chosen, perm_spch);
 		if (rc) {
diff --git a/src/osmo-bsc/osmo_bsc_lcls.c b/src/osmo-bsc/osmo_bsc_lcls.c
index 4ab0441..2c90d01 100644
--- a/src/osmo-bsc/osmo_bsc_lcls.c
+++ b/src/osmo-bsc/osmo_bsc_lcls.c
@@ -345,15 +345,14 @@
 		return false;
 	}
 
-	if (conn->lchan->tch_mode != conn->lcls.other->lchan->tch_mode
+	if (conn->lchan->current_ch_mode_rate.chan_mode != conn->lcls.other->lchan->current_ch_mode_rate.chan_mode
 	    && conn->sccp.msc->lcls_codec_mismatch_allow == false) {
 		LOGPFSM(conn->lcls.fi,
 			"Not enabling LS due to TCH-mode mismatch: %s:%s != %s:%s\n",
 			gsm_lchan_name(conn->lchan),
-			gsm48_chan_mode_name(conn->lchan->tch_mode),
+			gsm48_chan_mode_name(conn->lchan->current_ch_mode_rate.chan_mode),
 			gsm_lchan_name(conn->lcls.other->lchan),
-			gsm48_chan_mode_name(conn->lcls.other->lchan->
-					     tch_mode));
+			gsm48_chan_mode_name(conn->lcls.other->lchan->current_ch_mode_rate.chan_mode));
 		return false;
 	}
 
diff --git a/tests/handover/handover_test.c b/tests/handover/handover_test.c
index 2b8e877..c16b26d 100644
--- a/tests/handover/handover_test.c
+++ b/tests/handover/handover_test.c
@@ -397,14 +397,14 @@
 
 	create_conn(lchan);
 	if (!strcasecmp(codec, "FR") && full_rate)
-		lchan->tch_mode = GSM48_CMODE_SPEECH_V1;
+		lchan->current_ch_mode_rate.chan_mode = GSM48_CMODE_SPEECH_V1;
 	else if (!strcasecmp(codec, "HR") && !full_rate)
-		lchan->tch_mode = GSM48_CMODE_SPEECH_V1;
+		lchan->current_ch_mode_rate.chan_mode = GSM48_CMODE_SPEECH_V1;
 	else if (!strcasecmp(codec, "EFR") && full_rate)
-		lchan->tch_mode = GSM48_CMODE_SPEECH_EFR;
+		lchan->current_ch_mode_rate.chan_mode = GSM48_CMODE_SPEECH_EFR;
 	else if (!strcasecmp(codec, "AMR")) {
-		lchan->tch_mode = GSM48_CMODE_SPEECH_AMR;
-		lchan->activate.info.s15_s0 = 0x0002;
+		lchan->current_ch_mode_rate.chan_mode = GSM48_CMODE_SPEECH_AMR;
+		lchan->current_ch_mode_rate.s15_s0 = 0x0002;
 	} else {
 		fprintf(stderr, "Given codec unknown\n");
 		exit(EXIT_FAILURE);

-- 
To view, visit https://gerrit.osmocom.org/c/osmo-bsc/+/24352
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-bsc
Gerrit-Branch: master
Gerrit-Change-Id: Ie0da36124d73efc28a8809b63d7c96e2167fc412
Gerrit-Change-Number: 24352
Gerrit-PatchSet: 5
Gerrit-Owner: neels <nhofmeyr at sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: fixeria <vyanitskiy at sysmocom.de>
Gerrit-Reviewer: laforge <laforge at osmocom.org>
Gerrit-Reviewer: neels <nhofmeyr at sysmocom.de>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20210601/01a5d382/attachment.htm>


More information about the gerrit-log mailing list