<p>neels <strong>submitted</strong> this change.</p><p><a href="https://gerrit.osmocom.org/c/osmo-bsc/+/24352">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Jenkins Builder: Verified
  laforge: Looks good to me, approved
  fixeria: Looks good to me, but someone else must approve

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

<div style="display:none"> Gerrit-Project: osmo-bsc </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: Ie0da36124d73efc28a8809b63d7c96e2167fc412 </div>
<div style="display:none"> Gerrit-Change-Number: 24352 </div>
<div style="display:none"> Gerrit-PatchSet: 5 </div>
<div style="display:none"> Gerrit-Owner: neels <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder </div>
<div style="display:none"> Gerrit-Reviewer: fixeria <vyanitskiy@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: laforge <laforge@osmocom.org> </div>
<div style="display:none"> Gerrit-Reviewer: neels <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>