make sure channel mode and s15_s0 are updated only after an ACK

I noticed during testing that an lchan used as TCH/F in fact still had
its channel mode set to Signalling -- because on Assignment, the Speech
mode used to be placed in the *previous* lchan and the new lchan was
never updated after the Activ ACK. This is unbearable confusion which I
complained about numerous times, so far mostly for cosmetic reasons. But
implementing re-assignment properly actually requires this to be cleaned
up.

Keep all volatile chan mode settings in lchan->activate.* or
lchan->modify.*, and only update lchan->* members when an ACK has been
received for those settings. So a failed request keeps a sane state.

Make sure that those settings are in fact updated in the proper lchan,
upon an ACK, so that subsequent re-assignment or mode-modify know the
accurate lchan state.

Related are upcoming patches that sort out the AMR multirate
configuration in a similar fashion, see
Iebac2dc26412d877e5364f90d6f2ed7a7952351e
Ia7519d2fa9e7f0b61b222d27d077bde4660c40b9
Ie57f9d0e3912632903d9740291225bfd1634ed47.

Related: SYS#5315 OS#4940 OS#3787 OS#3833
Change-Id: Ie0da36124d73efc28a8809b63d7c96e2167fc412
This commit is contained in:
Neels Hofmeyr 2021-04-27 23:17:14 +00:00
parent d587574d7b
commit 0951d75665
13 changed files with 186 additions and 163 deletions

View File

