<p>Harald Welte <strong>merged</strong> this change.</p><p><a href="https://gerrit.osmocom.org/12625">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Jenkins Builder: Verified
  tnt: Looks good to me, but someone else must approve
  Harald Welte: Looks good to me, approved

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">assignment_fsm: fix channel allocator preferences<br><br>When the MSC allocates a channel through the ASSIGNMENT REQUEST, it may<br>ask for a TCH/H and a TCH/F at the same time and tell which of the two<br>types it prefers.<br><br>The process of channel allocation currently selects, based on the BTS,<br>MSC and MS capabilites exactly one apropriate codec/rate (e.g. TCH/H)<br>and then tries to allocate it. If that allocation fails, there is no way<br>to try the second choice and the assignment fails.<br><br>For example: The MSC asks for TCH/F and TCH/H, prefering TCH/F, then the<br>channel allocator will try TCH/F and if it fails (all TCH/F are<br>currently in use), then TCH/H is never tried.<br><br>Since the BSC currently only trys the first best codec/rate that is<br>supported it also ignores the preference.<br><br>Lets fix those problems by including the preference information and both<br>possible codec/rate settings into the channel allocation decision.<br><br>Change-Id: I5239e05c1cfbcb8af28f43373a58fa6c2d216c51<br>Related: OS#3503<br>---<br>M include/osmocom/bsc/codec_pref.h<br>M include/osmocom/bsc/gsm_data.h<br>M src/osmo-bsc/assignment_fsm.c<br>M src/osmo-bsc/bsc_vty.c<br>M src/osmo-bsc/codec_pref.c<br>M src/osmo-bsc/handover_fsm.c<br>M src/osmo-bsc/osmo_bsc_bssap.c<br>M tests/codec_pref/codec_pref_test.c<br>8 files changed, 321 insertions(+), 113 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/include/osmocom/bsc/codec_pref.h b/include/osmocom/bsc/codec_pref.h</span><br><span>index 51340c1..adefe04 100644</span><br><span>--- a/include/osmocom/bsc/codec_pref.h</span><br><span>+++ b/include/osmocom/bsc/codec_pref.h</span><br><span>@@ -9,14 +9,19 @@</span><br><span> struct bts_codec_conf;</span><br><span> struct bsc_msc_data;</span><br><span> struct gsm_bts;</span><br><span style="color: hsl(120, 100%, 40%);">+struct channel_mode_and_rate;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-int match_codec_pref(enum gsm48_chan_mode *chan_mode,</span><br><span style="color: hsl(0, 100%, 40%);">-                 bool *full_rate,</span><br><span style="color: hsl(0, 100%, 40%);">-                uint16_t *s15_s0,</span><br><span style="color: hsl(120, 100%, 40%);">+enum rate_pref {</span><br><span style="color: hsl(120, 100%, 40%);">+      RATE_PREF_NONE,</span><br><span style="color: hsl(120, 100%, 40%);">+       RATE_PREF_HR,</span><br><span style="color: hsl(120, 100%, 40%);">+ RATE_PREF_FR,</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%);">+int match_codec_pref(struct channel_mode_and_rate *ch_mode_rate,</span><br><span>                     const struct gsm0808_channel_type *ct,</span><br><span>               const struct gsm0808_speech_codec_list *scl,</span><br><span>                 const struct bsc_msc_data *msc,</span><br><span style="color: hsl(0, 100%, 40%);">-                 const struct gsm_bts *bts);</span><br><span style="color: hsl(120, 100%, 40%);">+                   const struct gsm_bts *bts, enum rate_pref rate_pref);</span><br><span> </span><br><span> void gen_bss_supported_codec_list(struct gsm0808_speech_codec_list *scl,</span><br><span>                             const struct bsc_msc_data *msc,</span><br><span>diff --git a/include/osmocom/bsc/gsm_data.h b/include/osmocom/bsc/gsm_data.h</span><br><span>index f6c5129..4d27a2e 100644</span><br><span>--- a/include/osmocom/bsc/gsm_data.h</span><br><span>+++ b/include/osmocom/bsc/gsm_data.h</span><br><span>@@ -99,6 +99,12 @@</span><br><span>  SUBSCR_SCCP_ST_CONNECTED</span><br><span> };</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+struct channel_mode_and_rate {</span><br><span style="color: hsl(120, 100%, 40%);">+    enum gsm48_chan_mode chan_mode;</span><br><span style="color: hsl(120, 100%, 40%);">+       bool full_rate;</span><br><span style="color: hsl(120, 100%, 40%);">+       uint16_t s15_s0;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /* Information retrieved during an Assignment Request from the MSC. This is storage of the Assignment instructions</span><br><span>  * parsed from the Assignment Request message, to pass on until the gscon and assignment FSMs have decided whether an</span><br><span>  * Assignment is actually going to be carried out. Should remain unchanged after initial decoding. */</span><br><span>@@ -110,9 +116,12 @@</span><br><span>        char msc_rtp_addr[INET_ADDRSTRLEN];</span><br><span>  uint16_t msc_rtp_port;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-      enum gsm48_chan_mode chan_mode;</span><br><span style="color: hsl(0, 100%, 40%);">- bool full_rate;</span><br><span style="color: hsl(0, 100%, 40%);">- uint16_t s15_s0;</span><br><span style="color: hsl(120, 100%, 40%);">+      /* Prefered rate/codec setting (mandatory) */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct channel_mode_and_rate ch_mode_rate_pref;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* Alternate rate/codec setting (optional) */</span><br><span style="color: hsl(120, 100%, 40%);">+ bool ch_mode_rate_alt_present;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct channel_mode_and_rate ch_mode_rate_alt;</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>@@ -629,6 +638,13 @@</span><br><span>    struct gsm48_req_ref *rqd_ref;</span><br><span> </span><br><span>   struct gsm_subscriber_connection *conn;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* Depending on the preferences that where submitted together with</span><br><span style="color: hsl(120, 100%, 40%);">+     * the assignment and the current channel load, the BSC has to select</span><br><span style="color: hsl(120, 100%, 40%);">+  * one of the offered codec/rates. The final selection by the BSC is</span><br><span style="color: hsl(120, 100%, 40%);">+   * stored here and is used when sending the assignment complete or</span><br><span style="color: hsl(120, 100%, 40%);">+     * when performing a handover procedure. */</span><br><span style="color: hsl(120, 100%, 40%);">+   struct channel_mode_and_rate 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/assignment_fsm.c b/src/osmo-bsc/assignment_fsm.c</span><br><span>index 2ad3959..67937d6 100644</span><br><span>--- a/src/osmo-bsc/assignment_fsm.c</span><br><span>+++ b/src/osmo-bsc/assignment_fsm.c</span><br><span>@@ -170,7 +170,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->assignment.req.s15_s0;</span><br><span style="color: hsl(120, 100%, 40%);">+                      sc.cfg = conn->lchan->ch_mode_rate.s15_s0;</span><br><span>                     sc_ptr = &sc;</span><br><span>            }</span><br><span>    }</span><br><span>@@ -248,9 +248,11 @@</span><br><span>                               new_lchan->nr);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static bool lchan_type_compat_with_mode(enum gsm_chan_t type,</span><br><span style="color: hsl(0, 100%, 40%);">-                                      enum gsm48_chan_mode chan_mode, int full_rate)</span><br><span style="color: hsl(120, 100%, 40%);">+static bool lchan_type_compat_with_mode(enum gsm_chan_t type, const struct channel_mode_and_rate *ch_mode_rate)</span><br><span> {</span><br><span style="color: hsl(120, 100%, 40%);">+  enum gsm48_chan_mode chan_mode = ch_mode_rate->chan_mode;</span><br><span style="color: hsl(120, 100%, 40%);">+  bool full_rate = ch_mode_rate->full_rate;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>       switch (chan_mode) {</span><br><span>         case GSM48_CMODE_SIGN:</span><br><span>               switch (type) {</span><br><span>@@ -293,6 +295,107 @@</span><br><span>      OSMO_ASSERT(osmo_fsm_register(&assignment_fsm) == 0);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static int check_requires_voice(bool *requires_voice, enum gsm48_chan_mode chan_mode)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  *requires_voice = false;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    switch (chan_mode) {</span><br><span style="color: hsl(120, 100%, 40%);">+  case GSM48_CMODE_SPEECH_V1:</span><br><span style="color: hsl(120, 100%, 40%);">+   case GSM48_CMODE_SPEECH_EFR:</span><br><span style="color: hsl(120, 100%, 40%);">+  case GSM48_CMODE_SPEECH_AMR:</span><br><span style="color: hsl(120, 100%, 40%);">+          *requires_voice = true;</span><br><span style="color: hsl(120, 100%, 40%);">+               break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case GSM48_CMODE_SIGN:</span><br><span style="color: hsl(120, 100%, 40%);">+                *requires_voice = false;</span><br><span style="color: hsl(120, 100%, 40%);">+              break;</span><br><span style="color: hsl(120, 100%, 40%);">+        default:</span><br><span style="color: hsl(120, 100%, 40%);">+              return -EINVAL;</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%);">+   return 0;</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%);">+/* Check if the incoming assignment requests requires a voice stream or not,</span><br><span style="color: hsl(120, 100%, 40%);">+ * we will look at the preferred and the alternate channel mode and also make</span><br><span style="color: hsl(120, 100%, 40%);">+ * sure that both are consistent. */</span><br><span style="color: hsl(120, 100%, 40%);">+static int check_requires_voice_stream(struct gsm_subscriber_connection *conn)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  bool result_requires_voice_alt;</span><br><span style="color: hsl(120, 100%, 40%);">+       bool result_requires_voice_pref;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct assignment_request *req = &conn->assignment.req;</span><br><span style="color: hsl(120, 100%, 40%);">+        struct osmo_fsm_inst *fi = conn->fi;</span><br><span style="color: hsl(120, 100%, 40%);">+       int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* When the assignment request indicates that there is an alternate</span><br><span style="color: hsl(120, 100%, 40%);">+    * rate available (e.g. "Full or Half rate channel, Half rate</span><br><span style="color: hsl(120, 100%, 40%);">+     * preferred..."), then both must be either voice or either signalling,</span><br><span style="color: hsl(120, 100%, 40%);">+   * a mismatch is not permitted */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Check the prefered setting */</span><br><span style="color: hsl(120, 100%, 40%);">+      rc = check_requires_voice(&result_requires_voice_pref, req->ch_mode_rate_pref.chan_mode);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+              assignment_fail(GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_NOT_SUPP,</span><br><span style="color: hsl(120, 100%, 40%);">+                              "Prefered channel mode not supported: %s",</span><br><span style="color: hsl(120, 100%, 40%);">+                          gsm48_chan_mode_name(req->ch_mode_rate_pref.chan_mode));</span><br><span style="color: hsl(120, 100%, 40%);">+           return -EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+     conn->assignment.requires_voice_stream = result_requires_voice_pref;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* If there is an alternate setting, check that one as well */</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!req->ch_mode_rate_alt_present)</span><br><span style="color: hsl(120, 100%, 40%);">+                return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+     rc = check_requires_voice(&result_requires_voice_alt, req->ch_mode_rate_alt.chan_mode);</span><br><span style="color: hsl(120, 100%, 40%);">+        if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+              assignment_fail(GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_NOT_SUPP,</span><br><span style="color: hsl(120, 100%, 40%);">+                              "Alternate channel mode not supported: %s",</span><br><span style="color: hsl(120, 100%, 40%);">+                         gsm48_chan_mode_name(req->ch_mode_rate_alt.chan_mode));</span><br><span style="color: hsl(120, 100%, 40%);">+            return -EINVAL;</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%);">+   /* Make sure both settings match */</span><br><span style="color: hsl(120, 100%, 40%);">+   if (result_requires_voice_pref != result_requires_voice_alt) {</span><br><span style="color: hsl(120, 100%, 40%);">+                assignment_fail(GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_NOT_SUPP,</span><br><span style="color: hsl(120, 100%, 40%);">+                              "Inconsistent channel modes: %s != %s",</span><br><span style="color: hsl(120, 100%, 40%);">+                             gsm48_chan_mode_name(req->ch_mode_rate_pref.chan_mode),</span><br><span style="color: hsl(120, 100%, 40%);">+                            gsm48_chan_mode_name(req->ch_mode_rate_alt.chan_mode));</span><br><span style="color: hsl(120, 100%, 40%);">+            return -EINVAL;</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%);">+   return 0;</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%);">+/* Check if the conn is already associated with an lchan. If yes, we will check</span><br><span style="color: hsl(120, 100%, 40%);">+ * if that lchan is compatible with the preferred rate/codec. If the lchan</span><br><span style="color: hsl(120, 100%, 40%);">+ * turns out to be incompatible we try with the alternate rate/codec. */</span><br><span style="color: hsl(120, 100%, 40%);">+static bool reuse_existing_lchan(struct gsm_subscriber_connection *conn)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    struct assignment_request *req = &conn->assignment.req;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!conn->lchan)</span><br><span style="color: hsl(120, 100%, 40%);">+          return false;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       /* Check if the currently existing lchan is compatible with the</span><br><span style="color: hsl(120, 100%, 40%);">+        * preferred rate/codec. */</span><br><span style="color: hsl(120, 100%, 40%);">+   if (lchan_type_compat_with_mode(conn->lchan->type, &req->ch_mode_rate_pref))</span><br><span style="color: hsl(120, 100%, 40%);">+             conn->lchan->ch_mode_rate = req->ch_mode_rate_pref;</span><br><span style="color: hsl(120, 100%, 40%);">+  else if (req->ch_mode_rate_alt_present</span><br><span style="color: hsl(120, 100%, 40%);">+              && lchan_type_compat_with_mode(conn->lchan->type, &req->ch_mode_rate_alt))</span><br><span style="color: hsl(120, 100%, 40%);">+              conn->lchan->ch_mode_rate = req->ch_mode_rate_alt;</span><br><span style="color: hsl(120, 100%, 40%);">+   else</span><br><span style="color: hsl(120, 100%, 40%);">+          return false;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (conn->lchan->tch_mode != conn->lchan->ch_mode_rate.chan_mode) {</span><br><span style="color: hsl(120, 100%, 40%);">+               /* FIXME: send Channel Mode Modify to put the current lchan in the right mode, and kick</span><br><span style="color: hsl(120, 100%, 40%);">+                * off its RTP stream setup code path. See gsm48_lchan_modify() and</span><br><span style="color: hsl(120, 100%, 40%);">+            * gsm48_rx_rr_modif_ack(), and see lchan_fsm.h LCHAN_EV_CHAN_MODE_MODIF_* */</span><br><span style="color: hsl(120, 100%, 40%);">+         LOG_ASSIGNMENT(conn, LOGL_DEBUG,</span><br><span style="color: hsl(120, 100%, 40%);">+                             "Current lchan would be compatible, but Channel Mode Modify is not implemented\n");</span><br><span style="color: hsl(120, 100%, 40%);">+          return false;</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%);">+   return true;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> void assignment_fsm_start(struct gsm_subscriber_connection *conn, struct gsm_bts *bts,</span><br><span>                    struct assignment_request *req)</span><br><span> {</span><br><span>@@ -311,75 +414,77 @@</span><br><span>       conn->assignment.fi = fi;</span><br><span>         fi->priv = conn;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+       /* Create a copy of the request data and use that copy from now on. */</span><br><span>       conn->assignment.req = *req;</span><br><span style="color: hsl(120, 100%, 40%);">+       req = &conn->assignment.req;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- switch (req->chan_mode) {</span><br><span style="color: hsl(120, 100%, 40%);">+  /* Check if we need a voice stream. If yes, set the approriate struct</span><br><span style="color: hsl(120, 100%, 40%);">+  * members in conn */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (check_requires_voice_stream(conn) < 0)</span><br><span style="color: hsl(120, 100%, 40%);">+         return;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-     case GSM48_CMODE_SPEECH_V1:</span><br><span style="color: hsl(0, 100%, 40%);">-     case GSM48_CMODE_SPEECH_EFR:</span><br><span style="color: hsl(0, 100%, 40%);">-    case GSM48_CMODE_SPEECH_AMR:</span><br><span style="color: hsl(0, 100%, 40%);">-            conn->assignment.requires_voice_stream = true;</span><br><span style="color: hsl(0, 100%, 40%);">-               /* Select an lchan below. */</span><br><span style="color: hsl(0, 100%, 40%);">-            break;</span><br><span style="color: hsl(120, 100%, 40%);">+        /* There may be an already existing lchan, if yes, try to work with</span><br><span style="color: hsl(120, 100%, 40%);">+    * the existing lchan */</span><br><span style="color: hsl(120, 100%, 40%);">+      if (reuse_existing_lchan(conn)) {</span><br><span style="color: hsl(120, 100%, 40%);">+             LOG_ASSIGNMENT(conn, LOGL_DEBUG,</span><br><span style="color: hsl(120, 100%, 40%);">+                             "Current lchan is compatible with requested chan_mode,"</span><br><span style="color: hsl(120, 100%, 40%);">+                             " sending BSSMAP Assignment Complete directly."</span><br><span style="color: hsl(120, 100%, 40%);">+                             " requested chan_mode=%s; current lchan is %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                           gsm48_chan_mode_name(conn->lchan->ch_mode_rate.chan_mode),</span><br><span style="color: hsl(120, 100%, 40%);">+                              gsm_lchan_name(conn->lchan));</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-     case GSM48_CMODE_SIGN:</span><br><span style="color: hsl(0, 100%, 40%);">-          conn->assignment.requires_voice_stream = false;</span><br><span style="color: hsl(0, 100%, 40%);">-              /* Select an lchan below. */</span><br><span style="color: hsl(0, 100%, 40%);">-            break;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-  default:</span><br><span style="color: hsl(0, 100%, 40%);">-                assignment_fail(GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_NOT_SUPP,</span><br><span style="color: hsl(0, 100%, 40%);">-                                "Channel mode not supported: %s",</span><br><span style="color: hsl(0, 100%, 40%);">-                             gsm48_chan_mode_name(req->chan_mode));</span><br><span style="color: hsl(120, 100%, 40%);">+             send_assignment_complete(conn);</span><br><span style="color: hsl(120, 100%, 40%);">+               /* If something went wrong during send_assignment_complete(), the fi will be gone from</span><br><span style="color: hsl(120, 100%, 40%);">+                 * error handling in there. */</span><br><span style="color: hsl(120, 100%, 40%);">+                if (conn->assignment.fi) {</span><br><span style="color: hsl(120, 100%, 40%);">+                 assignment_count_result(BSC_CTR_ASSIGNMENT_COMPLETED);</span><br><span style="color: hsl(120, 100%, 40%);">+                        osmo_fsm_inst_term(conn->assignment.fi, OSMO_FSM_TERM_REGULAR, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+         }</span><br><span>            return;</span><br><span>      }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   if (conn->lchan</span><br><span style="color: hsl(0, 100%, 40%);">-          && lchan_type_compat_with_mode(conn->lchan->type, req->chan_mode, req->full_rate)) {</span><br><span style="color: hsl(120, 100%, 40%);">+  /* Try to allocate a new lchan with the preferred codec/rate choice */</span><br><span style="color: hsl(120, 100%, 40%);">+        conn->assignment.new_lchan =</span><br><span style="color: hsl(120, 100%, 40%);">+           lchan_select_by_chan_mode(bts, req->ch_mode_rate_pref.chan_mode, req->ch_mode_rate_pref.full_rate);</span><br><span style="color: hsl(120, 100%, 40%);">+ conn->lchan->ch_mode_rate = req->ch_mode_rate_pref;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-                if (conn->lchan->tch_mode == req->chan_mode) {</span><br><span style="color: hsl(0, 100%, 40%);">-                 /* current lchan suffices and already is in the right mode. We're done. */</span><br><span style="color: hsl(0, 100%, 40%);">-                  LOG_ASSIGNMENT(conn, LOGL_DEBUG,</span><br><span style="color: hsl(0, 100%, 40%);">-                                       "Current lchan is compatible with requested chan_mode,"</span><br><span style="color: hsl(0, 100%, 40%);">-                                       " sending BSSMAP Assignment Complete directly."</span><br><span style="color: hsl(0, 100%, 40%);">-                                       " requested chan_mode=%s; current lchan is %s\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                                     gsm48_chan_mode_name(req->chan_mode),</span><br><span style="color: hsl(0, 100%, 40%);">-                                gsm_lchan_name(conn->lchan));</span><br><span style="color: hsl(0, 100%, 40%);">-                 send_assignment_complete(conn);</span><br><span style="color: hsl(0, 100%, 40%);">-                 return;</span><br><span style="color: hsl(0, 100%, 40%);">-         }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-               /* FIXME: send Channel Mode Modify to put the current lchan in the right mode, and kick</span><br><span style="color: hsl(0, 100%, 40%);">-          * off its RTP stream setup code path. See gsm48_lchan_modify() and</span><br><span style="color: hsl(0, 100%, 40%);">-              * gsm48_rx_rr_modif_ack(), and see lchan_fsm.h LCHAN_EV_CHAN_MODE_MODIF_* */</span><br><span style="color: hsl(0, 100%, 40%);">-           LOG_ASSIGNMENT(conn, LOGL_ERROR,</span><br><span style="color: hsl(0, 100%, 40%);">-                               "NOT IMPLEMENTED:"</span><br><span style="color: hsl(0, 100%, 40%);">-                            " Current lchan would be compatible, we should send Channel Mode Modify\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ /* In case the lchan allocation fails, we try with the alternat codec/</span><br><span style="color: hsl(120, 100%, 40%);">+         * rate choice (if possible) */</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!conn->assignment.new_lchan && req->ch_mode_rate_alt_present) {</span><br><span style="color: hsl(120, 100%, 40%);">+             conn->assignment.new_lchan =</span><br><span style="color: hsl(120, 100%, 40%);">+                   lchan_select_by_chan_mode(bts, req->ch_mode_rate_alt.chan_mode, req->ch_mode_rate_alt.full_rate);</span><br><span style="color: hsl(120, 100%, 40%);">+           conn->lchan->ch_mode_rate = req->ch_mode_rate_alt;</span><br><span>  }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   conn->assignment.new_lchan = lchan_select_by_chan_mode(bts, req->chan_mode, req->full_rate);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Check whether the lchan allocation was successful or not and tear</span><br><span style="color: hsl(120, 100%, 40%);">+   * down the assignment in case of failure. */</span><br><span>        if (!conn->assignment.new_lchan) {</span><br><span>                assignment_count_result(BSC_CTR_ASSIGNMENT_NO_CHANNEL);</span><br><span>              assignment_fail(GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE,</span><br><span>                           "BSSMAP Assignment Command:"</span><br><span style="color: hsl(0, 100%, 40%);">-                          " No lchan available for: chan_mode=%s, full_rate=%i\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                              get_value_string(gsm48_chan_mode_names, req->chan_mode), req->full_rate);</span><br><span style="color: hsl(120, 100%, 40%);">+                               " No lchan available for: preferred=%s%s / alternate=%s%s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                               gsm48_chan_mode_name(req->ch_mode_rate_pref.chan_mode),</span><br><span style="color: hsl(120, 100%, 40%);">+                            req->ch_mode_rate_pref.full_rate ? ",FR" : ",HR",</span><br><span style="color: hsl(120, 100%, 40%);">+                              req->ch_mode_rate_alt_present ?</span><br><span style="color: hsl(120, 100%, 40%);">+                                    gsm48_chan_mode_name(req->ch_mode_rate_alt.chan_mode) : "none",</span><br><span style="color: hsl(120, 100%, 40%);">+                          req->ch_mode_rate_alt_present ?</span><br><span style="color: hsl(120, 100%, 40%);">+                                    (req->ch_mode_rate_alt.full_rate ? ",FR" : ",HR") : "");</span><br><span>            return;</span><br><span>      }</span><br><span> </span><br><span>        assignment_fsm_update_id(conn);</span><br><span>      LOG_ASSIGNMENT(conn, LOGL_INFO, "Starting Assignment: chan_mode=%s, full_rate=%d,"</span><br><span>                        " aoip=%s MSC-rtp=%s:%u\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                   gsm48_chan_mode_name(req->chan_mode), req->full_rate,</span><br><span style="color: hsl(120, 100%, 40%);">+                   gsm48_chan_mode_name(conn->lchan->ch_mode_rate.chan_mode),</span><br><span style="color: hsl(120, 100%, 40%);">+                      conn->lchan->ch_mode_rate.full_rate,</span><br><span>                   req->aoip ? "yes" : "no", req->msc_rtp_addr, req->msc_rtp_port);</span><br><span> </span><br><span>     assignment_fsm_state_chg(ASSIGNMENT_ST_WAIT_LCHAN_ACTIVE);</span><br><span>   info = (struct lchan_activate_info){</span><br><span>                 .activ_for = FOR_ASSIGNMENT,</span><br><span>                 .for_conn = conn,</span><br><span style="color: hsl(0, 100%, 40%);">-               .chan_mode = req->chan_mode,</span><br><span style="color: hsl(0, 100%, 40%);">-         .s15_s0 = req->s15_s0,</span><br><span style="color: hsl(120, 100%, 40%);">+             .chan_mode = conn->lchan->ch_mode_rate.chan_mode,</span><br><span style="color: hsl(120, 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 357ee9e..9413d36 100644</span><br><span>--- a/src/osmo-bsc/bsc_vty.c</span><br><span>+++ b/src/osmo-bsc/bsc_vty.c</span><br><span>@@ -4802,6 +4802,7 @@</span><br><span>       ts = vty_get_ts(vty, argv[0], argv[1], argv[2]);</span><br><span>     if (!ts)</span><br><span>             return CMD_WARNING;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>        lchan = &ts->lchan[ss_nr];</span><br><span>    if (!lchan->fi)</span><br><span>           return CMD_WARNING;</span><br><span>diff --git a/src/osmo-bsc/codec_pref.c b/src/osmo-bsc/codec_pref.c</span><br><span>index c99c383..a94d6a8 100644</span><br><span>--- a/src/osmo-bsc/codec_pref.c</span><br><span>+++ b/src/osmo-bsc/codec_pref.c</span><br><span>@@ -266,21 +266,18 @@</span><br><span> </span><br><span> /*! Match the codec preferences from local config with a received codec preferences IEs received from the</span><br><span>  * MSC and the BTS' codec configuration.</span><br><span style="color: hsl(0, 100%, 40%);">- *  \param[out] chan_mode GSM 04.08 channel mode.</span><br><span style="color: hsl(0, 100%, 40%);">- *  \param[out] full_rate true if full-rate.</span><br><span style="color: hsl(0, 100%, 40%);">- *  \param[out] s15_s0 codec configuration bits S15-S0 (AMR)</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[out] ch_mode_rate resulting codec and rate information</span><br><span>  *  \param[in] ct GSM 08.08 channel type received from MSC.</span><br><span>  *  \param[in] scl GSM 08.08 speech codec list received from MSC (optional).</span><br><span>  *  \param[in] msc associated msc (current codec settings).</span><br><span>  *  \param[in] bts associated bts (current codec settings).</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] pref selected rate preference (full, half or none).</span><br><span>  *  \returns 0 on success, -1 in case no match was found */</span><br><span style="color: hsl(0, 100%, 40%);">-int match_codec_pref(enum gsm48_chan_mode *chan_mode,</span><br><span style="color: hsl(0, 100%, 40%);">-               bool *full_rate,</span><br><span style="color: hsl(0, 100%, 40%);">-                uint16_t *s15_s0,</span><br><span style="color: hsl(120, 100%, 40%);">+int match_codec_pref(struct channel_mode_and_rate *ch_mode_rate,</span><br><span>                  const struct gsm0808_channel_type *ct,</span><br><span>               const struct gsm0808_speech_codec_list *scl,</span><br><span>                 const struct bsc_msc_data *msc,</span><br><span style="color: hsl(0, 100%, 40%);">-                 const struct gsm_bts *bts)</span><br><span style="color: hsl(120, 100%, 40%);">+                    const struct gsm_bts *bts, enum rate_pref rate_pref)</span><br><span> {</span><br><span>       unsigned int i;</span><br><span>      uint8_t perm_spch;</span><br><span>@@ -297,6 +294,18 @@</span><br><span>            /* Pick a permitted speech value from the global codec configuration list */</span><br><span>                 perm_spch = audio_support_to_gsm88(msc->audio_support[i]);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+             /* Determine if the result is a half or full rate codec */</span><br><span style="color: hsl(120, 100%, 40%);">+            rc = full_rate_from_perm_spch(&ch_mode_rate->full_rate, perm_spch);</span><br><span style="color: hsl(120, 100%, 40%);">+            if (rc < 0)</span><br><span style="color: hsl(120, 100%, 40%);">+                        return -EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+             /* If we have a preference for FR or HR in our request, we</span><br><span style="color: hsl(120, 100%, 40%);">+             * discard the potential match */</span><br><span style="color: hsl(120, 100%, 40%);">+             if (rate_pref == RATE_PREF_HR && ch_mode_rate->full_rate)</span><br><span style="color: hsl(120, 100%, 40%);">+                  continue;</span><br><span style="color: hsl(120, 100%, 40%);">+             if (rate_pref == RATE_PREF_FR && !ch_mode_rate->full_rate)</span><br><span style="color: hsl(120, 100%, 40%);">+                 continue;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>          /* Check this permitted speech value against the BTS specific parameters.</span><br><span>             * if the BTS does not support the codec, try the next one */</span><br><span>                if (!test_codec_support_bts(bts, perm_spch))</span><br><span>@@ -312,19 +321,14 @@</span><br><span> </span><br><span>     /* Exit without result, in case no match can be deteched */</span><br><span>  if (!match) {</span><br><span style="color: hsl(0, 100%, 40%);">-           *full_rate = false;</span><br><span style="color: hsl(0, 100%, 40%);">-             *chan_mode = GSM48_CMODE_SIGN;</span><br><span style="color: hsl(0, 100%, 40%);">-          *s15_s0 = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+          ch_mode_rate->full_rate = false;</span><br><span style="color: hsl(120, 100%, 40%);">+           ch_mode_rate->chan_mode = GSM48_CMODE_SIGN;</span><br><span style="color: hsl(120, 100%, 40%);">+                ch_mode_rate->s15_s0 = 0;</span><br><span>                 return -1;</span><br><span>   }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   /* Determine if the result is a half or full rate codec */</span><br><span style="color: hsl(0, 100%, 40%);">-      rc = full_rate_from_perm_spch(full_rate, perm_spch);</span><br><span style="color: hsl(0, 100%, 40%);">-    if (rc < 0)</span><br><span style="color: hsl(0, 100%, 40%);">-          return -EINVAL;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>      /* Lookup a channel mode for the selected codec */</span><br><span style="color: hsl(0, 100%, 40%);">-      *chan_mode = gsm88_to_chan_mode(perm_spch);</span><br><span style="color: hsl(120, 100%, 40%);">+   ch_mode_rate->chan_mode = gsm88_to_chan_mode(perm_spch);</span><br><span> </span><br><span>      /* Special handling for AMR */</span><br><span>       if (perm_spch == GSM0808_PERM_HR3 || perm_spch == GSM0808_PERM_FR3) {</span><br><span>@@ -337,9 +341,9 @@</span><br><span>           * result */</span><br><span>                 amr_s15_s0_supported = gen_bss_supported_amr_s15_s0(msc, bts, (perm_spch == GSM0808_PERM_HR3));</span><br><span>              if (sc_match)</span><br><span style="color: hsl(0, 100%, 40%);">-                   *s15_s0 = sc_match->cfg & amr_s15_s0_supported;</span><br><span style="color: hsl(120, 100%, 40%);">+                        ch_mode_rate->s15_s0 = sc_match->cfg & amr_s15_s0_supported;</span><br><span>               else</span><br><span style="color: hsl(0, 100%, 40%);">-                    *s15_s0 = amr_s15_s0_supported;</span><br><span style="color: hsl(120, 100%, 40%);">+                       ch_mode_rate->s15_s0 = amr_s15_s0_supported;</span><br><span> </span><br><span>          /* NOTE: The function test_codec_pref() will populate the</span><br><span>             * sc_match pointer from the searched speech codec list. For</span><br><span>@@ -349,7 +353,7 @@</span><br><span>            * However s15_s0 is always populated with a meaningful value,</span><br><span>                * regardless if AoIP is in use or not. */</span><br><span>   } else</span><br><span style="color: hsl(0, 100%, 40%);">-          *s15_s0 = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+          ch_mode_rate->s15_s0 = 0;</span><br><span> </span><br><span>     return 0;</span><br><span> }</span><br><span>diff --git a/src/osmo-bsc/handover_fsm.c b/src/osmo-bsc/handover_fsm.c</span><br><span>index 578cff3..30297f6 100644</span><br><span>--- a/src/osmo-bsc/handover_fsm.c</span><br><span>+++ b/src/osmo-bsc/handover_fsm.c</span><br><span>@@ -521,10 +521,8 @@</span><br><span>       struct bsc_msc_data *msc = conn->sccp.msc;</span><br><span>        struct handover_in_req *req = &ho->inter_bsc_in;</span><br><span>      int match_idx;</span><br><span style="color: hsl(0, 100%, 40%);">-  enum gsm48_chan_mode mode;</span><br><span style="color: hsl(0, 100%, 40%);">-      bool full_rate = false;</span><br><span style="color: hsl(0, 100%, 40%);">- uint16_t s15_s0;</span><br><span>     struct osmo_fsm_inst *fi;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct channel_mode_and_rate ch_mode_rate = {};</span><br><span> </span><br><span>  handover_fsm_alloc(conn);</span><br><span> </span><br><span>@@ -562,7 +560,7 @@</span><br><span>                 bts->nr, req->cell_id_target_name);</span><br><span> </span><br><span>                 /* Figure out channel type */</span><br><span style="color: hsl(0, 100%, 40%);">-           if (match_codec_pref(&mode, &full_rate, &s15_s0, &req->ct, &req->scl, msc, bts)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          if (match_codec_pref(&ch_mode_rate, &req->ct, &req->scl, msc, bts, RATE_PREF_NONE)) {</span><br><span>                      LOG_HO(conn, LOGL_DEBUG,</span><br><span>                            "BTS %u has no matching channel codec (%s, speech codec list len = %u)\n",</span><br><span>                         bts->nr, gsm0808_channel_type_name(&req->ct), req->scl.len);</span><br><span>@@ -570,10 +568,10 @@</span><br><span>             }</span><br><span> </span><br><span>                LOG_HO(conn, LOGL_DEBUG, "BTS %u: Found matching audio type: %s %s (for %s)\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                      bts->nr, gsm48_chan_mode_name(mode), full_rate? "full-rate" : "half-rate",</span><br><span style="color: hsl(120, 100%, 40%);">+                     bts->nr, gsm48_chan_mode_name(ch_mode_rate.chan_mode), ch_mode_rate.full_rate? "full-rate" : "half-rate",</span><br><span>                     gsm0808_channel_type_name(&req->ct));</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-         lchan = lchan_select_by_chan_mode(bts, mode, full_rate);</span><br><span style="color: hsl(120, 100%, 40%);">+              lchan = lchan_select_by_chan_mode(bts, ch_mode_rate.chan_mode, ch_mode_rate.full_rate);</span><br><span>              if (!lchan) {</span><br><span>                        LOG_HO(conn, LOGL_DEBUG, "BTS %u has no matching free channels\n", bts->nr);</span><br><span>                    continue;</span><br><span>@@ -605,9 +603,9 @@</span><br><span>      info = (struct lchan_activate_info){</span><br><span>                 .activ_for = FOR_HANDOVER,</span><br><span>           .for_conn = conn,</span><br><span style="color: hsl(0, 100%, 40%);">-               .chan_mode = mode,</span><br><span style="color: hsl(0, 100%, 40%);">-              .s15_s0 = s15_s0,</span><br><span style="color: hsl(0, 100%, 40%);">-               .requires_voice_stream = chan_mode_is_tch(mode),</span><br><span style="color: hsl(120, 100%, 40%);">+              .chan_mode = ch_mode_rate.chan_mode,</span><br><span style="color: hsl(120, 100%, 40%);">+          .s15_s0 = ch_mode_rate.s15_s0,</span><br><span style="color: hsl(120, 100%, 40%);">+                .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> </span><br><span>@@ -715,7 +713,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, ho_perf_params.speech_version_chosen);</span><br><span style="color: hsl(0, 100%, 40%);">-                     sc.cfg = conn->lchan->s15_s0;</span><br><span style="color: hsl(120, 100%, 40%);">+                   sc.cfg = conn->lchan->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/osmo_bsc_bssap.c b/src/osmo-bsc/osmo_bsc_bssap.c</span><br><span>index ab7f79c..85aab22 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>@@ -616,6 +616,101 @@</span><br><span>   return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/* Select a prefered and an alternative codec rate depending on the available</span><br><span style="color: hsl(120, 100%, 40%);">+ * capabilities. This decision does not include the actual channel load yet,</span><br><span style="color: hsl(120, 100%, 40%);">+ * this is also the reason why the result is a prefered and an alternate</span><br><span style="color: hsl(120, 100%, 40%);">+ * setting. The final decision is made in assignment_fsm.c when the actual</span><br><span style="color: hsl(120, 100%, 40%);">+ * lchan is requested. The preferred lchan will be requested first. If we</span><br><span style="color: hsl(120, 100%, 40%);">+ * find an alternate setting here, this one will be tried secondly if our</span><br><span style="color: hsl(120, 100%, 40%);">+ * primary coice fails. */</span><br><span style="color: hsl(120, 100%, 40%);">+static int select_codecs(struct assignment_request *req, struct gsm0808_channel_type *ct,</span><br><span style="color: hsl(120, 100%, 40%);">+                     struct gsm_subscriber_connection *conn)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct bsc_msc_data *msc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   msc = conn->sccp.msc;</span><br><span style="color: hsl(120, 100%, 40%);">+      req->ch_mode_rate_alt_present = false;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   switch (ct->ch_rate_type) {</span><br><span style="color: hsl(120, 100%, 40%);">+        case GSM0808_SPEECH_FULL_BM:</span><br><span style="color: hsl(120, 100%, 40%);">+          rc = match_codec_pref(&req->ch_mode_rate_pref, ct, &conn->codec_list, msc, conn_get_bts(conn),</span><br><span style="color: hsl(120, 100%, 40%);">+                                RATE_PREF_FR);</span><br><span style="color: hsl(120, 100%, 40%);">+          break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case GSM0808_SPEECH_HALF_LM:</span><br><span style="color: hsl(120, 100%, 40%);">+          rc = match_codec_pref(&req->ch_mode_rate_pref, ct, &conn->codec_list, msc, conn_get_bts(conn),</span><br><span style="color: hsl(120, 100%, 40%);">+                                RATE_PREF_HR);</span><br><span style="color: hsl(120, 100%, 40%);">+          break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case GSM0808_SPEECH_PERM:</span><br><span style="color: hsl(120, 100%, 40%);">+     case GSM0808_SPEECH_PERM_NO_CHANGE:</span><br><span style="color: hsl(120, 100%, 40%);">+   case GSM0808_SPEECH_FULL_PREF_NO_CHANGE:</span><br><span style="color: hsl(120, 100%, 40%);">+      case GSM0808_SPEECH_FULL_PREF:</span><br><span style="color: hsl(120, 100%, 40%);">+                rc = match_codec_pref(&req->ch_mode_rate_pref, ct, &conn->codec_list, msc, conn_get_bts(conn),</span><br><span style="color: hsl(120, 100%, 40%);">+                                RATE_PREF_FR);</span><br><span style="color: hsl(120, 100%, 40%);">+          if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      rc = match_codec_pref(&req->ch_mode_rate_pref, ct, &conn->codec_list, msc, conn_get_bts(conn),</span><br><span style="color: hsl(120, 100%, 40%);">+                                        RATE_PREF_HR);</span><br><span style="color: hsl(120, 100%, 40%);">+                  break;</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+             rc = match_codec_pref(&req->ch_mode_rate_alt, ct, &conn->codec_list, msc, conn_get_bts(conn),</span><br><span style="color: hsl(120, 100%, 40%);">+                                 RATE_PREF_HR);</span><br><span style="color: hsl(120, 100%, 40%);">+          if (rc == 0)</span><br><span style="color: hsl(120, 100%, 40%);">+                  req->ch_mode_rate_alt_present = true;</span><br><span style="color: hsl(120, 100%, 40%);">+              rc = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+               break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case GSM0808_SPEECH_HALF_PREF_NO_CHANGE:</span><br><span style="color: hsl(120, 100%, 40%);">+      case GSM0808_SPEECH_HALF_PREF:</span><br><span style="color: hsl(120, 100%, 40%);">+                rc = match_codec_pref(&req->ch_mode_rate_pref, ct, &conn->codec_list, msc, conn_get_bts(conn),</span><br><span style="color: hsl(120, 100%, 40%);">+                                RATE_PREF_HR);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+                if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+                      rc = match_codec_pref(&req->ch_mode_rate_pref, ct, &conn->codec_list, msc, conn_get_bts(conn),</span><br><span style="color: hsl(120, 100%, 40%);">+                                        RATE_PREF_FR);</span><br><span style="color: hsl(120, 100%, 40%);">+                  break;</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+             rc = match_codec_pref(&req->ch_mode_rate_alt, ct, &conn->codec_list, msc, conn_get_bts(conn),</span><br><span style="color: hsl(120, 100%, 40%);">+                                 RATE_PREF_FR);</span><br><span style="color: hsl(120, 100%, 40%);">+          if (rc == 0)</span><br><span style="color: hsl(120, 100%, 40%);">+                  req->ch_mode_rate_alt_present = true;</span><br><span style="color: hsl(120, 100%, 40%);">+              rc = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+               break;</span><br><span style="color: hsl(120, 100%, 40%);">+        default:</span><br><span style="color: hsl(120, 100%, 40%);">+              rc = -EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+         break;</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%);">+   if (rc < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+              LOGP(DMSC, LOGL_ERROR, "No supported audio type found for channel_type ="</span><br><span style="color: hsl(120, 100%, 40%);">+                " { ch_indctr=0x%x, ch_rate_type=0x%x, perm_spch=[%s] }\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                 ct->ch_indctr, ct->ch_rate_type, osmo_hexdump(ct->perm_spch, ct->perm_spch_len));</span><br><span style="color: hsl(120, 100%, 40%);">+            /* TODO: actually output codec names, e.g. implement</span><br><span style="color: hsl(120, 100%, 40%);">+           * gsm0808_permitted_speech_names[] and iterate perm_spch. */</span><br><span style="color: hsl(120, 100%, 40%);">+         return -EINVAL;</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%);">+   if (req->ch_mode_rate_alt_present) {</span><br><span style="color: hsl(120, 100%, 40%);">+               DEBUGP(DMSC, "Found matching audio type (preferred): %s %s for channel_type ="</span><br><span style="color: hsl(120, 100%, 40%);">+                     " { ch_indctr=0x%x, ch_rate_type=0x%x, perm_spch=[ %s] }\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                req->ch_mode_rate_pref.full_rate ? "full rate" : "half rate",</span><br><span style="color: hsl(120, 100%, 40%);">+                  get_value_string(gsm48_chan_mode_names, req->ch_mode_rate_pref.chan_mode),</span><br><span style="color: hsl(120, 100%, 40%);">+                 ct->ch_indctr, ct->ch_rate_type, osmo_hexdump(ct->perm_spch, ct->perm_spch_len));</span><br><span style="color: hsl(120, 100%, 40%);">+          DEBUGP(DMSC, "Found matching audio type (alternative): %s %s for channel_type ="</span><br><span style="color: hsl(120, 100%, 40%);">+                   " { ch_indctr=0x%x, ch_rate_type=0x%x, perm_spch=[ %s] }\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                req->ch_mode_rate_alt.full_rate ? "full rate" : "half rate",</span><br><span style="color: hsl(120, 100%, 40%);">+                   get_value_string(gsm48_chan_mode_names, req->ch_mode_rate_alt.chan_mode),</span><br><span style="color: hsl(120, 100%, 40%);">+                  ct->ch_indctr, ct->ch_rate_type, osmo_hexdump(ct->perm_spch, ct->perm_spch_len));</span><br><span style="color: hsl(120, 100%, 40%);">+  } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              DEBUGP(DMSC, "Found matching audio type: %s %s for channel_type ="</span><br><span style="color: hsl(120, 100%, 40%);">+                 " { ch_indctr=0x%x, ch_rate_type=0x%x, perm_spch=[ %s] }\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                req->ch_mode_rate_pref.full_rate ? "full rate" : "half rate",</span><br><span style="color: hsl(120, 100%, 40%);">+                  get_value_string(gsm48_chan_mode_names, req->ch_mode_rate_pref.chan_mode),</span><br><span style="color: hsl(120, 100%, 40%);">+                 ct->ch_indctr, ct->ch_rate_type, osmo_hexdump(ct->perm_spch, ct->perm_spch_len));</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 style="color: hsl(120, 100%, 40%);">+   return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*</span><br><span>  * Handle the assignment request message.</span><br><span>  *</span><br><span>@@ -625,18 +720,15 @@</span><br><span>                                   struct msgb *msg, unsigned int length)</span><br><span> {</span><br><span>     struct msgb *resp;</span><br><span style="color: hsl(0, 100%, 40%);">-      struct bsc_msc_data *msc;</span><br><span>    struct tlv_parsed tp;</span><br><span>        uint16_t cic = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-       enum gsm48_chan_mode chan_mode = GSM48_CMODE_SIGN;</span><br><span style="color: hsl(0, 100%, 40%);">-      bool full_rate = false;</span><br><span style="color: hsl(0, 100%, 40%);">- uint16_t s15_s0 = 0;</span><br><span>         bool aoip = false;</span><br><span>   struct sockaddr_storage rtp_addr;</span><br><span>    struct gsm0808_channel_type ct;</span><br><span>      uint8_t cause;</span><br><span>       int rc;</span><br><span>      struct assignment_request req = {};</span><br><span style="color: hsl(120, 100%, 40%);">+   struct channel_mode_and_rate ch_mode_rate_pref = {};</span><br><span> </span><br><span>     if (!conn) {</span><br><span>                 LOGP(DMSC, LOGL_ERROR,</span><br><span>@@ -644,7 +736,6 @@</span><br><span>                 return -1;</span><br><span>   }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   msc = conn->sccp.msc;</span><br><span>     aoip = gscon_is_aoip(conn);</span><br><span> </span><br><span>      tlv_parse(&tp, gsm0808_att_tlvdef(), msg->l4h + 1, length - 1, 0, 0);</span><br><span>@@ -732,33 +823,19 @@</span><br><span>                         goto reject;</span><br><span>                 }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+         req = (struct assignment_request){</span><br><span style="color: hsl(120, 100%, 40%);">+                    .aoip = aoip,</span><br><span style="color: hsl(120, 100%, 40%);">+                 .msc_assigned_cic = cic,</span><br><span style="color: hsl(120, 100%, 40%);">+              };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>                 /* Match codec information from the assignment command against the</span><br><span>            * local preferences of the BSC and BTS */</span><br><span style="color: hsl(0, 100%, 40%);">-              rc = match_codec_pref(&chan_mode, &full_rate, &s15_s0, &ct, &conn->codec_list,</span><br><span style="color: hsl(0, 100%, 40%);">-                                 msc, conn_get_bts(conn));</span><br><span style="color: hsl(120, 100%, 40%);">+               rc = select_codecs(&req, &ct, conn);</span><br><span>                 if (rc < 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-                        LOGP(DMSC, LOGL_ERROR, "No supported audio type found for channel_type ="</span><br><span style="color: hsl(0, 100%, 40%);">-                          " { ch_indctr=0x%x, ch_rate_type=0x%x, perm_spch=[ %s] }\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                          ct.ch_indctr, ct.ch_rate_type, osmo_hexdump(ct.perm_spch, ct.perm_spch_len));</span><br><span style="color: hsl(0, 100%, 40%);">-                      /* TODO: actually output codec names, e.g. implement</span><br><span style="color: hsl(0, 100%, 40%);">-                     * gsm0808_permitted_speech_names[] and iterate perm_spch. */</span><br><span>                        cause = GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_UNAVAIL;</span><br><span>                      goto reject;</span><br><span>                 }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-           DEBUGP(DMSC, "Found matching audio type: %s %s for channel_type ="</span><br><span style="color: hsl(0, 100%, 40%);">-                   " { ch_indctr=0x%x, ch_rate_type=0x%x, perm_spch=[ %s] }\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                  full_rate? "full rate" : "half rate",</span><br><span style="color: hsl(0, 100%, 40%);">-                       get_value_string(gsm48_chan_mode_names, chan_mode),</span><br><span style="color: hsl(0, 100%, 40%);">-                     ct.ch_indctr, ct.ch_rate_type, osmo_hexdump(ct.perm_spch, ct.perm_spch_len));</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-            req = (struct assignment_request){</span><br><span style="color: hsl(0, 100%, 40%);">-                      .aoip = aoip,</span><br><span style="color: hsl(0, 100%, 40%);">-                   .msc_assigned_cic = cic,</span><br><span style="color: hsl(0, 100%, 40%);">-                        .chan_mode = chan_mode,</span><br><span style="color: hsl(0, 100%, 40%);">-                 .full_rate = full_rate,</span><br><span style="color: hsl(0, 100%, 40%);">-                 .s15_s0 = s15_s0</span><br><span style="color: hsl(0, 100%, 40%);">-                };</span><br><span>           if (aoip) {</span><br><span>                  unsigned int rc = osmo_sockaddr_to_str_and_uint(req.msc_rtp_addr,</span><br><span>                                                                    sizeof(req.msc_rtp_addr),</span><br><span>@@ -772,9 +849,13 @@</span><br><span>             }</span><br><span>            break;</span><br><span>       case GSM0808_CHAN_SIGN:</span><br><span style="color: hsl(120, 100%, 40%);">+               ch_mode_rate_pref = (struct channel_mode_and_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 style="color: hsl(120, 100%, 40%);">+</span><br><span>                 req = (struct assignment_request){</span><br><span>                   .aoip = aoip,</span><br><span style="color: hsl(0, 100%, 40%);">-                   .chan_mode = chan_mode,</span><br><span style="color: hsl(120, 100%, 40%);">+                       .ch_mode_rate_pref = ch_mode_rate_pref,</span><br><span>              };</span><br><span>           break;</span><br><span>       default:</span><br><span>diff --git a/tests/codec_pref/codec_pref_test.c b/tests/codec_pref/codec_pref_test.c</span><br><span>index 534b99e..bb5468a 100644</span><br><span>--- a/tests/codec_pref/codec_pref_test.c</span><br><span>+++ b/tests/codec_pref/codec_pref_test.c</span><br><span>@@ -380,9 +380,7 @@</span><br><span> {</span><br><span>     int rc;</span><br><span>      unsigned int i;</span><br><span style="color: hsl(0, 100%, 40%);">- bool full_rate;</span><br><span style="color: hsl(0, 100%, 40%);">- enum gsm48_chan_mode chan_mode;</span><br><span style="color: hsl(0, 100%, 40%);">- uint16_t s15_s0;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct channel_mode_and_rate ch_mode_rate = { };</span><br><span> </span><br><span>         printf("Determining channel mode and rate:\n");</span><br><span> </span><br><span>@@ -407,9 +405,9 @@</span><br><span>  printf("   codec->efr=%u\n", bts->codec.efr);</span><br><span>        printf("   codec->amr=%u\n", bts->codec.amr);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-       rc = match_codec_pref(&chan_mode, &full_rate, &s15_s0, ct, scl, msc, bts);</span><br><span style="color: hsl(120, 100%, 40%);">+        rc = match_codec_pref(&ch_mode_rate, ct, scl, msc, bts, RATE_PREF_NONE);</span><br><span>         printf(" * result: rc=%i, full_rate=%i, s15_s0=%04x, chan_mode=%s\n",</span><br><span style="color: hsl(0, 100%, 40%);">-        rc, full_rate, s15_s0, gsm48_chan_mode_name(chan_mode));</span><br><span style="color: hsl(120, 100%, 40%);">+              rc, ch_mode_rate.full_rate, ch_mode_rate.s15_s0, gsm48_chan_mode_name(ch_mode_rate.chan_mode));</span><br><span> </span><br><span>   printf("\n");</span><br><span> </span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/12625">change 12625</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/12625"/><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-MessageType: merged </div>
<div style="display:none"> Gerrit-Change-Id: I5239e05c1cfbcb8af28f43373a58fa6c2d216c51 </div>
<div style="display:none"> Gerrit-Change-Number: 12625 </div>
<div style="display:none"> Gerrit-PatchSet: 12 </div>
<div style="display:none"> Gerrit-Owner: dexter <pmaier@sysmocom.de> </div>
<div style="display:none"> Gerrit-Assignee: Neels Hofmeyr <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Harald Welte <laforge@gnumonks.org> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder (1000002) </div>
<div style="display:none"> Gerrit-Reviewer: Max <msuraev@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Neels Hofmeyr <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: dexter <pmaier@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: tnt <tnt@246tNt.com> </div>
<div style="display:none"> Gerrit-CC: Vadim Yanitskiy <axilirator@gmail.com> </div>