osmo-bsc/src/osmo-bsc/lchan_rtp_fsm.c

773 lines
23 KiB
C
Raw Normal View History

/* osmo-bsc API to switch the RTP stream for an lchan.
*
* (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Neels Hofmeyr <neels@hofmeyr.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <osmocom/core/fsm.h>
#include <osmocom/bsc/gsm_data.h>
#include <osmocom/bsc/gsm_timers.h>
#include <osmocom/bsc/lchan_fsm.h>
#include <osmocom/bsc/lchan_rtp_fsm.h>
#include <osmocom/bsc/mgw_endpoint_fsm.h>
#include <osmocom/bsc/bsc_subscr_conn_fsm.h>
#include <osmocom/bsc/abis_rsl.h>
#include <osmocom/bsc/bsc_msc_data.h>
static struct osmo_fsm lchan_rtp_fsm;
struct gsm_lchan *lchan_rtp_fi_lchan(struct osmo_fsm_inst *fi)
{
OSMO_ASSERT(fi);
OSMO_ASSERT(fi->fsm == &lchan_rtp_fsm);
OSMO_ASSERT(fi->priv);
return fi->priv;
}
struct state_timeout lchan_rtp_fsm_timeouts[32] = {
[LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_AVAILABLE] = { .T=23004 },
[LCHAN_RTP_ST_WAIT_IPACC_CRCX_ACK] = { .T=23005 },
[LCHAN_RTP_ST_WAIT_IPACC_MDCX_ACK] = { .T=23006 },
[LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_CONFIGURED] = { .T=23004 },
};
/* Transition to a state, using the T timer defined in lchan_rtp_fsm_timeouts.
* The actual timeout value is in turn obtained from network->T_defs.
* Assumes local variable fi exists. */
#define lchan_rtp_fsm_state_chg(state) \
fsm_inst_state_chg_T(fi, state, \
lchan_rtp_fsm_timeouts, \
((struct gsm_lchan*)(fi->priv))->ts->trx->bts->network->T_defs, \
5)
/* Set a failure message, trigger the common actions to take on failure, transition to a state to
* continue with (using state timeouts from lchan_rtp_fsm_timeouts[]). Assumes local variable fi exists. */
#define lchan_rtp_fail(fmt, args...) do { \
struct gsm_lchan *_lchan = fi->priv; \
uint32_t state_was = fi->state; \
lchan_set_last_error(_lchan, "lchan-rtp failure in state %s: " fmt, \
osmo_fsm_state_name(fi->fsm, state_was), ## args); \
osmo_fsm_inst_dispatch(_lchan->fi, LCHAN_EV_RTP_ERROR, 0); \
} while(0)
/* Called from lchan_fsm_init(), does not need to be visible in lchan_rtp_fsm.h */
void lchan_rtp_fsm_init()
{
OSMO_ASSERT(osmo_fsm_register(&lchan_rtp_fsm) == 0);
}
static void lchan_rtp_fsm_update_id(struct gsm_lchan *lchan)
{
OSMO_ASSERT(lchan->fi);
OSMO_ASSERT(lchan->fi_rtp);
osmo_fsm_inst_update_id_f(lchan->fi_rtp, lchan->fi->id);
}
bool lchan_rtp_established(struct gsm_lchan *lchan)
{
if (!lchan->fi_rtp)
return false;
switch (lchan->fi_rtp->state) {
case LCHAN_RTP_ST_READY:
case LCHAN_RTP_ST_ESTABLISHED:
case LCHAN_RTP_ST_ROLLBACK:
return true;
default:
return false;
}
}
void lchan_rtp_fsm_start(struct gsm_lchan *lchan)
{
struct osmo_fsm_inst *fi;
OSMO_ASSERT(lchan->ts);
OSMO_ASSERT(lchan->ts->fi);
OSMO_ASSERT(lchan->fi);
OSMO_ASSERT(!lchan->fi_rtp);
fi = osmo_fsm_inst_alloc_child(&lchan_rtp_fsm, lchan->fi, LCHAN_EV_RTP_RELEASED);
OSMO_ASSERT(fi);
fi->priv = lchan;
lchan->fi_rtp = fi;
lchan_rtp_fsm_update_id(lchan);
/* Use old lchan only if there is an MGW endpoint present. Otherwise, on ROLLBACK, we might put
* an endpoint "back" to an lchan that never had one to begin with. */
make sure early lchan act failure resets the lchan Fix crash after AMR configuration fails. The crash is due to an assertion that finds a non-NULL conn in the lchan, when re-using an lchan that has failed in AMR configuration earlier on. That is because the AMR config still happens in state UNUSED. DCHAN ERROR lchan(0-0-2-TCH_F_TCH_H_PDCH-0)[0x6120000066a0]{UNUSED}: (type=TCH_F) lchan allocation failed in state UNUSED: Can not generate multirate configuration IE ... DCHAN DEBUG lchan(0-0-2-TCH_F_TCH_H_PDCH-0)[0x6120000066a0]{UNUSED}: (type=TCH_F) After failure handling, already in state UNUSED ... ... DCHAN DEBUG lchan(0-0-2-TCH_F_TCH_H_PDCH-0)[0x6120000066a0]{UNUSED}: Received Event LCHAN_EV_ACTIVATE (lchan_fsm.c:324) Assert failed !lchan->conn ../../../../src/osmo-bsc/src/osmo-bsc/lchan_fsm.c:491 The FSM design idea is that when returning to the UNUSED state, all lchan state is cleared. However, when calling lchan_activate(), a failure may happen still in state UNUSED, so that we don't transition *back* to UNUSED properly. So, first transition out of UNUSED before failures can happen. (Other ways to solve this would be to invoke lchan clearing even if already in UNUSED, but semantically, transitioning first makes more sense.) Upon LCHAN_EV_ACTIVATE, just remember the lchan_activate_info and transition to WAIT_TS_READY, so that on lchan_fail(), we can normally transition back to UNUSED and clear the lchan. Move the initial lchan activation code to lchan_fsm_wait_ts_ready_onenter(). Also, there is a bit of duplication of members of the lchan->activate (lchan state) and the lchan_activate_info (passed to lchan_activate()) structs. The fix for this also removes the dup: Add struct lchan_activate_info as child struct at lchan->activate.info, drop the other lchan->activate members that would dup .info.*. Move struct lchan_activate_info declaration to gsm_data.h. Apply the new '.info' member struct throughout the code. Related: OS#3737 Change-Id: Ide665b10fa3f4583059c55346db8da833959e3cc
2018-12-19 16:19:02 +00:00
if (lchan->activate.info.re_use_mgw_endpoint_from_lchan
&& !lchan->activate.info.re_use_mgw_endpoint_from_lchan->mgw_endpoint_ci_bts)
lchan->activate.info.re_use_mgw_endpoint_from_lchan = NULL;
lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_AVAILABLE);
}
/* While activating an lchan, for example for Handover, we may want to re-use another lchan's MGW
* endpoint CI. If Handover fails half way, the old lchan must keep its MGW endpoint CI, and we must not
* clean it up. Hence keep another lchan's mgw_endpoint_ci_bts out of lchan until all is done. */
struct mgwep_ci *lchan_use_mgw_endpoint_ci_bts(struct gsm_lchan *lchan)
{
if (lchan->mgw_endpoint_ci_bts)
return lchan->mgw_endpoint_ci_bts;
if (lchan_state_is(lchan, LCHAN_ST_ESTABLISHED))
return NULL;
make sure early lchan act failure resets the lchan Fix crash after AMR configuration fails. The crash is due to an assertion that finds a non-NULL conn in the lchan, when re-using an lchan that has failed in AMR configuration earlier on. That is because the AMR config still happens in state UNUSED. DCHAN ERROR lchan(0-0-2-TCH_F_TCH_H_PDCH-0)[0x6120000066a0]{UNUSED}: (type=TCH_F) lchan allocation failed in state UNUSED: Can not generate multirate configuration IE ... DCHAN DEBUG lchan(0-0-2-TCH_F_TCH_H_PDCH-0)[0x6120000066a0]{UNUSED}: (type=TCH_F) After failure handling, already in state UNUSED ... ... DCHAN DEBUG lchan(0-0-2-TCH_F_TCH_H_PDCH-0)[0x6120000066a0]{UNUSED}: Received Event LCHAN_EV_ACTIVATE (lchan_fsm.c:324) Assert failed !lchan->conn ../../../../src/osmo-bsc/src/osmo-bsc/lchan_fsm.c:491 The FSM design idea is that when returning to the UNUSED state, all lchan state is cleared. However, when calling lchan_activate(), a failure may happen still in state UNUSED, so that we don't transition *back* to UNUSED properly. So, first transition out of UNUSED before failures can happen. (Other ways to solve this would be to invoke lchan clearing even if already in UNUSED, but semantically, transitioning first makes more sense.) Upon LCHAN_EV_ACTIVATE, just remember the lchan_activate_info and transition to WAIT_TS_READY, so that on lchan_fail(), we can normally transition back to UNUSED and clear the lchan. Move the initial lchan activation code to lchan_fsm_wait_ts_ready_onenter(). Also, there is a bit of duplication of members of the lchan->activate (lchan state) and the lchan_activate_info (passed to lchan_activate()) structs. The fix for this also removes the dup: Add struct lchan_activate_info as child struct at lchan->activate.info, drop the other lchan->activate members that would dup .info.*. Move struct lchan_activate_info declaration to gsm_data.h. Apply the new '.info' member struct throughout the code. Related: OS#3737 Change-Id: Ide665b10fa3f4583059c55346db8da833959e3cc
2018-12-19 16:19:02 +00:00
if (lchan->activate.info.re_use_mgw_endpoint_from_lchan)
return lchan->activate.info.re_use_mgw_endpoint_from_lchan->mgw_endpoint_ci_bts;
return NULL;
}
static void lchan_rtp_fsm_wait_mgw_endpoint_available_onenter(struct osmo_fsm_inst *fi,
uint32_t prev_state)
{
struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);
struct mgw_endpoint *mgwep;
struct mgwep_ci *use_mgwep_ci = lchan_use_mgw_endpoint_ci_bts(lchan);
struct mgcp_conn_peer crcx_info = {};
if (use_mgwep_ci) {
LOG_LCHAN_RTP(lchan, LOGL_DEBUG, "MGW endpoint already available: %s\n",
mgwep_ci_name(use_mgwep_ci));
lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_WAIT_LCHAN_READY);
return;
}
make sure early lchan act failure resets the lchan Fix crash after AMR configuration fails. The crash is due to an assertion that finds a non-NULL conn in the lchan, when re-using an lchan that has failed in AMR configuration earlier on. That is because the AMR config still happens in state UNUSED. DCHAN ERROR lchan(0-0-2-TCH_F_TCH_H_PDCH-0)[0x6120000066a0]{UNUSED}: (type=TCH_F) lchan allocation failed in state UNUSED: Can not generate multirate configuration IE ... DCHAN DEBUG lchan(0-0-2-TCH_F_TCH_H_PDCH-0)[0x6120000066a0]{UNUSED}: (type=TCH_F) After failure handling, already in state UNUSED ... ... DCHAN DEBUG lchan(0-0-2-TCH_F_TCH_H_PDCH-0)[0x6120000066a0]{UNUSED}: Received Event LCHAN_EV_ACTIVATE (lchan_fsm.c:324) Assert failed !lchan->conn ../../../../src/osmo-bsc/src/osmo-bsc/lchan_fsm.c:491 The FSM design idea is that when returning to the UNUSED state, all lchan state is cleared. However, when calling lchan_activate(), a failure may happen still in state UNUSED, so that we don't transition *back* to UNUSED properly. So, first transition out of UNUSED before failures can happen. (Other ways to solve this would be to invoke lchan clearing even if already in UNUSED, but semantically, transitioning first makes more sense.) Upon LCHAN_EV_ACTIVATE, just remember the lchan_activate_info and transition to WAIT_TS_READY, so that on lchan_fail(), we can normally transition back to UNUSED and clear the lchan. Move the initial lchan activation code to lchan_fsm_wait_ts_ready_onenter(). Also, there is a bit of duplication of members of the lchan->activate (lchan state) and the lchan_activate_info (passed to lchan_activate()) structs. The fix for this also removes the dup: Add struct lchan_activate_info as child struct at lchan->activate.info, drop the other lchan->activate members that would dup .info.*. Move struct lchan_activate_info declaration to gsm_data.h. Apply the new '.info' member struct throughout the code. Related: OS#3737 Change-Id: Ide665b10fa3f4583059c55346db8da833959e3cc
2018-12-19 16:19:02 +00:00
mgwep = gscon_ensure_mgw_endpoint(lchan->conn, lchan->activate.info.msc_assigned_cic);
if (!mgwep) {
lchan_rtp_fail("Internal error: cannot obtain MGW endpoint handle for conn");
return;
}
lchan->mgw_endpoint_ci_bts = mgw_endpoint_ci_add(mgwep, "to-BTS");
if (lchan->conn) {
crcx_info.call_id = lchan->conn->sccp.conn_id;
if (lchan->conn->sccp.msc)
crcx_info.x_osmo_ign = lchan->conn->sccp.msc->x_osmo_ign;
}
crcx_info.ptime = 20;
mgcp_pick_codec(&crcx_info, lchan, true);
mgw_endpoint_ci_request(lchan->mgw_endpoint_ci_bts, MGCP_VERB_CRCX, &crcx_info,
fi, LCHAN_RTP_EV_MGW_ENDPOINT_AVAILABLE, LCHAN_RTP_EV_MGW_ENDPOINT_ERROR,
0);
}
static void lchan_rtp_fsm_wait_mgw_endpoint_available(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);
switch (event) {
case LCHAN_RTP_EV_MGW_ENDPOINT_AVAILABLE:
LOG_LCHAN_RTP(lchan, LOGL_DEBUG, "MGW endpoint: %s\n",
mgwep_ci_name(lchan_use_mgw_endpoint_ci_bts(lchan)));
lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_WAIT_LCHAN_READY);
return;
case LCHAN_RTP_EV_LCHAN_READY:
/* will notice lchan->activate.activ_ack == true in
* lchan_rtp_fsm_wait_lchan_ready_onenter() */
return;
case LCHAN_RTP_EV_MGW_ENDPOINT_ERROR:
lchan_rtp_fail("Failure to create MGW endpoint");
return;
case LCHAN_RTP_EV_ROLLBACK:
case LCHAN_RTP_EV_RELEASE:
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REQUEST, 0);
return;
default:
OSMO_ASSERT(false);
}
}
static void lchan_rtp_fsm_post_lchan_ready(struct osmo_fsm_inst *fi);
static void lchan_rtp_fsm_wait_lchan_ready_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);
if (lchan->activate.activ_ack) {
LOG_LCHAN_RTP(lchan, LOGL_DEBUG, "Activ Ack received earlier, no need to wait\n");
lchan_rtp_fsm_post_lchan_ready(fi);
}
}
static void lchan_rtp_fsm_wait_lchan_ready(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
switch (event) {
case LCHAN_RTP_EV_LCHAN_READY:
lchan_rtp_fsm_post_lchan_ready(fi);
return;
case LCHAN_RTP_EV_ROLLBACK:
case LCHAN_RTP_EV_RELEASE:
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REQUEST, 0);
return;
default:
OSMO_ASSERT(false);
}
}
static void lchan_rtp_fsm_switch_rtp(struct osmo_fsm_inst *fi)
{
struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);
make sure early lchan act failure resets the lchan Fix crash after AMR configuration fails. The crash is due to an assertion that finds a non-NULL conn in the lchan, when re-using an lchan that has failed in AMR configuration earlier on. That is because the AMR config still happens in state UNUSED. DCHAN ERROR lchan(0-0-2-TCH_F_TCH_H_PDCH-0)[0x6120000066a0]{UNUSED}: (type=TCH_F) lchan allocation failed in state UNUSED: Can not generate multirate configuration IE ... DCHAN DEBUG lchan(0-0-2-TCH_F_TCH_H_PDCH-0)[0x6120000066a0]{UNUSED}: (type=TCH_F) After failure handling, already in state UNUSED ... ... DCHAN DEBUG lchan(0-0-2-TCH_F_TCH_H_PDCH-0)[0x6120000066a0]{UNUSED}: Received Event LCHAN_EV_ACTIVATE (lchan_fsm.c:324) Assert failed !lchan->conn ../../../../src/osmo-bsc/src/osmo-bsc/lchan_fsm.c:491 The FSM design idea is that when returning to the UNUSED state, all lchan state is cleared. However, when calling lchan_activate(), a failure may happen still in state UNUSED, so that we don't transition *back* to UNUSED properly. So, first transition out of UNUSED before failures can happen. (Other ways to solve this would be to invoke lchan clearing even if already in UNUSED, but semantically, transitioning first makes more sense.) Upon LCHAN_EV_ACTIVATE, just remember the lchan_activate_info and transition to WAIT_TS_READY, so that on lchan_fail(), we can normally transition back to UNUSED and clear the lchan. Move the initial lchan activation code to lchan_fsm_wait_ts_ready_onenter(). Also, there is a bit of duplication of members of the lchan->activate (lchan state) and the lchan_activate_info (passed to lchan_activate()) structs. The fix for this also removes the dup: Add struct lchan_activate_info as child struct at lchan->activate.info, drop the other lchan->activate members that would dup .info.*. Move struct lchan_activate_info declaration to gsm_data.h. Apply the new '.info' member struct throughout the code. Related: OS#3737 Change-Id: Ide665b10fa3f4583059c55346db8da833959e3cc
2018-12-19 16:19:02 +00:00
if (lchan->activate.info.wait_before_switching_rtp) {
LOG_LCHAN_RTP(lchan, LOGL_DEBUG,
"Waiting for an event by caller before switching RTP\n");
lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_WAIT_READY_TO_SWITCH_RTP);
} else
lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_CONFIGURED);
}
static void lchan_rtp_fsm_post_lchan_ready(struct osmo_fsm_inst *fi)
{
struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);
if (is_ipaccess_bts(lchan->ts->trx->bts))
lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_WAIT_IPACC_CRCX_ACK);
else
lchan_rtp_fsm_switch_rtp(fi);
}
static void lchan_rtp_fsm_wait_ipacc_crcx_ack_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
int rc;
int val;
struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);
if (lchan->release.requested) {
lchan_rtp_fail("Release requested while activating");
return;
}
val = ipacc_speech_mode(lchan->tch_mode, lchan->type);
if (val < 0) {
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),
gsm_lchant_name(lchan->type));
return;
}
lchan->abis_ip.speech_mode = val;
val = ipacc_payload_type(lchan->tch_mode, lchan->type);
if (val < 0) {
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),
gsm_lchant_name(lchan->type));
return;
}
lchan->abis_ip.rtp_payload = val;
/* recv-only */
ipacc_speech_mode_set_direction(&lchan->abis_ip.speech_mode, false);
rc = rsl_tx_ipacc_crcx(lchan);
if (rc)
lchan_rtp_fail("Failure to transmit IPACC CRCX to BTS (rc=%d, %s)",
rc, strerror(-rc));
}
static void lchan_rtp_fsm_wait_ipacc_crcx_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);
switch (event) {
case LCHAN_RTP_EV_IPACC_CRCX_ACK:
/* the CRCX ACK parsing has already noted the RTP port information at
* lchan->abis_ip.bound_*, see ipac_parse_rtp(). We'll use that in
* lchan_rtp_fsm_wait_mgw_endpoint_configured_onenter(). */
lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_WAIT_IPACC_MDCX_ACK);
return;
case LCHAN_RTP_EV_IPACC_CRCX_NACK:
lchan_rtp_fail("Received NACK on IPACC CRCX");
return;
case LCHAN_RTP_EV_READY_TO_SWITCH_RTP:
make sure early lchan act failure resets the lchan Fix crash after AMR configuration fails. The crash is due to an assertion that finds a non-NULL conn in the lchan, when re-using an lchan that has failed in AMR configuration earlier on. That is because the AMR config still happens in state UNUSED. DCHAN ERROR lchan(0-0-2-TCH_F_TCH_H_PDCH-0)[0x6120000066a0]{UNUSED}: (type=TCH_F) lchan allocation failed in state UNUSED: Can not generate multirate configuration IE ... DCHAN DEBUG lchan(0-0-2-TCH_F_TCH_H_PDCH-0)[0x6120000066a0]{UNUSED}: (type=TCH_F) After failure handling, already in state UNUSED ... ... DCHAN DEBUG lchan(0-0-2-TCH_F_TCH_H_PDCH-0)[0x6120000066a0]{UNUSED}: Received Event LCHAN_EV_ACTIVATE (lchan_fsm.c:324) Assert failed !lchan->conn ../../../../src/osmo-bsc/src/osmo-bsc/lchan_fsm.c:491 The FSM design idea is that when returning to the UNUSED state, all lchan state is cleared. However, when calling lchan_activate(), a failure may happen still in state UNUSED, so that we don't transition *back* to UNUSED properly. So, first transition out of UNUSED before failures can happen. (Other ways to solve this would be to invoke lchan clearing even if already in UNUSED, but semantically, transitioning first makes more sense.) Upon LCHAN_EV_ACTIVATE, just remember the lchan_activate_info and transition to WAIT_TS_READY, so that on lchan_fail(), we can normally transition back to UNUSED and clear the lchan. Move the initial lchan activation code to lchan_fsm_wait_ts_ready_onenter(). Also, there is a bit of duplication of members of the lchan->activate (lchan state) and the lchan_activate_info (passed to lchan_activate()) structs. The fix for this also removes the dup: Add struct lchan_activate_info as child struct at lchan->activate.info, drop the other lchan->activate members that would dup .info.*. Move struct lchan_activate_info declaration to gsm_data.h. Apply the new '.info' member struct throughout the code. Related: OS#3737 Change-Id: Ide665b10fa3f4583059c55346db8da833959e3cc
2018-12-19 16:19:02 +00:00
lchan->activate.info.wait_before_switching_rtp = false;
return;
case LCHAN_RTP_EV_RELEASE:
case LCHAN_RTP_EV_ROLLBACK:
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REQUEST, 0);
return;
default:
OSMO_ASSERT(false);
}
}
static void lchan_rtp_fsm_wait_ipacc_mdcx_ack_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
int rc;
struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);
const struct mgcp_conn_peer *mgw_rtp;
if (lchan->release.requested) {
lchan_rtp_fail("Release requested while activating");
return;
}
mgw_rtp = mgwep_ci_get_rtp_info(lchan_use_mgw_endpoint_ci_bts(lchan));
if (!mgw_rtp) {
lchan_rtp_fail("Cannot send IPACC MDCX to BTS:"
" there is no RTP IP+port set that the BTS should send RTP to.");
return;
}
/* Other RTP settings were already setup in lchan_rtp_fsm_wait_ipacc_crcx_ack_onenter() */
lchan->abis_ip.connect_ip = ntohl(inet_addr(mgw_rtp->addr));
lchan->abis_ip.connect_port = mgw_rtp->port;
/* send-recv */
ipacc_speech_mode_set_direction(&lchan->abis_ip.speech_mode, true);
rc = rsl_tx_ipacc_mdcx(lchan);
if (rc)
lchan_rtp_fail("Failure to transmit IPACC MDCX to BTS (rc=%d, %s)",
rc, strerror(-rc));
}
static void lchan_rtp_fsm_wait_ipacc_mdcx_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);
switch (event) {
case LCHAN_RTP_EV_IPACC_MDCX_ACK:
lchan_rtp_fsm_switch_rtp(fi);
return;
case LCHAN_RTP_EV_IPACC_MDCX_NACK:
lchan_rtp_fail("Received NACK on IPACC MDCX");
return;
case LCHAN_RTP_EV_READY_TO_SWITCH_RTP:
make sure early lchan act failure resets the lchan Fix crash after AMR configuration fails. The crash is due to an assertion that finds a non-NULL conn in the lchan, when re-using an lchan that has failed in AMR configuration earlier on. That is because the AMR config still happens in state UNUSED. DCHAN ERROR lchan(0-0-2-TCH_F_TCH_H_PDCH-0)[0x6120000066a0]{UNUSED}: (type=TCH_F) lchan allocation failed in state UNUSED: Can not generate multirate configuration IE ... DCHAN DEBUG lchan(0-0-2-TCH_F_TCH_H_PDCH-0)[0x6120000066a0]{UNUSED}: (type=TCH_F) After failure handling, already in state UNUSED ... ... DCHAN DEBUG lchan(0-0-2-TCH_F_TCH_H_PDCH-0)[0x6120000066a0]{UNUSED}: Received Event LCHAN_EV_ACTIVATE (lchan_fsm.c:324) Assert failed !lchan->conn ../../../../src/osmo-bsc/src/osmo-bsc/lchan_fsm.c:491 The FSM design idea is that when returning to the UNUSED state, all lchan state is cleared. However, when calling lchan_activate(), a failure may happen still in state UNUSED, so that we don't transition *back* to UNUSED properly. So, first transition out of UNUSED before failures can happen. (Other ways to solve this would be to invoke lchan clearing even if already in UNUSED, but semantically, transitioning first makes more sense.) Upon LCHAN_EV_ACTIVATE, just remember the lchan_activate_info and transition to WAIT_TS_READY, so that on lchan_fail(), we can normally transition back to UNUSED and clear the lchan. Move the initial lchan activation code to lchan_fsm_wait_ts_ready_onenter(). Also, there is a bit of duplication of members of the lchan->activate (lchan state) and the lchan_activate_info (passed to lchan_activate()) structs. The fix for this also removes the dup: Add struct lchan_activate_info as child struct at lchan->activate.info, drop the other lchan->activate members that would dup .info.*. Move struct lchan_activate_info declaration to gsm_data.h. Apply the new '.info' member struct throughout the code. Related: OS#3737 Change-Id: Ide665b10fa3f4583059c55346db8da833959e3cc
2018-12-19 16:19:02 +00:00
lchan->activate.info.wait_before_switching_rtp = false;
return;
case LCHAN_RTP_EV_RELEASE:
case LCHAN_RTP_EV_ROLLBACK:
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REQUEST, 0);
return;
default:
OSMO_ASSERT(false);
}
}
static void lchan_rtp_fsm_wait_ready_to_switch_rtp(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
switch (event) {
case LCHAN_RTP_EV_READY_TO_SWITCH_RTP:
lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_CONFIGURED);
return;
case LCHAN_RTP_EV_RELEASE:
case LCHAN_RTP_EV_ROLLBACK:
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REQUEST, 0);
return;
default:
OSMO_ASSERT(false);
}
}
static void connect_mgw_endpoint_to_lchan(struct osmo_fsm_inst *fi,
struct mgwep_ci *ci,
struct gsm_lchan *to_lchan)
{
int rc;
struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);
struct mgcp_conn_peer mdcx_info;
struct in_addr addr;
const char *addr_str;
mdcx_info = (struct mgcp_conn_peer){
.port = to_lchan->abis_ip.bound_port,
.ptime = 20,
};
mgcp_pick_codec(&mdcx_info, to_lchan, true);
addr.s_addr = ntohl(to_lchan->abis_ip.bound_ip);
addr_str = inet_ntoa(addr);
rc = osmo_strlcpy(mdcx_info.addr, addr_str, sizeof(mdcx_info.addr));
if (rc <= 0 || rc >= sizeof(mdcx_info.addr)) {
lchan_rtp_fail("Cannot compose BTS side RTP IP address to send to MGW: '%s'",
addr_str);
return;
}
if (!ci) {
lchan_rtp_fail("No MGW endpoint ci configured");
return;
}
LOG_LCHAN_RTP(lchan, LOGL_DEBUG, "Sending BTS side RTP port info %s:%u to MGW %s\n",
mdcx_info.addr, mdcx_info.port, mgwep_ci_name(ci));
mgw_endpoint_ci_request(ci, MGCP_VERB_MDCX, &mdcx_info,
fi, LCHAN_RTP_EV_MGW_ENDPOINT_CONFIGURED,
LCHAN_RTP_EV_MGW_ENDPOINT_ERROR, 0);
}
static void lchan_rtp_fsm_wait_mgw_endpoint_configured_onenter(struct osmo_fsm_inst *fi,
uint32_t prev_state)
{
struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);
make sure early lchan act failure resets the lchan Fix crash after AMR configuration fails. The crash is due to an assertion that finds a non-NULL conn in the lchan, when re-using an lchan that has failed in AMR configuration earlier on. That is because the AMR config still happens in state UNUSED. DCHAN ERROR lchan(0-0-2-TCH_F_TCH_H_PDCH-0)[0x6120000066a0]{UNUSED}: (type=TCH_F) lchan allocation failed in state UNUSED: Can not generate multirate configuration IE ... DCHAN DEBUG lchan(0-0-2-TCH_F_TCH_H_PDCH-0)[0x6120000066a0]{UNUSED}: (type=TCH_F) After failure handling, already in state UNUSED ... ... DCHAN DEBUG lchan(0-0-2-TCH_F_TCH_H_PDCH-0)[0x6120000066a0]{UNUSED}: Received Event LCHAN_EV_ACTIVATE (lchan_fsm.c:324) Assert failed !lchan->conn ../../../../src/osmo-bsc/src/osmo-bsc/lchan_fsm.c:491 The FSM design idea is that when returning to the UNUSED state, all lchan state is cleared. However, when calling lchan_activate(), a failure may happen still in state UNUSED, so that we don't transition *back* to UNUSED properly. So, first transition out of UNUSED before failures can happen. (Other ways to solve this would be to invoke lchan clearing even if already in UNUSED, but semantically, transitioning first makes more sense.) Upon LCHAN_EV_ACTIVATE, just remember the lchan_activate_info and transition to WAIT_TS_READY, so that on lchan_fail(), we can normally transition back to UNUSED and clear the lchan. Move the initial lchan activation code to lchan_fsm_wait_ts_ready_onenter(). Also, there is a bit of duplication of members of the lchan->activate (lchan state) and the lchan_activate_info (passed to lchan_activate()) structs. The fix for this also removes the dup: Add struct lchan_activate_info as child struct at lchan->activate.info, drop the other lchan->activate members that would dup .info.*. Move struct lchan_activate_info declaration to gsm_data.h. Apply the new '.info' member struct throughout the code. Related: OS#3737 Change-Id: Ide665b10fa3f4583059c55346db8da833959e3cc
2018-12-19 16:19:02 +00:00
struct gsm_lchan *old_lchan = lchan->activate.info.re_use_mgw_endpoint_from_lchan;
if (lchan->release.requested) {
lchan_rtp_fail("Release requested while activating");
return;
}
/* At this point, we are taking over an old lchan's MGW endpoint (if any). */
if (!lchan->mgw_endpoint_ci_bts && old_lchan) {
/* The old lchan shall forget the enpoint now. We might put it back upon ROLLBACK */
lchan->mgw_endpoint_ci_bts = old_lchan->mgw_endpoint_ci_bts;
old_lchan->mgw_endpoint_ci_bts = NULL;
}
if (!lchan->mgw_endpoint_ci_bts) {
lchan_rtp_fail("No MGW endpoint ci configured");
return;
}
connect_mgw_endpoint_to_lchan(fi, lchan->mgw_endpoint_ci_bts, lchan);
}
static void lchan_rtp_fsm_wait_mgw_endpoint_configured(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
switch (event) {
case LCHAN_RTP_EV_MGW_ENDPOINT_CONFIGURED:
lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_READY);
return;
case LCHAN_RTP_EV_MGW_ENDPOINT_ERROR:
lchan_rtp_fail("Error while redirecting the MGW to the lchan's RTP port");
return;
case LCHAN_RTP_EV_ROLLBACK:
lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_ROLLBACK);
return;
case LCHAN_RTP_EV_RELEASE:
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, 0);
return;
default:
OSMO_ASSERT(false);
}
}
static void lchan_rtp_fsm_ready_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);
osmo_fsm_inst_dispatch(lchan->fi, LCHAN_EV_RTP_READY, 0);
}
static void lchan_rtp_fsm_ready(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
switch (event) {
case LCHAN_RTP_EV_ESTABLISHED:
lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_ESTABLISHED);
return;
case LCHAN_RTP_EV_RELEASE:
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REQUEST, 0);
return;
case LCHAN_RTP_EV_ROLLBACK:
lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_ROLLBACK);
return;
default:
OSMO_ASSERT(false);
}
}
static void lchan_rtp_fsm_rollback_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);
make sure early lchan act failure resets the lchan Fix crash after AMR configuration fails. The crash is due to an assertion that finds a non-NULL conn in the lchan, when re-using an lchan that has failed in AMR configuration earlier on. That is because the AMR config still happens in state UNUSED. DCHAN ERROR lchan(0-0-2-TCH_F_TCH_H_PDCH-0)[0x6120000066a0]{UNUSED}: (type=TCH_F) lchan allocation failed in state UNUSED: Can not generate multirate configuration IE ... DCHAN DEBUG lchan(0-0-2-TCH_F_TCH_H_PDCH-0)[0x6120000066a0]{UNUSED}: (type=TCH_F) After failure handling, already in state UNUSED ... ... DCHAN DEBUG lchan(0-0-2-TCH_F_TCH_H_PDCH-0)[0x6120000066a0]{UNUSED}: Received Event LCHAN_EV_ACTIVATE (lchan_fsm.c:324) Assert failed !lchan->conn ../../../../src/osmo-bsc/src/osmo-bsc/lchan_fsm.c:491 The FSM design idea is that when returning to the UNUSED state, all lchan state is cleared. However, when calling lchan_activate(), a failure may happen still in state UNUSED, so that we don't transition *back* to UNUSED properly. So, first transition out of UNUSED before failures can happen. (Other ways to solve this would be to invoke lchan clearing even if already in UNUSED, but semantically, transitioning first makes more sense.) Upon LCHAN_EV_ACTIVATE, just remember the lchan_activate_info and transition to WAIT_TS_READY, so that on lchan_fail(), we can normally transition back to UNUSED and clear the lchan. Move the initial lchan activation code to lchan_fsm_wait_ts_ready_onenter(). Also, there is a bit of duplication of members of the lchan->activate (lchan state) and the lchan_activate_info (passed to lchan_activate()) structs. The fix for this also removes the dup: Add struct lchan_activate_info as child struct at lchan->activate.info, drop the other lchan->activate members that would dup .info.*. Move struct lchan_activate_info declaration to gsm_data.h. Apply the new '.info' member struct throughout the code. Related: OS#3737 Change-Id: Ide665b10fa3f4583059c55346db8da833959e3cc
2018-12-19 16:19:02 +00:00
struct gsm_lchan *old_lchan = lchan->activate.info.re_use_mgw_endpoint_from_lchan;
if (!lchan->mgw_endpoint_ci_bts || !old_lchan) {
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REQUEST, 0);
return;
}
connect_mgw_endpoint_to_lchan(fi, lchan->mgw_endpoint_ci_bts, old_lchan);
}
static void lchan_rtp_fsm_rollback(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);
make sure early lchan act failure resets the lchan Fix crash after AMR configuration fails. The crash is due to an assertion that finds a non-NULL conn in the lchan, when re-using an lchan that has failed in AMR configuration earlier on. That is because the AMR config still happens in state UNUSED. DCHAN ERROR lchan(0-0-2-TCH_F_TCH_H_PDCH-0)[0x6120000066a0]{UNUSED}: (type=TCH_F) lchan allocation failed in state UNUSED: Can not generate multirate configuration IE ... DCHAN DEBUG lchan(0-0-2-TCH_F_TCH_H_PDCH-0)[0x6120000066a0]{UNUSED}: (type=TCH_F) After failure handling, already in state UNUSED ... ... DCHAN DEBUG lchan(0-0-2-TCH_F_TCH_H_PDCH-0)[0x6120000066a0]{UNUSED}: Received Event LCHAN_EV_ACTIVATE (lchan_fsm.c:324) Assert failed !lchan->conn ../../../../src/osmo-bsc/src/osmo-bsc/lchan_fsm.c:491 The FSM design idea is that when returning to the UNUSED state, all lchan state is cleared. However, when calling lchan_activate(), a failure may happen still in state UNUSED, so that we don't transition *back* to UNUSED properly. So, first transition out of UNUSED before failures can happen. (Other ways to solve this would be to invoke lchan clearing even if already in UNUSED, but semantically, transitioning first makes more sense.) Upon LCHAN_EV_ACTIVATE, just remember the lchan_activate_info and transition to WAIT_TS_READY, so that on lchan_fail(), we can normally transition back to UNUSED and clear the lchan. Move the initial lchan activation code to lchan_fsm_wait_ts_ready_onenter(). Also, there is a bit of duplication of members of the lchan->activate (lchan state) and the lchan_activate_info (passed to lchan_activate()) structs. The fix for this also removes the dup: Add struct lchan_activate_info as child struct at lchan->activate.info, drop the other lchan->activate members that would dup .info.*. Move struct lchan_activate_info declaration to gsm_data.h. Apply the new '.info' member struct throughout the code. Related: OS#3737 Change-Id: Ide665b10fa3f4583059c55346db8da833959e3cc
2018-12-19 16:19:02 +00:00
struct gsm_lchan *old_lchan = lchan->activate.info.re_use_mgw_endpoint_from_lchan;
switch (event) {
case LCHAN_RTP_EV_MGW_ENDPOINT_CONFIGURED:
old_lchan->mgw_endpoint_ci_bts = lchan->mgw_endpoint_ci_bts;
lchan->mgw_endpoint_ci_bts = NULL;
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, 0);
return;
case LCHAN_RTP_EV_MGW_ENDPOINT_ERROR:
LOG_LCHAN_RTP(lchan, LOGL_ERROR,
"Error while connecting the MGW back to the old lchan's RTP port:"
" %s %s\n",
mgwep_ci_name(lchan->mgw_endpoint_ci_bts),
gsm_lchan_name(old_lchan));
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, 0);
return;
case LCHAN_RTP_EV_RELEASE:
case LCHAN_RTP_EV_ROLLBACK:
/* Already rolling back, ignore. */
return;
default:
OSMO_ASSERT(false);
}
}
static void lchan_rtp_fsm_established_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);
/* Make sure that we will not hand back the MGW endpoint to any old lchan from here on. */
make sure early lchan act failure resets the lchan Fix crash after AMR configuration fails. The crash is due to an assertion that finds a non-NULL conn in the lchan, when re-using an lchan that has failed in AMR configuration earlier on. That is because the AMR config still happens in state UNUSED. DCHAN ERROR lchan(0-0-2-TCH_F_TCH_H_PDCH-0)[0x6120000066a0]{UNUSED}: (type=TCH_F) lchan allocation failed in state UNUSED: Can not generate multirate configuration IE ... DCHAN DEBUG lchan(0-0-2-TCH_F_TCH_H_PDCH-0)[0x6120000066a0]{UNUSED}: (type=TCH_F) After failure handling, already in state UNUSED ... ... DCHAN DEBUG lchan(0-0-2-TCH_F_TCH_H_PDCH-0)[0x6120000066a0]{UNUSED}: Received Event LCHAN_EV_ACTIVATE (lchan_fsm.c:324) Assert failed !lchan->conn ../../../../src/osmo-bsc/src/osmo-bsc/lchan_fsm.c:491 The FSM design idea is that when returning to the UNUSED state, all lchan state is cleared. However, when calling lchan_activate(), a failure may happen still in state UNUSED, so that we don't transition *back* to UNUSED properly. So, first transition out of UNUSED before failures can happen. (Other ways to solve this would be to invoke lchan clearing even if already in UNUSED, but semantically, transitioning first makes more sense.) Upon LCHAN_EV_ACTIVATE, just remember the lchan_activate_info and transition to WAIT_TS_READY, so that on lchan_fail(), we can normally transition back to UNUSED and clear the lchan. Move the initial lchan activation code to lchan_fsm_wait_ts_ready_onenter(). Also, there is a bit of duplication of members of the lchan->activate (lchan state) and the lchan_activate_info (passed to lchan_activate()) structs. The fix for this also removes the dup: Add struct lchan_activate_info as child struct at lchan->activate.info, drop the other lchan->activate members that would dup .info.*. Move struct lchan_activate_info declaration to gsm_data.h. Apply the new '.info' member struct throughout the code. Related: OS#3737 Change-Id: Ide665b10fa3f4583059c55346db8da833959e3cc
2018-12-19 16:19:02 +00:00
lchan->activate.info.re_use_mgw_endpoint_from_lchan = NULL;
}
static void lchan_rtp_fsm_established(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);
switch (event) {
case LCHAN_RTP_EV_RELEASE:
case LCHAN_RTP_EV_ROLLBACK:
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, 0);
return;
case LCHAN_RTP_EV_IPACC_MDCX_ACK:
LOG_LCHAN_RTP(lchan, LOGL_NOTICE,
"Received MDCX ACK on established lchan's RTP port: %s\n",
mgwep_ci_name(lchan->mgw_endpoint_ci_bts));
return;
default:
OSMO_ASSERT(false);
}
}
#define S(x) (1 << (x))
static const struct osmo_fsm_state lchan_rtp_fsm_states[] = {
[LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_AVAILABLE] = {
.name = "WAIT_MGW_ENDPOINT_AVAILABLE",
.onenter = lchan_rtp_fsm_wait_mgw_endpoint_available_onenter,
.action = lchan_rtp_fsm_wait_mgw_endpoint_available,
.in_event_mask = 0
| S(LCHAN_RTP_EV_MGW_ENDPOINT_AVAILABLE)
| S(LCHAN_RTP_EV_MGW_ENDPOINT_ERROR)
| S(LCHAN_RTP_EV_LCHAN_READY)
| S(LCHAN_RTP_EV_RELEASE)
| S(LCHAN_RTP_EV_ROLLBACK)
,
.out_state_mask = 0
| S(LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_AVAILABLE) /* for init */
| S(LCHAN_RTP_ST_WAIT_LCHAN_READY)
,
},
[LCHAN_RTP_ST_WAIT_LCHAN_READY] = {
.name = "WAIT_LCHAN_READY",
.onenter = lchan_rtp_fsm_wait_lchan_ready_onenter,
.action = lchan_rtp_fsm_wait_lchan_ready,
.in_event_mask = 0
| S(LCHAN_RTP_EV_LCHAN_READY)
| S(LCHAN_RTP_EV_RELEASE)
| S(LCHAN_RTP_EV_ROLLBACK)
,
.out_state_mask = 0
| S(LCHAN_RTP_ST_WAIT_IPACC_CRCX_ACK)
| S(LCHAN_RTP_ST_WAIT_READY_TO_SWITCH_RTP)
| S(LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_CONFIGURED)
,
},
[LCHAN_RTP_ST_WAIT_IPACC_CRCX_ACK] = {
.name = "WAIT_IPACC_CRCX_ACK",
.onenter = lchan_rtp_fsm_wait_ipacc_crcx_ack_onenter,
.action = lchan_rtp_fsm_wait_ipacc_crcx_ack,
.in_event_mask = 0
| S(LCHAN_RTP_EV_READY_TO_SWITCH_RTP)
| S(LCHAN_RTP_EV_IPACC_CRCX_ACK)
| S(LCHAN_RTP_EV_IPACC_CRCX_NACK)
| S(LCHAN_RTP_EV_RELEASE)
| S(LCHAN_RTP_EV_ROLLBACK)
,
.out_state_mask = 0
| S(LCHAN_RTP_ST_WAIT_IPACC_MDCX_ACK)
,
},
[LCHAN_RTP_ST_WAIT_IPACC_MDCX_ACK] = {
.name = "WAIT_IPACC_MDCX_ACK",
.onenter = lchan_rtp_fsm_wait_ipacc_mdcx_ack_onenter,
.action = lchan_rtp_fsm_wait_ipacc_mdcx_ack,
.in_event_mask = 0
| S(LCHAN_RTP_EV_READY_TO_SWITCH_RTP)
| S(LCHAN_RTP_EV_IPACC_MDCX_ACK)
| S(LCHAN_RTP_EV_IPACC_MDCX_NACK)
| S(LCHAN_RTP_EV_RELEASE)
| S(LCHAN_RTP_EV_ROLLBACK)
,
.out_state_mask = 0
| S(LCHAN_RTP_ST_WAIT_READY_TO_SWITCH_RTP)
| S(LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_CONFIGURED)
,
},
[LCHAN_RTP_ST_WAIT_READY_TO_SWITCH_RTP] = {
.name = "WAIT_READY_TO_SWITCH_RTP",
.action = lchan_rtp_fsm_wait_ready_to_switch_rtp,
.in_event_mask = 0
| S(LCHAN_RTP_EV_READY_TO_SWITCH_RTP)
| S(LCHAN_RTP_EV_RELEASE)
| S(LCHAN_RTP_EV_ROLLBACK)
,
.out_state_mask = 0
| S(LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_CONFIGURED)
,
},
[LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_CONFIGURED] = {
.name = "WAIT_MGW_ENDPOINT_CONFIGURED",
.onenter = lchan_rtp_fsm_wait_mgw_endpoint_configured_onenter,
.action = lchan_rtp_fsm_wait_mgw_endpoint_configured,
.in_event_mask = 0
| S(LCHAN_RTP_EV_MGW_ENDPOINT_CONFIGURED)
| S(LCHAN_RTP_EV_MGW_ENDPOINT_ERROR)
| S(LCHAN_RTP_EV_RELEASE)
| S(LCHAN_RTP_EV_ROLLBACK)
,
.out_state_mask = 0
| S(LCHAN_RTP_ST_READY)
,
},
[LCHAN_RTP_ST_READY] = {
.name = "READY",
.onenter = lchan_rtp_fsm_ready_onenter,
.action = lchan_rtp_fsm_ready,
.in_event_mask = 0
| S(LCHAN_RTP_EV_ESTABLISHED)
| S(LCHAN_RTP_EV_RELEASE)
| S(LCHAN_RTP_EV_ROLLBACK)
,
.out_state_mask = 0
| S(LCHAN_RTP_ST_ESTABLISHED)
| S(LCHAN_RTP_ST_ROLLBACK)
,
},
[LCHAN_RTP_ST_ESTABLISHED] = {
.name = "ESTABLISHED",
.onenter = lchan_rtp_fsm_established_onenter,
.action = lchan_rtp_fsm_established,
.in_event_mask = 0
| S(LCHAN_RTP_EV_RELEASE)
| S(LCHAN_RTP_EV_ROLLBACK)
| S(LCHAN_RTP_EV_IPACC_MDCX_ACK)
,
},
[LCHAN_RTP_ST_ROLLBACK] = {
.name = "ROLLBACK",
.onenter = lchan_rtp_fsm_rollback_onenter,
.action = lchan_rtp_fsm_rollback,
.in_event_mask = 0
| S(LCHAN_RTP_EV_MGW_ENDPOINT_CONFIGURED)
| S(LCHAN_RTP_EV_MGW_ENDPOINT_ERROR)
| S(LCHAN_RTP_EV_RELEASE)
| S(LCHAN_RTP_EV_ROLLBACK)
,
},
};
static const struct value_string lchan_rtp_fsm_event_names[] = {
OSMO_VALUE_STRING(LCHAN_RTP_EV_LCHAN_READY),
OSMO_VALUE_STRING(LCHAN_RTP_EV_READY_TO_SWITCH_RTP),
OSMO_VALUE_STRING(LCHAN_RTP_EV_MGW_ENDPOINT_AVAILABLE),
OSMO_VALUE_STRING(LCHAN_RTP_EV_MGW_ENDPOINT_ERROR),
OSMO_VALUE_STRING(LCHAN_RTP_EV_IPACC_CRCX_ACK),
OSMO_VALUE_STRING(LCHAN_RTP_EV_IPACC_CRCX_NACK),
OSMO_VALUE_STRING(LCHAN_RTP_EV_IPACC_MDCX_ACK),
OSMO_VALUE_STRING(LCHAN_RTP_EV_IPACC_MDCX_NACK),
OSMO_VALUE_STRING(LCHAN_RTP_EV_READY_TO_SWITCH),
OSMO_VALUE_STRING(LCHAN_RTP_EV_MGW_ENDPOINT_CONFIGURED),
OSMO_VALUE_STRING(LCHAN_RTP_EV_ROLLBACK),
OSMO_VALUE_STRING(LCHAN_RTP_EV_ESTABLISHED),
OSMO_VALUE_STRING(LCHAN_RTP_EV_RELEASE),
{}
};
int lchan_rtp_fsm_timer_cb(struct osmo_fsm_inst *fi)
{
struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);
lchan->release.in_error = true;
lchan->release.rsl_error_cause = RSL_ERR_EQUIPMENT_FAIL;
lchan_rtp_fail("Timeout");
return 0;
}
void lchan_rtp_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
{
struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);
if (lchan->mgw_endpoint_ci_bts) {
mgw_endpoint_ci_dlcx(lchan->mgw_endpoint_ci_bts);
lchan->mgw_endpoint_ci_bts = NULL;
}
lchan->fi_rtp = NULL;
bsc: lchan_rtp_fsm: Avoid duplicate LCHAN_EV_RTP_RELEASED event When lchan_rtp_fsm instance is allcoated with osmo_fsm_inst_alloc_child(..., LCHAN_EV_RTP_RELEASED) we already let fsm code to take care of sending that event ito the parent when the fsm is terminated (but only if freeing cause is not OSMO_FSM_TERM_PARENT). The lchan_rtp_fsm cleanup() callback, which is called immediatelly before sending to the parent the event defined during osmo_gsm_install_alloc_child(), currently also sends that same event, which ends up in a duplicated event being sent as shown in log files below. Let's only send the event in cleanup() if we are in the cause=OSMO_FSM_TERM_PARENT scenario, to make sure parent always receives the event, but only once. 20181128193707326 DAS osmo-bsc/assignment_fsm.c:127 assignment(conn4_0-0-6-TCH_F_PDCHasPDCH-0)[0x6120000024a0]{WAIT_LCHAN_ACTIVE}: (bts=0,trx=0,ts=6,ss=0) Assignment failed 20181128193707326 DAS osmo-bsc/assignment_fsm.c:128 assignment(conn4_0-0-6-TCH_F_PDCHasPDCH-0)[0x6120000024a0]{WAIT_LCHAN_ACTIVE}: Terminating (cause = OSMO_FSM_TERM_ERROR) 20181128193707326 DAS osmo-bsc/assignment_fsm.c:128 assignment(conn4_0-0-6-TCH_F_PDCHasPDCH-0)[0x6120000024a0]{WAIT_LCHAN_ACTIVE}: Removing from parent SUBSCR_CONN(conn4)[0x612000002920] 20181128193707326 DCHAN osmo-bsc/lchan_fsm.c:1333 lchan_rtp(0-0-6-TCH_F_PDCH-0)[0x612000002320]{WAIT_MGW_ENDPOINT_AVAILABLE}: Received Event LCHAN_RTP_EV_ROLLBACK 20181128193707326 DCHAN osmo-bsc/lchan_rtp_fsm.c:193 lchan_rtp(0-0-6-TCH_F_PDCH-0)[0x612000002320]{WAIT_MGW_ENDPOINT_AVAILABLE}: Terminating (cause = OSMO_FSM_TERM_REQUEST) 20181128193707326 DCHAN osmo-bsc/lchan_rtp_fsm.c:193 lchan_rtp(0-0-6-TCH_F_PDCH-0)[0x612000002320]{WAIT_MGW_ENDPOINT_AVAILABLE}: Removing from parent lchan(0-0-6-TCH_F_PDCH-0)[0x6120000039a0] 20181128193707326 DRSL osmo-bsc/mgw_endpoint_fsm.c:441 mgw-endpoint(conn4)[0x6120000021a0]{WAIT_MGW_RESPONSE}: (rtpbridge/*@mgw) CI[0] to-BTS: DLCX :0: notify=NULL 20181128193707326 DRSL osmo-bsc/mgw_endpoint_fsm.c:482 mgw-endpoint(conn4)[0x6120000021a0]{WAIT_MGW_RESPONSE}: (rtpbridge/*@mgw) CI[0] to-BTS: DLCX :0: Scheduling 20181128193707326 DCHAN osmo-bsc/lchan_rtp_fsm.c:742 lchan(0-0-6-TCH_F_PDCH-0)[0x6120000039a0]{WAIT_TS_READY}: Received Event LCHAN_EV_RTP_RELEASED 20181128193707326 DCHAN osmo-bsc/lchan_rtp_fsm.c:193 lchan_rtp(0-0-6-TCH_F_PDCH-0)[0x612000002320]{WAIT_MGW_ENDPOINT_AVAILABLE}: Freeing instance 20181128193707327 DCHAN fsm.c:381 lchan_rtp(0-0-6-TCH_F_PDCH-0)[0x612000002320]{WAIT_MGW_ENDPOINT_AVAILABLE}: Deallocated 20181128193707327 DCHAN osmo-bsc/lchan_rtp_fsm.c:193 lchan(0-0-6-TCH_F_PDCH-0)[0x6120000039a0]{WAIT_TS_READY}: Received Event LCHAN_EV_RTP_RELEASED 20181128193707330 DCHAN osmo-bsc/lchan_fsm.c:1347 lchan(0-0-6-TCH_F_PDCH-0)[0x6120000039a0]{WAIT_TS_READY}: transition to state WAIT_RLL_RTP_RELEASED not permitted! 20181128193707330 DAS osmo-bsc/assignment_fsm.c:128 assignment(conn4_0-0-6-TCH_F_PDCHasPDCH-0)[0x6120000024a0]{WAIT_LCHAN_ACTIVE}: Freeing instance 20181128193707330 DAS fsm.c:381 assignment(conn4_0-0-6-TCH_F_PDCHasPDCH-0)[0x6120000024a0]{WAIT_LCHAN_ACTIVE}: Deallocated Change-Id: I3e95a21e5a5ec6c35b1ab20b7a642fd7eb81e556
2018-11-28 18:53:31 +00:00
/* In all other cause, FSM already takes care of sending the event we
* configured at osmo_fsm_inst_alloc_child() time immediately after
* returning here. */
if (lchan->fi && cause == OSMO_FSM_TERM_PARENT)
osmo_fsm_inst_dispatch(lchan->fi, LCHAN_EV_RTP_RELEASED, 0);
}
/* The mgw_endpoint was invalidated, just and simply forget the pointer without cleanup. */
void lchan_forget_mgw_endpoint(struct gsm_lchan *lchan)
{
if (!lchan)
return;
lchan->mgw_endpoint_ci_bts = NULL;
}
static struct osmo_fsm lchan_rtp_fsm = {
.name = "lchan_rtp",
.states = lchan_rtp_fsm_states,
.num_states = ARRAY_SIZE(lchan_rtp_fsm_states),
.log_subsys = DCHAN,
.event_names = lchan_rtp_fsm_event_names,
.timer_cb = lchan_rtp_fsm_timer_cb,
.cleanup = lchan_rtp_fsm_cleanup,
};