lchan and assignment FSMs: make Channel Mode Modify more sane

The Channel Mode Modify procedure is currently implemented for changing
a TCH lchan from signalling to voice mode. For that, however, it is
re-using (abusing) the channel activation structs and state transitions,
and thus always implies activating a voice stream when the mode
modification is done.

I will add a Channel Mode Modify to enable VAMOS mode soon, so I require
separate structs and state transitions which also work on an lchan that
already has a voice stream established: a struct lchan_modify_info and
LCHAN_EV_REQUEST_MODE_MODIFY, and dedicated assignment FSM state
ASSIGNMENT_ST_WAIT_LCHAN_MODIFIED.

For the part where a Channel Mode Modify enables a voice stream after
switching from signalling to speech mode, still use the channel
activation code path, but only once the mode modification is done.

General improvements:
- To ask for a mode modification, emit an FSM event that ensures a mode
  modify only happens when the lchan state allows it.
- The new lchan_modify_info struct reflects only those parts that have
  an effect during a mode modification (before the lchan_activate_info
  was fully populated, many values not having an effect).
- More accurate logging, indicating "Mode Modify" instead of "Channel
  Activation"

A TTCN3 test for the Channel Mode Modify procedure is added in
Idf4efaed986de0bbd2b663313e837352cc139f0f, and the test passes both
before and after this patch is applied.

Related: SYS#4895
Change-Id: I4986844f839b1c9672c61d916eb3d33d0042d747
This commit is contained in:
Neels Hofmeyr 2021-04-14 19:39:01 +02:00
parent 9607d09203
commit d5cb0eb8fd
7 changed files with 224 additions and 70 deletions

View File

@ -12,6 +12,7 @@ labelloc=t; label="Assignment FSM"
gscon2 [label="conn FSM",shape=box3d]
lchan [label="lchan FSM\n(new lchan)",shape=box3d]
old_lchan [label="old lchan",shape=box3d]
lchan2 [label="lchan FSM",shape=box3d]
bssap [label="osmo_bsc_bssap.c",shape=box]
@ -22,8 +23,7 @@ labelloc=t; label="Assignment FSM"
bssap -> gscon [label="GSCON_EV_ASSIGNMENT_START\ndata=struct assignment_request",style=dotted]
gscon -> WAIT_LCHAN_ACTIVE [label="assignment_fsm_start()",style=dotted]
gscon -> WAIT_LCHAN_ESTABLISHED [label="assignment_fsm_start()\n(mode modify)",style=dotted]
WAIT_LCHAN_ACTIVE -> lchan [label="lchan_activate()\nFOR_ASSIGNMENT",style=dotted]
WAIT_LCHAN_ACTIVE -> lchan [label="lchan_activate()\nFOR_ASSIGNMENT",style=dotted]
lchan -> WAIT_LCHAN_ACTIVE [label="ASSIGNMENT_EV_\nLCHAN_\nACTIVE,ERROR",style=dotted]
lchan -> WAIT_LCHAN_ESTABLISHED [label="ASSIGNMENT_EV_\nLCHAN_\nESTABLISHED,ERROR",style=dotted]
@ -40,4 +40,10 @@ labelloc=t; label="Assignment FSM"
WAIT_MGW_ENDPOINT_TO_MSC -> gscon2 [label="gscon_connect_\nmgw_to_msc()",style=dotted]
gscon2 -> WAIT_MGW_ENDPOINT_TO_MSC [label="ASSIGNMENT_EV_\nMSC_MGW_OK",style=dotted]
terminate -> gscon2 [label="GSCON_EV_\nASSIGNMENT_END",style=dotted]
WAIT_LCHAN_ACTIVE -> WAIT_LCHAN_MODIFIED [label="assignment_fsm_start()\n(mode modify)"]
WAIT_LCHAN_MODIFIED -> lchan2 [label="lchan_mode_modify()\nMODIFY_FOR_ASSIGNMENT",style=dotted]
lchan2 -> WAIT_LCHAN_MODIFIED [label="ASSIGNMENT_EV_\nLCHAN_\nMODIFIED,ERROR",style=dotted]
WAIT_LCHAN_MODIFIED -> WAIT_MGW_ENDPOINT_TO_MSC [label="needs\nvoice\nstream"]
WAIT_LCHAN_MODIFIED -> terminate [label="no change\nin voice\nstream"]
}

