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