@ -105,6 +105,8 @@ enum channel_rate {
CH_RATE_FULL, CH_RATE_FULL,
}; };
enum channel_rate chan_t_to_chan_rate(enum gsm_chan_t chan_t);
struct channel_mode_and_rate { struct channel_mode_and_rate {
enum gsm48_chan_mode chan_mode; enum gsm48_chan_mode chan_mode;
enum channel_rate chan_rate; enum channel_rate chan_rate;
@ -137,7 +139,7 @@ struct assignment_request {
/* Rate/codec setting in preference order (need at least 1 !) */ /* Rate/codec setting in preference order (need at least 1 !) */
int n_ch_mode_rate; int n_ch_mode_rate;
struct channel_mode_and_rate ch_mode_rate[3]; struct channel_mode_and_rate ch_mode_rate_list[3];
}; };
/* State of an ongoing Assignment, while the assignment_fsm is still busy. This serves as state separation to keep the /* State of an ongoing Assignment, while the assignment_fsm is still busy. This serves as state separation to keep the
@ -146,6 +148,7 @@ struct assignment_request {
struct assignment_fsm_data { struct assignment_fsm_data {
struct assignment_request req; struct assignment_request req;
bool requires_voice_stream; bool requires_voice_stream;
struct channel_mode_and_rate selected_ch_mode_rate;
struct osmo_fsm_inst *fi; struct osmo_fsm_inst *fi;
struct gsm_lchan *new_lchan; struct gsm_lchan *new_lchan;
@ -585,12 +588,8 @@ static inline const char *lchan_activate_mode_name(enum lchan_activate_for activ
struct lchan_activate_info { struct lchan_activate_info {
enum lchan_activate_for activ_for; enum lchan_activate_for activ_for;
struct gsm_subscriber_connection *for_conn; struct gsm_subscriber_connection *for_conn;
/* This always is for a specific lchan, so its lchan->type indicates full or half rate. struct channel_mode_and_rate ch_mode_rate;
* When a dyn TS was selected, the lchan->type has been set to the desired rate. */
enum gsm48_chan_mode chan_mode;
struct gsm_encr encr; struct gsm_encr encr;
/* AMR config */
uint16_t s15_s0;
bool requires_voice_stream; bool requires_voice_stream;
bool wait_before_switching_rtp; /*< true = requires LCHAN_EV_READY_TO_SWITCH_RTP */ bool wait_before_switching_rtp; /*< true = requires LCHAN_EV_READY_TO_SWITCH_RTP */
uint16_t msc_assigned_cic; uint16_t msc_assigned_cic;
@ -613,11 +612,9 @@ static inline const char *lchan_modify_for_name(enum lchan_modify_for modify_for
struct lchan_modify_info { struct lchan_modify_info {
enum lchan_modify_for modify_for; enum lchan_modify_for modify_for;
enum gsm48_chan_mode chan_mode; struct channel_mode_and_rate ch_mode_rate;
bool requires_voice_stream; bool requires_voice_stream;
uint16_t msc_assigned_cic; uint16_t msc_assigned_cic;
/* AMR config */
uint16_t s15_s0;
}; };
struct gsm_lchan { struct gsm_lchan {
@ -672,8 +669,6 @@ struct gsm_lchan {
enum gsm_chan_t type; enum gsm_chan_t type;
/* RSL channel mode */ /* RSL channel mode */
enum rsl_cmod_spd rsl_cmode; enum rsl_cmod_spd rsl_cmode;
/* If TCH, traffic channel mode */
enum gsm48_chan_mode tch_mode;
enum lchan_csd_mode csd_mode; enum lchan_csd_mode csd_mode;
/* Power levels for MS and BTS */ /* Power levels for MS and BTS */
uint8_t bs_power; uint8_t bs_power;
@ -684,8 +679,6 @@ struct gsm_lchan {
/* AMR bits */ /* AMR bits */
uint8_t mr_ms_lv[7]; uint8_t mr_ms_lv[7];
uint8_t mr_bts_lv[7]; uint8_t mr_bts_lv[7];
/* AMR bits were based on these rate bits: */
uint16_t s15_s0;
/* Established data link layer services */ /* Established data link layer services */
uint8_t sapis[8]; uint8_t sapis[8];
@ -727,12 +720,9 @@ struct gsm_lchan {
struct gsm_subscriber_connection *conn; struct gsm_subscriber_connection *conn;
/* Depending on the preferences that where submitted together with /* After the Channel Activation ACK or RSL Mode Modify ACK is received, this reflects the actually used
* the assignment and the current channel load, the BSC has to select * channel_mode_and_rate. */
* one of the offered codec/rates. The final selection by the BSC is struct channel_mode_and_rate current_ch_mode_rate;
* 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 */ /* One Timeslot in a TRX */

View File

@ -70,7 +70,7 @@ static void count_codecs(struct gsm_bts *bts, struct gsm_lchan *lchan)
OSMO_ASSERT(bts); OSMO_ASSERT(bts);
if (lchan->type == GSM_LCHAN_TCH_H) { if (lchan->type == GSM_LCHAN_TCH_H) {
switch (lchan->tch_mode) { switch (lchan->current_ch_mode_rate.chan_mode) {
case GSM48_CMODE_SPEECH_AMR: case GSM48_CMODE_SPEECH_AMR:
rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_CODEC_AMR_H]); rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_CODEC_AMR_H]);
break; break;
@ -81,7 +81,7 @@ static void count_codecs(struct gsm_bts *bts, struct gsm_lchan *lchan)
break; break;
} }
} else if (lchan->type == GSM_LCHAN_TCH_F) { } else if (lchan->type == GSM_LCHAN_TCH_F) {
switch (lchan->tch_mode) { switch (lchan->current_ch_mode_rate.chan_mode) {
case GSM48_CMODE_SPEECH_AMR: case GSM48_CMODE_SPEECH_AMR:
rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_CODEC_AMR_F]); rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_CODEC_AMR_F]);
break; break;
@ -351,7 +351,8 @@ int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan)
} }
static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm, static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm,
struct gsm_lchan *lchan) struct gsm_lchan *lchan,
const struct channel_mode_and_rate *ch_mode_rate)
{ {
memset(cm, 0, sizeof(*cm)); memset(cm, 0, sizeof(*cm));
@ -366,7 +367,7 @@ static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm,
cm->spd_ind = lchan->rsl_cmode; cm->spd_ind = lchan->rsl_cmode;
if (lchan->rsl_cmode == RSL_CMOD_SPD_SIGN && if (lchan->rsl_cmode == RSL_CMOD_SPD_SIGN &&
lchan->tch_mode != GSM48_CMODE_SIGN) ch_mode_rate->chan_mode != GSM48_CMODE_SIGN)
LOGP(DRSL, LOGL_ERROR, "unsupported: rsl_mode == signalling, " LOGP(DRSL, LOGL_ERROR, "unsupported: rsl_mode == signalling, "
"but tch_mode != signalling\n"); "but tch_mode != signalling\n");
@ -389,7 +390,7 @@ static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm,
return -EINVAL; return -EINVAL;
} }
switch (lchan->tch_mode) { switch (ch_mode_rate->chan_mode) {
case GSM48_CMODE_SIGN: case GSM48_CMODE_SIGN:
cm->chan_rate = 0; cm->chan_rate = 0;
break; break;
@ -408,7 +409,7 @@ static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm,
switch (lchan->csd_mode) { switch (lchan->csd_mode) {
case LCHAN_CSD_M_NT: case LCHAN_CSD_M_NT:
/* non-transparent CSD with RLP */ /* non-transparent CSD with RLP */
switch (lchan->tch_mode) { switch (ch_mode_rate->chan_mode) {
case GSM48_CMODE_DATA_14k5: case GSM48_CMODE_DATA_14k5:
cm->chan_rate = RSL_CMOD_SP_NT_14k5; cm->chan_rate = RSL_CMOD_SP_NT_14k5;
break; break;
@ -421,7 +422,7 @@ static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm,
default: default:
LOGP(DRSL, LOGL_ERROR, LOGP(DRSL, LOGL_ERROR,
"unsupported lchan->tch_mode %u\n", "unsupported lchan->tch_mode %u\n",
lchan->tch_mode); ch_mode_rate->chan_mode);
return -EINVAL; return -EINVAL;
} }
break; break;
@ -458,20 +459,19 @@ static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm,
} }
break; break;
default: default:
LOGP(DRSL, LOGL_ERROR, LOGP(DRSL, LOGL_ERROR, "unsupported channel mode %u\n", ch_mode_rate->chan_mode);
"unsupported lchan->tch_mode %u\n",
lchan->tch_mode);
return -EINVAL; return -EINVAL;
} }
return 0; return 0;
} }
static void mr_config_for_bts(struct gsm_lchan *lchan, struct msgb *msg) static void mr_config_for_bts(struct gsm_lchan *lchan, struct msgb *msg,
const struct channel_mode_and_rate *ch_mode_rate)
{ {
uint8_t len; uint8_t len;
if (lchan->tch_mode != GSM48_CMODE_SPEECH_AMR) if (ch_mode_rate->chan_mode != GSM48_CMODE_SPEECH_AMR)
return; return;
len = lchan->mr_bts_lv[0]; len = lchan->mr_bts_lv[0];
@ -532,7 +532,7 @@ int rsl_tx_chan_activ(struct gsm_lchan *lchan, uint8_t act_type, uint8_t ho_ref)
/* PDCH activation is a job for rsl_tx_dyn_ts_pdch_act_deact(); */ /* PDCH activation is a job for rsl_tx_dyn_ts_pdch_act_deact(); */
OSMO_ASSERT(act_type != RSL_ACT_OSMO_PDCH); OSMO_ASSERT(act_type != RSL_ACT_OSMO_PDCH);
rc = channel_mode_from_lchan(&cm, lchan); rc = channel_mode_from_lchan(&cm, lchan, &lchan->activate.info.ch_mode_rate);
if (rc < 0) { if (rc < 0) {
LOGP(DRSL, LOGL_ERROR, LOGP(DRSL, LOGL_ERROR,
"%s Cannot find channel mode from lchan type\n", "%s Cannot find channel mode from lchan type\n",
@ -606,7 +606,7 @@ int rsl_tx_chan_activ(struct gsm_lchan *lchan, uint8_t act_type, uint8_t ho_ref)
add_power_control_params(msg, RSL_IE_BS_POWER_PARAM, lchan); add_power_control_params(msg, RSL_IE_BS_POWER_PARAM, lchan);
add_power_control_params(msg, RSL_IE_MS_POWER_PARAM, lchan); add_power_control_params(msg, RSL_IE_MS_POWER_PARAM, lchan);
mr_config_for_bts(lchan, msg); mr_config_for_bts(lchan, msg, &lchan->activate.info.ch_mode_rate);
rep_acch_cap_for_bts(lchan, msg); rep_acch_cap_for_bts(lchan, msg);
msg->dst = trx->rsl_link; msg->dst = trx->rsl_link;
@ -637,7 +637,7 @@ int rsl_chan_mode_modify_req(struct gsm_lchan *lchan)
uint8_t chan_nr = gsm_lchan2chan_nr(lchan); uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
struct rsl_ie_chan_mode cm; struct rsl_ie_chan_mode cm;
rc = channel_mode_from_lchan(&cm, lchan); rc = channel_mode_from_lchan(&cm, lchan, &lchan->modify.info.ch_mode_rate);
if (rc < 0) if (rc < 0)
return rc; return rc;
@ -656,7 +656,7 @@ int rsl_chan_mode_modify_req(struct gsm_lchan *lchan)
msgb_tlv_put(msg, RSL_IE_ENCR_INFO, rc, encr_info); msgb_tlv_put(msg, RSL_IE_ENCR_INFO, rc, encr_info);
} }
mr_config_for_bts(lchan, msg); mr_config_for_bts(lchan, msg, &lchan->modify.info.ch_mode_rate);
rep_acch_cap_for_bts(lchan, msg); rep_acch_cap_for_bts(lchan, msg);
msg->dst = lchan->ts->trx->rsl_link; msg->dst = lchan->ts->trx->rsl_link;
@ -1768,7 +1768,10 @@ void abis_rsl_chan_rqd_queue_poll(struct gsm_bts *bts)
gsm_chreq_name(rqd->reason), rqd->ref.ra, rqd->ta); gsm_chreq_name(rqd->reason), rqd->ref.ra, rqd->ta);
info = (struct lchan_activate_info){ info = (struct lchan_activate_info){
.activ_for = ACTIVATE_FOR_MS_CHANNEL_REQUEST, .activ_for = ACTIVATE_FOR_MS_CHANNEL_REQUEST,
.ch_mode_rate = {
.chan_mode = GSM48_CMODE_SIGN, .chan_mode = GSM48_CMODE_SIGN,
.chan_rate = CH_RATE_SDCCH,
},
.ta = rqd->ta, .ta = rqd->ta,
.ta_known = true, .ta_known = true,
}; };

View File

@ -85,7 +85,7 @@ static const struct osmo_tdef_state_timeout assignment_fsm_timeouts[32] = {
if (bts) { \ if (bts) { \
rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_##counter]); \ rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_##counter]); \
if (BTS_##counter != BTS_CTR_ASSIGNMENT_NO_CHANNEL) { \ if (BTS_##counter != BTS_CTR_ASSIGNMENT_NO_CHANNEL) { \
switch (conn->lchan->ch_mode_rate.chan_mode) { \ switch (conn->assignment.req.ch_mode_rate_list[0].chan_mode) { \
case GSM48_CMODE_SIGN: \ case GSM48_CMODE_SIGN: \
rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_##counter##_SIGN]); \ rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_##counter##_SIGN]); \
break; \ break; \
@ -175,18 +175,18 @@ static void send_assignment_complete(struct gsm_subscriber_connection *conn)
struct gsm_lchan *lchan = conn->lchan; struct gsm_lchan *lchan = conn->lchan;
struct osmo_fsm_inst *fi = conn->fi; struct osmo_fsm_inst *fi = conn->fi;
chosen_channel = gsm0808_chosen_channel(lchan->type, lchan->tch_mode); chosen_channel = gsm0808_chosen_channel(lchan->type, lchan->current_ch_mode_rate.chan_mode);
if (!chosen_channel) { if (!chosen_channel) {
assignment_fail(GSM0808_CAUSE_EQUIPMENT_FAILURE, assignment_fail(GSM0808_CAUSE_EQUIPMENT_FAILURE,
"Unable to compose Chosen Channel for mode=%s type=%s", "Unable to compose Chosen Channel for mode=%s type=%s",
get_value_string(gsm48_chan_mode_names, lchan->tch_mode), get_value_string(gsm48_chan_mode_names, lchan->current_ch_mode_rate.chan_mode),
gsm_lchant_name(lchan->type)); gsm_lchant_name(lchan->type));
return; return;
} }
/* Generate voice related fields */ /* Generate voice related fields */
if (conn->assignment.requires_voice_stream) { if (conn->assignment.requires_voice_stream) {
perm_spch = gsm0808_permitted_speech(lchan->type, lchan->tch_mode); perm_spch = gsm0808_permitted_speech(lchan->type, lchan->current_ch_mode_rate.chan_mode);
if (gscon_is_aoip(conn)) { if (gscon_is_aoip(conn)) {
if (!osmo_mgcpc_ep_ci_get_crcx_info_to_sockaddr(conn->user_plane.mgw_endpoint_ci_msc, if (!osmo_mgcpc_ep_ci_get_crcx_info_to_sockaddr(conn->user_plane.mgw_endpoint_ci_msc,
@ -212,7 +212,7 @@ static void send_assignment_complete(struct gsm_subscriber_connection *conn)
if (gscon_is_aoip(conn)) { if (gscon_is_aoip(conn)) {
/* Extrapolate speech codec from speech mode */ /* Extrapolate speech codec from speech mode */
gsm0808_speech_codec_from_chan_type(&sc, perm_spch); gsm0808_speech_codec_from_chan_type(&sc, perm_spch);
sc.cfg = conn->lchan->activate.info.s15_s0; sc.cfg = conn->lchan->activate.info.ch_mode_rate.s15_s0;
sc_ptr = &sc; sc_ptr = &sc;
} }
} }
@ -395,11 +395,11 @@ static int check_requires_voice_stream(struct gsm_subscriber_connection *conn)
* a mismatch is not permitted */ * a mismatch is not permitted */
for (i = 0; i < req->n_ch_mode_rate; i++) { for (i = 0; i < req->n_ch_mode_rate; i++) {
rc = check_requires_voice(&requires_voice_alt, req->ch_mode_rate[i].chan_mode); rc = check_requires_voice(&requires_voice_alt, req->ch_mode_rate_list[i].chan_mode);
if (rc < 0) { if (rc < 0) {
assignment_fail(GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_NOT_SUPP, assignment_fail(GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_NOT_SUPP,
"Channel mode not supported (prev level %d): %s", i, "Channel mode not supported (prev level %d): %s", i,
gsm48_chan_mode_name(req->ch_mode_rate[i].chan_mode)); gsm48_chan_mode_name(req->ch_mode_rate_list[i].chan_mode));
return -EINVAL; return -EINVAL;
} }
@ -408,8 +408,8 @@ static int check_requires_voice_stream(struct gsm_subscriber_connection *conn)
else if (requires_voice_alt != requires_voice_pref) { else if (requires_voice_alt != requires_voice_pref) {
assignment_fail(GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_NOT_SUPP, assignment_fail(GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_NOT_SUPP,
"Requested a mix of Signalling and non-Signalling channel modes: %s != %s", "Requested a mix of Signalling and non-Signalling channel modes: %s != %s",
gsm48_chan_mode_name(req->ch_mode_rate[0].chan_mode), gsm48_chan_mode_name(req->ch_mode_rate_list[0].chan_mode),
gsm48_chan_mode_name(req->ch_mode_rate[i].chan_mode)); gsm48_chan_mode_name(req->ch_mode_rate_list[i].chan_mode));
return -EINVAL; return -EINVAL;
} }
} }
@ -431,9 +431,9 @@ static bool reuse_existing_lchan(struct gsm_subscriber_connection *conn)
/* Check if the currently existing lchan is compatible with the /* Check if the currently existing lchan is compatible with the
* preferred rate/codec. */ * preferred rate/codec. */
for (i = 0; i < req->n_ch_mode_rate; i++) { for (i = 0; i < req->n_ch_mode_rate; i++) {
if (!lchan_type_compat_with_mode(conn->lchan->type, &req->ch_mode_rate[i])) if (!lchan_type_compat_with_mode(conn->lchan->type, &req->ch_mode_rate_list[i]))
continue; continue;
conn->lchan->ch_mode_rate = req->ch_mode_rate[i]; conn->assignment.selected_ch_mode_rate = req->ch_mode_rate_list[i];
return true; return true;
} }
@ -485,12 +485,12 @@ void assignment_fsm_start(struct gsm_subscriber_connection *conn, struct gsm_bts
/* If the requested mode and the current TCH mode matches up, just send the /* If the requested mode and the current TCH mode matches up, just send the
* assignment complete directly and be done with the assignment procedure. */ * assignment complete directly and be done with the assignment procedure. */
if (conn->lchan->tch_mode == conn->lchan->ch_mode_rate.chan_mode) { if (conn->lchan->current_ch_mode_rate.chan_mode == conn->assignment.selected_ch_mode_rate.chan_mode) {
LOG_ASSIGNMENT(conn, LOGL_DEBUG, LOG_ASSIGNMENT(conn, LOGL_DEBUG,
"Current lchan mode is compatible with requested chan_mode," "Current lchan mode is compatible with requested chan_mode,"
" sending BSSMAP Assignment Complete directly." " sending BSSMAP Assignment Complete directly."
" requested chan_mode=%s; current lchan is %s\n", " requested chan_mode=%s; current lchan is %s\n",
gsm48_chan_mode_name(conn->lchan->ch_mode_rate.chan_mode), gsm48_chan_mode_name(conn->assignment.selected_ch_mode_rate.chan_mode),
gsm_lchan_name(conn->lchan)); gsm_lchan_name(conn->lchan));
if (req->assign_for == ASSIGN_FOR_BSSMAP_REQ) if (req->assign_for == ASSIGN_FOR_BSSMAP_REQ)
@ -509,14 +509,13 @@ void assignment_fsm_start(struct gsm_subscriber_connection *conn, struct gsm_bts
LOG_ASSIGNMENT(conn, LOGL_DEBUG, LOG_ASSIGNMENT(conn, LOGL_DEBUG,
"Current lchan mode is not compatible with requested chan_mode," "Current lchan mode is not compatible with requested chan_mode,"
" so we will modify it. requested chan_mode=%s; current lchan is %s\n", " so we will modify it. requested chan_mode=%s; current lchan is %s\n",
gsm48_chan_mode_name(conn->lchan->ch_mode_rate.chan_mode), gsm48_chan_mode_name(conn->assignment.selected_ch_mode_rate.chan_mode),
gsm_lchan_name(conn->lchan)); gsm_lchan_name(conn->lchan));
modif_info = (struct lchan_modify_info){ modif_info = (struct lchan_modify_info){
.modify_for = MODIFY_FOR_ASSIGNMENT, .modify_for = MODIFY_FOR_ASSIGNMENT,
.chan_mode = conn->lchan->ch_mode_rate.chan_mode, .ch_mode_rate = conn->assignment.selected_ch_mode_rate,
.requires_voice_stream = conn->assignment.requires_voice_stream, .requires_voice_stream = conn->assignment.requires_voice_stream,
.s15_s0 = conn->lchan->ch_mode_rate.s15_s0,
.msc_assigned_cic = req->msc_assigned_cic, .msc_assigned_cic = req->msc_assigned_cic,
}; };
@ -529,14 +528,15 @@ void assignment_fsm_start(struct gsm_subscriber_connection *conn, struct gsm_bts
/* Try to allocate a new lchan in order of preference */ /* Try to allocate a new lchan in order of preference */
for (i = 0; i < req->n_ch_mode_rate; i++) { for (i = 0; i < req->n_ch_mode_rate; i++) {
conn->assignment.new_lchan = lchan_select_by_chan_mode(bts, conn->assignment.new_lchan = lchan_select_by_chan_mode(bts,
req->ch_mode_rate[i].chan_mode, req->ch_mode_rate[i].chan_rate); req->ch_mode_rate_list[i].chan_mode, req->ch_mode_rate_list[i].chan_rate);
/* FIXME: at this point there is merely an assignment request with a given ch_mode_rate. Writing this to if (!conn->assignment.new_lchan)
* conn->lchan->ch_mode_rate is a violation of scopes: the lchan->* state should only be modified continue;
* *after* the assignment is confirmed to be completed. Before that, this data should live in LOG_ASSIGNMENT(conn, LOGL_DEBUG, "selected new lchan %s for mode[%d] = %s channel_rate=%d\n",
* conn->assignment or the lchan_activate_info, the designated places for not-yet-confirmed data. See gsm_lchan_name(conn->assignment.new_lchan),
* OS#3833 */ i, gsm48_chan_mode_name(req->ch_mode_rate_list[i].chan_mode),
conn->lchan->ch_mode_rate = req->ch_mode_rate[i]; req->ch_mode_rate_list[i].chan_rate);
if (conn->assignment.new_lchan)
conn->assignment.selected_ch_mode_rate = req->ch_mode_rate_list[i];
break; break;
} }
@ -544,7 +544,7 @@ void assignment_fsm_start(struct gsm_subscriber_connection *conn, struct gsm_bts
* down the assignment in case of failure. */ * down the assignment in case of failure. */
if (!conn->assignment.new_lchan) { if (!conn->assignment.new_lchan) {
assignment_count_result(CTR_ASSIGNMENT_NO_CHANNEL); assignment_count_result(CTR_ASSIGNMENT_NO_CHANNEL);
switch (req->ch_mode_rate[0].chan_mode) { switch (req->ch_mode_rate_list[0].chan_mode) {
case GSM48_CMODE_SIGN: case GSM48_CMODE_SIGN:
rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_ASSIGNMENT_NO_CHANNEL_SIGN]); rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_ASSIGNMENT_NO_CHANNEL_SIGN]);
break; break;
@ -559,12 +559,12 @@ void assignment_fsm_start(struct gsm_subscriber_connection *conn, struct gsm_bts
assignment_fail(GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, assignment_fail(GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE,
"BSSMAP Assignment Command:" "BSSMAP Assignment Command:"
" No lchan available for: pref=%s:%s / alt1=%s:%s / alt2=%s:%s\n", " 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), gsm48_chan_mode_name(req->ch_mode_rate_list[0].chan_mode),
rate_names[req->ch_mode_rate[0].chan_rate], rate_names[req->ch_mode_rate_list[0].chan_rate],
req->n_ch_mode_rate > 1 ? gsm48_chan_mode_name(req->ch_mode_rate[1].chan_mode) : "", req->n_ch_mode_rate > 1 ? gsm48_chan_mode_name(req->ch_mode_rate_list[1].chan_mode) : "",
req->n_ch_mode_rate > 1 ? rate_names[req->ch_mode_rate[1].chan_rate] : "", req->n_ch_mode_rate > 1 ? rate_names[req->ch_mode_rate_list[1].chan_rate] : "",
req->n_ch_mode_rate > 2 ? gsm48_chan_mode_name(req->ch_mode_rate[2].chan_mode) : "", req->n_ch_mode_rate > 2 ? gsm48_chan_mode_name(req->ch_mode_rate_list[2].chan_mode) : "",
req->n_ch_mode_rate > 2 ? rate_names[req->ch_mode_rate[2].chan_rate] : "" req->n_ch_mode_rate > 2 ? rate_names[req->ch_mode_rate_list[2].chan_rate] : ""
); );
return; return;
} }
@ -572,8 +572,8 @@ void assignment_fsm_start(struct gsm_subscriber_connection *conn, struct gsm_bts
assignment_fsm_update_id(conn); assignment_fsm_update_id(conn);
LOG_ASSIGNMENT(conn, LOGL_INFO, "Starting Assignment: chan_mode=%s, chan_type=%s," LOG_ASSIGNMENT(conn, LOGL_INFO, "Starting Assignment: chan_mode=%s, chan_type=%s,"
" aoip=%s MSC-rtp=%s:%u (osmux=%s)\n", " aoip=%s MSC-rtp=%s:%u (osmux=%s)\n",
gsm48_chan_mode_name(conn->lchan->ch_mode_rate.chan_mode), gsm48_chan_mode_name(conn->assignment.selected_ch_mode_rate.chan_mode),
rate_names[conn->lchan->ch_mode_rate.chan_rate], rate_names[conn->assignment.selected_ch_mode_rate.chan_rate],
req->aoip ? "yes" : "no", req->msc_rtp_addr, req->msc_rtp_port, req->aoip ? "yes" : "no", req->msc_rtp_addr, req->msc_rtp_port,
req->use_osmux ? "yes" : "no"); req->use_osmux ? "yes" : "no");
@ -581,9 +581,8 @@ void assignment_fsm_start(struct gsm_subscriber_connection *conn, struct gsm_bts
activ_info = (struct lchan_activate_info){ activ_info = (struct lchan_activate_info){
.activ_for = ACTIVATE_FOR_ASSIGNMENT, .activ_for = ACTIVATE_FOR_ASSIGNMENT,
.for_conn = conn, .for_conn = conn,
.chan_mode = conn->lchan->ch_mode_rate.chan_mode, .ch_mode_rate = conn->assignment.selected_ch_mode_rate,
.encr = conn->lchan->encr, .encr = conn->lchan->encr,
.s15_s0 = conn->lchan->ch_mode_rate.s15_s0,
.requires_voice_stream = conn->assignment.requires_voice_stream, .requires_voice_stream = conn->assignment.requires_voice_stream,
.msc_assigned_cic = req->msc_assigned_cic, .msc_assigned_cic = req->msc_assigned_cic,
.re_use_mgw_endpoint_from_lchan = conn->lchan, .re_use_mgw_endpoint_from_lchan = conn->lchan,

View File

@ -1624,7 +1624,7 @@ static void lchan_dump_full_vty(struct vty *vty, struct gsm_lchan *lchan)
ms_pwr_dbm(lchan->ts->trx->bts->band, lchan->ms_power), ms_pwr_dbm(lchan->ts->trx->bts->band, lchan->ms_power),
VTY_NEWLINE); VTY_NEWLINE);
vty_out(vty, " Channel Mode / Codec: %s%s", vty_out(vty, " Channel Mode / Codec: %s%s",
gsm48_chan_mode_name(lchan->tch_mode), gsm48_chan_mode_name(lchan->current_ch_mode_rate.chan_mode),
VTY_NEWLINE); VTY_NEWLINE);
if (lchan->conn && lchan->conn->bsub) { if (lchan->conn && lchan->conn->bsub) {
vty_out(vty, " Subscriber:%s", VTY_NEWLINE); vty_out(vty, " Subscriber:%s", VTY_NEWLINE);
@ -6071,13 +6071,17 @@ static int lchan_act_single(struct vty *vty, struct gsm_lchan *lchan, const char
if (!strcmp(codec_str, "hr") || !strcmp(codec_str, "fr")) { if (!strcmp(codec_str, "hr") || !strcmp(codec_str, "fr")) {
info = (struct lchan_activate_info) { info = (struct lchan_activate_info) {
.activ_for = ACTIVATE_FOR_VTY, .activ_for = ACTIVATE_FOR_VTY,
.ch_mode_rate = {
.chan_mode = GSM48_CMODE_SPEECH_V1, .chan_mode = GSM48_CMODE_SPEECH_V1,
},
.requires_voice_stream = false, .requires_voice_stream = false,
}; };
} else if (!strcmp(codec_str, "efr")) { } else if (!strcmp(codec_str, "efr")) {
info = (struct lchan_activate_info) { info = (struct lchan_activate_info) {
.activ_for = ACTIVATE_FOR_VTY, .activ_for = ACTIVATE_FOR_VTY,
.ch_mode_rate = {
.chan_mode = GSM48_CMODE_SPEECH_EFR, .chan_mode = GSM48_CMODE_SPEECH_EFR,
},
.requires_voice_stream = false, .requires_voice_stream = false,
}; };
} else if (!strcmp(codec_str, "amr")) { } else if (!strcmp(codec_str, "amr")) {
@ -6087,14 +6091,18 @@ static int lchan_act_single(struct vty *vty, struct gsm_lchan *lchan, const char
} }
info = (struct lchan_activate_info) { info = (struct lchan_activate_info) {
.activ_for = ACTIVATE_FOR_VTY, .activ_for = ACTIVATE_FOR_VTY,
.ch_mode_rate = {
.chan_mode = GSM48_CMODE_SPEECH_AMR, .chan_mode = GSM48_CMODE_SPEECH_AMR,
.s15_s0 = amr_modes[amr_mode], .s15_s0 = amr_modes[amr_mode],
},
.requires_voice_stream = false, .requires_voice_stream = false,
}; };
} else if (!strcmp(codec_str, "sig")) { } else if (!strcmp(codec_str, "sig")) {
info = (struct lchan_activate_info) { info = (struct lchan_activate_info) {
.activ_for = ACTIVATE_FOR_VTY, .activ_for = ACTIVATE_FOR_VTY,
.ch_mode_rate = {
.chan_mode = GSM48_CMODE_SIGN, .chan_mode = GSM48_CMODE_SIGN,
},
.requires_voice_stream = false, .requires_voice_stream = false,
}; };
} else { } else {
@ -6102,6 +6110,8 @@ static int lchan_act_single(struct vty *vty, struct gsm_lchan *lchan, const char
return CMD_WARNING; return CMD_WARNING;
} }
info.ch_mode_rate.chan_rate = chan_t_to_chan_rate(lchan_t);
vty_out(vty, "%% activating lchan %s as %s%s", gsm_lchan_name(lchan), gsm_chan_t_name(lchan->type), vty_out(vty, "%% activating lchan %s as %s%s", gsm_lchan_name(lchan), gsm_chan_t_name(lchan->type),
VTY_NEWLINE); VTY_NEWLINE);
lchan_activate(lchan, &info); lchan_activate(lchan, &info);

View File

@ -234,7 +234,7 @@ int get_reason_by_chreq(uint8_t ra, int neci)
static void mr_config_for_ms(struct gsm_lchan *lchan, struct msgb *msg) static void mr_config_for_ms(struct gsm_lchan *lchan, struct msgb *msg)
{ {
if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) if (lchan->current_ch_mode_rate.chan_mode == GSM48_CMODE_SPEECH_AMR)
msgb_tlv_put(msg, GSM48_IE_MUL_RATE_CFG, lchan->mr_ms_lv[0], msgb_tlv_put(msg, GSM48_IE_MUL_RATE_CFG, lchan->mr_ms_lv[0],
lchan->mr_ms_lv + 1); lchan->mr_ms_lv + 1);
} }
@ -538,7 +538,7 @@ struct msgb *gsm48_make_ho_cmd(struct gsm_lchan *new_lchan, uint8_t power_comman
} }
/* FIXME: optional bits for type of synchronization? */ /* FIXME: optional bits for type of synchronization? */
msgb_tv_put(msg, GSM48_IE_CHANMODE_1, new_lchan->tch_mode); msgb_tv_put(msg, GSM48_IE_CHANMODE_1, new_lchan->current_ch_mode_rate.chan_mode);
/* Mobile Allocation (after time), TLV (see 3GPP TS 44.018, 10.5.2.21) */ /* Mobile Allocation (after time), TLV (see 3GPP TS 44.018, 10.5.2.21) */
if (new_lchan->ts->hopping.enabled) { if (new_lchan->ts->hopping.enabled) {
@ -548,7 +548,7 @@ struct msgb *gsm48_make_ho_cmd(struct gsm_lchan *new_lchan, uint8_t power_comman
} }
/* in case of multi rate we need to attach a config */ /* in case of multi rate we need to attach a config */
if (new_lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) if (new_lchan->current_ch_mode_rate.chan_mode == GSM48_CMODE_SPEECH_AMR)
msgb_tlv_put(msg, GSM48_IE_MUL_RATE_CFG, new_lchan->mr_ms_lv[0], msgb_tlv_put(msg, GSM48_IE_MUL_RATE_CFG, new_lchan->mr_ms_lv[0],
new_lchan->mr_ms_lv + 1); new_lchan->mr_ms_lv + 1);
@ -573,7 +573,8 @@ int gsm48_send_rr_ass_cmd(struct gsm_lchan *current_lchan, struct gsm_lchan *new
struct gsm48_ass_cmd *ass = struct gsm48_ass_cmd *ass =
(struct gsm48_ass_cmd *) msgb_put(msg, sizeof(*ass)); (struct gsm48_ass_cmd *) msgb_put(msg, sizeof(*ass));
DEBUGP(DRR, "-> ASSIGNMENT COMMAND tch_mode=0x%02x\n", new_lchan->tch_mode); DEBUGP(DRR, "-> ASSIGNMENT COMMAND tch_mode=0x%02x\n",
current_lchan->conn->assignment.selected_ch_mode_rate.chan_mode);
msg->lchan = current_lchan; msg->lchan = current_lchan;
gh->proto_discr = GSM48_PDISC_RR; gh->proto_discr = GSM48_PDISC_RR;
@ -599,7 +600,7 @@ int gsm48_send_rr_ass_cmd(struct gsm_lchan *current_lchan, struct gsm_lchan *new
si1->cell_channel_description); si1->cell_channel_description);
} }
msgb_tv_put(msg, GSM48_IE_CHANMODE_1, new_lchan->tch_mode); msgb_tv_put(msg, GSM48_IE_CHANMODE_1, current_lchan->conn->assignment.selected_ch_mode_rate.chan_mode);
/* Mobile Allocation (freq. hopping), TLV (see 3GPP TS 44.018, 10.5.2.21) */ /* Mobile Allocation (freq. hopping), TLV (see 3GPP TS 44.018, 10.5.2.21) */
if (new_lchan->ts->hopping.enabled) { if (new_lchan->ts->hopping.enabled) {
@ -645,7 +646,6 @@ int gsm48_lchan_modify(struct gsm_lchan *lchan, uint8_t mode)
DEBUGP(DRR, "-> CHANNEL MODE MODIFY mode=0x%02x\n", mode); DEBUGP(DRR, "-> CHANNEL MODE MODIFY mode=0x%02x\n", mode);
lchan->tch_mode = mode;
msg->lchan = lchan; msg->lchan = lchan;
gh->proto_discr = GSM48_PDISC_RR; gh->proto_discr = GSM48_PDISC_RR;
gh->msg_type = GSM48_MT_RR_CHAN_MODE_MODIF; gh->msg_type = GSM48_MT_RR_CHAN_MODE_MODIF;
@ -670,10 +670,10 @@ int gsm48_rx_rr_modif_ack(struct msgb *msg)
LOG_LCHAN(msg->lchan, LOGL_DEBUG, "CHANNEL MODE MODIFY ACK for %s\n", LOG_LCHAN(msg->lchan, LOGL_DEBUG, "CHANNEL MODE MODIFY ACK for %s\n",
gsm48_chan_mode_name(mod->mode)); gsm48_chan_mode_name(mod->mode));
if (mod->mode != msg->lchan->tch_mode) { if (mod->mode != msg->lchan->modify.info.ch_mode_rate.chan_mode) {
LOG_LCHAN(msg->lchan, LOGL_ERROR, LOG_LCHAN(msg->lchan, LOGL_ERROR,
"CHANNEL MODE MODIFY ACK has wrong mode: Wanted: %s Got: %s\n", "CHANNEL MODE MODIFY ACK has wrong mode: Wanted: %s Got: %s\n",
gsm48_chan_mode_name(msg->lchan->tch_mode), gsm48_chan_mode_name(msg->lchan->modify.info.ch_mode_rate.chan_mode),
gsm48_chan_mode_name(mod->mode)); gsm48_chan_mode_name(mod->mode));
return -1; return -1;
} }

View File

@ -685,6 +685,22 @@ enum gsm_phys_chan_config gsm_pchan_by_lchan_type(enum gsm_chan_t type)
} }
} }
enum channel_rate chan_t_to_chan_rate(enum gsm_chan_t chan_t)
{
switch (chan_t) {
case GSM_LCHAN_SDCCH:
return CH_RATE_SDCCH;
case GSM_LCHAN_TCH_F:
return CH_RATE_FULL;
case GSM_LCHAN_TCH_H:
return CH_RATE_HALF;
default:
/* For other channel types, the channel_rate value is never used. It is fine to return an invalid value,
* and callers don't actually need to check for this. */
return -1;
}
}
/* Can the timeslot in principle be used as this PCHAN kind? */ /* Can the timeslot in principle be used as this PCHAN kind? */
bool ts_is_capable_of_pchan(struct gsm_bts_trx_ts *ts, enum gsm_phys_chan_config pchan) bool ts_is_capable_of_pchan(struct gsm_bts_trx_ts *ts, enum gsm_phys_chan_config pchan)
{ {

View File

@ -52,7 +52,7 @@
lchan->ts->nr, \ lchan->ts->nr, \
lchan->nr, \ lchan->nr, \
gsm_lchant_name(lchan->type), \ gsm_lchant_name(lchan->type), \
gsm48_chan_mode_name(lchan->tch_mode), \ gsm48_chan_mode_name(lchan->current_ch_mode_rate.chan_mode), \
bsc_subscr_name(lchan->conn? lchan->conn->bsub : NULL), \ bsc_subscr_name(lchan->conn? lchan->conn->bsub : NULL), \
## args) ## args)
@ -63,7 +63,7 @@
lchan->ts->nr, \ lchan->ts->nr, \
lchan->nr, \ lchan->nr, \
gsm_lchant_name(lchan->type), \ gsm_lchant_name(lchan->type), \
gsm48_chan_mode_name(lchan->tch_mode), \ gsm48_chan_mode_name(lchan->current_ch_mode_rate.chan_mode), \
new_bts->nr, \ new_bts->nr, \
bsc_subscr_name(lchan->conn? lchan->conn->bsub : NULL), \ bsc_subscr_name(lchan->conn? lchan->conn->bsub : NULL), \
## args) ## args)
@ -75,7 +75,7 @@
lchan->ts->nr, \ lchan->ts->nr, \
lchan->nr, \ lchan->nr, \
gsm_lchant_name(lchan->type), \ gsm_lchant_name(lchan->type), \
gsm48_chan_mode_name(lchan->tch_mode), \ gsm48_chan_mode_name(lchan->current_ch_mode_rate.chan_mode), \
gsm0808_cell_id_list_name(remote_cil), \ gsm0808_cell_id_list_name(remote_cil), \
bsc_subscr_name(lchan->conn? lchan->conn->bsub : NULL), \ bsc_subscr_name(lchan->conn? lchan->conn->bsub : NULL), \
## args) ## args)
@ -478,7 +478,7 @@ static void check_requirements(struct ho_candidate *c)
/* compatibility check for codecs. /* compatibility check for codecs.
* if so, the candidates for full rate and half rate are selected */ * if so, the candidates for full rate and half rate are selected */
switch (c->current.lchan->tch_mode) { switch (c->current.lchan->current_ch_mode_rate.chan_mode) {
case GSM48_CMODE_SPEECH_V1: case GSM48_CMODE_SPEECH_V1:
switch (c->current.lchan->type) { switch (c->current.lchan->type) {
case GSM_LCHAN_TCH_F: /* mandatory */ case GSM_LCHAN_TCH_F: /* mandatory */
@ -489,7 +489,7 @@ static void check_requirements(struct ho_candidate *c)
LOGPHOLCHANTOBTS(c->current.lchan, c->target.bts, LOGL_DEBUG, LOGPHOLCHANTOBTS(c->current.lchan, c->target.bts, LOGL_DEBUG,
"tch_mode='%s' type='%s' not supported\n", "tch_mode='%s' type='%s' not supported\n",
get_value_string(gsm48_chan_mode_names, get_value_string(gsm48_chan_mode_names,
c->current.lchan->tch_mode), c->current.lchan->current_ch_mode_rate.chan_mode),
gsm_lchant_name(c->current.lchan->type)); gsm_lchant_name(c->current.lchan->type));
break; break;
} }
@ -498,7 +498,8 @@ static void check_requirements(struct ho_candidate *c)
break; break;
default: default:
LOGPHOLCHAN(c->current.lchan, LOGL_ERROR, "Unexpected channel type: neither TCH/F nor TCH/H for %s\n", LOGPHOLCHAN(c->current.lchan, LOGL_ERROR, "Unexpected channel type: neither TCH/F nor TCH/H for %s\n",
get_value_string(gsm48_chan_mode_names, c->current.lchan->tch_mode)); get_value_string(gsm48_chan_mode_names,
c->current.lchan->current_ch_mode_rate.chan_mode));
return; return;
} }
break; break;
@ -757,7 +758,7 @@ static void check_requirements_remote_bss(struct ho_candidate *c)
/* compatibility check for codecs -- we have no notion of what the remote BSS supports. We can /* compatibility check for codecs -- we have no notion of what the remote BSS supports. We can
* only assume that a handover would work, and use only the local requirements. */ * only assume that a handover would work, and use only the local requirements. */
switch (c->current.lchan->tch_mode) { switch (c->current.lchan->current_ch_mode_rate.chan_mode) {
case GSM48_CMODE_SPEECH_V1: case GSM48_CMODE_SPEECH_V1:
switch (c->current.lchan->type) { switch (c->current.lchan->type) {
case GSM_LCHAN_TCH_F: /* mandatory */ case GSM_LCHAN_TCH_F: /* mandatory */
@ -769,7 +770,8 @@ static void check_requirements_remote_bss(struct ho_candidate *c)
break; break;
default: default:
LOGPHOLCHAN(c->current.lchan, LOGL_ERROR, "Unexpected channel type: neither TCH/F nor TCH/H for %s\n", LOGPHOLCHAN(c->current.lchan, LOGL_ERROR, "Unexpected channel type: neither TCH/F nor TCH/H for %s\n",
get_value_string(gsm48_chan_mode_names, c->current.lchan->tch_mode)); get_value_string(gsm48_chan_mode_names,
c->current.lchan->current_ch_mode_rate.chan_mode));
return; return;
} }
break; break;
@ -815,7 +817,7 @@ static int trigger_local_ho_or_as(struct ho_candidate *c, uint8_t requirements)
bool full_rate = false; bool full_rate = false;
/* afs_bias becomes > 0, if AFS is used and is improved */ /* afs_bias becomes > 0, if AFS is used and is improved */
if (c->current.lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) if (c->current.lchan->current_ch_mode_rate.chan_mode == GSM48_CMODE_SPEECH_AMR)
afs_bias = ho_get_hodec2_afs_bias_rxlev(c->target.bts->ho); afs_bias = ho_get_hodec2_afs_bias_rxlev(c->target.bts->ho);
/* select TCH rate, prefer TCH/F if AFS is improved */ /* select TCH rate, prefer TCH/F if AFS is improved */
@ -1243,7 +1245,7 @@ static void collect_candidates_for_lchan(struct gsm_lchan *lchan,
static int find_alternative_lchan(struct gsm_lchan *lchan, bool include_weaker_rxlev) static int find_alternative_lchan(struct gsm_lchan *lchan, bool include_weaker_rxlev)
{ {
struct gsm_bts *bts = lchan->ts->trx->bts; struct gsm_bts *bts = lchan->ts->trx->bts;
int ahs = (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR int ahs = (lchan->current_ch_mode_rate.chan_mode == GSM48_CMODE_SPEECH_AMR
&& lchan->type == GSM_LCHAN_TCH_H); && lchan->type == GSM_LCHAN_TCH_H);
int rxlev_current; int rxlev_current;
struct ho_candidate clist[1 + ARRAY_SIZE(lchan->neigh_meas)]; struct ho_candidate clist[1 + ARRAY_SIZE(lchan->neigh_meas)];
@ -1451,7 +1453,7 @@ static void on_measurement_report(struct gsm_meas_rep *mr)
/* improve levels in case of AFS, if defined */ /* improve levels in case of AFS, if defined */
if (lchan->type == GSM_LCHAN_TCH_F if (lchan->type == GSM_LCHAN_TCH_F
&& lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) { && lchan->current_ch_mode_rate.chan_mode == GSM48_CMODE_SPEECH_AMR) {
int av_rxlev_was = av_rxlev; int av_rxlev_was = av_rxlev;
int av_rxqual_was = av_rxqual; int av_rxqual_was = av_rxqual;
int rxlev_bias = ho_get_hodec2_afs_bias_rxlev(bts->ho); int rxlev_bias = ho_get_hodec2_afs_bias_rxlev(bts->ho);

View File

@ -61,7 +61,7 @@
lchan ? lchan->ts->nr : 0, \ lchan ? lchan->ts->nr : 0, \
lchan ? gsm_lchant_name(lchan->type) : "?", \ lchan ? gsm_lchant_name(lchan->type) : "?", \
lchan ? lchan->nr : 0, \ lchan ? lchan->nr : 0, \
lchan ? gsm48_chan_mode_name(lchan->tch_mode) : "?" lchan ? gsm48_chan_mode_name(lchan->current_ch_mode_rate.chan_mode) : "?"
#define LOG_FMT_TO_LCHAN "%u-%u-%u-%s%s%s-%u" #define LOG_FMT_TO_LCHAN "%u-%u-%u-%s%s%s-%u"
#define LOG_ARGS_TO_LCHAN(lchan) \ #define LOG_ARGS_TO_LCHAN(lchan) \
@ -398,14 +398,14 @@ static void handover_start_intra_bsc(struct gsm_subscriber_connection *conn)
info = (struct lchan_activate_info){ info = (struct lchan_activate_info){
.activ_for = ACTIVATE_FOR_HANDOVER, .activ_for = ACTIVATE_FOR_HANDOVER,
.for_conn = conn, .for_conn = conn,
.chan_mode = conn->lchan->tch_mode, .ch_mode_rate = conn->lchan->current_ch_mode_rate,
.encr = conn->lchan->encr, .encr = conn->lchan->encr,
.requires_voice_stream = conn->lchan->mgw_endpoint_ci_bts ? true : false, .requires_voice_stream = conn->lchan->mgw_endpoint_ci_bts ? true : false,
.msc_assigned_cic = conn->ho.inter_bsc_in.msc_assigned_cic, .msc_assigned_cic = conn->ho.inter_bsc_in.msc_assigned_cic,
.re_use_mgw_endpoint_from_lchan = conn->lchan, .re_use_mgw_endpoint_from_lchan = conn->lchan,
.wait_before_switching_rtp = true, .wait_before_switching_rtp = true,
.s15_s0 = conn->lchan->activate.info.s15_s0,
}; };
info.ch_mode_rate.chan_rate = chan_t_to_chan_rate(ho->new_lchan->type);
/* For intra-cell handover, we know the accurate Timing Advance from the previous lchan. For inter-cell /* For intra-cell handover, we know the accurate Timing Advance from the previous lchan. For inter-cell
* handover, no Timing Advance for the new cell is known, so leave it unset. */ * handover, no Timing Advance for the new cell is known, so leave it unset. */
@ -696,8 +696,7 @@ void handover_start_inter_bsc_in(struct gsm_subscriber_connection *conn,
info = (struct lchan_activate_info){ info = (struct lchan_activate_info){
.activ_for = ACTIVATE_FOR_HANDOVER, .activ_for = ACTIVATE_FOR_HANDOVER,
.for_conn = conn, .for_conn = conn,
.chan_mode = ch_mode_rate.chan_mode, .ch_mode_rate = ch_mode_rate,
.s15_s0 = ch_mode_rate.s15_s0,
.requires_voice_stream = chan_mode_is_tch(ch_mode_rate.chan_mode), .requires_voice_stream = chan_mode_is_tch(ch_mode_rate.chan_mode),
.msc_assigned_cic = req->msc_assigned_cic, .msc_assigned_cic = req->msc_assigned_cic,
}; };
@ -849,7 +848,7 @@ static void send_handover_performed(struct gsm_subscriber_connection *conn)
}; };
/* Chosen Channel 3.2.2.33 */ /* Chosen Channel 3.2.2.33 */
ho_perf_params.chosen_channel = gsm0808_chosen_channel(lchan->type, lchan->tch_mode); ho_perf_params.chosen_channel = gsm0808_chosen_channel(lchan->type, lchan->current_ch_mode_rate.chan_mode);
if (!ho_perf_params.chosen_channel) { if (!ho_perf_params.chosen_channel) {
LOG_HO(conn, LOGL_ERROR, "Failed to generate Chosen Channel IE, can't send HANDOVER PERFORMED!\n"); LOG_HO(conn, LOGL_ERROR, "Failed to generate Chosen Channel IE, can't send HANDOVER PERFORMED!\n");
return; return;
@ -862,14 +861,15 @@ static void send_handover_performed(struct gsm_subscriber_connection *conn)
if (ho->new_lchan->activate.info.requires_voice_stream) { if (ho->new_lchan->activate.info.requires_voice_stream) {
/* Speech Version (chosen) 3.2.2.51 */ /* Speech Version (chosen) 3.2.2.51 */
ho_perf_params.speech_version_chosen = gsm0808_permitted_speech(lchan->type, lchan->tch_mode); ho_perf_params.speech_version_chosen = gsm0808_permitted_speech(lchan->type,
lchan->current_ch_mode_rate.chan_mode);
ho_perf_params.speech_version_chosen_present = true; ho_perf_params.speech_version_chosen_present = true;
/* Speech Codec (chosen) 3.2.2.104 */ /* Speech Codec (chosen) 3.2.2.104 */
if (gscon_is_aoip(conn)) { if (gscon_is_aoip(conn)) {
/* Extrapolate speech codec from speech mode */ /* Extrapolate speech codec from speech mode */
gsm0808_speech_codec_from_chan_type(&sc, ho_perf_params.speech_version_chosen); gsm0808_speech_codec_from_chan_type(&sc, ho_perf_params.speech_version_chosen);
sc.cfg = conn->lchan->ch_mode_rate.s15_s0; sc.cfg = conn->lchan->current_ch_mode_rate.s15_s0;
memcpy(&ho_perf_params.speech_codec_chosen, &sc, sizeof(sc)); memcpy(&ho_perf_params.speech_codec_chosen, &sc, sizeof(sc));
ho_perf_params.speech_codec_chosen_present = true; ho_perf_params.speech_codec_chosen_present = true;
} }

View File

@ -569,6 +569,8 @@ static int lchan_mr_config(struct gsm_lchan *lchan, uint16_t s15_s0)
/* Proceed with the generation of the multirate configuration IE /* Proceed with the generation of the multirate configuration IE
* (MS and BTS) */ * (MS and BTS) */
/* FIXME: this actually modifies the lchan->mr_ms_lv and ->mr_bts_lv before an ACK for these AMR bits has been
* received. Until an ACK is received, all state should live in lchan->activate.* or lchan->modify.* ONLY. */
rc = gsm48_multirate_config(lchan->mr_ms_lv, &mr_conf_filtered, mr->ms_mode, mr->num_modes); rc = gsm48_multirate_config(lchan->mr_ms_lv, &mr_conf_filtered, mr->ms_mode, mr->num_modes);
if (rc != 0) { if (rc != 0) {
LOG_LCHAN(lchan, LOGL_ERROR, "can not encode multirate configuration (MS)\n"); LOG_LCHAN(lchan, LOGL_ERROR, "can not encode multirate configuration (MS)\n");
@ -580,7 +582,6 @@ static int lchan_mr_config(struct gsm_lchan *lchan, uint16_t s15_s0)
return -EINVAL; return -EINVAL;
} }
lchan->s15_s0 = s15_s0;
return 0; return 0;
} }
@ -645,30 +646,28 @@ static void lchan_fsm_wait_ts_ready_onenter(struct osmo_fsm_inst *fi, uint32_t p
lchan->bs_power = bts->bs_power_ctrl.bs_power_val_db / 2; lchan->bs_power = bts->bs_power_ctrl.bs_power_val_db / 2;
} }
if (info->chan_mode == GSM48_CMODE_SPEECH_AMR) { if (info->ch_mode_rate.chan_mode == GSM48_CMODE_SPEECH_AMR) {
if (lchan_mr_config(lchan, info->s15_s0) < 0) { if (lchan_mr_config(lchan, info->ch_mode_rate.s15_s0) < 0) {
lchan_fail("Can not generate multirate configuration IE\n"); lchan_fail("Can not generate multirate configuration IE\n");
return; return;
} }
} }
switch (info->chan_mode) { switch (info->ch_mode_rate.chan_mode) {
case GSM48_CMODE_SIGN: case GSM48_CMODE_SIGN:
lchan->rsl_cmode = RSL_CMOD_SPD_SIGN; lchan->rsl_cmode = RSL_CMOD_SPD_SIGN;
lchan->tch_mode = GSM48_CMODE_SIGN;
break; break;
case GSM48_CMODE_SPEECH_V1: case GSM48_CMODE_SPEECH_V1:
case GSM48_CMODE_SPEECH_EFR: case GSM48_CMODE_SPEECH_EFR:
case GSM48_CMODE_SPEECH_AMR: case GSM48_CMODE_SPEECH_AMR:
lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH; lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH;
lchan->tch_mode = info->chan_mode;
break; break;
default: default:
lchan_fail("Not implemented: cannot activate for chan mode %s", lchan_fail("Not implemented: cannot activate for chan mode %s",
gsm48_chan_mode_name(info->chan_mode)); gsm48_chan_mode_name(info->ch_mode_rate.chan_mode));
return; return;
} }
@ -682,7 +681,7 @@ static void lchan_fsm_wait_ts_ready_onenter(struct osmo_fsm_inst *fi, uint32_t p
(use_mgwep_ci ? osmo_mgcpc_ep_ci_name(use_mgwep_ci) : "new") (use_mgwep_ci ? osmo_mgcpc_ep_ci_name(use_mgwep_ci) : "new")
: "none", : "none",
gsm_lchant_name(lchan->type), gsm_lchant_name(lchan->type),
gsm48_chan_mode_name(lchan->tch_mode), gsm48_chan_mode_name(lchan->activate.info.ch_mode_rate.chan_mode),
(lchan->activate.info.encr.alg_id ? : 1)-1, (lchan->activate.info.encr.alg_id ? : 1)-1,
lchan->activate.info.encr.key_len ? osmo_hexdump_nospc(lchan->activate.info.encr.key, lchan->activate.info.encr.key_len ? osmo_hexdump_nospc(lchan->activate.info.encr.key,
lchan->activate.info.encr.key_len) : "none"); lchan->activate.info.encr.key_len) : "none");
@ -822,6 +821,11 @@ static void lchan_fsm_post_activ_ack(struct osmo_fsm_inst *fi)
{ {
int rc; int rc;
struct gsm_lchan *lchan = lchan_fi_lchan(fi); struct gsm_lchan *lchan = lchan_fi_lchan(fi);
lchan->current_ch_mode_rate = lchan->activate.info.ch_mode_rate;
LOG_LCHAN(lchan, LOGL_INFO, "Rx Activ ACK %s\n",
gsm48_chan_mode_name(lchan->current_ch_mode_rate.chan_mode));
if (lchan->release.requested) { if (lchan->release.requested) {
lchan_fail_to(LCHAN_ST_WAIT_RF_RELEASE_ACK, "Release requested while activating"); lchan_fail_to(LCHAN_ST_WAIT_RF_RELEASE_ACK, "Release requested while activating");
return; return;
@ -948,7 +952,7 @@ static void lchan_fsm_wait_rll_rtp_establish(struct osmo_fsm_inst *fi, uint32_t
static void lchan_fsm_wait_rr_chan_mode_modify_ack_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) static void lchan_fsm_wait_rr_chan_mode_modify_ack_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{ {
struct gsm_lchan *lchan = lchan_fi_lchan(fi); struct gsm_lchan *lchan = lchan_fi_lchan(fi);
gsm48_lchan_modify(lchan, lchan->modify.info.chan_mode); gsm48_lchan_modify(lchan, lchan->modify.info.ch_mode_rate.chan_mode);
} }
static void lchan_fsm_wait_rr_chan_mode_modify_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data) static void lchan_fsm_wait_rr_chan_mode_modify_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@ -989,8 +993,7 @@ static void lchan_fsm_wait_rsl_chan_mode_modify_ack(struct osmo_fsm_inst *fi, ui
case LCHAN_EV_RSL_CHAN_MODE_MODIFY_ACK: case LCHAN_EV_RSL_CHAN_MODE_MODIFY_ACK:
/* The Channel Mode Modify was ACKed, now the requested values become the accepted and used values. */ /* The Channel Mode Modify was ACKed, now the requested values become the accepted and used values. */
lchan->tch_mode = lchan->modify.info.chan_mode; lchan->current_ch_mode_rate = lchan->modify.info.ch_mode_rate;
lchan->s15_s0 = lchan->modify.info.s15_s0;
if (lchan->modify.info.requires_voice_stream if (lchan->modify.info.requires_voice_stream
&& !lchan->fi_rtp) { && !lchan->fi_rtp) {
@ -999,7 +1002,7 @@ static void lchan_fsm_wait_rsl_chan_mode_modify_ack(struct osmo_fsm_inst *fi, ui
lchan->activate.info = (struct lchan_activate_info){ lchan->activate.info = (struct lchan_activate_info){
.activ_for = ACTIVATE_FOR_MODE_MODIFY_RTP, .activ_for = ACTIVATE_FOR_MODE_MODIFY_RTP,
.for_conn = lchan->conn, .for_conn = lchan->conn,
.s15_s0 = lchan->modify.info.s15_s0, .ch_mode_rate = lchan->modify.info.ch_mode_rate,
.requires_voice_stream = true, .requires_voice_stream = true,
.msc_assigned_cic = lchan->modify.info.msc_assigned_cic, .msc_assigned_cic = lchan->modify.info.msc_assigned_cic,
}; };
@ -1142,8 +1145,8 @@ static void lchan_fsm_established(struct osmo_fsm_inst *fi, uint32_t event, void
use_mgwep_ci = lchan_use_mgw_endpoint_ci_bts(lchan); use_mgwep_ci = lchan_use_mgw_endpoint_ci_bts(lchan);
if (modif_info->chan_mode == GSM48_CMODE_SPEECH_AMR) { if (modif_info->ch_mode_rate.chan_mode == GSM48_CMODE_SPEECH_AMR) {
if (lchan_mr_config(lchan, modif_info->s15_s0) < 0) { if (lchan_mr_config(lchan, modif_info->ch_mode_rate.s15_s0) < 0) {
lchan_fail("Can not generate multirate configuration IE\n"); lchan_fail("Can not generate multirate configuration IE\n");
return; return;
} }
@ -1157,7 +1160,7 @@ static void lchan_fsm_established(struct osmo_fsm_inst *fi, uint32_t event, void
(use_mgwep_ci ? osmo_mgcpc_ep_ci_name(use_mgwep_ci) : "new") (use_mgwep_ci ? osmo_mgcpc_ep_ci_name(use_mgwep_ci) : "new")
: "none", : "none",
gsm_lchant_name(lchan->type), gsm_lchant_name(lchan->type),
gsm48_chan_mode_name(lchan->tch_mode)); gsm48_chan_mode_name(lchan->modify.info.ch_mode_rate.chan_mode));
lchan_fsm_state_chg(LCHAN_ST_WAIT_RR_CHAN_MODE_MODIFY_ACK); lchan_fsm_state_chg(LCHAN_ST_WAIT_RR_CHAN_MODE_MODIFY_ACK);
return; return;

View File

@ -268,19 +268,19 @@ static void lchan_rtp_fsm_wait_ipacc_crcx_ack_onenter(struct osmo_fsm_inst *fi,
return; return;
} }
val = ipacc_speech_mode(lchan->tch_mode, lchan->type); val = ipacc_speech_mode(lchan->activate.info.ch_mode_rate.chan_mode, lchan->type);
if (val < 0) { if (val < 0) {
lchan_rtp_fail("Cannot determine Abis/IP speech mode for tch_mode=%s type=%s\n", lchan_rtp_fail("Cannot determine Abis/IP speech mode for tch_mode=%s type=%s\n",
get_value_string(gsm48_chan_mode_names, lchan->tch_mode), get_value_string(gsm48_chan_mode_names, lchan->activate.info.ch_mode_rate.chan_mode),
gsm_lchant_name(lchan->type)); gsm_lchant_name(lchan->type));
return; return;
} }
lchan->abis_ip.speech_mode = val; lchan->abis_ip.speech_mode = val;
val = ipacc_payload_type(lchan->tch_mode, lchan->type); val = ipacc_payload_type(lchan->activate.info.ch_mode_rate.chan_mode, lchan->type);
if (val < 0) { if (val < 0) {
lchan_rtp_fail("Cannot determine Abis/IP payload type for tch_mode=%s type=%s\n", lchan_rtp_fail("Cannot determine Abis/IP payload type for tch_mode=%s type=%s\n",
get_value_string(gsm48_chan_mode_names, lchan->tch_mode), get_value_string(gsm48_chan_mode_names, lchan->activate.info.ch_mode_rate.chan_mode),
gsm_lchant_name(lchan->type)); gsm_lchant_name(lchan->type));
return; return;
} }
@ -834,14 +834,14 @@ static int chan_mode_to_mgcp_bss_pt(enum mgcp_codecs codec)
void mgcp_pick_codec(struct mgcp_conn_peer *verb_info, const struct gsm_lchan *lchan, bool bss_side) void mgcp_pick_codec(struct mgcp_conn_peer *verb_info, const struct gsm_lchan *lchan, bool bss_side)
{ {
enum mgcp_codecs codec = chan_mode_to_mgcp_codec(lchan->tch_mode, enum mgcp_codecs codec = chan_mode_to_mgcp_codec(lchan->activate.info.ch_mode_rate.chan_mode,
lchan->type == GSM_LCHAN_TCH_H? false : true); lchan->type == GSM_LCHAN_TCH_H? false : true);
int custom_pt; int custom_pt;
if (codec < 0) { if (codec < 0) {
LOG_LCHAN(lchan, LOGL_ERROR, LOG_LCHAN(lchan, LOGL_ERROR,
"Unable to determine MGCP codec type for %s in chan-mode %s\n", "Unable to determine MGCP codec type for %s in chan-mode %s\n",
gsm_lchant_name(lchan->type), gsm48_chan_mode_name(lchan->tch_mode)); gsm_lchant_name(lchan->type), gsm48_chan_mode_name(lchan->activate.info.ch_mode_rate.chan_mode));
verb_info->codecs_len = 0; verb_info->codecs_len = 0;
return; return;
} }

View File

@ -679,12 +679,12 @@ static int select_codecs(struct assignment_request *req, struct gsm0808_channel_
switch (ct->ch_rate_type) { switch (ct->ch_rate_type) {
case GSM0808_SPEECH_FULL_BM: case GSM0808_SPEECH_FULL_BM:
rc = match_codec_pref(&req->ch_mode_rate[nc], ct, &conn->codec_list, msc, bts, rc = match_codec_pref(&req->ch_mode_rate_list[nc], ct, &conn->codec_list, msc, bts,
RATE_PREF_FR); RATE_PREF_FR);
nc += (rc == 0) ? 1 : 0; nc += (rc == 0) ? 1 : 0;
break; break;
case GSM0808_SPEECH_HALF_LM: case GSM0808_SPEECH_HALF_LM:
rc = match_codec_pref(&req->ch_mode_rate[nc], ct, &conn->codec_list, msc, bts, rc = match_codec_pref(&req->ch_mode_rate_list[nc], ct, &conn->codec_list, msc, bts,
RATE_PREF_HR); RATE_PREF_HR);
nc += (rc == 0) ? 1 : 0; nc += (rc == 0) ? 1 : 0;
break; break;
@ -692,19 +692,19 @@ static int select_codecs(struct assignment_request *req, struct gsm0808_channel_
case GSM0808_SPEECH_PERM_NO_CHANGE: case GSM0808_SPEECH_PERM_NO_CHANGE:
case GSM0808_SPEECH_FULL_PREF_NO_CHANGE: case GSM0808_SPEECH_FULL_PREF_NO_CHANGE:
case GSM0808_SPEECH_FULL_PREF: case GSM0808_SPEECH_FULL_PREF:
rc = match_codec_pref(&req->ch_mode_rate[nc], ct, &conn->codec_list, msc, bts, rc = match_codec_pref(&req->ch_mode_rate_list[nc], ct, &conn->codec_list, msc, bts,
RATE_PREF_FR); RATE_PREF_FR);
nc += (rc == 0) ? 1 : 0; nc += (rc == 0) ? 1 : 0;
rc = match_codec_pref(&req->ch_mode_rate[nc], ct, &conn->codec_list, msc, bts, rc = match_codec_pref(&req->ch_mode_rate_list[nc], ct, &conn->codec_list, msc, bts,
RATE_PREF_HR); RATE_PREF_HR);
nc += (rc == 0) ? 1 : 0; nc += (rc == 0) ? 1 : 0;
break; break;
case GSM0808_SPEECH_HALF_PREF_NO_CHANGE: case GSM0808_SPEECH_HALF_PREF_NO_CHANGE:
case GSM0808_SPEECH_HALF_PREF: case GSM0808_SPEECH_HALF_PREF:
rc = match_codec_pref(&req->ch_mode_rate[nc], ct, &conn->codec_list, msc, bts, rc = match_codec_pref(&req->ch_mode_rate_list[nc], ct, &conn->codec_list, msc, bts,
RATE_PREF_HR); RATE_PREF_HR);
nc += (rc == 0) ? 1 : 0; nc += (rc == 0) ? 1 : 0;
rc = match_codec_pref(&req->ch_mode_rate[nc], ct, &conn->codec_list, msc, bts, rc = match_codec_pref(&req->ch_mode_rate_list[nc], ct, &conn->codec_list, msc, bts,
RATE_PREF_FR); RATE_PREF_FR);
nc += (rc == 0) ? 1 : 0; nc += (rc == 0) ? 1 : 0;
break; break;
@ -726,8 +726,8 @@ static int select_codecs(struct assignment_request *req, struct gsm0808_channel_
DEBUGP(DMSC, "Found matching audio type (pref=%d): %s %s for channel_type =" 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", " { ch_indctr=0x%x, ch_rate_type=0x%x, perm_spch=[ %s] }\n",
i, i,
req->ch_mode_rate[i].chan_rate == CH_RATE_FULL ? "full rate" : "half rate", req->ch_mode_rate_list[i].chan_rate == CH_RATE_FULL ? "full rate" : "half rate",
get_value_string(gsm48_chan_mode_names, req->ch_mode_rate[i].chan_mode), get_value_string(gsm48_chan_mode_names, req->ch_mode_rate_list[i].chan_mode),
ct->ch_indctr, ct->ch_rate_type, osmo_hexdump(ct->perm_spch, ct->perm_spch_len)); ct->ch_indctr, ct->ch_rate_type, osmo_hexdump(ct->perm_spch, ct->perm_spch_len));
} }
@ -742,43 +742,43 @@ static int select_sign_chan(struct assignment_request *req, struct gsm0808_chann
switch (ct->ch_rate_type) { switch (ct->ch_rate_type) {
case GSM0808_SIGN_ANY: case GSM0808_SIGN_ANY:
req->ch_mode_rate[nc++].chan_rate = CH_RATE_SDCCH; req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_SDCCH;
req->ch_mode_rate[nc++].chan_rate = CH_RATE_HALF; req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_HALF;
req->ch_mode_rate[nc++].chan_rate = CH_RATE_FULL; req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_FULL;
break; break;
case GSM0808_SIGN_SDCCH: case GSM0808_SIGN_SDCCH:
req->ch_mode_rate[nc++].chan_rate = CH_RATE_SDCCH; req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_SDCCH;
break; break;
case GSM0808_SIGN_SDCCH_FULL_BM: case GSM0808_SIGN_SDCCH_FULL_BM:
req->ch_mode_rate[nc++].chan_rate = CH_RATE_SDCCH; req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_SDCCH;
req->ch_mode_rate[nc++].chan_rate = CH_RATE_FULL; req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_FULL;
break; break;
case GSM0808_SIGN_SDCCH_HALF_LM: case GSM0808_SIGN_SDCCH_HALF_LM:
req->ch_mode_rate[nc++].chan_rate = CH_RATE_SDCCH; req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_SDCCH;
req->ch_mode_rate[nc++].chan_rate = CH_RATE_HALF; req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_HALF;
break; break;
case GSM0808_SIGN_FULL_BM: case GSM0808_SIGN_FULL_BM:
req->ch_mode_rate[nc++].chan_rate = CH_RATE_FULL; req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_FULL;
break; break;
case GSM0808_SIGN_HALF_LM: case GSM0808_SIGN_HALF_LM:
req->ch_mode_rate[nc++].chan_rate = CH_RATE_HALF; req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_HALF;
break; break;
case GSM0808_SIGN_FULL_PREF: case GSM0808_SIGN_FULL_PREF:
case GSM0808_SIGN_FULL_PREF_NO_CHANGE: case GSM0808_SIGN_FULL_PREF_NO_CHANGE:
req->ch_mode_rate[nc++].chan_rate = CH_RATE_FULL; req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_FULL;
req->ch_mode_rate[nc++].chan_rate = CH_RATE_HALF; req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_HALF;
break; break;
case GSM0808_SIGN_HALF_PREF: case GSM0808_SIGN_HALF_PREF:
case GSM0808_SIGN_HALF_PREF_NO_CHANGE: case GSM0808_SIGN_HALF_PREF_NO_CHANGE:
req->ch_mode_rate[nc++].chan_rate = CH_RATE_HALF; req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_HALF;
req->ch_mode_rate[nc++].chan_rate = CH_RATE_FULL; req->ch_mode_rate_list[nc++].chan_rate = CH_RATE_FULL;
break; break;
default: default:
break; break;
} }
for (i = 0; i < nc; i++) for (i = 0; i < nc; i++)
req->ch_mode_rate[i].chan_mode = GSM48_CMODE_SIGN; req->ch_mode_rate_list[i].chan_mode = GSM48_CMODE_SIGN;
req->n_ch_mode_rate = nc; req->n_ch_mode_rate = nc;
@ -1379,7 +1379,7 @@ int bsc_tx_bssmap_ho_required(struct gsm_lchan *lchan, const struct gsm0808_cell
case GSM_LCHAN_TCH_H: case GSM_LCHAN_TCH_H:
params.speech_version_used_present = true; params.speech_version_used_present = true;
params.speech_version_used = gsm0808_permitted_speech(lchan->type, params.speech_version_used = gsm0808_permitted_speech(lchan->type,
lchan->tch_mode); lchan->current_ch_mode_rate.chan_mode);
if (!params.speech_version_used) { if (!params.speech_version_used) {
LOG_HO(lchan->conn, LOGL_ERROR, "Cannot encode Speech Version (Used)" LOG_HO(lchan->conn, LOGL_ERROR, "Cannot encode Speech Version (Used)"
" for BSSMAP Handover Required message\n"); " for BSSMAP Handover Required message\n");
@ -1417,9 +1417,10 @@ int bsc_tx_bssmap_ho_request_ack(struct gsm_subscriber_connection *conn, struct
.l3_info = rr_ho_command->data, .l3_info = rr_ho_command->data,
.l3_info_len = rr_ho_command->len, .l3_info_len = rr_ho_command->len,
.chosen_channel_present = true, .chosen_channel_present = true,
.chosen_channel = gsm0808_chosen_channel(new_lchan->type, new_lchan->tch_mode), .chosen_channel = gsm0808_chosen_channel(new_lchan->type, new_lchan->current_ch_mode_rate.chan_mode),
.chosen_encr_alg = new_lchan->encr.alg_id, .chosen_encr_alg = new_lchan->encr.alg_id,
.chosen_speech_version = gsm0808_permitted_speech(new_lchan->type, new_lchan->tch_mode), .chosen_speech_version = gsm0808_permitted_speech(new_lchan->type,
new_lchan->current_ch_mode_rate.chan_mode),
}; };
if (gscon_is_aoip(conn)) { if (gscon_is_aoip(conn)) {
@ -1475,7 +1476,7 @@ enum handover_result bsc_tx_bssmap_ho_complete(struct gsm_subscriber_connection
.chosen_encr_alg = lchan->encr.alg_id, .chosen_encr_alg = lchan->encr.alg_id,
.chosen_channel_present = true, .chosen_channel_present = true,
.chosen_channel = gsm0808_chosen_channel(lchan->type, lchan->tch_mode), .chosen_channel = gsm0808_chosen_channel(lchan->type, lchan->current_ch_mode_rate.chan_mode),
.lcls_bss_status_present = (lcls_status != 0xff), .lcls_bss_status_present = (lcls_status != 0xff),
.lcls_bss_status = lcls_status, .lcls_bss_status = lcls_status,
@ -1483,7 +1484,7 @@ enum handover_result bsc_tx_bssmap_ho_complete(struct gsm_subscriber_connection
/* speech_codec_chosen */ /* speech_codec_chosen */
if (ho->new_lchan->activate.info.requires_voice_stream && gscon_is_aoip(conn)) { if (ho->new_lchan->activate.info.requires_voice_stream && gscon_is_aoip(conn)) {
int perm_spch = gsm0808_permitted_speech(lchan->type, lchan->tch_mode); int perm_spch = gsm0808_permitted_speech(lchan->type, lchan->current_ch_mode_rate.chan_mode);
params.speech_codec_chosen_present = true; params.speech_codec_chosen_present = true;
rc = gsm0808_speech_codec_from_chan_type(&params.speech_codec_chosen, perm_spch); rc = gsm0808_speech_codec_from_chan_type(&params.speech_codec_chosen, perm_spch);
if (rc) { if (rc) {

View File

@ -345,15 +345,14 @@ static bool lcls_enable_possible(const struct gsm_subscriber_connection *conn)
return false; return false;
} }
if (conn->lchan->tch_mode != conn->lcls.other->lchan->tch_mode if (conn->lchan->current_ch_mode_rate.chan_mode != conn->lcls.other->lchan->current_ch_mode_rate.chan_mode
&& conn->sccp.msc->lcls_codec_mismatch_allow == false) { && conn->sccp.msc->lcls_codec_mismatch_allow == false) {
LOGPFSM(conn->lcls.fi, LOGPFSM(conn->lcls.fi,
"Not enabling LS due to TCH-mode mismatch: %s:%s != %s:%s\n", "Not enabling LS due to TCH-mode mismatch: %s:%s != %s:%s\n",
gsm_lchan_name(conn->lchan), gsm_lchan_name(conn->lchan),
gsm48_chan_mode_name(conn->lchan->tch_mode), gsm48_chan_mode_name(conn->lchan->current_ch_mode_rate.chan_mode),
gsm_lchan_name(conn->lcls.other->lchan), gsm_lchan_name(conn->lcls.other->lchan),
gsm48_chan_mode_name(conn->lcls.other->lchan-> gsm48_chan_mode_name(conn->lcls.other->lchan->current_ch_mode_rate.chan_mode));
tch_mode));
return false; return false;
} }

View File

@ -397,14 +397,14 @@ struct gsm_lchan *lchan_act(struct gsm_lchan *lchan, int full_rate, const char *
create_conn(lchan); create_conn(lchan);
if (!strcasecmp(codec, "FR") && full_rate) if (!strcasecmp(codec, "FR") && full_rate)
lchan->tch_mode = GSM48_CMODE_SPEECH_V1; lchan->current_ch_mode_rate.chan_mode = GSM48_CMODE_SPEECH_V1;
else if (!strcasecmp(codec, "HR") && !full_rate) else if (!strcasecmp(codec, "HR") && !full_rate)
lchan->tch_mode = GSM48_CMODE_SPEECH_V1; lchan->current_ch_mode_rate.chan_mode = GSM48_CMODE_SPEECH_V1;
else if (!strcasecmp(codec, "EFR") && full_rate) else if (!strcasecmp(codec, "EFR") && full_rate)
lchan->tch_mode = GSM48_CMODE_SPEECH_EFR; lchan->current_ch_mode_rate.chan_mode = GSM48_CMODE_SPEECH_EFR;
else if (!strcasecmp(codec, "AMR")) { else if (!strcasecmp(codec, "AMR")) {
lchan->tch_mode = GSM48_CMODE_SPEECH_AMR; lchan->current_ch_mode_rate.chan_mode = GSM48_CMODE_SPEECH_AMR;
lchan->activate.info.s15_s0 = 0x0002; lchan->current_ch_mode_rate.s15_s0 = 0x0002;
} else { } else {
fprintf(stderr, "Given codec unknown\n"); fprintf(stderr, "Given codec unknown\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);