View File

@ -24,11 +24,13 @@ enum assignment_fsm_state {
ASSIGNMENT_ST_WAIT_RR_ASS_COMPLETE,
ASSIGNMENT_ST_WAIT_LCHAN_ESTABLISHED,
ASSIGNMENT_ST_WAIT_MGW_ENDPOINT_TO_MSC,
ASSIGNMENT_ST_WAIT_LCHAN_MODIFIED,
};
enum assignment_fsm_event {
ASSIGNMENT_EV_LCHAN_ACTIVE,
ASSIGNMENT_EV_LCHAN_ESTABLISHED,
ASSIGNMENT_EV_LCHAN_MODIFIED,
ASSIGNMENT_EV_LCHAN_ERROR,
ASSIGNMENT_EV_MSC_MGW_OK,
ASSIGNMENT_EV_MSC_MGW_FAIL,

View File

@ -564,6 +564,7 @@ enum lchan_activate_for {
ACTIVATE_FOR_ASSIGNMENT,
ACTIVATE_FOR_HANDOVER,
ACTIVATE_FOR_VTY,
ACTIVATE_FOR_MODE_MODIFY_RTP,
};
extern const struct value_string lchan_activate_mode_names[];
@ -589,6 +590,25 @@ struct lchan_activate_info {
uint8_t ta;
};
enum lchan_modify_for {
MODIFY_FOR_NONE,
MODIFY_FOR_ASSIGNMENT,
MODIFY_FOR_VTY,
};
extern const struct value_string lchan_modify_for_names[];
static inline const char *lchan_modify_for_name(enum lchan_modify_for modify_for)
{ return get_value_string(lchan_modify_for_names, modify_for); }
struct lchan_modify_info {
enum lchan_modify_for modify_for;
enum gsm48_chan_mode chan_mode;
bool requires_voice_stream;
uint16_t msc_assigned_cic;
/* AMR config */
uint16_t s15_s0;
};
struct gsm_lchan {
/* The TS that we're part of */
struct gsm_bts_trx_ts *ts;
@ -613,6 +633,11 @@ struct gsm_lchan {
enum gsm0808_cause gsm0808_error_cause;
} activate;
struct {
struct lchan_modify_info info;
bool concluded;
} modify;
struct {
/* If an event to release the lchan comes in while still waiting for responses, just mark this
* flag, so that the lchan will gracefully release at the next sensible junction. */

View File

@ -65,6 +65,7 @@ void lchan_release(struct gsm_lchan *lchan, bool do_rr_release,
void lchan_activate(struct gsm_lchan *lchan, struct lchan_activate_info *info);
void lchan_ready_to_switch_rtp(struct gsm_lchan *lchan);
void lchan_mode_modify(struct gsm_lchan *lchan, struct lchan_modify_info *info);
static inline const char *lchan_state_name(struct gsm_lchan *lchan)
{

View File

@ -241,20 +241,23 @@ static void send_assignment_complete(struct gsm_subscriber_connection *conn)
static void assignment_success(struct gsm_subscriber_connection *conn)
{
struct gsm_bts *bts;
bool lchan_changed = (conn->assignment.new_lchan != NULL);
/* Take on the new lchan */
gscon_change_primary_lchan(conn, conn->assignment.new_lchan);
conn->assignment.new_lchan = NULL;
/* Take on the new lchan. If there only was a Channel Mode Modify, then there is no new lchan to take on. */
if (lchan_changed) {
gscon_change_primary_lchan(conn, conn->assignment.new_lchan);
OSMO_ASSERT((bts = conn_get_bts(conn)) != NULL);
if (is_siemens_bts(bts) && ts_is_tch(conn->lchan->ts)) {
/* HACK: store the actual Classmark 2 LV from the subscriber and use it here! */
uint8_t cm2_lv[] = { 0x02, 0x00, 0x00 };
send_siemens_mrpci(conn->lchan, cm2_lv);
OSMO_ASSERT((bts = conn_get_bts(conn)) != NULL);
if (is_siemens_bts(bts) && ts_is_tch(conn->lchan->ts)) {
/* HACK: store the actual Classmark 2 LV from the subscriber and use it here! */
uint8_t cm2_lv[] = { 0x02, 0x00, 0x00 };
send_siemens_mrpci(conn->lchan, cm2_lv);
}
/* apply LCLS configuration (if any) */
lcls_apply_config(conn);
}
/* apply LCLS configuration (if any) */
lcls_apply_config(conn);
conn->assignment.new_lchan = NULL;
send_assignment_complete(conn);
/* If something went wrong during send_assignment_complete(), the fi will be gone from
@ -267,15 +270,17 @@ static void assignment_success(struct gsm_subscriber_connection *conn)
return;
}
/* Rembered this only for error handling: should assignment fail, assignment_reset() will release
* the MGW endpoint right away. If successful, the conn continues to use the endpoint. */
conn->assignment.created_ci_for_msc = NULL;
if (lchan_changed) {
/* Rembered this only for error handling: should assignment fail, assignment_reset() will release
* the MGW endpoint right away. If successful, the conn continues to use the endpoint. */
conn->assignment.created_ci_for_msc = NULL;
/* New RTP information is now accepted */
conn->user_plane.msc_assigned_cic = conn->assignment.req.msc_assigned_cic;
osmo_strlcpy(conn->user_plane.msc_assigned_rtp_addr, conn->assignment.req.msc_rtp_addr,
sizeof(conn->user_plane.msc_assigned_rtp_addr));
conn->user_plane.msc_assigned_rtp_port = conn->assignment.req.msc_rtp_port;
/* New RTP information is now accepted */
conn->user_plane.msc_assigned_cic = conn->assignment.req.msc_assigned_cic;
osmo_strlcpy(conn->user_plane.msc_assigned_rtp_addr, conn->assignment.req.msc_rtp_addr,
sizeof(conn->user_plane.msc_assigned_rtp_addr));
conn->user_plane.msc_assigned_rtp_port = conn->assignment.req.msc_rtp_port;
}
LOG_ASSIGNMENT(conn, LOGL_DEBUG, "Assignment successful\n");
osmo_fsm_inst_term(conn->assignment.fi, OSMO_FSM_TERM_REGULAR, 0);
@ -285,7 +290,9 @@ static void assignment_success(struct gsm_subscriber_connection *conn)
static void assignment_fsm_update_id(struct gsm_subscriber_connection *conn)
{
struct gsm_lchan *new_lchan = conn->assignment.new_lchan;
/* Assignment can do a new channel activation, in which case new_lchan points at the new lchan.
* Or assignment can Channel Mode Modify the already used lchan, in which case new_lchan == NULL. */
struct gsm_lchan *new_lchan = (conn->assignment.new_lchan ? : conn->lchan);
if (!new_lchan) {
osmo_fsm_inst_update_id(conn->assignment.fi, conn->fi->id);
return;
@ -435,7 +442,8 @@ void assignment_fsm_start(struct gsm_subscriber_connection *conn, struct gsm_bts
[CH_RATE_FULL] = "FR",
};
struct osmo_fsm_inst *fi;
struct lchan_activate_info info;
struct lchan_activate_info activ_info;
struct lchan_modify_info modif_info;
int i;
OSMO_ASSERT(conn);
@ -465,6 +473,8 @@ void assignment_fsm_start(struct gsm_subscriber_connection *conn, struct gsm_bts
* LCHAN_EV_REQUEST_MODE_MODIFY in lchan_fsm.c. To not break the lchan, do not even attempt to re-use an lchan
* that already has an RTP stream set up, rather establish a new lchan (that transition is well implemented). */
if (reuse_existing_lchan(conn) && !conn->lchan->fi_rtp) {
/* The new lchan is the old lchan, keep new_lchan == NULL. */
conn->assignment.new_lchan = NULL;
/* If the requested mode and the current TCH mode matches up, just send the
* assignment complete directly and be done with the assignment procedure. */
@ -494,28 +504,17 @@ void assignment_fsm_start(struct gsm_subscriber_connection *conn, struct gsm_bts
gsm48_chan_mode_name(conn->lchan->ch_mode_rate.chan_mode),
gsm_lchan_name(conn->lchan));
info = (struct lchan_activate_info){
.activ_for = ACTIVATE_FOR_ASSIGNMENT,
.for_conn = conn,
modif_info = (struct lchan_modify_info){
.modify_for = MODIFY_FOR_ASSIGNMENT,
.chan_mode = conn->lchan->ch_mode_rate.chan_mode,
.encr = conn->lchan->encr,
.s15_s0 = conn->lchan->ch_mode_rate.s15_s0,
.requires_voice_stream = conn->assignment.requires_voice_stream,
.s15_s0 = conn->lchan->ch_mode_rate.s15_s0,
.msc_assigned_cic = req->msc_assigned_cic,
.re_use_mgw_endpoint_from_lchan = conn->lchan,
.ta = conn->lchan->last_ta,
.ta_known = true,
};
osmo_fsm_inst_dispatch(conn->lchan->fi, LCHAN_EV_REQUEST_MODE_MODIFY, &info);
/* Since we opted not to allocate a new lchan, the new lchan is still the old lchan. */
conn->assignment.new_lchan = conn->lchan;
/* Also we need to skip the RR assignment, so we jump forward and wait for the lchan_fsm until it
* reaches the established state again. */
assignment_fsm_state_chg(ASSIGNMENT_ST_WAIT_LCHAN_ESTABLISHED);
if (assignment_fsm_state_chg(ASSIGNMENT_ST_WAIT_LCHAN_MODIFIED))
return;
lchan_mode_modify(conn->lchan, &modif_info);
return;
}
@ -571,7 +570,7 @@ void assignment_fsm_start(struct gsm_subscriber_connection *conn, struct gsm_bts
req->use_osmux ? "yes" : "no");
assignment_fsm_state_chg(ASSIGNMENT_ST_WAIT_LCHAN_ACTIVE);
info = (struct lchan_activate_info){
activ_info = (struct lchan_activate_info){
.activ_for = ACTIVATE_FOR_ASSIGNMENT,
.for_conn = conn,
.chan_mode = conn->lchan->ch_mode_rate.chan_mode,
@ -583,7 +582,7 @@ void assignment_fsm_start(struct gsm_subscriber_connection *conn, struct gsm_bts
.ta = conn->lchan->last_ta,
.ta_known = true,
};
lchan_activate(conn->assignment.new_lchan, &info);
lchan_activate(conn->assignment.new_lchan, &activ_info);
}
static void assignment_fsm_wait_lchan(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@ -695,8 +694,10 @@ static void assignment_fsm_wait_mgw_endpoint_to_msc_onenter(struct osmo_fsm_inst
conn->assignment.req.msc_rtp_addr,
conn->assignment.req.msc_rtp_port);
/* Assignment can do a new channel activation, in which case new_lchan points at the new lchan.
* Or assignment can Channel Mode Modify the already used lchan, in which case new_lchan == NULL. */
if (!gscon_connect_mgw_to_msc(conn,
conn->assignment.new_lchan,
conn->assignment.new_lchan ? : conn->lchan,
conn->assignment.req.msc_rtp_addr,
conn->assignment.req.msc_rtp_port,
fi,
@ -742,6 +743,19 @@ static void assignment_fsm_wait_mgw_endpoint_to_msc(struct osmo_fsm_inst *fi, ui
}
}
static void assignment_fsm_wait_lchan_modified(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
switch (event) {
case ASSIGNMENT_EV_LCHAN_MODIFIED:
assignment_fsm_post_lchan_established(fi);
return;
default:
OSMO_ASSERT(false);
}
}
#define S(x) (1 << (x))
static const struct osmo_fsm_state assignment_fsm_states[] = {
@ -754,7 +768,7 @@ static const struct osmo_fsm_state assignment_fsm_states[] = {
.out_state_mask = 0
| S(ASSIGNMENT_ST_WAIT_LCHAN_ACTIVE)
| S(ASSIGNMENT_ST_WAIT_RR_ASS_COMPLETE)
| S(ASSIGNMENT_ST_WAIT_LCHAN_ESTABLISHED) /* MODE MODIFY */
| S(ASSIGNMENT_ST_WAIT_LCHAN_MODIFIED)
,
},
[ASSIGNMENT_ST_WAIT_RR_ASS_COMPLETE] = {
@ -790,11 +804,22 @@ static const struct osmo_fsm_state assignment_fsm_states[] = {
| S(ASSIGNMENT_EV_MSC_MGW_FAIL)
,
},
[ASSIGNMENT_ST_WAIT_LCHAN_MODIFIED] = {
.name = "WAIT_LCHAN_MODIFIED",
.action = assignment_fsm_wait_lchan_modified,
.in_event_mask = 0
| S(ASSIGNMENT_EV_LCHAN_MODIFIED)
,
.out_state_mask = 0
| S(ASSIGNMENT_ST_WAIT_MGW_ENDPOINT_TO_MSC)
,
},
};
static const struct value_string assignment_fsm_event_names[] = {
OSMO_VALUE_STRING(ASSIGNMENT_EV_LCHAN_ACTIVE),
OSMO_VALUE_STRING(ASSIGNMENT_EV_LCHAN_ESTABLISHED),
OSMO_VALUE_STRING(ASSIGNMENT_EV_LCHAN_MODIFIED),
OSMO_VALUE_STRING(ASSIGNMENT_EV_LCHAN_ERROR),
OSMO_VALUE_STRING(ASSIGNMENT_EV_MSC_MGW_OK),
OSMO_VALUE_STRING(ASSIGNMENT_EV_MSC_MGW_FAIL),
@ -807,6 +832,11 @@ static const struct value_string assignment_fsm_event_names[] = {
void assignment_fsm_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_subscriber_connection *conn = assignment_fi_conn(fi);
/* Assignment can do a new channel activation, in which case new_lchan points at the new lchan.
* Or assignment can Channel Mode Modify the already used lchan, in which case new_lchan == NULL. */
struct gsm_lchan *new_lchan = conn->assignment.new_lchan ? : conn->lchan;
switch (event) {
case ASSIGNMENT_EV_CONN_RELEASING:
@ -815,10 +845,11 @@ void assignment_fsm_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, vo
return;
case ASSIGNMENT_EV_LCHAN_ERROR:
if (data != conn->assignment.new_lchan)
if (data != new_lchan)
return;
assignment_fail(conn->assignment.new_lchan->activate.gsm0808_error_cause,
"Failed to activate lchan %s",
assignment_fail(new_lchan->activate.gsm0808_error_cause,
"Failed to %s lchan %s",
conn->assignment.new_lchan ? "activate" : "modify",
gsm_lchan_name(conn->assignment.new_lchan));
return;

View File

@ -916,6 +916,13 @@ const struct value_string lchan_activate_mode_names[] = {
{}
};
const struct value_string lchan_modify_for_names[] = {
OSMO_VALUE_STRING(MODIFY_FOR_NONE),
OSMO_VALUE_STRING(MODIFY_FOR_ASSIGNMENT),
OSMO_VALUE_STRING(MODIFY_FOR_VTY),
{}
};
/* This may be specific to RR Channel Release, and the mappings were chosen by pure naive guessing without a proper
* specification available. */
enum gsm48_rr_cause bsc_gsm48_rr_cause_from_gsm0808_cause(enum gsm0808_cause c)

View File

@ -66,6 +66,53 @@ bool lchan_may_receive_data(struct gsm_lchan *lchan)
}
}
static void lchan_on_mode_modify_success(struct gsm_lchan *lchan)
{
lchan->modify.concluded = true;
switch (lchan->modify.info.modify_for) {
case MODIFY_FOR_ASSIGNMENT:
osmo_fsm_inst_dispatch(lchan->conn->assignment.fi, ASSIGNMENT_EV_LCHAN_MODIFIED, lchan);
break;
default:
break;
}
}
#define lchan_on_mode_modify_failure(lchan, modify_for, for_conn) \
_lchan_on_mode_modify_failure(lchan, modify_for, for_conn, \
__FILE__, __LINE__)
static void _lchan_on_mode_modify_failure(struct gsm_lchan *lchan, enum lchan_modify_for modify_for,
struct gsm_subscriber_connection *for_conn,
const char *file, int line)
{
if (lchan->modify.concluded)
return;
lchan->modify.concluded = true;
switch (modify_for) {
case MODIFY_FOR_ASSIGNMENT:
LOG_LCHAN(lchan, LOGL_NOTICE, "Signalling Assignment FSM of error (%s)\n",
lchan->last_error ? : "unknown error");
_osmo_fsm_inst_dispatch(for_conn->assignment.fi, ASSIGNMENT_EV_LCHAN_ERROR, lchan,
file, line);
return;
case MODIFY_FOR_VTY:
LOG_LCHAN(lchan, LOGL_ERROR, "VTY user invoked lchan Channel Mode Modify failed (%s)\n",
lchan->last_error ? : "unknown error");
break;
default:
LOG_LCHAN(lchan, LOGL_ERROR, "lchan Channel Mode Modify failed (%s)\n",
lchan->last_error ? : "unknown error");
break;
}
}
/* The idea here is that we must not require to change any lchan state in order to deny a request. */
#define lchan_on_activation_failure(lchan, for_conn, activ_for) \
_lchan_on_activation_failure(lchan, for_conn, activ_for, \
@ -122,6 +169,10 @@ static void _lchan_on_activation_failure(struct gsm_lchan *lchan, enum lchan_act
lchan->last_error ? : "unknown error");
break;
case ACTIVATE_FOR_MODE_MODIFY_RTP:
lchan_on_mode_modify_failure(lchan, lchan->modify.info.modify_for, for_conn);
break;
default:
LOG_LCHAN(lchan, LOGL_ERROR, "lchan activation failed (%s)\n",
lchan->last_error ? : "unknown error");
@ -183,6 +234,10 @@ static void lchan_on_fully_established(struct gsm_lchan *lchan)
* we will try to roll back a modified RTP connection. */
break;
case ACTIVATE_FOR_MODE_MODIFY_RTP:
lchan_on_mode_modify_success(lchan);
break;
default:
LOG_LCHAN(lchan, LOGL_NOTICE, "lchan %s fully established\n",
lchan_activate_mode_name(lchan->activate.info.activ_for));
@ -323,6 +378,18 @@ abort:
/* Remain in state UNUSED */
}
void lchan_mode_modify(struct gsm_lchan *lchan, struct lchan_modify_info *info)
{
OSMO_ASSERT(lchan && info);
/* To make sure that the lchan is actually allowed to initiate Mode Modify, feed through an FSM event. */
if (osmo_fsm_inst_dispatch(lchan->fi, LCHAN_EV_REQUEST_MODE_MODIFY, info)) {
LOG_LCHAN(lchan, LOGL_ERROR,
"Channel Mode Modify requested, but cannot dispatch LCHAN_EV_REQUEST_MODE_MODIFY event\n");
lchan_on_mode_modify_failure(lchan, info->modify_for, lchan->conn);
}
}
static void lchan_fsm_update_id(struct gsm_lchan *lchan)
{
osmo_fsm_inst_update_id_f(lchan->fi, "%u-%u-%u-%s-%u",
@ -881,7 +948,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)
{
struct gsm_lchan *lchan = lchan_fi_lchan(fi);
gsm48_lchan_modify(lchan, lchan->activate.info.chan_mode);
gsm48_lchan_modify(lchan, lchan->modify.info.chan_mode);
}
static void lchan_fsm_wait_rr_chan_mode_modify_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@ -921,10 +988,27 @@ static void lchan_fsm_wait_rsl_chan_mode_modify_ack(struct osmo_fsm_inst *fi, ui
switch (event) {
case LCHAN_EV_RSL_CHAN_MODE_MODIFY_ACK:
if (lchan->activate.info.requires_voice_stream)
/* 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->s15_s0 = lchan->modify.info.s15_s0;
if (lchan->modify.info.requires_voice_stream
&& !lchan->fi_rtp) {
/* Continue with RTP stream establishing as done in lchan_activate(). Place the requested values in
* lchan->activate.info and continue with voice stream setup. */
lchan->activate.info = (struct lchan_activate_info){
.activ_for = ACTIVATE_FOR_MODE_MODIFY_RTP,
.for_conn = lchan->conn,
.s15_s0 = lchan->modify.info.s15_s0,
.requires_voice_stream = true,
.msc_assigned_cic = lchan->modify.info.msc_assigned_cic,
};
lchan->activate.concluded = false;
lchan_fsm_state_chg(LCHAN_ST_WAIT_RLL_RTP_ESTABLISH);
else
} else {
lchan_fsm_state_chg(LCHAN_ST_ESTABLISHED);
lchan_on_mode_modify_success(lchan);
}
return;
case LCHAN_EV_RSL_CHAN_MODE_MODIFY_NACK:
@ -1014,7 +1098,7 @@ static void handle_rll_rel_ind_or_conf(struct osmo_fsm_inst *fi, uint32_t event,
static void lchan_fsm_established(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_lchan *lchan = lchan_fi_lchan(fi);
struct lchan_activate_info *info;
struct lchan_modify_info *modif_info;
struct osmo_mgcpc_ep_ci *use_mgwep_ci;
switch (event) {
@ -1052,35 +1136,29 @@ static void lchan_fsm_established(struct osmo_fsm_inst *fi, uint32_t event, void
return;
}
info = data;
lchan->activate.info = *info;
modif_info = data;
lchan->modify.info = *modif_info;
lchan->modify.concluded = false;
use_mgwep_ci = lchan_use_mgw_endpoint_ci_bts(lchan);
if (info->chan_mode == GSM48_CMODE_SPEECH_AMR) {
if (lchan_mr_config(lchan, info->s15_s0) < 0) {
if (modif_info->chan_mode == GSM48_CMODE_SPEECH_AMR) {
if (lchan_mr_config(lchan, modif_info->s15_s0) < 0) {
lchan_fail("Can not generate multirate configuration IE\n");
return;
}
}
LOG_LCHAN(lchan, LOGL_INFO,
"Modification requested: %s voice=%s MGW-ci=%s type=%s tch-mode=%s encr-alg=A5/%u ck=%s\n",
lchan_activate_mode_name(lchan->activate.info.activ_for),
lchan->activate.info.requires_voice_stream ? "yes" : "no",
lchan->activate.info.requires_voice_stream ?
"Modification requested: %s voice=%s MGW-ci=%s type=%s tch-mode=%s\n",
lchan_modify_for_name(lchan->modify.info.modify_for),
lchan->modify.info.requires_voice_stream ? "yes" : "no",
lchan->modify.info.requires_voice_stream ?
(use_mgwep_ci ? osmo_mgcpc_ep_ci_name(use_mgwep_ci) : "new")
: "none",
gsm_lchant_name(lchan->type),
gsm48_chan_mode_name(lchan->tch_mode),
(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) : "none");
gsm48_chan_mode_name(lchan->tch_mode));
/* While the mode is changed the lchan is virtually "not activated", at least
* from the FSM implementations perspective */
lchan->activate.concluded = false;
/* Initiate mode modification, start with the MS side (RR) */
lchan_fsm_state_chg(LCHAN_ST_WAIT_RR_CHAN_MODE_MODIFY_ACK);
return;
@ -1389,8 +1467,9 @@ static const struct osmo_fsm_state lchan_fsm_states[] = {
| S(LCHAN_EV_RR_CHAN_MODE_MODIFY_ERROR)
,
.out_state_mask = 0
| S(LCHAN_ST_BORKEN)
| S(LCHAN_ST_WAIT_RSL_CHAN_MODE_MODIFY_ACK)
| S(LCHAN_ST_WAIT_RF_RELEASE_ACK)
| S(LCHAN_ST_BORKEN)
,
},
[LCHAN_ST_WAIT_RSL_CHAN_MODE_MODIFY_ACK] = {
@ -1402,8 +1481,10 @@ static const struct osmo_fsm_state lchan_fsm_states[] = {
| S(LCHAN_EV_RSL_CHAN_MODE_MODIFY_NACK)
,
.out_state_mask = 0
| S(LCHAN_ST_BORKEN)
| S(LCHAN_ST_ESTABLISHED)
| S(LCHAN_ST_WAIT_RLL_RTP_ESTABLISH)
| S(LCHAN_ST_WAIT_RF_RELEASE_ACK)
| S(LCHAN_ST_BORKEN)
,
},
[LCHAN_ST_ESTABLISHED] = {
@ -1514,6 +1595,7 @@ static const struct value_string lchan_fsm_event_names[] = {
OSMO_VALUE_STRING(LCHAN_EV_RR_CHAN_MODE_MODIFY_ERROR),
OSMO_VALUE_STRING(LCHAN_EV_RSL_CHAN_MODE_MODIFY_ACK),
OSMO_VALUE_STRING(LCHAN_EV_RSL_CHAN_MODE_MODIFY_NACK),
OSMO_VALUE_STRING(LCHAN_EV_REQUEST_MODE_MODIFY),
{}
};