<p>Neels Hofmeyr has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.osmocom.org/10103">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">lchan_fsm: split off lchan_rtp_fsm, establish RTP a bit earlier<br><br>Change-Id: Id7a4407d9b63be05ce63f5f2768b7d7e3d5c86fb<br>---<br>M include/osmocom/bsc/Makefile.am<br>M include/osmocom/bsc/gsm_data.h<br>M include/osmocom/bsc/lchan_fsm.h<br>A include/osmocom/bsc/lchan_rtp_fsm.h<br>M src/osmo-bsc/Makefile.am<br>M src/osmo-bsc/abis_rsl.c<br>M src/osmo-bsc/bsc_subscr_conn_fsm.c<br>M src/osmo-bsc/handover_fsm.c<br>M src/osmo-bsc/lchan_fsm.c<br>A src/osmo-bsc/lchan_rtp_fsm.c<br>M tests/handover/Makefile.am<br>M tests/handover/handover_test.c<br>12 files changed, 1,038 insertions(+), 481 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.osmocom.org:29418/osmo-bsc refs/changes/03/10103/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/include/osmocom/bsc/Makefile.am b/include/osmocom/bsc/Makefile.am</span><br><span>index f1a9ce7..ac62d5e 100644</span><br><span>--- a/include/osmocom/bsc/Makefile.am</span><br><span>+++ b/include/osmocom/bsc/Makefile.am</span><br><span>@@ -29,6 +29,7 @@</span><br><span>        handover_vty.h \</span><br><span>     ipaccess.h \</span><br><span>         lchan_fsm.h \</span><br><span style="color: hsl(120, 100%, 40%);">+ lchan_rtp_fsm.h \</span><br><span>    lchan_select.h \</span><br><span>     meas_feed.h \</span><br><span>        meas_rep.h \</span><br><span>diff --git a/include/osmocom/bsc/gsm_data.h b/include/osmocom/bsc/gsm_data.h</span><br><span>index 7d89371..7bd0943 100644</span><br><span>--- a/include/osmocom/bsc/gsm_data.h</span><br><span>+++ b/include/osmocom/bsc/gsm_data.h</span><br><span>@@ -499,13 +499,15 @@</span><br><span>    char *name;</span><br><span> </span><br><span>      struct osmo_fsm_inst *fi;</span><br><span style="color: hsl(120, 100%, 40%);">+     struct osmo_fsm_inst *fi_rtp;</span><br><span>        struct mgwep_ci *mgw_endpoint_ci_bts;</span><br><span> </span><br><span>    struct {</span><br><span>             enum lchan_activate_mode activ_for;</span><br><span style="color: hsl(120, 100%, 40%);">+           bool activ_ack; /*< true as soon as RSL Chan Activ Ack is received */</span><br><span>             bool concluded; /*< true as soon as LCHAN_ST_ESTABLISHED is reached */</span><br><span>            bool requires_voice_stream;</span><br><span style="color: hsl(0, 100%, 40%);">-             bool mgw_endpoint_available;</span><br><span style="color: hsl(120, 100%, 40%);">+          bool wait_before_switching_rtp; /*< true = requires LCHAN_EV_READY_TO_SWITCH_RTP */</span><br><span>               uint16_t msc_assigned_cic;</span><br><span>           enum gsm0808_cause gsm0808_error_cause;</span><br><span>              struct gsm_lchan *re_use_mgw_endpoint_from_lchan;</span><br><span>diff --git a/include/osmocom/bsc/lchan_fsm.h b/include/osmocom/bsc/lchan_fsm.h</span><br><span>index 49701c1..35b8847 100644</span><br><span>--- a/include/osmocom/bsc/lchan_fsm.h</span><br><span>+++ b/include/osmocom/bsc/lchan_fsm.h</span><br><span>@@ -16,13 +16,9 @@</span><br><span>      LCHAN_ST_UNUSED,</span><br><span>     LCHAN_ST_WAIT_TS_READY,</span><br><span>      LCHAN_ST_WAIT_ACTIV_ACK, /*< After RSL Chan Act Ack, lchan is active but RTP not configured. */</span><br><span style="color: hsl(0, 100%, 40%);">-      LCHAN_ST_WAIT_RLL_ESTABLISH,</span><br><span style="color: hsl(0, 100%, 40%);">-    LCHAN_ST_WAIT_MGW_ENDPOINT_AVAILABLE,</span><br><span style="color: hsl(0, 100%, 40%);">-   LCHAN_ST_WAIT_IPACC_CRCX_ACK,</span><br><span style="color: hsl(0, 100%, 40%);">-   LCHAN_ST_WAIT_IPACC_MDCX_ACK,</span><br><span style="color: hsl(0, 100%, 40%);">-   LCHAN_ST_WAIT_MGW_ENDPOINT_CONFIGURED,</span><br><span style="color: hsl(120, 100%, 40%);">+        LCHAN_ST_WAIT_RLL_RTP_ESTABLISH,</span><br><span>     LCHAN_ST_ESTABLISHED, /*< Active and RTP is fully configured. */</span><br><span style="color: hsl(0, 100%, 40%);">-     LCHAN_ST_WAIT_SAPIS_RELEASED,</span><br><span style="color: hsl(120, 100%, 40%);">+ LCHAN_ST_WAIT_RLL_RTP_RELEASED,</span><br><span>      LCHAN_ST_WAIT_BEFORE_RF_RELEASE,</span><br><span>     LCHAN_ST_WAIT_RF_RELEASE_ACK,</span><br><span>        LCHAN_ST_WAIT_AFTER_ERROR,</span><br><span>@@ -36,13 +32,9 @@</span><br><span>      LCHAN_EV_RSL_CHAN_ACTIV_ACK,</span><br><span>         LCHAN_EV_RSL_CHAN_ACTIV_NACK,</span><br><span>        LCHAN_EV_RLL_ESTABLISH_IND,</span><br><span style="color: hsl(0, 100%, 40%);">-     LCHAN_EV_MGW_ENDPOINT_AVAILABLE,</span><br><span style="color: hsl(0, 100%, 40%);">-        LCHAN_EV_MGW_ENDPOINT_CONFIGURED,</span><br><span style="color: hsl(0, 100%, 40%);">-       LCHAN_EV_MGW_ENDPOINT_ERROR,</span><br><span style="color: hsl(0, 100%, 40%);">-    LCHAN_EV_IPACC_CRCX_ACK,</span><br><span style="color: hsl(0, 100%, 40%);">-        LCHAN_EV_IPACC_CRCX_NACK,</span><br><span style="color: hsl(0, 100%, 40%);">-       LCHAN_EV_IPACC_MDCX_ACK,</span><br><span style="color: hsl(0, 100%, 40%);">-        LCHAN_EV_IPACC_MDCX_NACK,</span><br><span style="color: hsl(120, 100%, 40%);">+     LCHAN_EV_RTP_READY,</span><br><span style="color: hsl(120, 100%, 40%);">+   LCHAN_EV_RTP_ERROR,</span><br><span style="color: hsl(120, 100%, 40%);">+   LCHAN_EV_RTP_RELEASED,</span><br><span>       LCHAN_EV_RLL_REL_IND,</span><br><span>        LCHAN_EV_RLL_REL_CONF,</span><br><span>       LCHAN_EV_RSL_RF_CHAN_REL_ACK,</span><br><span>@@ -66,11 +58,13 @@</span><br><span>   * When a dyn TS was selected, the lchan->type has been set to the desired rate. */</span><br><span>       enum gsm48_chan_mode chan_mode;</span><br><span>      bool requires_voice_stream;</span><br><span style="color: hsl(120, 100%, 40%);">+   bool wait_before_switching_rtp;</span><br><span>      uint16_t msc_assigned_cic;</span><br><span>   struct gsm_lchan *old_lchan;</span><br><span> };</span><br><span> </span><br><span> void lchan_activate(struct gsm_lchan *lchan, struct lchan_activate_info *info);</span><br><span style="color: hsl(120, 100%, 40%);">+void lchan_ready_to_switch_rtp(struct gsm_lchan *lchan);</span><br><span> </span><br><span> static inline const char *lchan_state_name(struct gsm_lchan *lchan)</span><br><span> {</span><br><span>@@ -86,4 +80,5 @@</span><br><span> bool lchan_may_receive_data(struct gsm_lchan *lchan);</span><br><span> </span><br><span> void lchan_forget_conn(struct gsm_lchan *lchan);</span><br><span style="color: hsl(0, 100%, 40%);">-void lchan_forget_mgw_endpoint(struct gsm_lchan *lchan);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void lchan_set_last_error(struct gsm_lchan *lchan, const char *fmt, ...);</span><br><span>diff --git a/include/osmocom/bsc/lchan_rtp_fsm.h b/include/osmocom/bsc/lchan_rtp_fsm.h</span><br><span>new file mode 100644</span><br><span>index 0000000..fa0e746</span><br><span>--- /dev/null</span><br><span>+++ b/include/osmocom/bsc/lchan_rtp_fsm.h</span><br><span>@@ -0,0 +1,45 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* osmo-bsc API to manage lchans, logical channels in GSM cells. */</span><br><span style="color: hsl(120, 100%, 40%);">+#pragma once</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define LOG_LCHAN_RTP(lchan, level, fmt, args...) do { \</span><br><span style="color: hsl(120, 100%, 40%);">+  if (lchan->fi_rtp) \</span><br><span style="color: hsl(120, 100%, 40%);">+               LOGPFSML(lchan->fi_rtp, level, fmt, ## args); \</span><br><span style="color: hsl(120, 100%, 40%);">+    else \</span><br><span style="color: hsl(120, 100%, 40%);">+                LOGP(DLMGCP, level, "%s (not initialized) " fmt, gsm_lchan_name(lchan), \</span><br><span style="color: hsl(120, 100%, 40%);">+                ## args); \</span><br><span style="color: hsl(120, 100%, 40%);">+      } while(0)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct gsm_lchan;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum lchan_rtp_fsm_state {</span><br><span style="color: hsl(120, 100%, 40%);">+ LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_AVAILABLE,</span><br><span style="color: hsl(120, 100%, 40%);">+     LCHAN_RTP_ST_WAIT_LCHAN_READY,</span><br><span style="color: hsl(120, 100%, 40%);">+        LCHAN_RTP_ST_WAIT_IPACC_CRCX_ACK,</span><br><span style="color: hsl(120, 100%, 40%);">+     LCHAN_RTP_ST_WAIT_IPACC_MDCX_ACK,</span><br><span style="color: hsl(120, 100%, 40%);">+     LCHAN_RTP_ST_WAIT_READY_TO_SWITCH_RTP,</span><br><span style="color: hsl(120, 100%, 40%);">+        LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_CONFIGURED,</span><br><span style="color: hsl(120, 100%, 40%);">+    LCHAN_RTP_ST_READY,</span><br><span style="color: hsl(120, 100%, 40%);">+   LCHAN_RTP_ST_ROLLBACK,</span><br><span style="color: hsl(120, 100%, 40%);">+        LCHAN_RTP_ST_ESTABLISHED,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+enum lchan_rtp_fsm_event {</span><br><span style="color: hsl(120, 100%, 40%);">+   LCHAN_RTP_EV_LCHAN_READY,</span><br><span style="color: hsl(120, 100%, 40%);">+     LCHAN_RTP_EV_READY_TO_SWITCH_RTP,</span><br><span style="color: hsl(120, 100%, 40%);">+     LCHAN_RTP_EV_MGW_ENDPOINT_AVAILABLE,</span><br><span style="color: hsl(120, 100%, 40%);">+  LCHAN_RTP_EV_MGW_ENDPOINT_ERROR,</span><br><span style="color: hsl(120, 100%, 40%);">+      LCHAN_RTP_EV_IPACC_CRCX_ACK,</span><br><span style="color: hsl(120, 100%, 40%);">+  LCHAN_RTP_EV_IPACC_CRCX_NACK,</span><br><span style="color: hsl(120, 100%, 40%);">+ LCHAN_RTP_EV_IPACC_MDCX_ACK,</span><br><span style="color: hsl(120, 100%, 40%);">+  LCHAN_RTP_EV_IPACC_MDCX_NACK,</span><br><span style="color: hsl(120, 100%, 40%);">+ LCHAN_RTP_EV_READY_TO_SWITCH,</span><br><span style="color: hsl(120, 100%, 40%);">+ LCHAN_RTP_EV_MGW_ENDPOINT_CONFIGURED,</span><br><span style="color: hsl(120, 100%, 40%);">+ LCHAN_RTP_EV_ROLLBACK, /*< Give the RTP back to the old lchan, if any */</span><br><span style="color: hsl(120, 100%, 40%);">+   LCHAN_RTP_EV_ESTABLISHED, /*< All done, forget about the old lchan, if any */</span><br><span style="color: hsl(120, 100%, 40%);">+      LCHAN_RTP_EV_RELEASE,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void lchan_rtp_fsm_start(struct gsm_lchan *lchan);</span><br><span style="color: hsl(120, 100%, 40%);">+struct mgwep_ci *lchan_use_mgw_endpoint_ci_bts(struct gsm_lchan *lchan);</span><br><span style="color: hsl(120, 100%, 40%);">+bool lchan_rtp_established(struct gsm_lchan *lchan);</span><br><span style="color: hsl(120, 100%, 40%);">+void lchan_forget_mgw_endpoint(struct gsm_lchan *lchan);</span><br><span>diff --git a/src/osmo-bsc/Makefile.am b/src/osmo-bsc/Makefile.am</span><br><span>index 0c9d93c..5b717a1 100644</span><br><span>--- a/src/osmo-bsc/Makefile.am</span><br><span>+++ b/src/osmo-bsc/Makefile.am</span><br><span>@@ -67,6 +67,7 @@</span><br><span>   handover_logic.c \</span><br><span>   handover_vty.c \</span><br><span>     lchan_fsm.c \</span><br><span style="color: hsl(120, 100%, 40%);">+ lchan_rtp_fsm.c \</span><br><span>    lchan_select.c \</span><br><span>     meas_feed.c \</span><br><span>        meas_rep.c \</span><br><span>diff --git a/src/osmo-bsc/abis_rsl.c b/src/osmo-bsc/abis_rsl.c</span><br><span>index 28002e4..7ab97a9 100644</span><br><span>--- a/src/osmo-bsc/abis_rsl.c</span><br><span>+++ b/src/osmo-bsc/abis_rsl.c</span><br><span>@@ -35,6 +35,7 @@</span><br><span> #include <osmocom/gsm/gsm_utils.h></span><br><span> #include <osmocom/bsc/abis_rsl.h></span><br><span> #include <osmocom/bsc/lchan_fsm.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/lchan_rtp_fsm.h></span><br><span> #include <osmocom/bsc/bsc_rll.h></span><br><span> #include <osmocom/bsc/debug.h></span><br><span> #include <osmocom/gsm/tlv.h></span><br><span>@@ -1885,6 +1886,11 @@</span><br><span>  struct tlv_parsed tv;</span><br><span>        struct gsm_lchan *lchan = msg->lchan;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+  if (!lchan->fi_rtp) {</span><br><span style="color: hsl(120, 100%, 40%);">+              LOG_LCHAN(msg->lchan, LOGL_ERROR, "Rx RSL IPACC: CRCX ACK message for unconfigured lchan");</span><br><span style="color: hsl(120, 100%, 40%);">+              return -EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  /* the BTS has acknowledged a local bind, it now tells us the IP</span><br><span>     * address and port number to which it has bound the given logical</span><br><span>    * channel */</span><br><span>@@ -1899,17 +1905,37 @@</span><br><span> </span><br><span>   ipac_parse_rtp(lchan, &tv, "CRCX");</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   osmo_fsm_inst_dispatch(lchan->fi, LCHAN_EV_IPACC_CRCX_ACK, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_fsm_inst_dispatch(lchan->fi_rtp, LCHAN_RTP_EV_IPACC_CRCX_ACK, 0);</span><br><span> </span><br><span>        return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static int abis_rsl_rx_ipacc_crcx_nack(struct msgb *msg)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct e1inp_sign_link *sign_link = msg->dst;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct gsm_lchan *lchan = msg->lchan;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    rate_ctr_inc(&sign_link->trx->bts->bts_ctrs->ctr[BTS_CTR_RSL_IPA_NACK]);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!lchan->fi_rtp) {</span><br><span style="color: hsl(120, 100%, 40%);">+              LOG_LCHAN(msg->lchan, LOGL_ERROR, "Rx RSL IPACC: CRCX NACK message for unconfigured lchan");</span><br><span style="color: hsl(120, 100%, 40%);">+             return -EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_fsm_inst_dispatch(msg->lchan->fi_rtp, LCHAN_RTP_EV_IPACC_CRCX_NACK, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+    return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int abis_rsl_rx_ipacc_mdcx_ack(struct msgb *msg)</span><br><span> {</span><br><span>       struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);</span><br><span>        struct tlv_parsed tv;</span><br><span>        struct gsm_lchan *lchan = msg->lchan;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+  if (!lchan->fi_rtp) {</span><br><span style="color: hsl(120, 100%, 40%);">+              LOG_LCHAN(msg->lchan, LOGL_ERROR, "Rx RSL IPACC: MDCX ACK message for unconfigured lchan");</span><br><span style="color: hsl(120, 100%, 40%);">+              return -EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>  /* the BTS has acknowledged a remote connect request and</span><br><span>      * it now tells us the IP address and port number to which it has</span><br><span>     * connected the given logical channel */</span><br><span>@@ -1917,11 +1943,26 @@</span><br><span>  rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh));</span><br><span>    ipac_parse_rtp(lchan, &tv, "MDCX");</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   osmo_fsm_inst_dispatch(lchan->fi, LCHAN_EV_IPACC_MDCX_ACK, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_fsm_inst_dispatch(lchan->fi_rtp, LCHAN_RTP_EV_IPACC_MDCX_ACK, 0);</span><br><span> </span><br><span>        return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static int abis_rsl_rx_ipacc_mdcx_nack(struct msgb *msg)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct e1inp_sign_link *sign_link = msg->dst;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct gsm_lchan *lchan = msg->lchan;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    rate_ctr_inc(&sign_link->trx->bts->bts_ctrs->ctr[BTS_CTR_RSL_IPA_NACK]);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!lchan->fi_rtp) {</span><br><span style="color: hsl(120, 100%, 40%);">+              LOG_LCHAN(msg->lchan, LOGL_ERROR, "Rx RSL IPACC: MDCX NACK message for unconfigured lchan");</span><br><span style="color: hsl(120, 100%, 40%);">+             return -EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_fsm_inst_dispatch(msg->lchan->fi_rtp, LCHAN_RTP_EV_IPACC_MDCX_NACK, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+    return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int abis_rsl_rx_ipacc_dlcx_ind(struct msgb *msg)</span><br><span> {</span><br><span>       struct abis_rsl_dchan_hdr *dh = msgb_l2(msg);</span><br><span>@@ -1964,8 +2005,7 @@</span><br><span>        case RSL_MT_IPAC_CRCX_NACK:</span><br><span>          /* somehow the BTS was unable to bind the lchan to its local</span><br><span>                  * port?!? */</span><br><span style="color: hsl(0, 100%, 40%);">-           rate_ctr_inc(&sign_link->trx->bts->bts_ctrs->ctr[BTS_CTR_RSL_IPA_NACK]);</span><br><span style="color: hsl(0, 100%, 40%);">-                osmo_fsm_inst_dispatch(msg->lchan->fi, LCHAN_EV_IPACC_CRCX_NACK, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+            rc = abis_rsl_rx_ipacc_crcx_nack(msg);</span><br><span>               break;</span><br><span>       case RSL_MT_IPAC_MDCX_ACK:</span><br><span>           /* the BTS tells us that a connect operation was successful */</span><br><span>@@ -1974,8 +2014,7 @@</span><br><span>       case RSL_MT_IPAC_MDCX_NACK:</span><br><span>          /* somehow the BTS was unable to connect the lchan to a remote</span><br><span>                * port */</span><br><span style="color: hsl(0, 100%, 40%);">-              rate_ctr_inc(&sign_link->trx->bts->bts_ctrs->ctr[BTS_CTR_RSL_IPA_NACK]);</span><br><span style="color: hsl(0, 100%, 40%);">-                osmo_fsm_inst_dispatch(msg->lchan->fi, LCHAN_EV_IPACC_MDCX_NACK, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+            rc = abis_rsl_rx_ipacc_mdcx_nack(msg);</span><br><span>               break;</span><br><span>       case RSL_MT_IPAC_DLCX_IND:</span><br><span>           rc = abis_rsl_rx_ipacc_dlcx_ind(msg);</span><br><span>diff --git a/src/osmo-bsc/bsc_subscr_conn_fsm.c b/src/osmo-bsc/bsc_subscr_conn_fsm.c</span><br><span>index f56b2af..c6c291a 100644</span><br><span>--- a/src/osmo-bsc/bsc_subscr_conn_fsm.c</span><br><span>+++ b/src/osmo-bsc/bsc_subscr_conn_fsm.c</span><br><span>@@ -28,6 +28,7 @@</span><br><span> #include <osmocom/bsc/gsm_data.h></span><br><span> #include <osmocom/bsc/handover_fsm.h></span><br><span> #include <osmocom/bsc/lchan_fsm.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/lchan_rtp_fsm.h></span><br><span> #include <osmocom/bsc/bsc_subscriber.h></span><br><span> #include <osmocom/bsc/osmo_bsc_sigtran.h></span><br><span> #include <osmocom/bsc/osmo_bsc_lcls.h></span><br><span>@@ -597,7 +598,10 @@</span><br><span>        conn->lchan = new_lchan;</span><br><span>  conn->lchan->conn = conn;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-     if (old_lchan) {</span><br><span style="color: hsl(120, 100%, 40%);">+      if (conn->lchan->fi_rtp)</span><br><span style="color: hsl(120, 100%, 40%);">+                osmo_fsm_inst_dispatch(conn->lchan->fi_rtp, LCHAN_RTP_EV_ESTABLISHED, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (old_lchan && (old_lchan != new_lchan)) {</span><br><span>                 lchan_forget_conn(old_lchan);</span><br><span>                lchan_release(old_lchan, false, false, 0);</span><br><span>   }</span><br><span>diff --git a/src/osmo-bsc/handover_fsm.c b/src/osmo-bsc/handover_fsm.c</span><br><span>index e3f64cd..d70d049 100644</span><br><span>--- a/src/osmo-bsc/handover_fsm.c</span><br><span>+++ b/src/osmo-bsc/handover_fsm.c</span><br><span>@@ -32,6 +32,7 @@</span><br><span> #include <osmocom/bsc/bsc_subscr_conn_fsm.h></span><br><span> #include <osmocom/bsc/lchan_select.h></span><br><span> #include <osmocom/bsc/lchan_fsm.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/lchan_rtp_fsm.h></span><br><span> #include <osmocom/bsc/gsm_04_08_utils.h></span><br><span> #include <osmocom/bsc/abis_rsl.h></span><br><span> #include <osmocom/bsc/bsc_msc_data.h></span><br><span>@@ -359,6 +360,7 @@</span><br><span>                      .requires_voice_stream = conn->lchan->mgw_endpoint_ci_bts ? true : false,</span><br><span>                      .msc_assigned_cic = conn->ho.inter_bsc_in.msc_assigned_cic,</span><br><span>                       .old_lchan = conn->lchan,</span><br><span style="color: hsl(120, 100%, 40%);">+                  .wait_before_switching_rtp = true,</span><br><span>           };</span><br><span> </span><br><span>               lchan_activate(ho->new_lchan, &info);</span><br><span>@@ -845,6 +847,9 @@</span><br><span>                   }</span><br><span>            }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+         if (ho->new_lchan->fi_rtp)</span><br><span style="color: hsl(120, 100%, 40%);">+                      osmo_fsm_inst_dispatch(ho->new_lchan->fi_rtp,</span><br><span style="color: hsl(120, 100%, 40%);">+                                          LCHAN_RTP_EV_READY_TO_SWITCH_RTP, 0);</span><br><span>                 ho_fsm_state_chg(HO_ST_WAIT_RR_HO_COMPLETE);</span><br><span>                 /* The lchan FSM will already start to redirect the RTP stream */</span><br><span>            return;</span><br><span>@@ -853,7 +858,9 @@</span><br><span>                LOG_HO(conn, LOGL_ERROR,</span><br><span>                     "Received RR Handover Complete, but haven't even seen a Handover Detect yet;"</span><br><span>                  " Accepting handover anyway\n");</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(120, 100%, 40%);">+            if (ho->new_lchan->fi_rtp)</span><br><span style="color: hsl(120, 100%, 40%);">+                      osmo_fsm_inst_dispatch(ho->new_lchan->fi_rtp,</span><br><span style="color: hsl(120, 100%, 40%);">+                                          LCHAN_RTP_EV_READY_TO_SWITCH_RTP, 0);</span><br><span>                 ho_fsm_state_chg(HO_ST_WAIT_LCHAN_ESTABLISHED);</span><br><span>              return;</span><br><span> </span><br><span>diff --git a/src/osmo-bsc/lchan_fsm.c b/src/osmo-bsc/lchan_fsm.c</span><br><span>index f25e7b9..58adb99 100644</span><br><span>--- a/src/osmo-bsc/lchan_fsm.c</span><br><span>+++ b/src/osmo-bsc/lchan_fsm.c</span><br><span>@@ -1,5 +1,4 @@</span><br><span style="color: hsl(0, 100%, 40%);">-/* osmo-bsc API to allocate an lchan, complete with dyn TS switchover and MGCP communication to allocate</span><br><span style="color: hsl(0, 100%, 40%);">- * RTP endpoints.</span><br><span style="color: hsl(120, 100%, 40%);">+/* osmo-bsc API to allocate an lchan, complete with dyn TS switchover.</span><br><span>  *</span><br><span>  * (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de></span><br><span>  * All Rights Reserved</span><br><span>@@ -26,6 +25,7 @@</span><br><span> </span><br><span> #include <osmocom/bsc/debug.h></span><br><span> #include <osmocom/bsc/lchan_fsm.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/lchan_rtp_fsm.h></span><br><span> #include <osmocom/bsc/timeslot_fsm.h></span><br><span> #include <osmocom/bsc/mgw_endpoint_fsm.h></span><br><span> #include <osmocom/bsc/bsc_subscr_conn_fsm.h></span><br><span>@@ -53,11 +53,7 @@</span><br><span>               return false;</span><br><span> </span><br><span>    switch (lchan->fi->state) {</span><br><span style="color: hsl(0, 100%, 40%);">-       case LCHAN_ST_WAIT_RLL_ESTABLISH:</span><br><span style="color: hsl(0, 100%, 40%);">-       case LCHAN_ST_WAIT_MGW_ENDPOINT_AVAILABLE:</span><br><span style="color: hsl(0, 100%, 40%);">-      case LCHAN_ST_WAIT_IPACC_CRCX_ACK:</span><br><span style="color: hsl(0, 100%, 40%);">-      case LCHAN_ST_WAIT_IPACC_MDCX_ACK:</span><br><span style="color: hsl(0, 100%, 40%);">-      case LCHAN_ST_WAIT_MGW_ENDPOINT_CONFIGURED:</span><br><span style="color: hsl(120, 100%, 40%);">+   case LCHAN_ST_WAIT_RLL_RTP_ESTABLISH:</span><br><span>        case LCHAN_ST_ESTABLISHED:</span><br><span>           return true;</span><br><span>         default:</span><br><span>@@ -65,7 +61,7 @@</span><br><span>         }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static void lchan_set_last_error(struct gsm_lchan *lchan, const char *fmt, ...)</span><br><span style="color: hsl(120, 100%, 40%);">+void lchan_set_last_error(struct gsm_lchan *lchan, const char *fmt, ...)</span><br><span> {</span><br><span>     va_list ap;</span><br><span>  /* This dance allows using an existing error reason in above fmt */</span><br><span>@@ -137,11 +133,12 @@</span><br><span>  }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static void lchan_on_activation_success(struct gsm_lchan *lchan)</span><br><span style="color: hsl(120, 100%, 40%);">+static void lchan_on_fully_established(struct gsm_lchan *lchan)</span><br><span> {</span><br><span>     switch (lchan->activate.activ_for) {</span><br><span>      case FOR_MS_CHANNEL_REQUEST:</span><br><span style="color: hsl(0, 100%, 40%);">-            /* Nothing to do here, MS is free to use the channel. */</span><br><span style="color: hsl(120, 100%, 40%);">+              /* No signalling to do here, MS is free to use the channel, and should go on to connect</span><br><span style="color: hsl(120, 100%, 40%);">+                * to the MSC and establish a subscriber connection. */</span><br><span>              break;</span><br><span> </span><br><span>   case FOR_ASSIGNMENT:</span><br><span>@@ -161,6 +158,9 @@</span><br><span>           }</span><br><span>            osmo_fsm_inst_dispatch(lchan->conn->assignment.fi, ASSIGNMENT_EV_LCHAN_ESTABLISHED,</span><br><span>                                   lchan);</span><br><span style="color: hsl(120, 100%, 40%);">+                /* The lchan->fi_rtp will be notified of LCHAN_RTP_EV_ESTABLISHED in</span><br><span style="color: hsl(120, 100%, 40%);">+                * gscon_change_primary_lchan() upon assignment_success(). On failure before then, we</span><br><span style="color: hsl(120, 100%, 40%);">+          * will try to roll back a modified RTP connection. */</span><br><span>               break;</span><br><span> </span><br><span>   case FOR_HANDOVER:</span><br><span>@@ -178,6 +178,9 @@</span><br><span>                     break;</span><br><span>               }</span><br><span>            osmo_fsm_inst_dispatch(lchan->conn->ho.fi, HO_EV_LCHAN_ESTABLISHED, lchan);</span><br><span style="color: hsl(120, 100%, 40%);">+             /* The lchan->fi_rtp will be notified of LCHAN_RTP_EV_ESTABLISHED in</span><br><span style="color: hsl(120, 100%, 40%);">+                * gscon_change_primary_lchan() upon handover_end(HO_RESULT_OK). On failure before then,</span><br><span style="color: hsl(120, 100%, 40%);">+               * we will try to roll back a modified RTP connection. */</span><br><span>            break;</span><br><span> </span><br><span>   default:</span><br><span>@@ -190,12 +193,8 @@</span><br><span> struct state_timeout lchan_fsm_timeouts[32] = {</span><br><span>   [LCHAN_ST_WAIT_TS_READY]        = { .T=23001 },</span><br><span>      [LCHAN_ST_WAIT_ACTIV_ACK]       = { .T=23002 },</span><br><span style="color: hsl(0, 100%, 40%);">- [LCHAN_ST_WAIT_RLL_ESTABLISH]   = { .T=3101 },</span><br><span style="color: hsl(0, 100%, 40%);">-  [LCHAN_ST_WAIT_MGW_ENDPOINT_AVAILABLE] = { .T=23004 },</span><br><span style="color: hsl(0, 100%, 40%);">-  [LCHAN_ST_WAIT_IPACC_CRCX_ACK]  = { .T=23005 },</span><br><span style="color: hsl(0, 100%, 40%);">- [LCHAN_ST_WAIT_IPACC_MDCX_ACK]  = { .T=23006 },</span><br><span style="color: hsl(0, 100%, 40%);">- [LCHAN_ST_WAIT_MGW_ENDPOINT_CONFIGURED] = { .T=23004 },</span><br><span style="color: hsl(0, 100%, 40%);">- [LCHAN_ST_WAIT_SAPIS_RELEASED]  = { .T=3109 },</span><br><span style="color: hsl(120, 100%, 40%);">+        [LCHAN_ST_WAIT_RLL_RTP_ESTABLISH]       = { .T=3101 },</span><br><span style="color: hsl(120, 100%, 40%);">+        [LCHAN_ST_WAIT_RLL_RTP_RELEASED]        = { .T=3109 },</span><br><span>       [LCHAN_ST_WAIT_BEFORE_RF_RELEASE]       = { .T=3111 },</span><br><span>       [LCHAN_ST_WAIT_RF_RELEASE_ACK]  = { .T=3111 },</span><br><span>       [LCHAN_ST_WAIT_AFTER_ERROR]     = { .T=993111 },</span><br><span>@@ -215,7 +214,7 @@</span><br><span> #define lchan_fail_to(state_chg, fmt, args...) do { \</span><br><span>              struct gsm_lchan *_lchan = fi->priv; \</span><br><span>            uint32_t state_was = fi->state; \</span><br><span style="color: hsl(0, 100%, 40%);">-            lchan_set_last_error(fi->priv, "lchan %s in state %s: " fmt, \</span><br><span style="color: hsl(120, 100%, 40%);">+           lchan_set_last_error(_lchan, "lchan %s in state %s: " fmt, \</span><br><span>                                    _lchan->activate.concluded ? "failure" : "allocation failed", \</span><br><span>                               osmo_fsm_state_name(fi->fsm, state_was), ## args); \</span><br><span>                 if (!_lchan->activate.concluded) \</span><br><span>@@ -229,13 +228,9 @@</span><br><span>         [LCHAN_ST_UNUSED]                       = LCHAN_ST_UNUSED,</span><br><span>   [LCHAN_ST_WAIT_TS_READY]                = LCHAN_ST_UNUSED,</span><br><span>   [LCHAN_ST_WAIT_ACTIV_ACK]               = LCHAN_ST_BORKEN,</span><br><span style="color: hsl(0, 100%, 40%);">-      [LCHAN_ST_WAIT_RLL_ESTABLISH]           = LCHAN_ST_WAIT_RF_RELEASE_ACK,</span><br><span style="color: hsl(0, 100%, 40%);">- [LCHAN_ST_WAIT_MGW_ENDPOINT_AVAILABLE]  = LCHAN_ST_WAIT_SAPIS_RELEASED,</span><br><span style="color: hsl(0, 100%, 40%);">- [LCHAN_ST_WAIT_IPACC_CRCX_ACK]          = LCHAN_ST_WAIT_SAPIS_RELEASED,</span><br><span style="color: hsl(0, 100%, 40%);">- [LCHAN_ST_WAIT_IPACC_MDCX_ACK]          = LCHAN_ST_WAIT_SAPIS_RELEASED,</span><br><span style="color: hsl(0, 100%, 40%);">- [LCHAN_ST_WAIT_MGW_ENDPOINT_CONFIGURED] = LCHAN_ST_WAIT_SAPIS_RELEASED,</span><br><span style="color: hsl(0, 100%, 40%);">- [LCHAN_ST_ESTABLISHED]                  = LCHAN_ST_WAIT_SAPIS_RELEASED,</span><br><span style="color: hsl(0, 100%, 40%);">- [LCHAN_ST_WAIT_SAPIS_RELEASED]          = LCHAN_ST_WAIT_RF_RELEASE_ACK,</span><br><span style="color: hsl(120, 100%, 40%);">+       [LCHAN_ST_WAIT_RLL_RTP_ESTABLISH]       = LCHAN_ST_WAIT_RF_RELEASE_ACK,</span><br><span style="color: hsl(120, 100%, 40%);">+       [LCHAN_ST_ESTABLISHED]                  = LCHAN_ST_WAIT_RLL_RTP_RELEASED,</span><br><span style="color: hsl(120, 100%, 40%);">+     [LCHAN_ST_WAIT_RLL_RTP_RELEASED]                = LCHAN_ST_WAIT_RF_RELEASE_ACK,</span><br><span>      [LCHAN_ST_WAIT_BEFORE_RF_RELEASE]       = LCHAN_ST_WAIT_RF_RELEASE_ACK,</span><br><span>      [LCHAN_ST_WAIT_RF_RELEASE_ACK]          = LCHAN_ST_BORKEN,</span><br><span>   [LCHAN_ST_WAIT_AFTER_ERROR]             = LCHAN_ST_UNUSED,</span><br><span>@@ -322,11 +317,16 @@</span><br><span>   osmo_fsm_inst_update_id_f(lchan->fi, "%u-%u-%u-%s-%u",</span><br><span>                            lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr,</span><br><span>                               gsm_pchan_id(lchan->ts->pchan_on_init), lchan->nr);</span><br><span style="color: hsl(120, 100%, 40%);">+        if (lchan->fi_rtp)</span><br><span style="color: hsl(120, 100%, 40%);">+         osmo_fsm_inst_update_id_f(lchan->fi_rtp, lchan->fi->id);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+extern void lchan_rtp_fsm_init();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> void lchan_fsm_init()</span><br><span> {</span><br><span>        OSMO_ASSERT(osmo_fsm_register(&lchan_fsm) == 0);</span><br><span style="color: hsl(120, 100%, 40%);">+  lchan_rtp_fsm_init();</span><br><span> }</span><br><span> </span><br><span> void lchan_fsm_alloc(struct gsm_lchan *lchan)</span><br><span>@@ -357,6 +357,8 @@</span><br><span>                talloc_free(lchan->rqd_ref);</span><br><span>              lchan->rqd_ref = NULL;</span><br><span>    }</span><br><span style="color: hsl(120, 100%, 40%);">+     if (lchan->fi_rtp)</span><br><span style="color: hsl(120, 100%, 40%);">+         osmo_fsm_inst_term(lchan->fi_rtp, OSMO_FSM_TERM_REQUEST, 0);</span><br><span>      if (lchan->mgw_endpoint_ci_bts) {</span><br><span>                 mgw_endpoint_ci_dlcx(lchan->mgw_endpoint_ci_bts);</span><br><span>                 lchan->mgw_endpoint_ci_bts = NULL;</span><br><span>@@ -424,6 +426,7 @@</span><br><span>          lchan->conn = info->for_conn;</span><br><span>          lchan->activate.activ_for = info->activ_for;</span><br><span>           lchan->activate.requires_voice_stream = info->requires_voice_stream;</span><br><span style="color: hsl(120, 100%, 40%);">+            lchan->activate.wait_before_switching_rtp = info->wait_before_switching_rtp;</span><br><span>           lchan->activate.msc_assigned_cic = info->msc_assigned_cic;</span><br><span>             lchan->activate.concluded = false;</span><br><span>                lchan->activate.re_use_mgw_endpoint_from_lchan = info->old_lchan;</span><br><span>@@ -477,24 +480,8 @@</span><br><span>       }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-/* While activating an lchan, for example for Handover, we may want to re-use another lchan's MGW</span><br><span style="color: hsl(0, 100%, 40%);">- * endpoint CI. If Handover fails half way, the old lchan must keep its MGW endpoint CI, and we must not</span><br><span style="color: hsl(0, 100%, 40%);">- * clean it up. Hence keep another lchan's mgw_endpoint_ci_bts out of lchan until all is done. */</span><br><span style="color: hsl(0, 100%, 40%);">-static struct mgwep_ci *lchan_use_mgw_endpoint_ci_bts(struct gsm_lchan *lchan)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-     if (lchan->mgw_endpoint_ci_bts)</span><br><span style="color: hsl(0, 100%, 40%);">-              return lchan->mgw_endpoint_ci_bts;</span><br><span style="color: hsl(0, 100%, 40%);">-   if (lchan_state_is(lchan, LCHAN_ST_ESTABLISHED))</span><br><span style="color: hsl(0, 100%, 40%);">-                return NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-    if (lchan->activate.re_use_mgw_endpoint_from_lchan)</span><br><span style="color: hsl(0, 100%, 40%);">-          return lchan->activate.re_use_mgw_endpoint_from_lchan->mgw_endpoint_ci_bts;</span><br><span style="color: hsl(0, 100%, 40%);">-       return NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> static void lchan_fsm_wait_ts_ready_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-        struct mgw_endpoint *mgwep;</span><br><span style="color: hsl(0, 100%, 40%);">-     struct mgcp_conn_peer crcx_info = {};</span><br><span>        struct gsm_lchan *lchan = lchan_fi_lchan(fi);</span><br><span>        struct mgwep_ci *use_mgwep_ci = lchan_use_mgw_endpoint_ci_bts(lchan);</span><br><span> </span><br><span>@@ -518,32 +505,8 @@</span><br><span>     osmo_fsm_inst_dispatch(lchan->ts->fi, TS_EV_LCHAN_REQUESTED, lchan);</span><br><span> </span><br><span>       /* Prepare an MGW endpoint CI if appropriate. */</span><br><span style="color: hsl(0, 100%, 40%);">-        if (!lchan->activate.requires_voice_stream)</span><br><span style="color: hsl(0, 100%, 40%);">-          return;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- if (use_mgwep_ci) {</span><br><span style="color: hsl(0, 100%, 40%);">-             lchan->activate.mgw_endpoint_available = true;</span><br><span style="color: hsl(0, 100%, 40%);">-               return;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       mgwep = gscon_ensure_mgw_endpoint(lchan->conn, lchan->activate.msc_assigned_cic);</span><br><span style="color: hsl(0, 100%, 40%);">- if (!mgwep) {</span><br><span style="color: hsl(0, 100%, 40%);">-           lchan_fail("Internal error: cannot obtain MGW endpoint handle for conn");</span><br><span style="color: hsl(0, 100%, 40%);">-             return;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       lchan->mgw_endpoint_ci_bts = mgw_endpoint_ci_add(mgwep, "to-BTS");</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- if (lchan->conn)</span><br><span style="color: hsl(0, 100%, 40%);">-             crcx_info.call_id = lchan->conn->sccp.conn_id;</span><br><span style="color: hsl(0, 100%, 40%);">-    crcx_info.ptime = 20;</span><br><span style="color: hsl(0, 100%, 40%);">-   mgcp_pick_codec(&crcx_info, lchan);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- mgw_endpoint_ci_request(lchan->mgw_endpoint_ci_bts,</span><br><span style="color: hsl(0, 100%, 40%);">-                          MGCP_VERB_CRCX, &crcx_info,</span><br><span style="color: hsl(0, 100%, 40%);">-                         lchan->fi,</span><br><span style="color: hsl(0, 100%, 40%);">-                           LCHAN_EV_MGW_ENDPOINT_AVAILABLE,</span><br><span style="color: hsl(0, 100%, 40%);">-                                LCHAN_EV_MGW_ENDPOINT_ERROR, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+      if (lchan->activate.requires_voice_stream)</span><br><span style="color: hsl(120, 100%, 40%);">+         lchan_rtp_fsm_start(lchan);</span><br><span> }</span><br><span> </span><br><span> static void lchan_fsm_wait_ts_ready(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span>@@ -556,10 +519,18 @@</span><br><span>            lchan_fsm_state_chg(LCHAN_ST_WAIT_ACTIV_ACK);</span><br><span>                break;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-      case LCHAN_EV_MGW_ENDPOINT_AVAILABLE:</span><br><span style="color: hsl(0, 100%, 40%);">-           /* conn FSM is already done preparing an MGW endpoint. Remember that. */</span><br><span style="color: hsl(0, 100%, 40%);">-                lchan->activate.mgw_endpoint_available = true;</span><br><span style="color: hsl(0, 100%, 40%);">-               break;</span><br><span style="color: hsl(120, 100%, 40%);">+        case LCHAN_EV_RTP_RELEASED:</span><br><span style="color: hsl(120, 100%, 40%);">+   case LCHAN_EV_RTP_ERROR:</span><br><span style="color: hsl(120, 100%, 40%);">+              if (lchan->release_requested) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    /* Already in release, the RTP is not the initial cause of failure.</span><br><span style="color: hsl(120, 100%, 40%);">+                    * Just ignore. */</span><br><span style="color: hsl(120, 100%, 40%);">+                    return;</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           lchan_fail("Failed to setup RTP stream: %s in state %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                     osmo_fsm_event_name(fi->fsm, event),</span><br><span style="color: hsl(120, 100%, 40%);">+                       osmo_fsm_inst_state_name(fi));</span><br><span style="color: hsl(120, 100%, 40%);">+             return;</span><br><span> </span><br><span>  default:</span><br><span>             OSMO_ASSERT(false);</span><br><span>@@ -597,18 +568,16 @@</span><br><span>          lchan_fail_to(LCHAN_ST_UNUSED, "Tx Chan Activ failed: %s (%d)", strerror(-rc), rc);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static void lchan_fsm_post_activ_ack(struct osmo_fsm_inst *fi);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static void lchan_fsm_wait_activ_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span> {</span><br><span>         struct gsm_lchan *lchan = lchan_fi_lchan(fi);</span><br><span>        switch (event) {</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    case LCHAN_EV_MGW_ENDPOINT_AVAILABLE:</span><br><span style="color: hsl(0, 100%, 40%);">-           lchan->activate.mgw_endpoint_available = true;</span><br><span style="color: hsl(0, 100%, 40%);">-               break;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>       case LCHAN_EV_RSL_CHAN_ACTIV_ACK:</span><br><span style="color: hsl(0, 100%, 40%);">-               /* Chan Activ was ack'd, but we need an RLL Establish to be sure it's working out. */</span><br><span style="color: hsl(0, 100%, 40%);">-           lchan_fsm_state_chg(LCHAN_ST_WAIT_RLL_ESTABLISH);</span><br><span style="color: hsl(120, 100%, 40%);">+             lchan->activate.activ_ack = true;</span><br><span style="color: hsl(120, 100%, 40%);">+          lchan_fsm_post_activ_ack(fi);</span><br><span>                break;</span><br><span> </span><br><span>   case LCHAN_EV_RSL_CHAN_ACTIV_NACK:</span><br><span>@@ -632,12 +601,26 @@</span><br><span>           }</span><br><span>            break;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+    case LCHAN_EV_RTP_RELEASED:</span><br><span style="color: hsl(120, 100%, 40%);">+   case LCHAN_EV_RTP_ERROR:</span><br><span style="color: hsl(120, 100%, 40%);">+              if (lchan->release_requested) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    /* Already in release, the RTP is not the initial cause of failure.</span><br><span style="color: hsl(120, 100%, 40%);">+                    * Just ignore. */</span><br><span style="color: hsl(120, 100%, 40%);">+                    return;</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           lchan_fail_to(LCHAN_ST_WAIT_RF_RELEASE_ACK,</span><br><span style="color: hsl(120, 100%, 40%);">+                         "Failed to setup RTP stream: %s in state %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                             osmo_fsm_event_name(fi->fsm, event),</span><br><span style="color: hsl(120, 100%, 40%);">+                       osmo_fsm_inst_state_name(fi));</span><br><span style="color: hsl(120, 100%, 40%);">+          return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>    default:</span><br><span>             OSMO_ASSERT(false);</span><br><span>  }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static void lchan_fsm_wait_rll_establish_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)</span><br><span style="color: hsl(120, 100%, 40%);">+static void lchan_fsm_post_activ_ack(struct osmo_fsm_inst *fi)</span><br><span> {</span><br><span>       int rc;</span><br><span>      struct gsm_lchan *lchan = lchan_fi_lchan(fi);</span><br><span>@@ -703,62 +686,44 @@</span><br><span>                          lchan_activate_mode_name(lchan->activate.activ_for));</span><br><span>           break;</span><br><span>       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   lchan_fsm_state_chg(LCHAN_ST_WAIT_RLL_RTP_ESTABLISH);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static void lchan_fsm_wait_rll_establish(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+static void lchan_fsm_wait_rll_rtp_establish_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct gsm_lchan *lchan = lchan_fi_lchan(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (lchan->fi_rtp)</span><br><span style="color: hsl(120, 100%, 40%);">+         osmo_fsm_inst_dispatch(lchan->fi_rtp, LCHAN_RTP_EV_LCHAN_READY, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void lchan_fsm_wait_rll_rtp_establish(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span> {</span><br><span>         struct gsm_lchan *lchan = lchan_fi_lchan(fi);</span><br><span>        switch (event) {</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-    case LCHAN_EV_MGW_ENDPOINT_AVAILABLE:</span><br><span style="color: hsl(0, 100%, 40%);">-           lchan->activate.mgw_endpoint_available = true;</span><br><span style="color: hsl(0, 100%, 40%);">-               break;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>       case LCHAN_EV_RLL_ESTABLISH_IND:</span><br><span style="color: hsl(0, 100%, 40%);">-                lchan->sapis[0] = LCHAN_SAPI_MS;</span><br><span style="color: hsl(0, 100%, 40%);">-             if (lchan->activate.requires_voice_stream) {</span><br><span style="color: hsl(0, 100%, 40%);">-                 /* For Abis/IP, we would technically only need the MGW endpoint one step later,</span><br><span style="color: hsl(0, 100%, 40%);">-                  * on IPACC MDCX. But usually the MGW endpoint is anyway done by now, so keep one</span><br><span style="color: hsl(0, 100%, 40%);">-                        * common endpoint wait state for all BTS types. */</span><br><span style="color: hsl(0, 100%, 40%);">-                     lchan_fsm_state_chg(LCHAN_ST_WAIT_MGW_ENDPOINT_AVAILABLE);</span><br><span style="color: hsl(0, 100%, 40%);">-              } else</span><br><span style="color: hsl(120, 100%, 40%);">+                if (!lchan->activate.requires_voice_stream</span><br><span style="color: hsl(120, 100%, 40%);">+             || lchan_rtp_established(lchan))</span><br><span>                         lchan_fsm_state_chg(LCHAN_ST_ESTABLISHED);</span><br><span style="color: hsl(0, 100%, 40%);">-              break;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-  default:</span><br><span style="color: hsl(0, 100%, 40%);">-                OSMO_ASSERT(false);</span><br><span style="color: hsl(0, 100%, 40%);">-     }</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static void lchan_fsm_tch_post_endpoint_available(struct osmo_fsm_inst *fi);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static void lchan_fsm_wait_mgw_endpoint_available_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- struct gsm_lchan *lchan = lchan_fi_lchan(fi);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-   if (lchan->release_requested) {</span><br><span style="color: hsl(0, 100%, 40%);">-              lchan_fail("Release requested while activating");</span><br><span style="color: hsl(0, 100%, 40%);">-             return;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       if (lchan->activate.mgw_endpoint_available) {</span><br><span style="color: hsl(0, 100%, 40%);">-                LOG_LCHAN(lchan, LOGL_DEBUG, "MGW endpoint already available\n");</span><br><span style="color: hsl(0, 100%, 40%);">-             lchan_fsm_tch_post_endpoint_available(fi);</span><br><span style="color: hsl(0, 100%, 40%);">-      }</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static void lchan_fsm_wait_mgw_endpoint_available(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-      struct gsm_lchan *lchan = lchan_fi_lchan(fi);</span><br><span style="color: hsl(0, 100%, 40%);">-   switch (event) {</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        case LCHAN_EV_MGW_ENDPOINT_AVAILABLE:</span><br><span style="color: hsl(0, 100%, 40%);">-           lchan->activate.mgw_endpoint_available = true;</span><br><span style="color: hsl(0, 100%, 40%);">-               lchan_fsm_tch_post_endpoint_available(fi);</span><br><span>           return;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-     case LCHAN_EV_RLL_ESTABLISH_IND:</span><br><span style="color: hsl(0, 100%, 40%);">-                /* abis_rsl.c has noticed that a SAPI was established, no need to take action here. */</span><br><span style="color: hsl(120, 100%, 40%);">+        case LCHAN_EV_RTP_READY:</span><br><span style="color: hsl(120, 100%, 40%);">+              if (lchan->sapis[0] != LCHAN_SAPI_UNUSED)</span><br><span style="color: hsl(120, 100%, 40%);">+                  lchan_fsm_state_chg(LCHAN_ST_ESTABLISHED);</span><br><span style="color: hsl(120, 100%, 40%);">+            return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     case LCHAN_EV_RTP_RELEASED:</span><br><span style="color: hsl(120, 100%, 40%);">+   case LCHAN_EV_RTP_ERROR:</span><br><span style="color: hsl(120, 100%, 40%);">+              if (lchan->release_requested) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    /* Already in release, the RTP is not the initial cause of failure.</span><br><span style="color: hsl(120, 100%, 40%);">+                    * Just ignore. */</span><br><span style="color: hsl(120, 100%, 40%);">+                    return;</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           lchan_fail("Failed to setup RTP stream: %s in state %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                     osmo_fsm_event_name(fi->fsm, event),</span><br><span style="color: hsl(120, 100%, 40%);">+                       osmo_fsm_inst_state_name(fi));</span><br><span>            return;</span><br><span> </span><br><span>  default:</span><br><span>@@ -766,208 +731,6 @@</span><br><span>     }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static void lchan_fsm_tch_post_endpoint_available(struct osmo_fsm_inst *fi)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-  struct gsm_lchan *lchan = lchan_fi_lchan(fi);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-   LOG_LCHAN(lchan, LOGL_DEBUG, "MGW endpoint: %s\n",</span><br><span style="color: hsl(0, 100%, 40%);">-              mgwep_ci_name(lchan_use_mgw_endpoint_ci_bts(lchan)));</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- if (is_ipaccess_bts(lchan->ts->trx->bts))</span><br><span style="color: hsl(0, 100%, 40%);">-              lchan_fsm_state_chg(LCHAN_ST_WAIT_IPACC_CRCX_ACK);</span><br><span style="color: hsl(0, 100%, 40%);">-      else</span><br><span style="color: hsl(0, 100%, 40%);">-            lchan_fsm_state_chg(LCHAN_ST_WAIT_MGW_ENDPOINT_CONFIGURED);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static void lchan_fsm_wait_ipacc_crcx_ack_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-   int rc;</span><br><span style="color: hsl(0, 100%, 40%);">- int val;</span><br><span style="color: hsl(0, 100%, 40%);">-        struct gsm_lchan *lchan = lchan_fi_lchan(fi);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-   if (lchan->release_requested) {</span><br><span style="color: hsl(0, 100%, 40%);">-              lchan_fail("Release requested while activating");</span><br><span style="color: hsl(0, 100%, 40%);">-             return;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       val = ipacc_speech_mode(lchan->tch_mode, lchan->type);</span><br><span style="color: hsl(0, 100%, 40%);">-    if (val < 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-               lchan_fail("Cannot determine Abis/IP speech mode for tch_mode=%s type=%s\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                     get_value_string(gsm48_chan_mode_names, lchan->tch_mode),</span><br><span style="color: hsl(0, 100%, 40%);">-                    gsm_lchant_name(lchan->type));</span><br><span style="color: hsl(0, 100%, 40%);">-            return;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-       lchan->abis_ip.speech_mode = val;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    val = ipacc_payload_type(lchan->tch_mode, lchan->type);</span><br><span style="color: hsl(0, 100%, 40%);">-   if (val < 0) {</span><br><span style="color: hsl(0, 100%, 40%);">-               lchan_fail("Cannot determine Abis/IP payload type for tch_mode=%s type=%s\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                    get_value_string(gsm48_chan_mode_names, lchan->tch_mode),</span><br><span style="color: hsl(0, 100%, 40%);">-                    gsm_lchant_name(lchan->type));</span><br><span style="color: hsl(0, 100%, 40%);">-            return;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-       lchan->abis_ip.rtp_payload = val;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    /* recv-only */</span><br><span style="color: hsl(0, 100%, 40%);">- ipacc_speech_mode_set_direction(&lchan->abis_ip.speech_mode, false);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-     rc = rsl_tx_ipacc_crcx(lchan);</span><br><span style="color: hsl(0, 100%, 40%);">-  if (rc)</span><br><span style="color: hsl(0, 100%, 40%);">-         lchan_fail("Failure to transmit IPACC CRCX to BTS (rc=%d, %s)",</span><br><span style="color: hsl(0, 100%, 40%);">-                          rc, strerror(-rc));</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static void lchan_fsm_wait_ipacc_crcx_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- switch (event) {</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        case LCHAN_EV_IPACC_CRCX_ACK:</span><br><span style="color: hsl(0, 100%, 40%);">-           /* the CRCX ACK parsing has already noted the RTP port information at</span><br><span style="color: hsl(0, 100%, 40%);">-            * lchan->abis_ip.bound_*, see ipac_parse_rtp(). We'll use that in</span><br><span style="color: hsl(0, 100%, 40%);">-                * lchan_fsm_wait_mgw_endpoint_configured_onenter(). */</span><br><span style="color: hsl(0, 100%, 40%);">-         lchan_fsm_state_chg(LCHAN_ST_WAIT_IPACC_MDCX_ACK);</span><br><span style="color: hsl(0, 100%, 40%);">-              return;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- case LCHAN_EV_IPACC_CRCX_NACK:</span><br><span style="color: hsl(0, 100%, 40%);">-          lchan_fail("Received NACK on IPACC CRCX");</span><br><span style="color: hsl(0, 100%, 40%);">-            return;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- case LCHAN_EV_RLL_ESTABLISH_IND:</span><br><span style="color: hsl(0, 100%, 40%);">-                /* abis_rsl.c has noticed that a SAPI was established, no need to take action here. */</span><br><span style="color: hsl(0, 100%, 40%);">-          return;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- default:</span><br><span style="color: hsl(0, 100%, 40%);">-                OSMO_ASSERT(false);</span><br><span style="color: hsl(0, 100%, 40%);">-     }</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static void lchan_fsm_wait_ipacc_mdcx_ack_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-     int rc;</span><br><span style="color: hsl(0, 100%, 40%);">- struct gsm_lchan *lchan = lchan_fi_lchan(fi);</span><br><span style="color: hsl(0, 100%, 40%);">-   const struct mgcp_conn_peer *mgw_rtp;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-   if (lchan->release_requested) {</span><br><span style="color: hsl(0, 100%, 40%);">-              lchan_fail("Release requested while activating");</span><br><span style="color: hsl(0, 100%, 40%);">-             return;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       mgw_rtp = mgwep_ci_get_rtp_info(lchan_use_mgw_endpoint_ci_bts(lchan));</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-  if (!mgw_rtp) {</span><br><span style="color: hsl(0, 100%, 40%);">-         lchan_fail("Cannot send IPACC MDCX to BTS:"</span><br><span style="color: hsl(0, 100%, 40%);">-                      " there is no RTP IP+port set that the BTS should send RTP to.");</span><br><span style="color: hsl(0, 100%, 40%);">-          return;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       /* Other RTP settings were already setup in lchan_fsm_wait_ipacc_crcx_ack_onenter() */</span><br><span style="color: hsl(0, 100%, 40%);">-  lchan->abis_ip.connect_ip = ntohl(inet_addr(mgw_rtp->addr));</span><br><span style="color: hsl(0, 100%, 40%);">-      lchan->abis_ip.connect_port = mgw_rtp->port;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-      /* send-recv */</span><br><span style="color: hsl(0, 100%, 40%);">- ipacc_speech_mode_set_direction(&lchan->abis_ip.speech_mode, true);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-      rc = rsl_tx_ipacc_mdcx(lchan);</span><br><span style="color: hsl(0, 100%, 40%);">-  if (rc)</span><br><span style="color: hsl(0, 100%, 40%);">-         lchan_fail("Failure to transmit IPACC MDCX to BTS (rc=%d, %s)",</span><br><span style="color: hsl(0, 100%, 40%);">-                          rc, strerror(-rc));</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static void lchan_fsm_wait_ipacc_mdcx_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- switch (event) {</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        case LCHAN_EV_IPACC_MDCX_ACK:</span><br><span style="color: hsl(0, 100%, 40%);">-           /* Finally, the lchan and its RTP are established. */</span><br><span style="color: hsl(0, 100%, 40%);">-           lchan_fsm_state_chg(LCHAN_ST_WAIT_MGW_ENDPOINT_CONFIGURED);</span><br><span style="color: hsl(0, 100%, 40%);">-             return;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- case LCHAN_EV_IPACC_MDCX_NACK:</span><br><span style="color: hsl(0, 100%, 40%);">-          lchan_fail("Received NACK on IPACC MDCX");</span><br><span style="color: hsl(0, 100%, 40%);">-            return;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- case LCHAN_EV_RLL_ESTABLISH_IND:</span><br><span style="color: hsl(0, 100%, 40%);">-                /* abis_rsl.c has noticed that a SAPI was established, no need to take action here. */</span><br><span style="color: hsl(0, 100%, 40%);">-          return;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- default:</span><br><span style="color: hsl(0, 100%, 40%);">-                OSMO_ASSERT(false);</span><br><span style="color: hsl(0, 100%, 40%);">-     }</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/* Tell the MGW endpoint about the RTP port allocated on BTS side. */</span><br><span style="color: hsl(0, 100%, 40%);">-static void lchan_fsm_wait_mgw_endpoint_configured_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-       int rc;</span><br><span style="color: hsl(0, 100%, 40%);">- struct mgcp_conn_peer mdcx_info;</span><br><span style="color: hsl(0, 100%, 40%);">-        struct in_addr addr;</span><br><span style="color: hsl(0, 100%, 40%);">-    const char *addr_str;</span><br><span style="color: hsl(0, 100%, 40%);">-   struct gsm_lchan *lchan = lchan_fi_lchan(fi);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-   if (lchan->release_requested) {</span><br><span style="color: hsl(0, 100%, 40%);">-              lchan_fail("Release requested while activating");</span><br><span style="color: hsl(0, 100%, 40%);">-             return;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       mdcx_info = (struct mgcp_conn_peer){</span><br><span style="color: hsl(0, 100%, 40%);">-            .port = lchan->abis_ip.bound_port,</span><br><span style="color: hsl(0, 100%, 40%);">-           .ptime = 20,</span><br><span style="color: hsl(0, 100%, 40%);">-    };</span><br><span style="color: hsl(0, 100%, 40%);">-      mgcp_pick_codec(&mdcx_info, lchan);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- addr.s_addr = osmo_ntohl(lchan->abis_ip.bound_ip);</span><br><span style="color: hsl(0, 100%, 40%);">-   addr_str = inet_ntoa(addr);</span><br><span style="color: hsl(0, 100%, 40%);">-     rc = osmo_strlcpy(mdcx_info.addr, addr_str, sizeof(mdcx_info.addr));</span><br><span style="color: hsl(0, 100%, 40%);">-    if (rc <= 0 || rc >= sizeof(mdcx_info.addr)) {</span><br><span style="color: hsl(0, 100%, 40%);">-            lchan_fail("Cannot compose BTS side RTP IP address to send to MGW: '%s'",</span><br><span style="color: hsl(0, 100%, 40%);">-                        addr_str);</span><br><span style="color: hsl(0, 100%, 40%);">-           return;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       /* At this point, we are taking over an old lchan's MGW endpoint (if any). */</span><br><span style="color: hsl(0, 100%, 40%);">-       if (!lchan->mgw_endpoint_ci_bts</span><br><span style="color: hsl(0, 100%, 40%);">-          && lchan->activate.re_use_mgw_endpoint_from_lchan) {</span><br><span style="color: hsl(0, 100%, 40%);">-             lchan->mgw_endpoint_ci_bts =</span><br><span style="color: hsl(0, 100%, 40%);">-                 lchan->activate.re_use_mgw_endpoint_from_lchan->mgw_endpoint_ci_bts;</span><br><span style="color: hsl(0, 100%, 40%);">-              /* The old lchan shall forget the enpoint now. */</span><br><span style="color: hsl(0, 100%, 40%);">-               lchan->activate.re_use_mgw_endpoint_from_lchan->mgw_endpoint_ci_bts = NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-       }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       if (!lchan->mgw_endpoint_ci_bts) {</span><br><span style="color: hsl(0, 100%, 40%);">-           lchan_fail("No MGW endpoint ci configured");</span><br><span style="color: hsl(0, 100%, 40%);">-          return;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       LOG_LCHAN(lchan, LOGL_DEBUG, "Sending BTS side RTP port info %s:%u to MGW %s\n",</span><br><span style="color: hsl(0, 100%, 40%);">-                mdcx_info.addr, mdcx_info.port, mgwep_ci_name(lchan->mgw_endpoint_ci_bts));</span><br><span style="color: hsl(0, 100%, 40%);">-        mgw_endpoint_ci_request(lchan->mgw_endpoint_ci_bts, MGCP_VERB_MDCX,</span><br><span style="color: hsl(0, 100%, 40%);">-                          &mdcx_info, fi, LCHAN_EV_MGW_ENDPOINT_CONFIGURED,</span><br><span style="color: hsl(0, 100%, 40%);">-                           LCHAN_EV_MGW_ENDPOINT_ERROR, 0);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static void lchan_fsm_wait_mgw_endpoint_configured(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-      switch (event) {</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        case LCHAN_EV_MGW_ENDPOINT_CONFIGURED:</span><br><span style="color: hsl(0, 100%, 40%);">-          lchan_fsm_state_chg(LCHAN_ST_ESTABLISHED);</span><br><span style="color: hsl(0, 100%, 40%);">-              return;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- case LCHAN_EV_MGW_ENDPOINT_ERROR:</span><br><span style="color: hsl(0, 100%, 40%);">-               lchan_fail("Error while redirecting the MGW to the BTS' RTP port");</span><br><span style="color: hsl(0, 100%, 40%);">-               return;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- case LCHAN_EV_RLL_ESTABLISH_IND:</span><br><span style="color: hsl(0, 100%, 40%);">-                /* abis_rsl.c has noticed that a SAPI was established, no need to take action here. */</span><br><span style="color: hsl(0, 100%, 40%);">-          return;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- default:</span><br><span style="color: hsl(0, 100%, 40%);">-                OSMO_ASSERT(false);</span><br><span style="color: hsl(0, 100%, 40%);">-     }</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> static void lchan_fsm_established_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)</span><br><span> {</span><br><span>  struct gsm_lchan *lchan = lchan_fi_lchan(fi);</span><br><span>@@ -981,7 +744,7 @@</span><br><span>   * like Immediate Assignment or BSSMAP Assignment Complete, and if then, way later, some other</span><br><span>        * error occurs, e.g. during release, that we don't send a NACK out of context. */</span><br><span>       lchan->activate.concluded = true;</span><br><span style="color: hsl(0, 100%, 40%);">-    lchan_on_activation_success(lchan);</span><br><span style="color: hsl(120, 100%, 40%);">+   lchan_on_fully_established(lchan);</span><br><span> }</span><br><span> </span><br><span> #define for_each_sapi(sapi, start, lchan) \</span><br><span>@@ -1015,8 +778,7 @@</span><br><span>    return sapis;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static void handle_rll_rel_ind_or_conf(struct osmo_fsm_inst *fi, uint32_t event, void *data,</span><br><span style="color: hsl(0, 100%, 40%);">-                                     bool wait_for_sapi0_rel)</span><br><span style="color: hsl(120, 100%, 40%);">+static void handle_rll_rel_ind_or_conf(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span> {</span><br><span>   uint8_t link_id;</span><br><span>     uint8_t sapi;</span><br><span>@@ -1043,12 +805,13 @@</span><br><span>               gscon_lchan_releasing(lchan->conn, lchan);</span><br><span>        }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   if (!lchan_active_sapis(lchan, wait_for_sapi0_rel? 0 : 1))</span><br><span style="color: hsl(0, 100%, 40%);">-              lchan_fsm_state_chg(LCHAN_ST_WAIT_BEFORE_RF_RELEASE);</span><br><span style="color: hsl(120, 100%, 40%);">+ /* The caller shall check whether all SAPIs are released and cause a state chg */</span><br><span> }</span><br><span> </span><br><span> static void lchan_fsm_established(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span> {</span><br><span style="color: hsl(120, 100%, 40%);">+    struct gsm_lchan *lchan = lchan_fi_lchan(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>      switch (event) {</span><br><span>     case LCHAN_EV_RLL_ESTABLISH_IND:</span><br><span>             /* abis_rsl.c has noticed that a SAPI was established, no need to take action here. */</span><br><span>@@ -1056,7 +819,22 @@</span><br><span> </span><br><span>   case LCHAN_EV_RLL_REL_IND:</span><br><span>   case LCHAN_EV_RLL_REL_CONF:</span><br><span style="color: hsl(0, 100%, 40%);">-             handle_rll_rel_ind_or_conf(fi, event, data, true);</span><br><span style="color: hsl(120, 100%, 40%);">+            handle_rll_rel_ind_or_conf(fi, event, data);</span><br><span style="color: hsl(120, 100%, 40%);">+          if (!lchan_active_sapis(lchan, 0))</span><br><span style="color: hsl(120, 100%, 40%);">+                    lchan_fsm_state_chg(LCHAN_ST_WAIT_RLL_RTP_RELEASED);</span><br><span style="color: hsl(120, 100%, 40%);">+          return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     case LCHAN_EV_RTP_RELEASED:</span><br><span style="color: hsl(120, 100%, 40%);">+   case LCHAN_EV_RTP_ERROR:</span><br><span style="color: hsl(120, 100%, 40%);">+              if (lchan->release_requested) {</span><br><span style="color: hsl(120, 100%, 40%);">+                    /* Already in release, the RTP is not the initial cause of failure.</span><br><span style="color: hsl(120, 100%, 40%);">+                    * Just ignore. */</span><br><span style="color: hsl(120, 100%, 40%);">+                    return;</span><br><span style="color: hsl(120, 100%, 40%);">+               }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           lchan_fail("RTP stream closed unexpectedly: %s in state %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                         osmo_fsm_event_name(fi->fsm, event),</span><br><span style="color: hsl(120, 100%, 40%);">+                       osmo_fsm_inst_state_name(fi));</span><br><span>            return;</span><br><span> </span><br><span>  default:</span><br><span>@@ -1079,7 +857,7 @@</span><br><span>      }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static void lchan_fsm_wait_sapis_released_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)</span><br><span style="color: hsl(120, 100%, 40%);">+static void lchan_fsm_wait_rll_rtp_released_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)</span><br><span> {</span><br><span>  int sapis;</span><br><span>   int sapi;</span><br><span>@@ -1089,9 +867,12 @@</span><br><span>            if (lchan->sapis[sapi])</span><br><span>                   LOG_LCHAN(lchan, LOGL_DEBUG, "SAPI[%d] = %d\n", sapi, lchan->sapis[sapi]);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-     if (lchan->conn)</span><br><span style="color: hsl(120, 100%, 40%);">+   if (lchan->conn && lchan->sapis[0] != LCHAN_SAPI_UNUSED)</span><br><span>               gsm48_send_rr_release(lchan);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+     if (lchan->fi_rtp)</span><br><span style="color: hsl(120, 100%, 40%);">+         osmo_fsm_inst_dispatch(lchan->fi_rtp, LCHAN_RTP_EV_RELEASE, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>         if (lchan->deact_sacch && should_sacch_deact(lchan))</span><br><span>              rsl_deact_sacch(lchan);</span><br><span> </span><br><span>@@ -1117,16 +898,33 @@</span><br><span>                 sapis = 0;</span><br><span>   }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-   if (!sapis)</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!sapis && !lchan->fi_rtp)</span><br><span>             lchan_fsm_state_chg(LCHAN_ST_WAIT_BEFORE_RF_RELEASE);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static void lchan_fsm_wait_sapis_released(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+static void lchan_fsm_wait_rll_rtp_released(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-   /* When we're telling the MS to release, we're fine to carry on with RF Channel Release when SAPI</span><br><span style="color: hsl(0, 100%, 40%);">-        * 0 release is not confirmed yet.</span><br><span style="color: hsl(0, 100%, 40%);">-       * TODO: that's how the code was before lchan FSM, is this correct/useful? */</span><br><span style="color: hsl(0, 100%, 40%);">-       handle_rll_rel_ind_or_conf(fi, event, data, false);</span><br><span style="color: hsl(120, 100%, 40%);">+   struct gsm_lchan *lchan = lchan_fi_lchan(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (event) {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    case LCHAN_EV_RLL_REL_IND:</span><br><span style="color: hsl(120, 100%, 40%);">+    case LCHAN_EV_RLL_REL_CONF:</span><br><span style="color: hsl(120, 100%, 40%);">+           /* When we're telling the MS to release, we're fine to carry on with RF Channel Release</span><br><span style="color: hsl(120, 100%, 40%);">+                * when SAPI 0 release is not confirmed yet.</span><br><span style="color: hsl(120, 100%, 40%);">+           * TODO: that's how the code was before lchan FSM, is this correct/useful? */</span><br><span style="color: hsl(120, 100%, 40%);">+             handle_rll_rel_ind_or_conf(fi, event, data);</span><br><span style="color: hsl(120, 100%, 40%);">+          break;</span><br><span style="color: hsl(120, 100%, 40%);">+        </span><br><span style="color: hsl(120, 100%, 40%);">+      case LCHAN_EV_RTP_RELEASED:</span><br><span style="color: hsl(120, 100%, 40%);">+   case LCHAN_EV_RTP_ERROR:</span><br><span style="color: hsl(120, 100%, 40%);">+              break;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      default:</span><br><span style="color: hsl(120, 100%, 40%);">+              OSMO_ASSERT(false);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!lchan_active_sapis(lchan, 1) && !lchan->fi_rtp)</span><br><span style="color: hsl(120, 100%, 40%);">+               lchan_fsm_state_chg(LCHAN_ST_WAIT_BEFORE_RF_RELEASE);</span><br><span> }</span><br><span> </span><br><span> static void lchan_fsm_wait_rf_release_ack_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)</span><br><span>@@ -1194,6 +992,10 @@</span><br><span>            * independently from the BTS model, right?? */</span><br><span>              return;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+   case LCHAN_EV_RTP_RELEASED:</span><br><span style="color: hsl(120, 100%, 40%);">+   case LCHAN_EV_RTP_ERROR:</span><br><span style="color: hsl(120, 100%, 40%);">+              return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span>    default:</span><br><span>             OSMO_ASSERT(false);</span><br><span>  }</span><br><span>@@ -1219,7 +1021,8 @@</span><br><span>            .action = lchan_fsm_wait_ts_ready,</span><br><span>           .in_event_mask = 0</span><br><span>                   | S(LCHAN_EV_TS_READY)</span><br><span style="color: hsl(0, 100%, 40%);">-                  | S(LCHAN_EV_MGW_ENDPOINT_AVAILABLE)</span><br><span style="color: hsl(120, 100%, 40%);">+                  | S(LCHAN_EV_RTP_ERROR)</span><br><span style="color: hsl(120, 100%, 40%);">+                       | S(LCHAN_EV_RTP_RELEASED)</span><br><span>                   ,</span><br><span>            .out_state_mask = 0</span><br><span>                  | S(LCHAN_ST_UNUSED)</span><br><span>@@ -1231,94 +1034,33 @@</span><br><span>               .onenter = lchan_fsm_wait_activ_ack_onenter,</span><br><span>                 .action = lchan_fsm_wait_activ_ack,</span><br><span>          .in_event_mask = 0</span><br><span style="color: hsl(0, 100%, 40%);">-                      | S(LCHAN_EV_MGW_ENDPOINT_AVAILABLE)</span><br><span>                         | S(LCHAN_EV_RSL_CHAN_ACTIV_ACK)</span><br><span>                     | S(LCHAN_EV_RSL_CHAN_ACTIV_NACK)</span><br><span style="color: hsl(120, 100%, 40%);">+                     | S(LCHAN_EV_RTP_ERROR)</span><br><span style="color: hsl(120, 100%, 40%);">+                       | S(LCHAN_EV_RTP_RELEASED)</span><br><span>                   ,</span><br><span>            .out_state_mask = 0</span><br><span>                  | S(LCHAN_ST_UNUSED)</span><br><span style="color: hsl(0, 100%, 40%);">-                    | S(LCHAN_ST_WAIT_RLL_ESTABLISH)</span><br><span style="color: hsl(120, 100%, 40%);">+                      | S(LCHAN_ST_WAIT_RLL_RTP_ESTABLISH)</span><br><span>                         | S(LCHAN_ST_BORKEN)</span><br><span>                         | S(LCHAN_ST_WAIT_RF_RELEASE_ACK)</span><br><span>                    ,</span><br><span>    },</span><br><span style="color: hsl(0, 100%, 40%);">-      [LCHAN_ST_WAIT_RLL_ESTABLISH] = {</span><br><span style="color: hsl(0, 100%, 40%);">-               .name = "WAIT_RLL_ESTABLISH",</span><br><span style="color: hsl(0, 100%, 40%);">-         .onenter = lchan_fsm_wait_rll_establish_onenter,</span><br><span style="color: hsl(0, 100%, 40%);">-                .action = lchan_fsm_wait_rll_establish,</span><br><span style="color: hsl(120, 100%, 40%);">+       [LCHAN_ST_WAIT_RLL_RTP_ESTABLISH] = {</span><br><span style="color: hsl(120, 100%, 40%);">+         .name = "WAIT_RLL_RTP_ESTABLISH",</span><br><span style="color: hsl(120, 100%, 40%);">+           .onenter = lchan_fsm_wait_rll_rtp_establish_onenter,</span><br><span style="color: hsl(120, 100%, 40%);">+          .action = lchan_fsm_wait_rll_rtp_establish,</span><br><span>          .in_event_mask = 0</span><br><span style="color: hsl(0, 100%, 40%);">-                      | S(LCHAN_EV_MGW_ENDPOINT_AVAILABLE)</span><br><span>                         | S(LCHAN_EV_RLL_ESTABLISH_IND)</span><br><span style="color: hsl(0, 100%, 40%);">-                 ,</span><br><span style="color: hsl(0, 100%, 40%);">-               .out_state_mask = 0</span><br><span style="color: hsl(0, 100%, 40%);">-                     | S(LCHAN_ST_UNUSED)</span><br><span style="color: hsl(0, 100%, 40%);">-                    | S(LCHAN_ST_WAIT_MGW_ENDPOINT_AVAILABLE)</span><br><span style="color: hsl(0, 100%, 40%);">-                       | S(LCHAN_ST_ESTABLISHED)</span><br><span style="color: hsl(0, 100%, 40%);">-                       | S(LCHAN_ST_WAIT_RF_RELEASE_ACK)</span><br><span style="color: hsl(0, 100%, 40%);">-                       | S(LCHAN_ST_WAIT_SAPIS_RELEASED)</span><br><span style="color: hsl(0, 100%, 40%);">-                       ,</span><br><span style="color: hsl(0, 100%, 40%);">-       },</span><br><span style="color: hsl(0, 100%, 40%);">-      [LCHAN_ST_WAIT_MGW_ENDPOINT_AVAILABLE] = {</span><br><span style="color: hsl(0, 100%, 40%);">-              .name = "WAIT_MGW_ENDPOINT_AVAILABLE",</span><br><span style="color: hsl(0, 100%, 40%);">-                .onenter = lchan_fsm_wait_mgw_endpoint_available_onenter,</span><br><span style="color: hsl(0, 100%, 40%);">-               .action = lchan_fsm_wait_mgw_endpoint_available,</span><br><span style="color: hsl(0, 100%, 40%);">-                .in_event_mask = 0</span><br><span style="color: hsl(0, 100%, 40%);">-                      | S(LCHAN_EV_MGW_ENDPOINT_AVAILABLE)</span><br><span style="color: hsl(0, 100%, 40%);">-                    | S(LCHAN_EV_RLL_ESTABLISH_IND) /* ignored */</span><br><span style="color: hsl(0, 100%, 40%);">-                   ,</span><br><span style="color: hsl(0, 100%, 40%);">-               .out_state_mask = 0</span><br><span style="color: hsl(0, 100%, 40%);">-                     | S(LCHAN_ST_UNUSED)</span><br><span style="color: hsl(0, 100%, 40%);">-                    | S(LCHAN_ST_WAIT_IPACC_CRCX_ACK)</span><br><span style="color: hsl(0, 100%, 40%);">-                       | S(LCHAN_ST_WAIT_MGW_ENDPOINT_CONFIGURED)</span><br><span style="color: hsl(0, 100%, 40%);">-                      | S(LCHAN_ST_WAIT_SAPIS_RELEASED)</span><br><span style="color: hsl(0, 100%, 40%);">-                       | S(LCHAN_ST_WAIT_RF_RELEASE_ACK)</span><br><span style="color: hsl(0, 100%, 40%);">-                       ,</span><br><span style="color: hsl(0, 100%, 40%);">-       },</span><br><span style="color: hsl(0, 100%, 40%);">-      [LCHAN_ST_WAIT_IPACC_CRCX_ACK] = {</span><br><span style="color: hsl(0, 100%, 40%);">-              .name = "WAIT_IPACC_CRCX_ACK",</span><br><span style="color: hsl(0, 100%, 40%);">-                .onenter = lchan_fsm_wait_ipacc_crcx_ack_onenter,</span><br><span style="color: hsl(0, 100%, 40%);">-               .action = lchan_fsm_wait_ipacc_crcx_ack,</span><br><span style="color: hsl(0, 100%, 40%);">-                .in_event_mask = 0</span><br><span style="color: hsl(0, 100%, 40%);">-                      | S(LCHAN_EV_IPACC_CRCX_ACK)</span><br><span style="color: hsl(0, 100%, 40%);">-                    | S(LCHAN_EV_IPACC_CRCX_NACK)</span><br><span style="color: hsl(0, 100%, 40%);">-                   | S(LCHAN_EV_RLL_ESTABLISH_IND) /* ignored */</span><br><span style="color: hsl(0, 100%, 40%);">-                   ,</span><br><span style="color: hsl(0, 100%, 40%);">-               .out_state_mask = 0</span><br><span style="color: hsl(0, 100%, 40%);">-                     | S(LCHAN_ST_UNUSED)</span><br><span style="color: hsl(0, 100%, 40%);">-                    | S(LCHAN_ST_WAIT_IPACC_MDCX_ACK)</span><br><span style="color: hsl(0, 100%, 40%);">-                       | S(LCHAN_ST_WAIT_SAPIS_RELEASED)</span><br><span style="color: hsl(0, 100%, 40%);">-                       | S(LCHAN_ST_WAIT_RF_RELEASE_ACK)</span><br><span style="color: hsl(0, 100%, 40%);">-                       ,</span><br><span style="color: hsl(0, 100%, 40%);">-       },</span><br><span style="color: hsl(0, 100%, 40%);">-      [LCHAN_ST_WAIT_IPACC_MDCX_ACK] = {</span><br><span style="color: hsl(0, 100%, 40%);">-              .name = "WAIT_IPACC_MDCX_ACK",</span><br><span style="color: hsl(0, 100%, 40%);">-                .onenter = lchan_fsm_wait_ipacc_mdcx_ack_onenter,</span><br><span style="color: hsl(0, 100%, 40%);">-               .action = lchan_fsm_wait_ipacc_mdcx_ack,</span><br><span style="color: hsl(0, 100%, 40%);">-                .in_event_mask = 0</span><br><span style="color: hsl(0, 100%, 40%);">-                      | S(LCHAN_EV_IPACC_MDCX_ACK)</span><br><span style="color: hsl(0, 100%, 40%);">-                    | S(LCHAN_EV_IPACC_MDCX_NACK)</span><br><span style="color: hsl(0, 100%, 40%);">-                   | S(LCHAN_EV_RLL_ESTABLISH_IND) /* ignored */</span><br><span style="color: hsl(0, 100%, 40%);">-                   ,</span><br><span style="color: hsl(0, 100%, 40%);">-               .out_state_mask = 0</span><br><span style="color: hsl(0, 100%, 40%);">-                     | S(LCHAN_ST_UNUSED)</span><br><span style="color: hsl(0, 100%, 40%);">-                    | S(LCHAN_ST_WAIT_MGW_ENDPOINT_CONFIGURED)</span><br><span style="color: hsl(0, 100%, 40%);">-                      | S(LCHAN_ST_WAIT_SAPIS_RELEASED)</span><br><span style="color: hsl(0, 100%, 40%);">-                       | S(LCHAN_ST_WAIT_RF_RELEASE_ACK)</span><br><span style="color: hsl(0, 100%, 40%);">-                       ,</span><br><span style="color: hsl(0, 100%, 40%);">-       },</span><br><span style="color: hsl(0, 100%, 40%);">-      [LCHAN_ST_WAIT_MGW_ENDPOINT_CONFIGURED] = {</span><br><span style="color: hsl(0, 100%, 40%);">-             .name = "WAIT_MGW_ENDPOINT_CONFIGURED",</span><br><span style="color: hsl(0, 100%, 40%);">-               .onenter = lchan_fsm_wait_mgw_endpoint_configured_onenter,</span><br><span style="color: hsl(0, 100%, 40%);">-              .action = lchan_fsm_wait_mgw_endpoint_configured,</span><br><span style="color: hsl(0, 100%, 40%);">-               .in_event_mask = 0</span><br><span style="color: hsl(0, 100%, 40%);">-                      | S(LCHAN_EV_MGW_ENDPOINT_CONFIGURED)</span><br><span style="color: hsl(0, 100%, 40%);">-                   | S(LCHAN_EV_RLL_ESTABLISH_IND) /* ignored */</span><br><span style="color: hsl(120, 100%, 40%);">+                 | S(LCHAN_EV_RTP_READY)</span><br><span style="color: hsl(120, 100%, 40%);">+                       | S(LCHAN_EV_RTP_ERROR)</span><br><span style="color: hsl(120, 100%, 40%);">+                       | S(LCHAN_EV_RTP_RELEASED)</span><br><span>                   ,</span><br><span>            .out_state_mask = 0</span><br><span>                  | S(LCHAN_ST_UNUSED)</span><br><span>                         | S(LCHAN_ST_ESTABLISHED)</span><br><span style="color: hsl(0, 100%, 40%);">-                       | S(LCHAN_ST_WAIT_SAPIS_RELEASED)</span><br><span>                    | S(LCHAN_ST_WAIT_RF_RELEASE_ACK)</span><br><span style="color: hsl(120, 100%, 40%);">+                     | S(LCHAN_ST_WAIT_RLL_RTP_RELEASED)</span><br><span>                  ,</span><br><span>    },</span><br><span>   [LCHAN_ST_ESTABLISHED] = {</span><br><span>@@ -1329,21 +1071,25 @@</span><br><span>                         | S(LCHAN_EV_RLL_REL_IND)</span><br><span>                    | S(LCHAN_EV_RLL_REL_CONF)</span><br><span>                   | S(LCHAN_EV_RLL_ESTABLISH_IND) /* ignored */</span><br><span style="color: hsl(120, 100%, 40%);">+                 | S(LCHAN_EV_RTP_ERROR)</span><br><span style="color: hsl(120, 100%, 40%);">+                       | S(LCHAN_EV_RTP_RELEASED)</span><br><span>                   ,</span><br><span>            .out_state_mask = 0</span><br><span>                  | S(LCHAN_ST_UNUSED)</span><br><span style="color: hsl(0, 100%, 40%);">-                    | S(LCHAN_ST_WAIT_SAPIS_RELEASED)</span><br><span style="color: hsl(120, 100%, 40%);">+                     | S(LCHAN_ST_WAIT_RLL_RTP_RELEASED)</span><br><span>                  | S(LCHAN_ST_WAIT_BEFORE_RF_RELEASE)</span><br><span>                         | S(LCHAN_ST_WAIT_RF_RELEASE_ACK)</span><br><span>                    ,</span><br><span>    },</span><br><span style="color: hsl(0, 100%, 40%);">-      [LCHAN_ST_WAIT_SAPIS_RELEASED] = {</span><br><span style="color: hsl(0, 100%, 40%);">-              .name = "WAIT_SAPIS_RELEASED",</span><br><span style="color: hsl(0, 100%, 40%);">-                .onenter = lchan_fsm_wait_sapis_released_onenter,</span><br><span style="color: hsl(0, 100%, 40%);">-               .action = lchan_fsm_wait_sapis_released,</span><br><span style="color: hsl(120, 100%, 40%);">+      [LCHAN_ST_WAIT_RLL_RTP_RELEASED] = {</span><br><span style="color: hsl(120, 100%, 40%);">+          .name = "WAIT_RLL_RTP_RELEASED",</span><br><span style="color: hsl(120, 100%, 40%);">+            .onenter = lchan_fsm_wait_rll_rtp_released_onenter,</span><br><span style="color: hsl(120, 100%, 40%);">+           .action = lchan_fsm_wait_rll_rtp_released,</span><br><span>           .in_event_mask = 0</span><br><span>                   | S(LCHAN_EV_RLL_REL_IND)</span><br><span>                    | S(LCHAN_EV_RLL_REL_CONF)</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(LCHAN_EV_RTP_ERROR)</span><br><span style="color: hsl(120, 100%, 40%);">+                       | S(LCHAN_EV_RTP_RELEASED)</span><br><span>                   ,</span><br><span>            .out_state_mask = 0</span><br><span>                  | S(LCHAN_ST_UNUSED)</span><br><span>@@ -1388,6 +1134,8 @@</span><br><span>                         | S(LCHAN_EV_RSL_CHAN_ACTIV_ACK)</span><br><span>                     | S(LCHAN_EV_RSL_CHAN_ACTIV_NACK)</span><br><span>                    | S(LCHAN_EV_RSL_RF_CHAN_REL_ACK)</span><br><span style="color: hsl(120, 100%, 40%);">+                     | S(LCHAN_EV_RTP_ERROR)</span><br><span style="color: hsl(120, 100%, 40%);">+                       | S(LCHAN_EV_RTP_RELEASED)</span><br><span>                   ,</span><br><span>            .out_state_mask = 0</span><br><span>                  | S(LCHAN_ST_UNUSED)</span><br><span>@@ -1403,13 +1151,9 @@</span><br><span>        OSMO_VALUE_STRING(LCHAN_EV_RSL_CHAN_ACTIV_ACK),</span><br><span>      OSMO_VALUE_STRING(LCHAN_EV_RSL_CHAN_ACTIV_NACK),</span><br><span>     OSMO_VALUE_STRING(LCHAN_EV_RLL_ESTABLISH_IND),</span><br><span style="color: hsl(0, 100%, 40%);">-  OSMO_VALUE_STRING(LCHAN_EV_MGW_ENDPOINT_AVAILABLE),</span><br><span style="color: hsl(0, 100%, 40%);">-     OSMO_VALUE_STRING(LCHAN_EV_MGW_ENDPOINT_CONFIGURED),</span><br><span style="color: hsl(0, 100%, 40%);">-    OSMO_VALUE_STRING(LCHAN_EV_MGW_ENDPOINT_ERROR),</span><br><span style="color: hsl(0, 100%, 40%);">- OSMO_VALUE_STRING(LCHAN_EV_IPACC_CRCX_ACK),</span><br><span style="color: hsl(0, 100%, 40%);">-     OSMO_VALUE_STRING(LCHAN_EV_IPACC_CRCX_NACK),</span><br><span style="color: hsl(0, 100%, 40%);">-    OSMO_VALUE_STRING(LCHAN_EV_IPACC_MDCX_ACK),</span><br><span style="color: hsl(0, 100%, 40%);">-     OSMO_VALUE_STRING(LCHAN_EV_IPACC_MDCX_NACK),</span><br><span style="color: hsl(120, 100%, 40%);">+  OSMO_VALUE_STRING(LCHAN_EV_RTP_READY),</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_VALUE_STRING(LCHAN_EV_RTP_ERROR),</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_VALUE_STRING(LCHAN_EV_RTP_RELEASED),</span><br><span>    OSMO_VALUE_STRING(LCHAN_EV_RLL_REL_IND),</span><br><span>     OSMO_VALUE_STRING(LCHAN_EV_RLL_REL_CONF),</span><br><span>    OSMO_VALUE_STRING(LCHAN_EV_RSL_RF_CHAN_REL_ACK),</span><br><span>@@ -1421,27 +1165,12 @@</span><br><span> </span><br><span> void lchan_fsm_allstate_action(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">-     struct gsm_lchan *lchan = lchan_fi_lchan(fi);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>        switch (event) {</span><br><span> </span><br><span>         case LCHAN_EV_TS_ERROR:</span><br><span>              lchan_fail_to(LCHAN_ST_UNUSED, "LCHAN_EV_TS_ERROR");</span><br><span>               return;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-     case LCHAN_EV_MGW_ENDPOINT_ERROR:</span><br><span style="color: hsl(0, 100%, 40%);">-               /* This event during activation means that it was not possible to establish an endpoint.</span><br><span style="color: hsl(0, 100%, 40%);">-                 * After activation was successful, it could also come in at any point to signal that the</span><br><span style="color: hsl(0, 100%, 40%);">-                * MGW side has become unavailable, which should lead to graceful release. */</span><br><span style="color: hsl(0, 100%, 40%);">-           if (fi->state == LCHAN_ST_WAIT_MGW_ENDPOINT_AVAILABLE) {</span><br><span style="color: hsl(0, 100%, 40%);">-                     /* This state is actually waiting for availability. Fail it immediately. */</span><br><span style="color: hsl(0, 100%, 40%);">-                     lchan_fail("LCHAN_EV_MGW_ENDPOINT_ERROR");</span><br><span style="color: hsl(0, 100%, 40%);">-                    return;</span><br><span style="color: hsl(0, 100%, 40%);">-         }</span><br><span style="color: hsl(0, 100%, 40%);">-               LOG_LCHAN(lchan, LOGL_ERROR, "Releasing due to MGW endpoint error\n");</span><br><span style="color: hsl(0, 100%, 40%);">-                lchan_release(lchan, false, true, RSL_ERR_EQUIPMENT_FAIL);</span><br><span style="color: hsl(0, 100%, 40%);">-              return;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>      default:</span><br><span>             return;</span><br><span>      }</span><br><span>@@ -1477,28 +1206,26 @@</span><br><span>  lchan->rsl_error_cause = cause_rr;</span><br><span>        lchan->deact_sacch = sacch_deact;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-        /* This would also happen later, but better to do this a sooner. */</span><br><span style="color: hsl(0, 100%, 40%);">-     if (lchan->mgw_endpoint_ci_bts) {</span><br><span style="color: hsl(0, 100%, 40%);">-            mgw_endpoint_ci_dlcx(lchan->mgw_endpoint_ci_bts);</span><br><span style="color: hsl(0, 100%, 40%);">-            lchan->mgw_endpoint_ci_bts = NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-   }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span>    /* States waiting for events will notice the desire to release when done waiting, so it is enough</span><br><span>     * to mark for release. */</span><br><span>   lchan->release_requested = true;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- /* But when in error, shortcut that. */</span><br><span style="color: hsl(120, 100%, 40%);">+       /* If we took the RTP over from another lchan, put it back. */</span><br><span style="color: hsl(120, 100%, 40%);">+        if (lchan->fi_rtp && lchan->release_in_error)</span><br><span style="color: hsl(120, 100%, 40%);">+           osmo_fsm_inst_dispatch(lchan->fi_rtp, LCHAN_RTP_EV_ROLLBACK, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* But when in error, don't wait for the next state to pick up release_requested. */</span><br><span>     if (lchan->release_in_error) {</span><br><span>            switch (lchan->fi->state) {</span><br><span>            default:</span><br><span style="color: hsl(0, 100%, 40%);">-                        /* Normally we deact SACCH in lchan_fsm_wait_sapis_released_onenter(). When</span><br><span style="color: hsl(120, 100%, 40%);">+                   /* Normally we deact SACCH in lchan_fsm_wait_rll_rtp_released_onenter(). When</span><br><span>                         * skipping that, but asked to SACCH deact, do it now. */</span><br><span>                    if (lchan->deact_sacch)</span><br><span>                           rsl_deact_sacch(lchan);</span><br><span>                      lchan_fsm_state_chg(LCHAN_ST_WAIT_RF_RELEASE_ACK);</span><br><span>                   return;</span><br><span>              case LCHAN_ST_WAIT_TS_READY:</span><br><span style="color: hsl(0, 100%, 40%);">-                    lchan_fsm_state_chg(LCHAN_ST_UNUSED);</span><br><span style="color: hsl(120, 100%, 40%);">+                 lchan_fsm_state_chg(LCHAN_ST_WAIT_RLL_RTP_RELEASED);</span><br><span>                         return;</span><br><span>              case LCHAN_ST_WAIT_RF_RELEASE_ACK:</span><br><span>           case LCHAN_ST_BORKEN:</span><br><span>@@ -1509,7 +1236,7 @@</span><br><span>        /* The only non-broken state that would stay stuck without noticing the release_requested flag</span><br><span>        * is: */</span><br><span>    if (fi->state == LCHAN_ST_ESTABLISHED)</span><br><span style="color: hsl(0, 100%, 40%);">-               lchan_fsm_state_chg(LCHAN_ST_WAIT_SAPIS_RELEASED);</span><br><span style="color: hsl(120, 100%, 40%);">+            lchan_fsm_state_chg(LCHAN_ST_WAIT_RLL_RTP_RELEASED);</span><br><span> }</span><br><span> </span><br><span> void lchan_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)</span><br><span>@@ -1525,14 +1252,6 @@</span><br><span>   lchan->fi = NULL;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-/* The mgw_endpoint was invalidated, just and simply forget the pointer without cleanup. */</span><br><span style="color: hsl(0, 100%, 40%);">-void lchan_forget_mgw_endpoint(struct gsm_lchan *lchan)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-        if (!lchan)</span><br><span style="color: hsl(0, 100%, 40%);">-             return;</span><br><span style="color: hsl(0, 100%, 40%);">- lchan->mgw_endpoint_ci_bts = NULL;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> /* The conn is deallocating, just forget all about it */</span><br><span> void lchan_forget_conn(struct gsm_lchan *lchan)</span><br><span> {</span><br><span>@@ -1551,7 +1270,6 @@</span><br><span>   .allstate_action = lchan_fsm_allstate_action,</span><br><span>        .allstate_event_mask = 0</span><br><span>             | S(LCHAN_EV_TS_ERROR)</span><br><span style="color: hsl(0, 100%, 40%);">-          | S(LCHAN_EV_MGW_ENDPOINT_ERROR)</span><br><span>             ,</span><br><span>    .timer_cb = lchan_fsm_timer_cb,</span><br><span>      .cleanup = lchan_fsm_cleanup,</span><br><span>diff --git a/src/osmo-bsc/lchan_rtp_fsm.c b/src/osmo-bsc/lchan_rtp_fsm.c</span><br><span>new file mode 100644</span><br><span>index 0000000..3530b8a</span><br><span>--- /dev/null</span><br><span>+++ b/src/osmo-bsc/lchan_rtp_fsm.c</span><br><span>@@ -0,0 +1,743 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/* osmo-bsc API to switch the RTP stream for an lchan.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de></span><br><span style="color: hsl(120, 100%, 40%);">+ * All Rights Reserved</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Author: Neels Hofmeyr <neels@hofmeyr.de></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software; you can redistribute it and/or modify</span><br><span style="color: hsl(120, 100%, 40%);">+ * it under the terms of the GNU Affero General Public License as published by</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Free Software Foundation; either version 3 of the License, or</span><br><span style="color: hsl(120, 100%, 40%);">+ * (at your option) any later version.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is distributed in the hope that it will be useful,</span><br><span style="color: hsl(120, 100%, 40%);">+ * but WITHOUT ANY WARRANTY; without even the implied warranty of</span><br><span style="color: hsl(120, 100%, 40%);">+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the</span><br><span style="color: hsl(120, 100%, 40%);">+ * GNU Affero General Public License for more details.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * You should have received a copy of the GNU Affero General Public License</span><br><span style="color: hsl(120, 100%, 40%);">+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/fsm.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/gsm_data.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/gsm_timers.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/lchan_fsm.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/lchan_rtp_fsm.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/mgw_endpoint_fsm.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/bsc_subscr_conn_fsm.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/bsc/abis_rsl.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct osmo_fsm lchan_rtp_fsm;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct gsm_lchan *lchan_rtp_fi_lchan(struct osmo_fsm_inst *fi)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_ASSERT(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(fi->fsm == &lchan_rtp_fsm);</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_ASSERT(fi->priv);</span><br><span style="color: hsl(120, 100%, 40%);">+     return fi->priv;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct state_timeout lchan_rtp_fsm_timeouts[32] = {</span><br><span style="color: hsl(120, 100%, 40%);">+ [LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_AVAILABLE] = { .T=23004 },</span><br><span style="color: hsl(120, 100%, 40%);">+    [LCHAN_RTP_ST_WAIT_IPACC_CRCX_ACK]      = { .T=23005 },</span><br><span style="color: hsl(120, 100%, 40%);">+       [LCHAN_RTP_ST_WAIT_IPACC_MDCX_ACK]      = { .T=23006 },</span><br><span style="color: hsl(120, 100%, 40%);">+       [LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_CONFIGURED] = { .T=23004 },</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Transition to a state, using the T timer defined in lchan_rtp_fsm_timeouts.</span><br><span style="color: hsl(120, 100%, 40%);">+ * The actual timeout value is in turn obtained from network->T_defs.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Assumes local variable fi exists. */</span><br><span style="color: hsl(120, 100%, 40%);">+#define lchan_rtp_fsm_state_chg(state) \</span><br><span style="color: hsl(120, 100%, 40%);">+        fsm_inst_state_chg_T(fi, state, \</span><br><span style="color: hsl(120, 100%, 40%);">+                          lchan_rtp_fsm_timeouts, \</span><br><span style="color: hsl(120, 100%, 40%);">+                             ((struct gsm_lchan*)(fi->priv))->ts->trx->bts->network->T_defs, \</span><br><span style="color: hsl(120, 100%, 40%);">+                           5)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Set a failure message, trigger the common actions to take on failure, transition to a state to</span><br><span style="color: hsl(120, 100%, 40%);">+ * continue with (using state timeouts from lchan_rtp_fsm_timeouts[]). Assumes local variable fi exists. */</span><br><span style="color: hsl(120, 100%, 40%);">+#define lchan_rtp_fail(fmt, args...) do { \</span><br><span style="color: hsl(120, 100%, 40%);">+                struct gsm_lchan *_lchan = fi->priv; \</span><br><span style="color: hsl(120, 100%, 40%);">+             uint32_t state_was = fi->state; \</span><br><span style="color: hsl(120, 100%, 40%);">+          lchan_set_last_error(_lchan, "lchan-rtp failure in state %s: " fmt, \</span><br><span style="color: hsl(120, 100%, 40%);">+                                    osmo_fsm_state_name(fi->fsm, state_was), ## args); \</span><br><span style="color: hsl(120, 100%, 40%);">+          osmo_fsm_inst_dispatch(_lchan->fi, LCHAN_EV_RTP_ERROR, 0); \</span><br><span style="color: hsl(120, 100%, 40%);">+       } while(0)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Called from lchan_fsm_init(), does not need to be visible in lchan_rtp_fsm.h */</span><br><span style="color: hsl(120, 100%, 40%);">+void lchan_rtp_fsm_init()</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_ASSERT(osmo_fsm_register(&lchan_rtp_fsm) == 0);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void lchan_rtp_fsm_update_id(struct gsm_lchan *lchan)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_ASSERT(lchan->fi);</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_ASSERT(lchan->fi_rtp);</span><br><span style="color: hsl(120, 100%, 40%);">+        osmo_fsm_inst_update_id_f(lchan->fi_rtp, lchan->fi->id);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+bool lchan_rtp_established(struct gsm_lchan *lchan)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        if (!lchan->fi_rtp)</span><br><span style="color: hsl(120, 100%, 40%);">+                return false;</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (lchan->fi_rtp->state) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case LCHAN_RTP_ST_READY:</span><br><span style="color: hsl(120, 100%, 40%);">+      case LCHAN_RTP_ST_ESTABLISHED:</span><br><span style="color: hsl(120, 100%, 40%);">+        case LCHAN_RTP_ST_ROLLBACK:</span><br><span style="color: hsl(120, 100%, 40%);">+           return true;</span><br><span style="color: hsl(120, 100%, 40%);">+  default:</span><br><span style="color: hsl(120, 100%, 40%);">+              return false;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void lchan_rtp_fsm_start(struct gsm_lchan *lchan)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct osmo_fsm_inst *fi;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_ASSERT(lchan->ts);</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_ASSERT(lchan->ts->fi);</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_ASSERT(lchan->fi);</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_ASSERT(!lchan->fi_rtp);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     fi = osmo_fsm_inst_alloc_child(&lchan_rtp_fsm, lchan->fi, LCHAN_EV_RTP_RELEASED);</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+      fi->priv = lchan;</span><br><span style="color: hsl(120, 100%, 40%);">+  lchan->fi_rtp = fi;</span><br><span style="color: hsl(120, 100%, 40%);">+        lchan_rtp_fsm_update_id(lchan);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* Use old lchan only if there is an MGW endpoint present. Otherwise, on ROLLBACK, we might put</span><br><span style="color: hsl(120, 100%, 40%);">+        * an endpoint "back" to an lchan that never had one to begin with. */</span><br><span style="color: hsl(120, 100%, 40%);">+      if (lchan->activate.re_use_mgw_endpoint_from_lchan</span><br><span style="color: hsl(120, 100%, 40%);">+     && !lchan->activate.re_use_mgw_endpoint_from_lchan->mgw_endpoint_ci_bts)</span><br><span style="color: hsl(120, 100%, 40%);">+            lchan->activate.re_use_mgw_endpoint_from_lchan = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_AVAILABLE);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* While activating an lchan, for example for Handover, we may want to re-use another lchan's MGW</span><br><span style="color: hsl(120, 100%, 40%);">+ * endpoint CI. If Handover fails half way, the old lchan must keep its MGW endpoint CI, and we must not</span><br><span style="color: hsl(120, 100%, 40%);">+ * clean it up. Hence keep another lchan's mgw_endpoint_ci_bts out of lchan until all is done. */</span><br><span style="color: hsl(120, 100%, 40%);">+struct mgwep_ci *lchan_use_mgw_endpoint_ci_bts(struct gsm_lchan *lchan)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   if (lchan->mgw_endpoint_ci_bts)</span><br><span style="color: hsl(120, 100%, 40%);">+            return lchan->mgw_endpoint_ci_bts;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (lchan_state_is(lchan, LCHAN_ST_ESTABLISHED))</span><br><span style="color: hsl(120, 100%, 40%);">+              return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  if (lchan->activate.re_use_mgw_endpoint_from_lchan)</span><br><span style="color: hsl(120, 100%, 40%);">+                return lchan->activate.re_use_mgw_endpoint_from_lchan->mgw_endpoint_ci_bts;</span><br><span style="color: hsl(120, 100%, 40%);">+     return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void lchan_rtp_fsm_wait_mgw_endpoint_available_onenter(struct osmo_fsm_inst *fi,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                          uint32_t prev_state)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+     struct mgw_endpoint *mgwep;</span><br><span style="color: hsl(120, 100%, 40%);">+   struct mgwep_ci *use_mgwep_ci = lchan_use_mgw_endpoint_ci_bts(lchan);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct mgcp_conn_peer crcx_info = {};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (use_mgwep_ci) {</span><br><span style="color: hsl(120, 100%, 40%);">+           LOG_LCHAN_RTP(lchan, LOGL_DEBUG, "MGW endpoint already available: %s",</span><br><span style="color: hsl(120, 100%, 40%);">+                            mgwep_ci_name(use_mgwep_ci));</span><br><span style="color: hsl(120, 100%, 40%);">+           lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_WAIT_LCHAN_READY);</span><br><span style="color: hsl(120, 100%, 40%);">+               return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   mgwep = gscon_ensure_mgw_endpoint(lchan->conn, lchan->activate.msc_assigned_cic);</span><br><span style="color: hsl(120, 100%, 40%);">+       if (!mgwep) {</span><br><span style="color: hsl(120, 100%, 40%);">+         lchan_rtp_fail("Internal error: cannot obtain MGW endpoint handle for conn");</span><br><span style="color: hsl(120, 100%, 40%);">+               return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   lchan->mgw_endpoint_ci_bts = mgw_endpoint_ci_add(mgwep, "to-BTS");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (lchan->conn)</span><br><span style="color: hsl(120, 100%, 40%);">+           crcx_info.call_id = lchan->conn->sccp.conn_id;</span><br><span style="color: hsl(120, 100%, 40%);">+  crcx_info.ptime = 20;</span><br><span style="color: hsl(120, 100%, 40%);">+ mgcp_pick_codec(&crcx_info, lchan);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     mgw_endpoint_ci_request(lchan->mgw_endpoint_ci_bts, MGCP_VERB_CRCX, &crcx_info,</span><br><span style="color: hsl(120, 100%, 40%);">+                                fi, LCHAN_RTP_EV_MGW_ENDPOINT_AVAILABLE, LCHAN_RTP_EV_MGW_ENDPOINT_ERROR,</span><br><span style="color: hsl(120, 100%, 40%);">+                             0);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void lchan_rtp_fsm_wait_mgw_endpoint_available(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+     switch (event) {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    case LCHAN_RTP_EV_MGW_ENDPOINT_AVAILABLE:</span><br><span style="color: hsl(120, 100%, 40%);">+             LOG_LCHAN_RTP(lchan, LOGL_DEBUG, "MGW endpoint: %s",</span><br><span style="color: hsl(120, 100%, 40%);">+                              mgwep_ci_name(lchan_use_mgw_endpoint_ci_bts(lchan)));</span><br><span style="color: hsl(120, 100%, 40%);">+           lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_WAIT_LCHAN_READY);</span><br><span style="color: hsl(120, 100%, 40%);">+               return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     case LCHAN_RTP_EV_LCHAN_READY:</span><br><span style="color: hsl(120, 100%, 40%);">+                /* will notice lchan->activate.activ_ack == true in</span><br><span style="color: hsl(120, 100%, 40%);">+                 * lchan_rtp_fsm_wait_lchan_ready_onenter() */</span><br><span style="color: hsl(120, 100%, 40%);">+                return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     case LCHAN_RTP_EV_MGW_ENDPOINT_ERROR:</span><br><span style="color: hsl(120, 100%, 40%);">+         lchan_rtp_fail("Failure to create MGW endpoint");</span><br><span style="color: hsl(120, 100%, 40%);">+           return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     case LCHAN_RTP_EV_ROLLBACK:</span><br><span style="color: hsl(120, 100%, 40%);">+   case LCHAN_RTP_EV_RELEASE:</span><br><span style="color: hsl(120, 100%, 40%);">+            osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REQUEST, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+             return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     default:</span><br><span style="color: hsl(120, 100%, 40%);">+              OSMO_ASSERT(false);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void lchan_rtp_fsm_post_lchan_ready(struct osmo_fsm_inst *fi);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void lchan_rtp_fsm_wait_lchan_ready_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (lchan->activate.activ_ack) {</span><br><span style="color: hsl(120, 100%, 40%);">+           LOG_LCHAN_RTP(lchan, LOGL_DEBUG, "Activ Ack received earlier, no need to wait");</span><br><span style="color: hsl(120, 100%, 40%);">+            lchan_rtp_fsm_post_lchan_ready(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void lchan_rtp_fsm_wait_lchan_ready(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   switch (event) {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    case LCHAN_RTP_EV_LCHAN_READY:</span><br><span style="color: hsl(120, 100%, 40%);">+                lchan_rtp_fsm_post_lchan_ready(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+           return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     case LCHAN_RTP_EV_ROLLBACK:</span><br><span style="color: hsl(120, 100%, 40%);">+   case LCHAN_RTP_EV_RELEASE:</span><br><span style="color: hsl(120, 100%, 40%);">+            osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REQUEST, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+             return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     default:</span><br><span style="color: hsl(120, 100%, 40%);">+              OSMO_ASSERT(false);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void lchan_rtp_fsm_switch_rtp(struct osmo_fsm_inst *fi)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (lchan->activate.wait_before_switching_rtp) {</span><br><span style="color: hsl(120, 100%, 40%);">+           LOG_LCHAN_RTP(lchan, LOGL_DEBUG,</span><br><span style="color: hsl(120, 100%, 40%);">+                            "Waiting for an event by caller before switching RTP\n");</span><br><span style="color: hsl(120, 100%, 40%);">+             lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_WAIT_READY_TO_SWITCH_RTP);</span><br><span style="color: hsl(120, 100%, 40%);">+       } else</span><br><span style="color: hsl(120, 100%, 40%);">+                lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_CONFIGURED);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void lchan_rtp_fsm_post_lchan_ready(struct osmo_fsm_inst *fi)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (is_ipaccess_bts(lchan->ts->trx->bts))</span><br><span style="color: hsl(120, 100%, 40%);">+            lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_WAIT_IPACC_CRCX_ACK);</span><br><span style="color: hsl(120, 100%, 40%);">+    else</span><br><span style="color: hsl(120, 100%, 40%);">+          lchan_rtp_fsm_switch_rtp(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void lchan_rtp_fsm_wait_ipacc_crcx_ack_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+       int val;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (lchan->release_requested) {</span><br><span style="color: hsl(120, 100%, 40%);">+            lchan_rtp_fail("Release requested while activating");</span><br><span style="color: hsl(120, 100%, 40%);">+               return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   val = ipacc_speech_mode(lchan->tch_mode, lchan->type);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (val < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+             lchan_rtp_fail("Cannot determine Abis/IP speech mode for tch_mode=%s type=%s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                       get_value_string(gsm48_chan_mode_names, lchan->tch_mode),</span><br><span style="color: hsl(120, 100%, 40%);">+                          gsm_lchant_name(lchan->type));</span><br><span style="color: hsl(120, 100%, 40%);">+          return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+     lchan->abis_ip.speech_mode = val;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        val = ipacc_payload_type(lchan->tch_mode, lchan->type);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (val < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+             lchan_rtp_fail("Cannot determine Abis/IP payload type for tch_mode=%s type=%s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                      get_value_string(gsm48_chan_mode_names, lchan->tch_mode),</span><br><span style="color: hsl(120, 100%, 40%);">+                          gsm_lchant_name(lchan->type));</span><br><span style="color: hsl(120, 100%, 40%);">+          return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+     lchan->abis_ip.rtp_payload = val;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /* recv-only */</span><br><span style="color: hsl(120, 100%, 40%);">+       ipacc_speech_mode_set_direction(&lchan->abis_ip.speech_mode, false);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ rc = rsl_tx_ipacc_crcx(lchan);</span><br><span style="color: hsl(120, 100%, 40%);">+        if (rc)</span><br><span style="color: hsl(120, 100%, 40%);">+               lchan_rtp_fail("Failure to transmit IPACC CRCX to BTS (rc=%d, %s)",</span><br><span style="color: hsl(120, 100%, 40%);">+                    rc, strerror(-rc));</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void lchan_rtp_fsm_wait_ipacc_crcx_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+     switch (event) {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    case LCHAN_RTP_EV_IPACC_CRCX_ACK:</span><br><span style="color: hsl(120, 100%, 40%);">+             /* the CRCX ACK parsing has already noted the RTP port information at</span><br><span style="color: hsl(120, 100%, 40%);">+          * lchan->abis_ip.bound_*, see ipac_parse_rtp(). We'll use that in</span><br><span style="color: hsl(120, 100%, 40%);">+              * lchan_rtp_fsm_wait_mgw_endpoint_configured_onenter(). */</span><br><span style="color: hsl(120, 100%, 40%);">+           lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_WAIT_IPACC_MDCX_ACK);</span><br><span style="color: hsl(120, 100%, 40%);">+            return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     case LCHAN_RTP_EV_IPACC_CRCX_NACK:</span><br><span style="color: hsl(120, 100%, 40%);">+            lchan_rtp_fail("Received NACK on IPACC CRCX");</span><br><span style="color: hsl(120, 100%, 40%);">+              return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     case LCHAN_RTP_EV_READY_TO_SWITCH_RTP:</span><br><span style="color: hsl(120, 100%, 40%);">+                lchan->activate.wait_before_switching_rtp = false;</span><br><span style="color: hsl(120, 100%, 40%);">+         return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     case LCHAN_RTP_EV_RELEASE:</span><br><span style="color: hsl(120, 100%, 40%);">+    case LCHAN_RTP_EV_ROLLBACK:</span><br><span style="color: hsl(120, 100%, 40%);">+           osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REQUEST, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+             return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     default:</span><br><span style="color: hsl(120, 100%, 40%);">+              OSMO_ASSERT(false);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void lchan_rtp_fsm_wait_ipacc_mdcx_ack_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+     const struct mgcp_conn_peer *mgw_rtp;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (lchan->release_requested) {</span><br><span style="color: hsl(120, 100%, 40%);">+            lchan_rtp_fail("Release requested while activating");</span><br><span style="color: hsl(120, 100%, 40%);">+               return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   mgw_rtp = mgwep_ci_get_rtp_info(lchan_use_mgw_endpoint_ci_bts(lchan));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      if (!mgw_rtp) {</span><br><span style="color: hsl(120, 100%, 40%);">+               lchan_rtp_fail("Cannot send IPACC MDCX to BTS:"</span><br><span style="color: hsl(120, 100%, 40%);">+                        " there is no RTP IP+port set that the BTS should send RTP to.");</span><br><span style="color: hsl(120, 100%, 40%);">+                return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Other RTP settings were already setup in lchan_rtp_fsm_wait_ipacc_crcx_ack_onenter() */</span><br><span style="color: hsl(120, 100%, 40%);">+    lchan->abis_ip.connect_ip = ntohl(inet_addr(mgw_rtp->addr));</span><br><span style="color: hsl(120, 100%, 40%);">+    lchan->abis_ip.connect_port = mgw_rtp->port;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  /* send-recv */</span><br><span style="color: hsl(120, 100%, 40%);">+       ipacc_speech_mode_set_direction(&lchan->abis_ip.speech_mode, true);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  rc = rsl_tx_ipacc_mdcx(lchan);</span><br><span style="color: hsl(120, 100%, 40%);">+        if (rc)</span><br><span style="color: hsl(120, 100%, 40%);">+               lchan_rtp_fail("Failure to transmit IPACC MDCX to BTS (rc=%d, %s)",</span><br><span style="color: hsl(120, 100%, 40%);">+                    rc, strerror(-rc));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void lchan_rtp_fsm_wait_ipacc_mdcx_ack(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+     switch (event) {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    case LCHAN_RTP_EV_IPACC_MDCX_ACK:</span><br><span style="color: hsl(120, 100%, 40%);">+             lchan_rtp_fsm_switch_rtp(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+         return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     case LCHAN_RTP_EV_IPACC_MDCX_NACK:</span><br><span style="color: hsl(120, 100%, 40%);">+            lchan_rtp_fail("Received NACK on IPACC MDCX");</span><br><span style="color: hsl(120, 100%, 40%);">+              return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     case LCHAN_RTP_EV_READY_TO_SWITCH_RTP:</span><br><span style="color: hsl(120, 100%, 40%);">+                lchan->activate.wait_before_switching_rtp = false;</span><br><span style="color: hsl(120, 100%, 40%);">+         return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     case LCHAN_RTP_EV_RELEASE:</span><br><span style="color: hsl(120, 100%, 40%);">+    case LCHAN_RTP_EV_ROLLBACK:</span><br><span style="color: hsl(120, 100%, 40%);">+           osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REQUEST, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+             return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     default:</span><br><span style="color: hsl(120, 100%, 40%);">+              OSMO_ASSERT(false);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void lchan_rtp_fsm_wait_ready_to_switch_rtp(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   switch (event) {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    case LCHAN_RTP_EV_READY_TO_SWITCH_RTP:</span><br><span style="color: hsl(120, 100%, 40%);">+                lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_CONFIGURED);</span><br><span style="color: hsl(120, 100%, 40%);">+           return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     case LCHAN_RTP_EV_RELEASE:</span><br><span style="color: hsl(120, 100%, 40%);">+    case LCHAN_RTP_EV_ROLLBACK:</span><br><span style="color: hsl(120, 100%, 40%);">+           osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REQUEST, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+             return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     default:</span><br><span style="color: hsl(120, 100%, 40%);">+              OSMO_ASSERT(false);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void connect_mgw_endpoint_to_lchan(struct osmo_fsm_inst *fi,</span><br><span style="color: hsl(120, 100%, 40%);">+                                     struct mgwep_ci *ci,</span><br><span style="color: hsl(120, 100%, 40%);">+                                          struct gsm_lchan *to_lchan)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+       struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+     struct mgcp_conn_peer mdcx_info;</span><br><span style="color: hsl(120, 100%, 40%);">+      struct in_addr addr;</span><br><span style="color: hsl(120, 100%, 40%);">+  const char *addr_str;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       mdcx_info = (struct mgcp_conn_peer){</span><br><span style="color: hsl(120, 100%, 40%);">+          .port = to_lchan->abis_ip.bound_port,</span><br><span style="color: hsl(120, 100%, 40%);">+              .ptime = 20,</span><br><span style="color: hsl(120, 100%, 40%);">+  };</span><br><span style="color: hsl(120, 100%, 40%);">+    mgcp_pick_codec(&mdcx_info, to_lchan);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+  addr.s_addr = ntohl(to_lchan->abis_ip.bound_ip);</span><br><span style="color: hsl(120, 100%, 40%);">+   addr_str = inet_ntoa(addr);</span><br><span style="color: hsl(120, 100%, 40%);">+   rc = osmo_strlcpy(mdcx_info.addr, addr_str, sizeof(mdcx_info.addr));</span><br><span style="color: hsl(120, 100%, 40%);">+  if (rc <= 0 || rc >= sizeof(mdcx_info.addr)) {</span><br><span style="color: hsl(120, 100%, 40%);">+          lchan_rtp_fail("Cannot compose BTS side RTP IP address to send to MGW: '%s'",</span><br><span style="color: hsl(120, 100%, 40%);">+                          addr_str);</span><br><span style="color: hsl(120, 100%, 40%);">+         return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!ci) {</span><br><span style="color: hsl(120, 100%, 40%);">+            lchan_rtp_fail("No MGW endpoint ci configured");</span><br><span style="color: hsl(120, 100%, 40%);">+            return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   LOG_LCHAN_RTP(lchan, LOGL_DEBUG, "Sending BTS side RTP port info %s:%u to MGW %s",</span><br><span style="color: hsl(120, 100%, 40%);">+                mdcx_info.addr, mdcx_info.port, mgwep_ci_name(ci));</span><br><span style="color: hsl(120, 100%, 40%);">+     mgw_endpoint_ci_request(ci, MGCP_VERB_MDCX, &mdcx_info,</span><br><span style="color: hsl(120, 100%, 40%);">+                           fi, LCHAN_RTP_EV_MGW_ENDPOINT_CONFIGURED,</span><br><span style="color: hsl(120, 100%, 40%);">+                             LCHAN_RTP_EV_MGW_ENDPOINT_ERROR, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void lchan_rtp_fsm_wait_mgw_endpoint_configured_onenter(struct osmo_fsm_inst *fi,</span><br><span style="color: hsl(120, 100%, 40%);">+                                                          uint32_t prev_state)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+     struct gsm_lchan *old_lchan = lchan->activate.re_use_mgw_endpoint_from_lchan;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (lchan->release_requested) {</span><br><span style="color: hsl(120, 100%, 40%);">+            lchan_rtp_fail("Release requested while activating");</span><br><span style="color: hsl(120, 100%, 40%);">+               return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* At this point, we are taking over an old lchan's MGW endpoint (if any). */</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!lchan->mgw_endpoint_ci_bts && old_lchan) {</span><br><span style="color: hsl(120, 100%, 40%);">+            /* The old lchan shall forget the enpoint now. We might put it back upon ROLLBACK */</span><br><span style="color: hsl(120, 100%, 40%);">+          lchan->mgw_endpoint_ci_bts = old_lchan->mgw_endpoint_ci_bts;</span><br><span style="color: hsl(120, 100%, 40%);">+            old_lchan->mgw_endpoint_ci_bts = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!lchan->mgw_endpoint_ci_bts) {</span><br><span style="color: hsl(120, 100%, 40%);">+         lchan_rtp_fail("No MGW endpoint ci configured");</span><br><span style="color: hsl(120, 100%, 40%);">+            return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   connect_mgw_endpoint_to_lchan(fi, lchan->mgw_endpoint_ci_bts, lchan);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void lchan_rtp_fsm_wait_mgw_endpoint_configured(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        switch (event) {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    case LCHAN_RTP_EV_MGW_ENDPOINT_CONFIGURED:</span><br><span style="color: hsl(120, 100%, 40%);">+            lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_READY);</span><br><span style="color: hsl(120, 100%, 40%);">+          return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     case LCHAN_RTP_EV_MGW_ENDPOINT_ERROR:</span><br><span style="color: hsl(120, 100%, 40%);">+         lchan_rtp_fail("Error while redirecting the MGW to the lchan's RTP port");</span><br><span style="color: hsl(120, 100%, 40%);">+              return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     default:</span><br><span style="color: hsl(120, 100%, 40%);">+              OSMO_ASSERT(false);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void lchan_rtp_fsm_ready_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+     osmo_fsm_inst_dispatch(lchan->fi, LCHAN_EV_RTP_READY, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void lchan_rtp_fsm_ready(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   switch (event) {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    case LCHAN_RTP_EV_ESTABLISHED:</span><br><span style="color: hsl(120, 100%, 40%);">+                lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_ESTABLISHED);</span><br><span style="color: hsl(120, 100%, 40%);">+            return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     case LCHAN_RTP_EV_RELEASE:</span><br><span style="color: hsl(120, 100%, 40%);">+            osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REQUEST, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+             return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     case LCHAN_RTP_EV_ROLLBACK:</span><br><span style="color: hsl(120, 100%, 40%);">+           lchan_rtp_fsm_state_chg(LCHAN_RTP_ST_ROLLBACK);</span><br><span style="color: hsl(120, 100%, 40%);">+               return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     default:</span><br><span style="color: hsl(120, 100%, 40%);">+              OSMO_ASSERT(false);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void lchan_rtp_fsm_rollback_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+     struct gsm_lchan *old_lchan = lchan->activate.re_use_mgw_endpoint_from_lchan;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    if (!lchan->mgw_endpoint_ci_bts || !old_lchan) {</span><br><span style="color: hsl(120, 100%, 40%);">+           osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REQUEST, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+             return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+     connect_mgw_endpoint_to_lchan(fi, lchan->mgw_endpoint_ci_bts, old_lchan);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void lchan_rtp_fsm_rollback(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+     struct gsm_lchan *old_lchan = lchan->activate.re_use_mgw_endpoint_from_lchan;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    switch (event) {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    case LCHAN_RTP_EV_MGW_ENDPOINT_CONFIGURED:</span><br><span style="color: hsl(120, 100%, 40%);">+            old_lchan->mgw_endpoint_ci_bts = lchan->mgw_endpoint_ci_bts;</span><br><span style="color: hsl(120, 100%, 40%);">+            lchan->mgw_endpoint_ci_bts = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+         osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+             return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     case LCHAN_RTP_EV_MGW_ENDPOINT_ERROR:</span><br><span style="color: hsl(120, 100%, 40%);">+         LOG_LCHAN_RTP(lchan, LOGL_ERROR,</span><br><span style="color: hsl(120, 100%, 40%);">+                            "Error while connecting the MGW back to the old lchan's RTP port:"</span><br><span style="color: hsl(120, 100%, 40%);">+                              " %s %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+                         mgwep_ci_name(lchan->mgw_endpoint_ci_bts),</span><br><span style="color: hsl(120, 100%, 40%);">+                         gsm_lchan_name(old_lchan));</span><br><span style="color: hsl(120, 100%, 40%);">+             osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+               return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     default:</span><br><span style="color: hsl(120, 100%, 40%);">+              OSMO_ASSERT(false);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void lchan_rtp_fsm_established_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Make sure that we will not hand back the MGW endpoint to any old lchan from here on. */</span><br><span style="color: hsl(120, 100%, 40%);">+    lchan->activate.re_use_mgw_endpoint_from_lchan = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void lchan_rtp_fsm_established(struct osmo_fsm_inst *fi, uint32_t event, void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        switch (event) {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    case LCHAN_RTP_EV_RELEASE:</span><br><span style="color: hsl(120, 100%, 40%);">+    case LCHAN_RTP_EV_ROLLBACK:</span><br><span style="color: hsl(120, 100%, 40%);">+           osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+             return;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     default:</span><br><span style="color: hsl(120, 100%, 40%);">+              OSMO_ASSERT(false);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define S(x)    (1 << (x))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const struct osmo_fsm_state lchan_rtp_fsm_states[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+     [LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_AVAILABLE] = {</span><br><span style="color: hsl(120, 100%, 40%);">+                .name = "WAIT_MGW_ENDPOINT_AVAILABLE",</span><br><span style="color: hsl(120, 100%, 40%);">+              .onenter = lchan_rtp_fsm_wait_mgw_endpoint_available_onenter,</span><br><span style="color: hsl(120, 100%, 40%);">+         .action = lchan_rtp_fsm_wait_mgw_endpoint_available,</span><br><span style="color: hsl(120, 100%, 40%);">+          .in_event_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(LCHAN_RTP_EV_MGW_ENDPOINT_AVAILABLE)</span><br><span style="color: hsl(120, 100%, 40%);">+                      | S(LCHAN_RTP_EV_MGW_ENDPOINT_ERROR)</span><br><span style="color: hsl(120, 100%, 40%);">+                  | S(LCHAN_RTP_EV_LCHAN_READY)</span><br><span style="color: hsl(120, 100%, 40%);">+                 | S(LCHAN_RTP_EV_RELEASE)</span><br><span style="color: hsl(120, 100%, 40%);">+                     | S(LCHAN_RTP_EV_ROLLBACK)</span><br><span style="color: hsl(120, 100%, 40%);">+                    ,</span><br><span style="color: hsl(120, 100%, 40%);">+             .out_state_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                   | S(LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_AVAILABLE) /* for init */</span><br><span style="color: hsl(120, 100%, 40%);">+                  | S(LCHAN_RTP_ST_WAIT_LCHAN_READY)</span><br><span style="color: hsl(120, 100%, 40%);">+                    ,</span><br><span style="color: hsl(120, 100%, 40%);">+     },</span><br><span style="color: hsl(120, 100%, 40%);">+    [LCHAN_RTP_ST_WAIT_LCHAN_READY] = {</span><br><span style="color: hsl(120, 100%, 40%);">+           .name = "WAIT_LCHAN_READY",</span><br><span style="color: hsl(120, 100%, 40%);">+         .onenter = lchan_rtp_fsm_wait_lchan_ready_onenter,</span><br><span style="color: hsl(120, 100%, 40%);">+            .action = lchan_rtp_fsm_wait_lchan_ready,</span><br><span style="color: hsl(120, 100%, 40%);">+             .in_event_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(LCHAN_RTP_EV_LCHAN_READY)</span><br><span style="color: hsl(120, 100%, 40%);">+                 | S(LCHAN_RTP_EV_RELEASE)</span><br><span style="color: hsl(120, 100%, 40%);">+                     | S(LCHAN_RTP_EV_ROLLBACK)</span><br><span style="color: hsl(120, 100%, 40%);">+                    ,</span><br><span style="color: hsl(120, 100%, 40%);">+             .out_state_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                   | S(LCHAN_RTP_ST_WAIT_IPACC_CRCX_ACK)</span><br><span style="color: hsl(120, 100%, 40%);">+                 | S(LCHAN_RTP_ST_WAIT_READY_TO_SWITCH_RTP)</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_CONFIGURED)</span><br><span style="color: hsl(120, 100%, 40%);">+                        ,</span><br><span style="color: hsl(120, 100%, 40%);">+     },</span><br><span style="color: hsl(120, 100%, 40%);">+    [LCHAN_RTP_ST_WAIT_IPACC_CRCX_ACK] = {</span><br><span style="color: hsl(120, 100%, 40%);">+                .name = "WAIT_IPACC_CRCX_ACK",</span><br><span style="color: hsl(120, 100%, 40%);">+              .onenter = lchan_rtp_fsm_wait_ipacc_crcx_ack_onenter,</span><br><span style="color: hsl(120, 100%, 40%);">+         .action = lchan_rtp_fsm_wait_ipacc_crcx_ack,</span><br><span style="color: hsl(120, 100%, 40%);">+          .in_event_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(LCHAN_RTP_EV_READY_TO_SWITCH_RTP)</span><br><span style="color: hsl(120, 100%, 40%);">+                 | S(LCHAN_RTP_EV_IPACC_CRCX_ACK)</span><br><span style="color: hsl(120, 100%, 40%);">+                      | S(LCHAN_RTP_EV_IPACC_CRCX_NACK)</span><br><span style="color: hsl(120, 100%, 40%);">+                     | S(LCHAN_RTP_EV_RELEASE)</span><br><span style="color: hsl(120, 100%, 40%);">+                     | S(LCHAN_RTP_EV_ROLLBACK)</span><br><span style="color: hsl(120, 100%, 40%);">+                    ,</span><br><span style="color: hsl(120, 100%, 40%);">+             .out_state_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                   | S(LCHAN_RTP_ST_WAIT_IPACC_MDCX_ACK)</span><br><span style="color: hsl(120, 100%, 40%);">+                 ,</span><br><span style="color: hsl(120, 100%, 40%);">+     },</span><br><span style="color: hsl(120, 100%, 40%);">+    [LCHAN_RTP_ST_WAIT_IPACC_MDCX_ACK] = {</span><br><span style="color: hsl(120, 100%, 40%);">+                .name = "WAIT_IPACC_MDCX_ACK",</span><br><span style="color: hsl(120, 100%, 40%);">+              .onenter = lchan_rtp_fsm_wait_ipacc_mdcx_ack_onenter,</span><br><span style="color: hsl(120, 100%, 40%);">+         .action = lchan_rtp_fsm_wait_ipacc_mdcx_ack,</span><br><span style="color: hsl(120, 100%, 40%);">+          .in_event_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(LCHAN_RTP_EV_READY_TO_SWITCH_RTP)</span><br><span style="color: hsl(120, 100%, 40%);">+                 | S(LCHAN_RTP_EV_IPACC_MDCX_ACK)</span><br><span style="color: hsl(120, 100%, 40%);">+                      | S(LCHAN_RTP_EV_IPACC_MDCX_NACK)</span><br><span style="color: hsl(120, 100%, 40%);">+                     | S(LCHAN_RTP_EV_RELEASE)</span><br><span style="color: hsl(120, 100%, 40%);">+                     | S(LCHAN_RTP_EV_ROLLBACK)</span><br><span style="color: hsl(120, 100%, 40%);">+                    ,</span><br><span style="color: hsl(120, 100%, 40%);">+             .out_state_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                   | S(LCHAN_RTP_ST_WAIT_READY_TO_SWITCH_RTP)</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_CONFIGURED)</span><br><span style="color: hsl(120, 100%, 40%);">+                        ,</span><br><span style="color: hsl(120, 100%, 40%);">+     },</span><br><span style="color: hsl(120, 100%, 40%);">+    [LCHAN_RTP_ST_WAIT_READY_TO_SWITCH_RTP] = {</span><br><span style="color: hsl(120, 100%, 40%);">+           .name = "WAIT_READY_TO_SWITCH_RTP",</span><br><span style="color: hsl(120, 100%, 40%);">+         .action = lchan_rtp_fsm_wait_ready_to_switch_rtp,</span><br><span style="color: hsl(120, 100%, 40%);">+             .in_event_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(LCHAN_RTP_EV_READY_TO_SWITCH_RTP)</span><br><span style="color: hsl(120, 100%, 40%);">+                 | S(LCHAN_RTP_EV_RELEASE)</span><br><span style="color: hsl(120, 100%, 40%);">+                     | S(LCHAN_RTP_EV_ROLLBACK)</span><br><span style="color: hsl(120, 100%, 40%);">+                    ,</span><br><span style="color: hsl(120, 100%, 40%);">+             .out_state_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                   | S(LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_CONFIGURED)</span><br><span style="color: hsl(120, 100%, 40%);">+                        ,</span><br><span style="color: hsl(120, 100%, 40%);">+     },</span><br><span style="color: hsl(120, 100%, 40%);">+    [LCHAN_RTP_ST_WAIT_MGW_ENDPOINT_CONFIGURED] = {</span><br><span style="color: hsl(120, 100%, 40%);">+               .name = "WAIT_MGW_ENDPOINT_CONFIGURED",</span><br><span style="color: hsl(120, 100%, 40%);">+             .onenter = lchan_rtp_fsm_wait_mgw_endpoint_configured_onenter,</span><br><span style="color: hsl(120, 100%, 40%);">+                .action = lchan_rtp_fsm_wait_mgw_endpoint_configured,</span><br><span style="color: hsl(120, 100%, 40%);">+         .in_event_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(LCHAN_RTP_EV_MGW_ENDPOINT_CONFIGURED)</span><br><span style="color: hsl(120, 100%, 40%);">+                     | S(LCHAN_RTP_EV_MGW_ENDPOINT_ERROR)</span><br><span style="color: hsl(120, 100%, 40%);">+                  | S(LCHAN_RTP_EV_RELEASE)</span><br><span style="color: hsl(120, 100%, 40%);">+                     | S(LCHAN_RTP_EV_ROLLBACK)</span><br><span style="color: hsl(120, 100%, 40%);">+                    ,</span><br><span style="color: hsl(120, 100%, 40%);">+             .out_state_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                   | S(LCHAN_RTP_ST_READY)</span><br><span style="color: hsl(120, 100%, 40%);">+                       ,</span><br><span style="color: hsl(120, 100%, 40%);">+     },</span><br><span style="color: hsl(120, 100%, 40%);">+    [LCHAN_RTP_ST_READY] = {</span><br><span style="color: hsl(120, 100%, 40%);">+              .name = "READY",</span><br><span style="color: hsl(120, 100%, 40%);">+            .onenter = lchan_rtp_fsm_ready_onenter,</span><br><span style="color: hsl(120, 100%, 40%);">+               .action = lchan_rtp_fsm_ready,</span><br><span style="color: hsl(120, 100%, 40%);">+                .in_event_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(LCHAN_RTP_EV_ESTABLISHED)</span><br><span style="color: hsl(120, 100%, 40%);">+                 | S(LCHAN_RTP_EV_RELEASE)</span><br><span style="color: hsl(120, 100%, 40%);">+                     | S(LCHAN_RTP_EV_ROLLBACK)</span><br><span style="color: hsl(120, 100%, 40%);">+                    ,</span><br><span style="color: hsl(120, 100%, 40%);">+             .out_state_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                   | S(LCHAN_RTP_ST_ESTABLISHED)</span><br><span style="color: hsl(120, 100%, 40%);">+                 | S(LCHAN_RTP_ST_ROLLBACK)</span><br><span style="color: hsl(120, 100%, 40%);">+                    ,</span><br><span style="color: hsl(120, 100%, 40%);">+     },</span><br><span style="color: hsl(120, 100%, 40%);">+    [LCHAN_RTP_ST_ESTABLISHED] = {</span><br><span style="color: hsl(120, 100%, 40%);">+                .name = "ESTABLISHED",</span><br><span style="color: hsl(120, 100%, 40%);">+              .onenter = lchan_rtp_fsm_established_onenter,</span><br><span style="color: hsl(120, 100%, 40%);">+         .action = lchan_rtp_fsm_established,</span><br><span style="color: hsl(120, 100%, 40%);">+          .in_event_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(LCHAN_RTP_EV_RELEASE)</span><br><span style="color: hsl(120, 100%, 40%);">+                     | S(LCHAN_RTP_EV_ROLLBACK)</span><br><span style="color: hsl(120, 100%, 40%);">+                    ,</span><br><span style="color: hsl(120, 100%, 40%);">+     },</span><br><span style="color: hsl(120, 100%, 40%);">+    [LCHAN_RTP_ST_ROLLBACK] = {</span><br><span style="color: hsl(120, 100%, 40%);">+           .name = "ROLLBACK",</span><br><span style="color: hsl(120, 100%, 40%);">+         .onenter = lchan_rtp_fsm_rollback_onenter,</span><br><span style="color: hsl(120, 100%, 40%);">+            .action = lchan_rtp_fsm_rollback,</span><br><span style="color: hsl(120, 100%, 40%);">+             .in_event_mask = 0</span><br><span style="color: hsl(120, 100%, 40%);">+                    | S(LCHAN_RTP_EV_MGW_ENDPOINT_CONFIGURED)</span><br><span style="color: hsl(120, 100%, 40%);">+                     | S(LCHAN_RTP_EV_MGW_ENDPOINT_ERROR)</span><br><span style="color: hsl(120, 100%, 40%);">+                  | S(LCHAN_RTP_EV_RELEASE)</span><br><span style="color: hsl(120, 100%, 40%);">+                     | S(LCHAN_RTP_EV_ROLLBACK)</span><br><span style="color: hsl(120, 100%, 40%);">+                    ,</span><br><span style="color: hsl(120, 100%, 40%);">+     },</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const struct value_string lchan_rtp_fsm_event_names[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_VALUE_STRING(LCHAN_RTP_EV_LCHAN_READY),</span><br><span style="color: hsl(120, 100%, 40%);">+  OSMO_VALUE_STRING(LCHAN_RTP_EV_READY_TO_SWITCH_RTP),</span><br><span style="color: hsl(120, 100%, 40%);">+  OSMO_VALUE_STRING(LCHAN_RTP_EV_MGW_ENDPOINT_AVAILABLE),</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_VALUE_STRING(LCHAN_RTP_EV_MGW_ENDPOINT_ERROR),</span><br><span style="color: hsl(120, 100%, 40%);">+   OSMO_VALUE_STRING(LCHAN_RTP_EV_IPACC_CRCX_ACK),</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_VALUE_STRING(LCHAN_RTP_EV_IPACC_CRCX_NACK),</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_VALUE_STRING(LCHAN_RTP_EV_IPACC_MDCX_ACK),</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_VALUE_STRING(LCHAN_RTP_EV_IPACC_MDCX_NACK),</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_VALUE_STRING(LCHAN_RTP_EV_READY_TO_SWITCH),</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_VALUE_STRING(LCHAN_RTP_EV_MGW_ENDPOINT_CONFIGURED),</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_VALUE_STRING(LCHAN_RTP_EV_ROLLBACK),</span><br><span style="color: hsl(120, 100%, 40%);">+     OSMO_VALUE_STRING(LCHAN_RTP_EV_ESTABLISHED),</span><br><span style="color: hsl(120, 100%, 40%);">+  OSMO_VALUE_STRING(LCHAN_RTP_EV_RELEASE),</span><br><span style="color: hsl(120, 100%, 40%);">+      {}</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int lchan_rtp_fsm_timer_cb(struct osmo_fsm_inst *fi)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+     lchan->release_in_error = true;</span><br><span style="color: hsl(120, 100%, 40%);">+    lchan_rtp_fail("Timeout");</span><br><span style="color: hsl(120, 100%, 40%);">+  return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+void lchan_rtp_fsm_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct gsm_lchan *lchan = lchan_rtp_fi_lchan(fi);</span><br><span style="color: hsl(120, 100%, 40%);">+     if (lchan->mgw_endpoint_ci_bts) {</span><br><span style="color: hsl(120, 100%, 40%);">+          mgw_endpoint_ci_dlcx(lchan->mgw_endpoint_ci_bts);</span><br><span style="color: hsl(120, 100%, 40%);">+          lchan->mgw_endpoint_ci_bts = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+     lchan->fi_rtp = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+      if (lchan->fi)</span><br><span style="color: hsl(120, 100%, 40%);">+             osmo_fsm_inst_dispatch(lchan->fi, LCHAN_EV_RTP_RELEASED, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* The mgw_endpoint was invalidated, just and simply forget the pointer without cleanup. */</span><br><span style="color: hsl(120, 100%, 40%);">+void lchan_forget_mgw_endpoint(struct gsm_lchan *lchan)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!lchan)</span><br><span style="color: hsl(120, 100%, 40%);">+           return;</span><br><span style="color: hsl(120, 100%, 40%);">+       lchan->mgw_endpoint_ci_bts = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct osmo_fsm lchan_rtp_fsm = {</span><br><span style="color: hsl(120, 100%, 40%);">+  .name = "lchan_rtp",</span><br><span style="color: hsl(120, 100%, 40%);">+        .states = lchan_rtp_fsm_states,</span><br><span style="color: hsl(120, 100%, 40%);">+       .num_states = ARRAY_SIZE(lchan_rtp_fsm_states),</span><br><span style="color: hsl(120, 100%, 40%);">+       .log_subsys = DRSL,</span><br><span style="color: hsl(120, 100%, 40%);">+   .event_names = lchan_rtp_fsm_event_names,</span><br><span style="color: hsl(120, 100%, 40%);">+     .timer_cb = lchan_rtp_fsm_timer_cb,</span><br><span style="color: hsl(120, 100%, 40%);">+   .cleanup = lchan_rtp_fsm_cleanup,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span>diff --git a/tests/handover/Makefile.am b/tests/handover/Makefile.am</span><br><span>index 709a87b..c28c417 100644</span><br><span>--- a/tests/handover/Makefile.am</span><br><span>+++ b/tests/handover/Makefile.am</span><br><span>@@ -63,6 +63,7 @@</span><br><span>       $(top_builddir)/src/osmo-bsc/handover_fsm.o \</span><br><span>        $(top_builddir)/src/osmo-bsc/handover_logic.o \</span><br><span>      $(top_builddir)/src/osmo-bsc/lchan_fsm.o \</span><br><span style="color: hsl(120, 100%, 40%);">+    $(top_builddir)/src/osmo-bsc/lchan_rtp_fsm.o \</span><br><span>       $(top_builddir)/src/osmo-bsc/lchan_select.o \</span><br><span>        $(top_builddir)/src/osmo-bsc/meas_rep.o \</span><br><span>    $(top_builddir)/src/osmo-bsc/mgw_endpoint_fsm.o \</span><br><span>diff --git a/tests/handover/handover_test.c b/tests/handover/handover_test.c</span><br><span>index 3a5748e..e76cc85 100644</span><br><span>--- a/tests/handover/handover_test.c</span><br><span>+++ b/tests/handover/handover_test.c</span><br><span>@@ -393,6 +393,7 @@</span><br><span>         struct gsm48_ho_cpl *hc;</span><br><span> </span><br><span>         send_est_ind(lchan);</span><br><span style="color: hsl(120, 100%, 40%);">+  osmo_fsm_inst_dispatch(lchan->fi, LCHAN_EV_RTP_READY, 0);</span><br><span> </span><br><span>     rh = (struct abis_rsl_rll_hdr *) msgb_put(msg, sizeof(*rh));</span><br><span>         rh->c.msg_discr = ABIS_RSL_MDISC_RLL;</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/10103">change 10103</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/10103"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: osmo-bsc </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>
<div style="display:none"> Gerrit-Change-Id: Id7a4407d9b63be05ce63f5f2768b7d7e3d5c86fb </div>
<div style="display:none"> Gerrit-Change-Number: 10103 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Neels Hofmeyr <nhofmeyr@sysmocom.de> </div>