diff options
-rw-r--r-- | include/osmocom/bsc/codec_pref.h | 13 | ||||
-rw-r--r-- | include/osmocom/bsc/gsm_data.h | 22 | ||||
-rw-r--r-- | src/osmo-bsc/assignment_fsm.c | 201 | ||||
-rw-r--r-- | src/osmo-bsc/bsc_vty.c | 1 | ||||
-rw-r--r-- | src/osmo-bsc/codec_pref.c | 42 | ||||
-rw-r--r-- | src/osmo-bsc/handover_fsm.c | 18 | ||||
-rw-r--r-- | src/osmo-bsc/osmo_bsc_bssap.c | 133 | ||||
-rw-r--r-- | tests/codec_pref/codec_pref_test.c | 8 |
8 files changed, 323 insertions, 115 deletions
diff --git a/include/osmocom/bsc/codec_pref.h b/include/osmocom/bsc/codec_pref.h index 51340c118..adefe0473 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 f6c5129c9..4d27a2ec2 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 2ad39596c..67937d634 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 = ≻ } } @@ -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 357ee9eae..9413d36f8 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 c99c38335..a94d6a834 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 578cff351..30297f620 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 ab7f79ce1..85aab22d2 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 534b99ef7..bb5468a4c 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"); |