From aa82492ad6d6011b4e8026086cdc0fd837153808 Mon Sep 17 00:00:00 2001 From: Sylvain Munaut Date: Thu, 7 Mar 2019 16:32:02 +0100 Subject: assignment_fsm: Properly support assigning signalling mode TCH/x To support the 3 possible preferences, the changes needed were: - Replace 'full_rate' bool with a 3 option enum to represent the channels types for signalling - Switch from _pref/_alt to using an array sorted in preference order Originally merged as Change-Id I4c7499c8c866ea3ff7b1327edb3615d003d927d3, reverted because the change broke voice calls. Re-submitting with the fix: don't forget to set conn->assignment.requires_voice_stream. Signed-off-by: Sylvain Munaut Change-Id: I7513d2cbe8b695ba6f031ad11560c63a6535cf2d --- include/osmocom/bsc/gsm_data.h | 17 ++--- include/osmocom/bsc/lchan_select.h | 2 +- src/osmo-bsc/assignment_fsm.c | 125 +++++++++++++++++-------------------- src/osmo-bsc/codec_pref.c | 10 +-- src/osmo-bsc/handover_fsm.c | 5 +- src/osmo-bsc/lchan_select.c | 17 +++-- src/osmo-bsc/osmo_bsc_bssap.c | 124 ++++++++++++++++++++++-------------- tests/codec_pref/codec_pref_test.c | 2 +- 8 files changed, 169 insertions(+), 133 deletions(-) diff --git a/include/osmocom/bsc/gsm_data.h b/include/osmocom/bsc/gsm_data.h index dacc63b15..dc686c366 100644 --- a/include/osmocom/bsc/gsm_data.h +++ b/include/osmocom/bsc/gsm_data.h @@ -98,9 +98,15 @@ enum subscr_sccp_state { SUBSCR_SCCP_ST_CONNECTED }; +enum channel_rate { + CH_RATE_SDCCH, + CH_RATE_HALF, + CH_RATE_FULL, +}; + struct channel_mode_and_rate { enum gsm48_chan_mode chan_mode; - bool full_rate; + enum channel_rate chan_rate; uint16_t s15_s0; }; @@ -115,12 +121,9 @@ struct assignment_request { char msc_rtp_addr[INET_ADDRSTRLEN]; uint16_t msc_rtp_port; - /* 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; + /* Rate/codec setting in preference order (need at least 1 !) */ + int n_ch_mode_rate; + struct channel_mode_and_rate ch_mode_rate[3]; }; /* State of an ongoing Assignment, while the assignment_fsm is still busy. This serves as state separation to keep the diff --git a/include/osmocom/bsc/lchan_select.h b/include/osmocom/bsc/lchan_select.h index 4aecdf676..865181bf5 100644 --- a/include/osmocom/bsc/lchan_select.h +++ b/include/osmocom/bsc/lchan_select.h @@ -3,4 +3,4 @@ struct gsm_lchan *lchan_select_by_type(struct gsm_bts *bts, enum gsm_chan_t type); struct gsm_lchan *lchan_select_by_chan_mode(struct gsm_bts *bts, - enum gsm48_chan_mode chan_mode, bool full_rate); + enum gsm48_chan_mode chan_mode, enum channel_rate chan_rate); diff --git a/src/osmo-bsc/assignment_fsm.c b/src/osmo-bsc/assignment_fsm.c index fdfc0803f..834b58b97 100644 --- a/src/osmo-bsc/assignment_fsm.c +++ b/src/osmo-bsc/assignment_fsm.c @@ -250,17 +250,15 @@ static void assignment_fsm_update_id(struct gsm_subscriber_connection *conn) 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; + enum channel_rate chan_rate = ch_mode_rate->chan_rate; switch (chan_mode) { case GSM48_CMODE_SIGN: switch (type) { - case GSM_LCHAN_TCH_F: - case GSM_LCHAN_TCH_H: - case GSM_LCHAN_SDCCH: - return true; - default: - return false; + case GSM_LCHAN_TCH_F: return chan_rate == CH_RATE_FULL; + case GSM_LCHAN_TCH_H: return chan_rate == CH_RATE_HALF; + case GSM_LCHAN_SDCCH: return chan_rate == CH_RATE_SDCCH; + default: return false; } case GSM48_CMODE_SPEECH_V1: @@ -268,12 +266,12 @@ static bool lchan_type_compat_with_mode(enum gsm_chan_t type, const struct chann case GSM48_CMODE_DATA_3k6: case GSM48_CMODE_DATA_6k0: /* these services can all run on TCH/H, but we may have - * an explicit override by the 'full_rate' argument */ + * an explicit override by the 'chan_rate' argument */ switch (type) { case GSM_LCHAN_TCH_F: - return full_rate; + return chan_rate == CH_RATE_FULL; case GSM_LCHAN_TCH_H: - return !full_rate; + return chan_rate == CH_RATE_HALF; default: return false; } @@ -319,47 +317,37 @@ static int check_requires_voice(bool *requires_voice, enum gsm48_chan_mode chan_ * 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; + bool requires_voice_pref = false, requires_voice_alt; struct assignment_request *req = &conn->assignment.req; struct osmo_fsm_inst *fi = conn->fi; - int rc; + int i, 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; - } + for (i = 0; i < req->n_ch_mode_rate; i++) { + rc = check_requires_voice(&requires_voice_alt, req->ch_mode_rate[i].chan_mode); + if (rc < 0) { + assignment_fail(GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_NOT_SUPP, + "Channel mode not supported (prev level %d): %s", i, + gsm48_chan_mode_name(req->ch_mode_rate[i].chan_mode)); + 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; + if (i==0) + requires_voice_pref = requires_voice_alt; + else if (requires_voice_alt != requires_voice_pref) { + assignment_fail(GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_NOT_SUPP, + "Inconsistent channel modes: %s != %s", + gsm48_chan_mode_name(req->ch_mode_rate[0].chan_mode), + gsm48_chan_mode_name(req->ch_mode_rate[i].chan_mode)); + return -EINVAL; + } } + conn->assignment.requires_voice_stream = requires_voice_pref; return 0; } @@ -369,18 +357,20 @@ static int check_requires_voice_stream(struct gsm_subscriber_connection *conn) static bool reuse_existing_lchan(struct gsm_subscriber_connection *conn) { struct assignment_request *req = &conn->assignment.req; + int i; 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 + for (i = 0; i < req->n_ch_mode_rate; i++) + if (lchan_type_compat_with_mode(conn->lchan->type, &req->ch_mode_rate[i])) { + conn->lchan->ch_mode_rate = req->ch_mode_rate[i]; + break; + } + + if (i == req->n_ch_mode_rate) return false; if (conn->lchan->tch_mode != conn->lchan->ch_mode_rate.chan_mode) { @@ -398,8 +388,14 @@ static bool reuse_existing_lchan(struct gsm_subscriber_connection *conn) void assignment_fsm_start(struct gsm_subscriber_connection *conn, struct gsm_bts *bts, struct assignment_request *req) { + static const char *rate_names[] = { + [CH_RATE_SDCCH] = "SDCCH", + [CH_RATE_HALF] = "HR", + [CH_RATE_FULL] = "FR", + }; struct osmo_fsm_inst *fi; struct lchan_activate_info info; + int i; OSMO_ASSERT(conn); OSMO_ASSERT(conn->fi); @@ -442,17 +438,13 @@ void assignment_fsm_start(struct gsm_subscriber_connection *conn, struct gsm_bts return; } - /* 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; + /* Try to allocate a new lchan in order of preference */ + for (i = 0; i < req->n_ch_mode_rate; i++) { + conn->assignment.new_lchan = lchan_select_by_chan_mode(bts, + req->ch_mode_rate[i].chan_mode, req->ch_mode_rate[i].chan_rate); + conn->lchan->ch_mode_rate = req->ch_mode_rate[i]; + if (conn->assignment.new_lchan) + break; } /* Check whether the lchan allocation was successful or not and tear @@ -461,21 +453,22 @@ void assignment_fsm_start(struct gsm_subscriber_connection *conn, struct gsm_bts assignment_count_result(BSC_CTR_ASSIGNMENT_NO_CHANNEL); assignment_fail(GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, "BSSMAP Assignment Command:" - " 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") : ""); + " No lchan available for: pref=%s:%s / alt1=%s:%s / alt2=%s:%s\n", + gsm48_chan_mode_name(req->ch_mode_rate[0].chan_mode), + rate_names[req->ch_mode_rate[0].chan_rate], + req->n_ch_mode_rate >= 1 ? gsm48_chan_mode_name(req->ch_mode_rate[0].chan_mode) : "", + req->n_ch_mode_rate >= 1 ? rate_names[req->ch_mode_rate[0].chan_rate] : "", + req->n_ch_mode_rate >= 2 ? gsm48_chan_mode_name(req->ch_mode_rate[0].chan_mode) : "", + req->n_ch_mode_rate >= 2 ? rate_names[req->ch_mode_rate[0].chan_rate] : "" + ); return; } assignment_fsm_update_id(conn); - LOG_ASSIGNMENT(conn, LOGL_INFO, "Starting Assignment: chan_mode=%s, full_rate=%d," + LOG_ASSIGNMENT(conn, LOGL_INFO, "Starting Assignment: chan_mode=%s, chan_type=%s," " aoip=%s MSC-rtp=%s:%u\n", gsm48_chan_mode_name(conn->lchan->ch_mode_rate.chan_mode), - conn->lchan->ch_mode_rate.full_rate, + rate_names[conn->lchan->ch_mode_rate.chan_rate], req->aoip ? "yes" : "no", req->msc_rtp_addr, req->msc_rtp_port); assignment_fsm_state_chg(ASSIGNMENT_ST_WAIT_LCHAN_ACTIVE); diff --git a/src/osmo-bsc/codec_pref.c b/src/osmo-bsc/codec_pref.c index 3e06114dd..b27defcde 100644 --- a/src/osmo-bsc/codec_pref.c +++ b/src/osmo-bsc/codec_pref.c @@ -324,6 +324,7 @@ int match_codec_pref(struct channel_mode_and_rate *ch_mode_rate, { unsigned int i; uint8_t perm_spch; + bool full_rate; bool match = false; const struct gsm0808_speech_codec *sc_match = NULL; int rc; @@ -337,15 +338,16 @@ int match_codec_pref(struct channel_mode_and_rate *ch_mode_rate, 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); + rc = full_rate_from_perm_spch(&full_rate, perm_spch); if (rc < 0) return -EINVAL; + ch_mode_rate->chan_rate = full_rate ? CH_RATE_FULL : CH_RATE_HALF; /* 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) + if (rate_pref == RATE_PREF_HR && ch_mode_rate->chan_rate == CH_RATE_FULL) continue; - if (rate_pref == RATE_PREF_FR && !ch_mode_rate->full_rate) + if (rate_pref == RATE_PREF_FR && ch_mode_rate->chan_rate == CH_RATE_HALF) continue; /* Check this permitted speech value against the BTS specific parameters. @@ -375,8 +377,8 @@ int match_codec_pref(struct channel_mode_and_rate *ch_mode_rate, /* Exit without result, in case no match can be deteched */ if (!match) { - ch_mode_rate->full_rate = false; ch_mode_rate->chan_mode = GSM48_CMODE_SIGN; + ch_mode_rate->chan_rate = CH_RATE_SDCCH; ch_mode_rate->s15_s0 = 0; return -1; } diff --git a/src/osmo-bsc/handover_fsm.c b/src/osmo-bsc/handover_fsm.c index 0d1449fc8..c044407bb 100644 --- a/src/osmo-bsc/handover_fsm.c +++ b/src/osmo-bsc/handover_fsm.c @@ -582,10 +582,11 @@ 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(ch_mode_rate.chan_mode), ch_mode_rate.full_rate? "full-rate" : "half-rate", + bts->nr, gsm48_chan_mode_name(ch_mode_rate.chan_mode), + ch_mode_rate.chan_rate == CH_RATE_FULL ? "full-rate" : "half-rate", gsm0808_channel_type_name(&req->ct)); - lchan = lchan_select_by_chan_mode(bts, ch_mode_rate.chan_mode, ch_mode_rate.full_rate); + lchan = lchan_select_by_chan_mode(bts, ch_mode_rate.chan_mode, ch_mode_rate.chan_rate); if (!lchan) { LOG_HO(conn, LOGL_DEBUG, "BTS %u has no matching free channels\n", bts->nr); continue; diff --git a/src/osmo-bsc/lchan_select.c b/src/osmo-bsc/lchan_select.c index 0f4dd6527..0a9752e44 100644 --- a/src/osmo-bsc/lchan_select.c +++ b/src/osmo-bsc/lchan_select.c @@ -128,22 +128,31 @@ _lc_find_bts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan) } struct gsm_lchan *lchan_select_by_chan_mode(struct gsm_bts *bts, - enum gsm48_chan_mode chan_mode, bool full_rate) + enum gsm48_chan_mode chan_mode, enum channel_rate chan_rate) { enum gsm_chan_t type; switch (chan_mode) { case GSM48_CMODE_SIGN: - type = GSM_LCHAN_SDCCH; + switch (chan_rate) { + case CH_RATE_SDCCH: type = GSM_LCHAN_SDCCH; break; + case CH_RATE_HALF: type = GSM_LCHAN_TCH_H; break; + case CH_RATE_FULL: type = GSM_LCHAN_TCH_F; break; + default: return NULL; + } break; case GSM48_CMODE_SPEECH_EFR: /* EFR works over FR channels only */ - if (!full_rate) + if (chan_rate != CH_RATE_FULL) return NULL; /* fall through */ case GSM48_CMODE_SPEECH_V1: case GSM48_CMODE_SPEECH_AMR: - type = full_rate ? GSM_LCHAN_TCH_F : GSM_LCHAN_TCH_H; + switch (chan_rate) { + case CH_RATE_HALF: type = GSM_LCHAN_TCH_H; break; + case CH_RATE_FULL: type = GSM_LCHAN_TCH_F; break; + default: return NULL; + } break; default: return NULL; diff --git a/src/osmo-bsc/osmo_bsc_bssap.c b/src/osmo-bsc/osmo_bsc_bssap.c index 2d5d4ae80..470ae4248 100644 --- a/src/osmo-bsc/osmo_bsc_bssap.c +++ b/src/osmo-bsc/osmo_bsc_bssap.c @@ -627,60 +627,48 @@ static int bssmap_handle_lcls_connect_ctrl(struct gsm_subscriber_connection *con static int select_codecs(struct assignment_request *req, struct gsm0808_channel_type *ct, struct gsm_subscriber_connection *conn) { - int rc; + int rc, i, nc = 0; 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), + rc = match_codec_pref(&req->ch_mode_rate[nc], ct, &conn->codec_list, msc, conn_get_bts(conn), RATE_PREF_FR); + nc += (rc == 0); break; case GSM0808_SPEECH_HALF_LM: - rc = match_codec_pref(&req->ch_mode_rate_pref, ct, &conn->codec_list, msc, conn_get_bts(conn), + rc = match_codec_pref(&req->ch_mode_rate[nc], ct, &conn->codec_list, msc, conn_get_bts(conn), RATE_PREF_HR); + nc += (rc == 0); 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), + rc = match_codec_pref(&req->ch_mode_rate[nc], 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), + nc += (rc == 0); + rc = match_codec_pref(&req->ch_mode_rate[nc], ct, &conn->codec_list, msc, conn_get_bts(conn), RATE_PREF_HR); - if (rc == 0) - req->ch_mode_rate_alt_present = true; - rc = 0; + nc += (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), + rc = match_codec_pref(&req->ch_mode_rate[nc], 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), + nc += (rc == 0); + rc = match_codec_pref(&req->ch_mode_rate[nc], ct, &conn->codec_list, msc, conn_get_bts(conn), RATE_PREF_FR); - if (rc == 0) - req->ch_mode_rate_alt_present = true; - rc = 0; + nc += (rc == 0); break; default: rc = -EINVAL; break; } - if (rc < 0) { + if (!nc) { 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)); @@ -689,29 +677,69 @@ static int select_codecs(struct assignment_request *req, struct gsm0808_channel_ return -EINVAL; } - if (req->ch_mode_rate_alt_present) { - DEBUGP(DMSC, "Found matching audio type (preferred): %s %s for channel_type =" + for (i = 0; i < nc; i++ ) { + DEBUGP(DMSC, "Found matching audio type (pref=%d): %s %s for channel_type =" " { ch_indctr=0x%x, ch_rate_type=0x%x, perm_spch=[ %s] }\n", - 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), + i, + req->ch_mode_rate[i].chan_rate == CH_RATE_FULL ? "full rate" : "half rate", + get_value_string(gsm48_chan_mode_names, req->ch_mode_rate[i].chan_mode), 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)); - } + req->n_ch_mode_rate = nc; + return 0; } +static int select_sign_chan(struct assignment_request *req, struct gsm0808_channel_type *ct) +{ + int i, nc = 0; + + switch (ct->ch_rate_type) { + case GSM0808_SIGN_ANY: + req->ch_mode_rate[nc++].chan_rate = CH_RATE_SDCCH; + req->ch_mode_rate[nc++].chan_rate = CH_RATE_HALF; + req->ch_mode_rate[nc++].chan_rate = CH_RATE_FULL; + break; + case GSM0808_SIGN_SDCCH: + req->ch_mode_rate[nc++].chan_rate = CH_RATE_SDCCH; + break; + case GSM0808_SIGN_SDCCH_FULL_BM: + req->ch_mode_rate[nc++].chan_rate = CH_RATE_SDCCH; + req->ch_mode_rate[nc++].chan_rate = CH_RATE_FULL; + break; + case GSM0808_SIGN_SDCCH_HALF_LM: + req->ch_mode_rate[nc++].chan_rate = CH_RATE_SDCCH; + req->ch_mode_rate[nc++].chan_rate = CH_RATE_HALF; + break; + case GSM0808_SIGN_FULL_BM: + req->ch_mode_rate[nc++].chan_rate = CH_RATE_FULL; + break; + case GSM0808_SIGN_HALF_LM: + req->ch_mode_rate[nc++].chan_rate = CH_RATE_HALF; + break; + case GSM0808_SIGN_FULL_PREF: + case GSM0808_SIGN_FULL_PREF_NO_CHANGE: + req->ch_mode_rate[nc++].chan_rate = CH_RATE_FULL; + req->ch_mode_rate[nc++].chan_rate = CH_RATE_HALF; + break; + case GSM0808_SIGN_HALF_PREF: + case GSM0808_SIGN_HALF_PREF_NO_CHANGE: + req->ch_mode_rate[nc++].chan_rate = CH_RATE_HALF; + req->ch_mode_rate[nc++].chan_rate = CH_RATE_FULL; + break; + default: + break; + } + + for (i = 0; i < nc; i++) + req->ch_mode_rate[i].chan_mode = GSM48_CMODE_SIGN; + + req->n_ch_mode_rate = nc; + + return nc > 0 ? 0 : -EINVAL; +} + /* * Handle the assignment request message. * @@ -729,7 +757,6 @@ static int bssmap_handle_assignm_req(struct gsm_subscriber_connection *conn, uint8_t cause; int rc; struct assignment_request req = {}; - struct channel_mode_and_rate ch_mode_rate_pref = {}; if (!conn) { LOGP(DMSC, LOGL_ERROR, @@ -850,14 +877,15 @@ 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, - .ch_mode_rate_pref = ch_mode_rate_pref, }; + + rc = select_sign_chan(&req, &ct); + if (rc < 0) { + cause = GSM0808_CAUSE_INCORRECT_VALUE; + goto reject; + } break; default: cause = GSM0808_CAUSE_INVALID_MESSAGE_CONTENTS; diff --git a/tests/codec_pref/codec_pref_test.c b/tests/codec_pref/codec_pref_test.c index bb5468a4c..ce82f3d93 100644 --- a/tests/codec_pref/codec_pref_test.c +++ b/tests/codec_pref/codec_pref_test.c @@ -407,7 +407,7 @@ static int test_match_codec_pref(const struct gsm0808_channel_type *ct, const st 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, ch_mode_rate.full_rate, ch_mode_rate.s15_s0, gsm48_chan_mode_name(ch_mode_rate.chan_mode)); + rc, ch_mode_rate.chan_rate == CH_RATE_FULL, ch_mode_rate.s15_s0, gsm48_chan_mode_name(ch_mode_rate.chan_mode)); printf("\n"); -- cgit v1.2.3