aboutsummaryrefslogtreecommitdiffstats
path: root/src/osmo-bsc/bsc_subscr_conn_fsm.c
diff options
context:
space:
mode:
authorNeels Hofmeyr <neels@hofmeyr.de>2018-05-14 18:14:15 +0200
committerNeels Hofmeyr <neels@hofmeyr.de>2018-07-28 12:18:23 +0200
commit31f525e7560ad13e32cfc5e0b5f1862c0efcb991 (patch)
tree305b224d3e555eb69798d09e0edb269c32699418 /src/osmo-bsc/bsc_subscr_conn_fsm.c
parent596c402835bab2a01fba7d7233512009eb4142f5 (diff)
large refactoring: use FSMs for lchans; add inter-BSC HO
Add FSMs: - timeslot_fsm: handle dynamic timeslots and OML+RSL availability. - lchan_fsm: handle an individual lchan activation, RTP stream and release, signal the appropriate calling FSMs on success, failure, release. - mgw_endpoint_fsm: handle one entire endpoint with several CI. - assignment_fsm: BSSMAP Assignment Request. - handover_fsm: all of intra, inter-MO and inter-MT handover. Above FSMs absorb large parts of the gscon FSM. The gscon FSM was surpassing the maximum amount events (32), and it is more logical to treat assignment, handover and MGW procedures in separate FSMs. - Add logging macros for each FSM type: - LOG_TS() - LOG_LCHAN() - LOG_MGWEP(), LOG_CI() - LOG_ASSIGNMENT() - LOG_HO() These log with the osmo_fsm_inst where present. New style decision: logging without a final newline char is awkward, especially for gsmtap logging and when other logs interleave LOGPC() calls; we have various cases where the final \n goes missing, and also this invokes the log category checking N times instead of once. So I decided to make these macros *always* append a newline, but only if there is no final newline yet. I hope that the compiler optimizes the strlen() of the constant format strings away. Thus I can log with or without typing "\n" and always get an \n termination anyway. General: - replace osmo_timers, state enums and program-wide osmo_signal_dispatch() with dedicated FSM timeouts, states and events. - introduce a common way to handle Tnnn timers: gsm_timers.h/.c: struct T_def. These can be used (with some macro magic) to define a state's timeout once, and not make mistakes for each osmo_fsm_inst_state_chg(). Details: bsc_subscr_conn_fsm.c: - move most states of this FSM to lchan_fsm, assignment_fsm, handover_fsm and mgw_endpoint_fsm. - There is exactly one state for an ongoing Assignment, with all details handled in conn->assignment.fi. The state relies on the assignment_fsm's timeout. - There is one state for an ongoing Handover; except for an incoming Handover from a remote BSS, the gscon remains in ST_INIT until the new lchan and conn are both established. - move bssmap_add_lcls_status() to osmo_bsc_lcls.c abis_rsl.c: - move all dynamic timeslot logic away into timeslot_fsm. Only keep plain send/receive functions in abis_rsl.c - reduce some rsl functions to merely send a message, rename to "_tx_". - rsl_ipacc_mdcx(): add '_tx_' in the name; move parts that change the lchan state out into the lchan_fsm, the lchan->abis_ip.* are now set there prior to invoking this function. - move all timers and error/release handling away into various FSMs. - tweak ipa_smod_s_for_lchan() and ipa_rtp_pt_for_lchan() to not require an lchan passed, but just mode,type that they require. Rename to ipacc_speech_mode*() and ipacc_payload_type(). - add rsl_forward_layer3_info, used for inter-BSC HO MO, to just send the RR message received during BSSMAP Handover Command. - move various logging to LOG_LCHAN() in order to log with the lchan FSM instance. One drawback is that the lchan FSM is limited to one logging category, i.e. this moves some logging from DRR to DRSL. It might actually make sense to combine those categories. - lose LOGP...LOGPC logging cascades: they are bad for gsmtap logging and for performance. - handle_classmark_chg(): change logging, move cm2 len check out of the cm3 condition (I hope that's correct). - gsm48_send_ho_cmd(): split off gsm48_make_ho_cmd() which doesn't send right away, so that during inter-bsc HO we can make an RR Handover Command to send via the MSC to the remote BSS. assignment_fsm.c: - the Chan Mode Modify in case of re-using the same lchan is not implemented yet, because this was also missing in the previous implementation (OS#3357). osmo_bsc_api.c: - simplify bsc_mr_config() and move to lchan_fsm.c, the only caller; rename to lchan_mr_config(). (bsc_mr_config() used to copy the values to mr_bts_lv twice, once by member assignment and then again with a memcpy.) - During handover, we used to copy the MR config from the old lchan. Since we may handover between FR and HR, rather set the MR Config anew every time, so that FR rates are always available on FR lchans, and never on HR lchans. Depends: I03ee7ce840ecfa0b6a33358e7385528aabd4873f (libosmocore), I1f2918418c38918c5ac70acaa51a47adfca12b5e (libosmocore) Change-Id: I82e3f918295daa83274a4cf803f046979f284366
Diffstat (limited to 'src/osmo-bsc/bsc_subscr_conn_fsm.c')
-rw-r--r--src/osmo-bsc/bsc_subscr_conn_fsm.c1199
1 files changed, 455 insertions, 744 deletions
diff --git a/src/osmo-bsc/bsc_subscr_conn_fsm.c b/src/osmo-bsc/bsc_subscr_conn_fsm.c
index f97b7781c..0ed98d838 100644
--- a/src/osmo-bsc/bsc_subscr_conn_fsm.c
+++ b/src/osmo-bsc/bsc_subscr_conn_fsm.c
@@ -26,8 +26,8 @@
#include <osmocom/bsc/a_reset.h>
#include <osmocom/bsc/bsc_api.h>
#include <osmocom/bsc/gsm_data.h>
-#include <osmocom/bsc/handover.h>
-#include <osmocom/bsc/chan_alloc.h>
+#include <osmocom/bsc/handover_fsm.h>
+#include <osmocom/bsc/lchan_fsm.h>
#include <osmocom/bsc/bsc_subscriber.h>
#include <osmocom/bsc/osmo_bsc_sigtran.h>
#include <osmocom/bsc/osmo_bsc_lcls.h>
@@ -37,6 +37,9 @@
#include <osmocom/bsc/bsc_rll.h>
#include <osmocom/bsc/abis_rsl.h>
#include <osmocom/bsc/gsm_timers.h>
+#include <osmocom/bsc/gsm_04_08_rr.h>
+#include <osmocom/bsc/mgw_endpoint_fsm.h>
+#include <osmocom/bsc/assignment_fsm.h>
#include <osmocom/mgcp_client/mgcp_client_fsm.h>
#include <osmocom/core/byteswap.h>
@@ -48,112 +51,40 @@
#define MGCP_MGW_HO_TIMEOUT 4 /* in seconds */
#define MGCP_MGW_HO_TIMEOUT_TIMER_NR 2
-#define ENDPOINT_ID "rtpbridge/*@mgw"
-
enum gscon_fsm_states {
ST_INIT,
/* waiting for CC from MSC */
ST_WAIT_CC,
/* active connection */
ST_ACTIVE,
- /* during assignment; waiting for ASS_CMPL */
- ST_WAIT_ASS_CMPL,
+ ST_ASSIGNMENT,
+ ST_HANDOVER,
/* BSSMAP CLEAR has been received */
ST_CLEARING,
-
-/* MGW handling */
- /* during assignment; waiting for MGW response to CRCX for BTS */
- ST_WAIT_CRCX_BTS,
- /* during assignment; waiting for MGW response to MDCX for BTS */
- ST_WAIT_MDCX_BTS,
- /* during assignment; waiting for MGW response to CRCX for MSC */
- ST_WAIT_CRCX_MSC,
-
-/* MT (inbound) handover */
- /* Wait for Handover Access from MS/BTS */
- ST_WAIT_MT_HO_ACC,
- /* Wait for RR Handover Complete from MS/BTS */
- ST_WAIT_MT_HO_COMPL,
-
-/* MO (outbound) handover */
- /* Wait for Handover Command / Handover Required Reject from MSC */
- ST_WAIT_MO_HO_CMD,
- /* Wait for Clear Command from MSC */
- ST_MO_HO_PROCEEDING,
-
-/* Internal HO handling */
- /* Wait for the handover logic to complete the handover */
- ST_WAIT_HO_COMPL,
- /* during handover; waiting for MGW response to MDCX for BTS */
- ST_WAIT_MDCX_BTS_HO,
};
static const struct value_string gscon_fsm_event_names[] = {
{GSCON_EV_A_CONN_IND, "MT-CONNECT.ind"},
{GSCON_EV_A_CONN_REQ, "MO-CONNECT.req"},
{GSCON_EV_A_CONN_CFM, "MO-CONNECT.cfm"},
- {GSCON_EV_A_ASSIGNMENT_CMD, "ASSIGNMENT_CMD"},
{GSCON_EV_A_CLEAR_CMD, "CLEAR_CMD"},
{GSCON_EV_A_DISC_IND, "DISCONNET.ind"},
- {GSCON_EV_A_HO_REQ, "HANDOVER_REQUEST"},
-
- {GSCON_EV_RR_ASS_COMPL, "RR_ASSIGN_COMPL"},
- {GSCON_EV_RR_ASS_FAIL, "RR_ASSIGN_FAIL"},
- {GSCON_EV_RLL_REL_IND, "RLL_RELEASE.ind"},
- {GSCON_EV_RSL_CONN_FAIL, "RSL_CONN_FAIL.ind"},
- {GSCON_EV_RSL_CLEAR_COMPL, "RSL_CLEAR_COMPLETE"},
-
- {GSCON_EV_MO_DTAP, "MO-DTAP"},
- {GSCON_EV_MT_DTAP, "MT-DTAP"},
+ {GSCON_EV_ASSIGNMENT_START, "ASSIGNMENT_START"},
+ {GSCON_EV_ASSIGNMENT_END, "ASSIGNMENT_END"},
+ {GSCON_EV_HANDOVER_START, "HANDOVER_START"},
+ {GSCON_EV_HANDOVER_END, "HANDOVER_END"},
+ {GSCON_EV_RSL_CONN_FAIL, "RSL_CONN_FAIL"},
+ {GSCON_EV_MO_DTAP, "MO_DTAP"},
+ {GSCON_EV_MT_DTAP, "MT_DTAP"},
{GSCON_EV_TX_SCCP, "TX_SCCP"},
-
- {GSCON_EV_MGW_FAIL_BTS, "MGW_FAILURE_BTS"},
- {GSCON_EV_MGW_FAIL_MSC, "MGW_FAILURE_MSC"},
- {GSCON_EV_MGW_CRCX_RESP_BTS, "MGW_CRCX_RESPONSE_BTS"},
- {GSCON_EV_MGW_MDCX_RESP_BTS, "MGW_MDCX_RESPONSE_BTS"},
- {GSCON_EV_MGW_CRCX_RESP_MSC, "MGW_CRCX_RESPONSE_MSC"},
- {GSCON_EV_MGW_MDCX_RESP_MSC, "MGW_MDCX_RESPONSE_MSC"},
-
- {GSCON_EV_HO_START, "HO_START"},
- {GSCON_EV_HO_TIMEOUT, "HO_TIMEOUT"},
- {GSCON_EV_HO_FAIL, "HO_FAIL"},
- {GSCON_EV_HO_COMPL, "HO_COMPL"},
+ {GSCON_EV_MGW_MDCX_RESP_MSC, "MGW_MDCX_RESP_MSC"},
{GSCON_EV_LCLS_FAIL, "LCLS_FAIL"},
-
- {0, NULL}
+ {GSCON_EV_FORGET_LCHAN, "FORGET_LCHAN"},
+ {GSCON_EV_FORGET_MGW_ENDPOINT, "FORGET_MGW_ENDPOINT"},
+ {}
};
-/* Depending on the channel mode and rate, set the codec type that is signalled
- * towards the MGW. */
-void bsc_subscr_pick_codec(struct mgcp_conn_peer *conn_peer, struct gsm_subscriber_connection *conn)
-{
- switch (conn->user_plane.chan_mode) {
- case GSM48_CMODE_SPEECH_V1:
- if (conn->user_plane.full_rate)
- conn_peer->codecs[0] = CODEC_GSM_8000_1;
- else
- conn_peer->codecs[0] = CODEC_GSMHR_8000_1;
- conn_peer->codecs_len = 1;
- break;
- case GSM48_CMODE_SPEECH_EFR:
- conn_peer->codecs[0] = CODEC_GSMEFR_8000_1;
- conn_peer->codecs_len = 1;
- break;
- case GSM48_CMODE_SPEECH_AMR:
- conn_peer->codecs[0] = CODEC_AMR_8000_1;
- conn_peer->codecs_len = 1;
- break;
- default:
- conn_peer->codecs_len = 0;
- }
-}
-
struct state_timeout conn_fsm_timeouts[32] = {
- [ST_WAIT_ASS_CMPL] = { .T = 10 },
- [ST_WAIT_CRCX_BTS] = { .T = 992427 },
- [ST_WAIT_MDCX_BTS] = { .T = 992427 },
- [ST_WAIT_CRCX_MSC] = { .T = 992427 },
- [ST_WAIT_MDCX_BTS_HO] = { .T = 992427 },
[ST_WAIT_CC] = { .T = 993210 },
[ST_CLEARING] = { .T = 999 },
};
@@ -168,158 +99,131 @@ struct state_timeout conn_fsm_timeouts[32] = {
-1)
/* forward MT DTAP from BSSAP side to RSL side */
-static inline void submit_dtap(struct gsm_subscriber_connection *conn, struct msgb *msg,
- struct osmo_fsm_inst *fi)
+static inline void submit_dtap(struct gsm_subscriber_connection *conn, struct msgb *msg)
{
- OSMO_ASSERT(fi);
OSMO_ASSERT(msg);
OSMO_ASSERT(conn);
gscon_submit_rsl_dtap(conn, msg, OBSC_LINKID_CB(msg), 1);
}
-/* Send data SCCP message through SCCP connection. All sigtran messages
- * that are send from this FSM must use this function. Never use
- * osmo_bsc_sigtran_send() directly since this would defeat the checks
- * provided by this function. */
-static void sigtran_send(struct gsm_subscriber_connection *conn, struct msgb *msg, struct osmo_fsm_inst *fi)
+static void gscon_dtap_queue_flush(struct gsm_subscriber_connection *conn, int send);
+
+int gscon_sigtran_send(struct gsm_subscriber_connection *conn, struct msgb *msg)
{
int rc;
+ if (!msg)
+ return -ENOMEM;
+
/* Make sure that we only attempt to send SCCP messages if we have
* a life SCCP connection. Otherwise drop the message. */
- if (fi->state == ST_INIT || fi->state == ST_WAIT_CC) {
- LOGPFSML(fi, LOGL_ERROR, "No active SCCP connection, dropping message!\n");
+ if (conn->fi->state == ST_INIT || conn->fi->state == ST_WAIT_CC) {
+ LOGPFSML(conn->fi, LOGL_ERROR, "No active SCCP connection, dropping message\n");
msgb_free(msg);
- return;
+ return -ENODEV;
}
rc = osmo_bsc_sigtran_send(conn, msg);
if (rc < 0)
- LOGPFSML(fi, LOGL_ERROR, "Unable to deliver SCCP message!\n");
+ LOGPFSML(conn->fi, LOGL_ERROR, "Unable to deliver SCCP message\n");
+ return rc;
}
-/* Add the LCLS BSS Status IE to a BSSMAP message. We assume this is
- * called on a msgb that was returned by gsm0808_create_ass_compl() */
-static void bssmap_add_lcls_status(struct msgb *msg, enum gsm0808_lcls_status status)
+static void gscon_bssmap_clear(struct gsm_subscriber_connection *conn,
+ enum gsm0808_cause cause)
{
- OSMO_ASSERT(msg->l3h[0] == BSSAP_MSG_BSS_MANAGEMENT);
- OSMO_ASSERT(msg->l3h[2] == BSS_MAP_MSG_ASSIGMENT_COMPLETE ||
- msg->l3h[2] == BSS_MAP_MSG_HANDOVER_RQST_ACKNOWLEDGE ||
- msg->l3h[2] == BSS_MAP_MSG_HANDOVER_COMPLETE ||
- msg->l3h[2] == BSS_MAP_MSG_HANDOVER_PERFORMED);
- OSMO_ASSERT(msgb_tailroom(msg) >= 2);
-
- /* append IE to end of message */
- msgb_tv_put(msg, GSM0808_IE_LCLS_BSS_STATUS, status);
- /* increment the "length" byte in the BSSAP header */
- msg->l3h[1] += 2;
+ struct msgb *resp = gsm0808_create_clear_rqst(cause);
+ gscon_sigtran_send(conn, resp);
}
-/* Add (append) the LCLS BSS Status IE to a BSSMAP message, if there is any LCLS
- * active on the given \a conn */
-static void bssmap_add_lcls_status_if_needed(struct gsm_subscriber_connection *conn,
- struct msgb *msg)
+/* forward MO DTAP from RSL side to BSSAP side */
+static void forward_dtap(struct gsm_subscriber_connection *conn, struct msgb *msg, struct osmo_fsm_inst *fi)
{
- enum gsm0808_lcls_status status = lcls_get_status(conn);
- if (status != 0xff) {
- LOGPFSM(conn->fi, "Adding LCLS BSS-Status (%s) to %s\n",
- gsm0808_lcls_status_name(status),
- gsm0808_bssmap_name(msg->l3h[2]));
- bssmap_add_lcls_status(msg, status);
- }
+ struct msgb *resp = NULL;
+
+ OSMO_ASSERT(msg);
+ OSMO_ASSERT(conn);
+
+ resp = gsm0808_create_dtap(msg, OBSC_LINKID_CB(msg));
+ gscon_sigtran_send(conn, resp);
}
-/* Generate and send assignment complete message */
-static void send_ass_compl(struct gsm_lchan *lchan, struct osmo_fsm_inst *fi, bool voice)
+void gscon_release_lchans(struct gsm_subscriber_connection *conn, bool do_sacch_deact)
{
- struct msgb *resp;
- struct gsm0808_speech_codec sc;
- struct gsm0808_speech_codec *sc_ptr = NULL;
- struct gsm_subscriber_connection *conn;
- struct sockaddr_storage *addr_local = NULL;
- int perm_spch = 0;
- uint8_t chosen_channel;
+ if (conn->ho.fi)
+ handover_end(conn, HO_RESULT_CONN_RELEASE);
- conn = lchan->conn;
- OSMO_ASSERT(conn);
+ assignment_reset(conn);
- /* apply LCLS configuration (if any) */
- lcls_apply_config(conn);
-
- LOGPFSML(fi, LOGL_DEBUG, "Sending assignment complete message... (id=%i)\n", conn->sccp.conn_id);
-
- /* Generate voice related fields */
- if (voice) {
- perm_spch = gsm0808_permitted_speech(lchan->type, lchan->tch_mode);
- switch (conn->sccp.msc->a.asp_proto) {
- case OSMO_SS7_ASP_PROT_IPA:
- /* don't add any AoIP specific fields. CIC allocated by MSC */
- break;
- default:
- OSMO_ASSERT(lchan->abis_ip.ass_compl.valid);
- addr_local = &conn->user_plane.aoip_rtp_addr_local;
-
- /* Extrapolate speech codec from speech mode */
- gsm0808_speech_codec_from_chan_type(&sc, perm_spch);
- sc_ptr = &sc;
- break;
- }
- /* FIXME: AMR codec configuration must be derived from lchan1! */
- }
-
- chosen_channel = gsm0808_chosen_channel(lchan->tch_mode, lchan->type);
- if (!chosen_channel)
- LOGP(DMSC, LOGL_ERROR, "Unknown lchan type or TCH mode: %s\n", gsm_lchan_name(lchan));
+ lchan_release(conn->lchan, do_sacch_deact, false, 0);
+ lchan_forget_conn(conn->lchan);
+ conn->lchan = NULL;
+}
- /* Generate message */
- resp = gsm0808_create_ass_compl(lchan->abis_ip.ass_compl.rr_cause,
- chosen_channel,
- lchan->encr.alg_id, perm_spch,
- addr_local, sc_ptr, NULL);
+static void handle_bssap_n_connect(struct osmo_fsm_inst *fi, struct osmo_scu_prim *scu_prim)
+{
+ struct gsm_subscriber_connection *conn = fi->priv;
+ struct msgb *msg = scu_prim->oph.msg;
+ struct bssmap_header *bs;
+ uint8_t bssmap_type;
- if (!resp) {
- LOGPFSML(fi, LOGL_ERROR, "Failed to generate assignment completed message! (id=%i)\n",
- conn->sccp.conn_id);
+ msg->l3h = msgb_l2(msg);
+ if (!msgb_l3(msg)) {
+ LOGPFSML(fi, LOGL_ERROR, "internal error: no l3 in msg\n");
+ goto refuse;
}
- /* Add LCLS BSS-Status IE in case there is any LCLS status for this connection */
- bssmap_add_lcls_status_if_needed(conn, resp);
+ if (msgb_l3len(msg) < sizeof(*bs)) {
+ LOGPFSML(fi, LOGL_NOTICE, "message too short for BSSMAP header (%u < %zu)\n",
+ msgb_l3len(msg), sizeof(*bs));
+ goto refuse;
+ }
- sigtran_send(conn, resp, fi);
-}
+ bs = (struct bssmap_header*)msgb_l3(msg);
+ if (msgb_l3len(msg) < (bs->length + sizeof(*bs))) {
+ LOGPFSML(fi, LOGL_NOTICE,
+ "message too short for length indicated in BSSMAP header (%u < %u)\n",
+ msgb_l3len(msg), bs->length);
+ goto refuse;
+ }
-/* forward MO DTAP from RSL side to BSSAP side */
-static void forward_dtap(struct gsm_subscriber_connection *conn, struct msgb *msg, struct osmo_fsm_inst *fi)
-{
- struct msgb *resp = NULL;
+ switch (bs->type) {
+ case BSSAP_MSG_BSS_MANAGEMENT:
+ break;
+ default:
+ LOGPFSML(fi, LOGL_NOTICE,
+ "message type not allowed for N-CONNECT: %s\n", gsm0808_bssap_name(bs->type));
+ goto refuse;
+ }
- OSMO_ASSERT(msg);
- OSMO_ASSERT(conn);
+ msg->l4h = &msg->l3h[sizeof(*bs)];
+ bssmap_type = msg->l4h[0];
- resp = gsm0808_create_dtap(msg, OBSC_LINKID_CB(msg));
- sigtran_send(conn, resp, fi);
-}
+ LOGPFSML(fi, LOGL_DEBUG, "Rx N-CONNECT: %s: %s\n", gsm0808_bssap_name(bs->type),
+ gsm0808_bssmap_name(bssmap_type));
-/* In case there are open MGCP connections, toss
- * those connections */
-static void toss_mgcp_conn(struct gsm_subscriber_connection *conn, struct osmo_fsm_inst *fi)
-{
- LOGPFSML(fi, LOGL_ERROR, "tossing all MGCP connections...\n");
+ switch (bssmap_type) {
+ case BSS_MAP_MSG_HANDOVER_RQST:
+ /* First off, accept the new conn. */
+ osmo_sccp_tx_conn_resp(conn->sccp.msc->a.sccp_user, scu_prim->u.connect.conn_id,
+ &scu_prim->u.connect.called_addr, NULL, 0);
- if (conn->user_plane.fi_bts) {
- mgcp_conn_delete(conn->user_plane.fi_bts);
- conn->user_plane.fi_bts = NULL;
- }
+ /* Make sure the conn FSM will osmo_sccp_tx_disconn() on term */
+ conn->sccp.state = SUBSCR_SCCP_ST_CONNECTED;
- if (conn->user_plane.fi_msc) {
- mgcp_conn_delete(conn->user_plane.fi_msc);
- conn->user_plane.fi_msc = NULL;
+ /* Inter-BSC MT Handover Request, another BSS is handovering to us. */
+ handover_start_inter_bsc_in(conn, msg);
+ return;
+ default:
+ break;
}
- if (conn->user_plane.mgw_endpoint) {
- talloc_free(conn->user_plane.mgw_endpoint);
- conn->user_plane.mgw_endpoint = NULL;
- }
+ LOGPFSML(fi, LOGL_NOTICE, "No support for N-CONNECT: %s: %s\n",
+ gsm0808_bssap_name(bs->type), gsm0808_bssmap_name(bssmap_type));
+refuse:
+ osmo_sccp_tx_disconn(conn->sccp.msc->a.sccp_user, scu_prim->u.connect.conn_id,
+ &scu_prim->u.connect.called_addr, 0);
+ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
}
static void gscon_fsm_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -328,6 +232,7 @@ static void gscon_fsm_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
struct osmo_scu_prim *scu_prim = NULL;
struct msgb *msg = NULL;
int rc;
+ enum handover_result ho_result;
switch (event) {
case GSCON_EV_A_CONN_REQ:
@@ -346,8 +251,10 @@ static void gscon_fsm_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
* using T3210 (20s), T3220 (5s) or T3230 (10s) */
conn_fsm_state_chg(ST_WAIT_CC);
}
+ gscon_update_id(conn);
break;
case GSCON_EV_A_CONN_IND:
+ gscon_update_id(conn);
scu_prim = data;
if (!conn->sccp.msc) {
LOGPFSML(fi, LOGL_NOTICE, "N-CONNECT.ind from unknown MSC %s\n",
@@ -355,18 +262,37 @@ static void gscon_fsm_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
osmo_sccp_tx_disconn(conn->sccp.msc->a.sccp_user, scu_prim->u.connect.conn_id,
&scu_prim->u.connect.called_addr, 0);
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
+ return;
}
/* FIXME: Extract optional IMSI and update FSM using osmo_fsm_inst_set_id()
* related: OS2969 (same as above) */
- LOGPFSML(fi, LOGL_NOTICE, "No support for MSC-originated SCCP Connections yet\n");
- osmo_sccp_tx_disconn(conn->sccp.msc->a.sccp_user, scu_prim->u.connect.conn_id,
- &scu_prim->u.connect.called_addr, 0);
- osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
- break;
+ handle_bssap_n_connect(fi, scu_prim);
+ break;
+ case GSCON_EV_HANDOVER_END:
+ ho_result = HO_RESULT_ERROR;
+ if (data)
+ ho_result = *(enum handover_result*)data;
+ LOGPFSML(fi, LOGL_DEBUG, "Handover result: %s\n", handover_result_name(ho_result));
+ if (ho_result == HO_RESULT_OK) {
+ /* In this case the ho struct should still be populated. */
+ if (conn->ho.scope & HO_INTER_BSC_IN) {
+ /* Done with establishing a conn where we accept another BSC's MS via
+ * inter-BSC handover */
+
+ osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0);
+ gscon_dtap_queue_flush(conn, 1);
+ return;
+ }
+ LOG_HO(conn, LOGL_ERROR,
+ "Conn is in state %s, the only accepted handover kind is inter-BSC MT\n",
+ osmo_fsm_inst_state_name(conn->fi));
+ }
+ gscon_bssmap_clear(conn, GSM0808_CAUSE_EQUIPMENT_FAILURE);
+ osmo_fsm_inst_state_chg(fi, ST_CLEARING, 60, 999);
+ return;
default:
OSMO_ASSERT(false);
- break;
}
}
@@ -384,481 +310,240 @@ static void gscon_fsm_wait_cc(struct osmo_fsm_inst *fi, uint32_t event, void *da
break;
default:
OSMO_ASSERT(false);
- break;
}
}
-static const char *get_mgw_ep_name(struct gsm_subscriber_connection *conn)
-{
- static char ep_name[256];
- struct bsc_msc_data *msc = conn->sccp.msc;
-
- switch (conn->sccp.msc->a.asp_proto) {
- case OSMO_SS7_ASP_PROT_IPA:
- /* derive endpoint name from CIC on A interface side */
- snprintf(ep_name, sizeof(ep_name), "%x@mgw",
- mgcp_port_to_cic(conn->user_plane.rtp_port, msc->rtp_base));
- break;
- default:
- /* use dynamic RTPBRIDGE endpoint allocation in MGW */
- osmo_strlcpy(ep_name, ENDPOINT_ID, sizeof(ep_name));
- break;
- }
- return ep_name;
-}
-
-#define assignment_failed(fi, cause) \
- _assignment_failed(fi, cause, __FILE__, __LINE__)
-static void _assignment_failed(struct osmo_fsm_inst *fi, enum gsm0808_cause cause,
- const char *file, int line)
+static void gscon_fsm_active_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_subscriber_connection *conn = fi->priv;
- struct msgb *resp = NULL;
-
- LOGPFSMLSRC(fi, LOGL_ERROR, file, line, "Assignment failed: %s\n", gsm0808_cause_name(cause));
-
- resp = gsm0808_create_assignment_failure(cause, NULL);
- sigtran_send(conn, resp, fi);
- if (fi->state != ST_ACTIVE)
- conn_fsm_state_chg(ST_ACTIVE);
+ if (!conn->lchan)
+ gscon_bssmap_clear(conn, GSM0808_CAUSE_EQUIPMENT_FAILURE);
}
/* We're on an active subscriber connection, passing DTAP back and forth */
static void gscon_fsm_active(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_subscriber_connection *conn = fi->priv;
- struct msgb *resp = NULL;
- struct mgcp_conn_peer conn_peer;
- int rc;
+ struct gsm_bts *bts;
switch (event) {
- case GSCON_EV_A_ASSIGNMENT_CMD:
- /* MSC requests us to perform assignment, this code section is
- * triggered via signal GSCON_EV_A_ASSIGNMENT_CMD from
- * bssmap_handle_assignm_req() in osmo_bsc_bssap.c, which does
- * the parsing of incoming assignment requests. */
-
- LOGPFSML(fi, LOGL_NOTICE, "Channel assignment: chan_mode=%s, full_rate=%i\n",
- get_value_string(gsm48_chan_mode_names, conn->user_plane.chan_mode),
- conn->user_plane.full_rate);
-
- /* FIXME: We need to check if current channel is sufficient. If
- * yes, do MODIFY. If not, do assignment (see commented lines below) */
-
- switch (conn->user_plane.chan_mode) {
- case GSM48_CMODE_SPEECH_V1:
- case GSM48_CMODE_SPEECH_EFR:
- case GSM48_CMODE_SPEECH_AMR:
- /* A voice channel is requested, so we run down the
- * mgcp-ass-mgcp state-chain (see FIXME above) */
- memset(&conn_peer, 0, sizeof(conn_peer));
- bsc_subscr_pick_codec(&conn_peer, conn);
- conn_peer.call_id = conn->sccp.conn_id;
- conn_peer.ptime = 20;
- osmo_strlcpy(conn_peer.endpoint, get_mgw_ep_name(conn), sizeof(conn_peer.endpoint));
-
- /* (Pre)Change state and create the connection */
- conn_fsm_state_chg(ST_WAIT_CRCX_BTS);
- conn->user_plane.fi_bts =
- mgcp_conn_create(conn->network->mgw.client, fi, GSCON_EV_MGW_FAIL_BTS,
- GSCON_EV_MGW_CRCX_RESP_BTS, &conn_peer);
- if (!conn->user_plane.fi_bts) {
- assignment_failed(fi, GSM0808_CAUSE_EQUIPMENT_FAILURE);
- return;
- }
- break;
- case GSM48_CMODE_SIGN:
- /* A signalling channel is requested, so we perform the
- * channel assignment directly without performing any
- * MGCP actions. ST_WAIT_ASS_CMPL will see by the
- * conn->user_plane.chan_mode parameter that this
- * assignment is for a signalling channel and will then
- * change back to ST_ACTIVE (here) immediately. */
- rc = gsm0808_assign_req(conn, conn->user_plane.chan_mode,
- conn->user_plane.full_rate);
-
- if (rc == 1) {
- send_ass_compl(conn->lchan, fi, false);
- return;
- } else if (rc != 0) {
- assignment_failed(fi, GSM0808_CAUSE_EQUIPMENT_FAILURE);
- return;
- }
- conn_fsm_state_chg(ST_WAIT_ASS_CMPL);
- break;
- default:
- /* An unsupported channel is requested, so we have to
- * reject this request by sending an assignment failure
- * message immediately */
- LOGPFSML(fi, LOGL_ERROR, "Requested channel mode is not supported! chan_mode=%s full_rate=%d\n",
- get_value_string(gsm48_chan_mode_names, conn->user_plane.chan_mode),
- conn->user_plane.full_rate);
-
- /* The requested channel mode is not supported */
- assignment_failed(fi, GSM0808_CAUSE_REQ_CODEC_TYPE_OR_CONFIG_NOT_SUPP);
- break;
- }
- break;
- case GSCON_EV_HO_START:
- rc = bsc_handover_start_gscon(conn);
- if (rc) {
- resp = gsm0808_create_clear_rqst(GSM0808_CAUSE_EQUIPMENT_FAILURE);
- sigtran_send(conn, resp, fi);
- conn_fsm_state_chg(ST_CLEARING);
+ case GSCON_EV_ASSIGNMENT_START:
+ bts = conn->lchan? conn->lchan->ts->trx->bts : NULL;
+
+ if (!bts) {
+ LOGPFSML(fi, LOGL_ERROR, "Cannot do assignment, no active BTS\n");
return;
}
- /* Note: No timeout is set here, T3103 in handover_logic.c
- * will generate a GSCON_EV_HO_TIMEOUT event should the
- * handover time out, so we do not need another timeout
- * here (maybe its worth to think about giving GSCON
- * more power over the actual handover process). */
- conn_fsm_state_chg(ST_WAIT_HO_COMPL);
- break;
- case GSCON_EV_A_HO_REQ:
- /* FIXME: reject any handover requests with HO FAIL until implemented */
+ /* Rely on assignment_fsm timeout */
+ osmo_fsm_inst_state_chg(fi, ST_ASSIGNMENT, 0, 0);
+ assignment_fsm_start(conn, bts, data);
+ return;
+
+ case GSCON_EV_HANDOVER_START:
+ rate_ctr_inc(&conn->network->bsc_ctrs->ctr[BSC_CTR_HANDOVER_ATTEMPTED]);
+ /* Rely on handover_fsm timeout */
+ if (osmo_fsm_inst_state_chg(fi, ST_HANDOVER, 0, 0))
+ LOGPFSML(fi, LOGL_ERROR, "Cannot transition to HANDOVER state, discarding\n");
+ else
+ handover_start(data);
break;
+
case GSCON_EV_MO_DTAP:
forward_dtap(conn, (struct msgb *)data, fi);
break;
case GSCON_EV_MT_DTAP:
- submit_dtap(conn, (struct msgb *)data, fi);
+ submit_dtap(conn, (struct msgb *)data);
break;
case GSCON_EV_TX_SCCP:
- sigtran_send(conn, (struct msgb *)data, fi);
+ gscon_sigtran_send(conn, (struct msgb *)data);
break;
default:
OSMO_ASSERT(false);
- break;
}
}
-/* Before we may start the channel assignment we need to get an IP/Port for the
- * RTP connection from the MGW */
-static void gscon_fsm_wait_crcx_bts(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+static void gscon_fsm_assignment(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_subscriber_connection *conn = fi->priv;
- struct mgcp_conn_peer *conn_peer = NULL;
- int rc;
switch (event) {
- case GSCON_EV_MGW_CRCX_RESP_BTS:
- conn_peer = data;
-
- /* Check if the MGW has assigned an enpoint to us, otherwise we
- * can not proceed. */
- if (strlen(conn_peer->endpoint) <= 0) {
- assignment_failed(fi, GSM0808_CAUSE_EQUIPMENT_FAILURE);
- return;
- }
-
- /* Memorize the endpoint name we got assigned from the MGW.
- * When the BTS sided connection is done, we need to create
- * a second connection on that same endpoint, so we need
- * to know its ID */
- if (!conn->user_plane.mgw_endpoint)
- conn->user_plane.mgw_endpoint = talloc_zero_size(conn, MGCP_ENDPOINT_MAXLEN);
- OSMO_ASSERT(conn->user_plane.mgw_endpoint);
- osmo_strlcpy(conn->user_plane.mgw_endpoint, conn_peer->endpoint, MGCP_ENDPOINT_MAXLEN);
-
- /* Store the IP-Address and the port the MGW assigned to us,
- * then start the channel assignment. */
- conn->user_plane.rtp_port = conn_peer->port;
- conn->user_plane.rtp_ip = osmo_ntohl(inet_addr(conn_peer->addr));
- rc = gsm0808_assign_req(conn, conn->user_plane.chan_mode, conn->user_plane.full_rate);
- if (rc != 0) {
- assignment_failed(fi, GSM0808_CAUSE_RQSTED_SPEECH_VERSION_UNAVAILABLE);
- return;
- }
+ case GSCON_EV_ASSIGNMENT_END:
+ osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0);
+ gscon_dtap_queue_flush(conn, 1);
+ return;
- conn_fsm_state_chg(ST_WAIT_ASS_CMPL);
- break;
case GSCON_EV_MO_DTAP:
forward_dtap(conn, (struct msgb *)data, fi);
break;
case GSCON_EV_MT_DTAP:
- submit_dtap(conn, (struct msgb *)data, fi);
+ submit_dtap(conn, (struct msgb *)data);
break;
case GSCON_EV_TX_SCCP:
- sigtran_send(conn, (struct msgb *)data, fi);
+ gscon_sigtran_send(conn, (struct msgb *)data);
break;
default:
OSMO_ASSERT(false);
- break;
}
}
-/* We're waiting for an ASSIGNMENT COMPLETE from MS */
-static void gscon_fsm_wait_ass_cmpl(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+static void gscon_fsm_handover(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_subscriber_connection *conn = fi->priv;
- struct gsm_lchan *lchan = conn->lchan;
- struct mgcp_conn_peer conn_peer;
- struct in_addr addr;
- int rc;
switch (event) {
- case GSCON_EV_RR_ASS_COMPL:
- switch (conn->user_plane.chan_mode) {
- case GSM48_CMODE_SPEECH_V1:
- case GSM48_CMODE_SPEECH_EFR:
- case GSM48_CMODE_SPEECH_AMR:
- /* FIXME: What if we are using SCCP-Lite? */
-
- /* We are dealing with a voice channel, so we can not
- * confirm the assignment directly. We must first do
- * some final steps on the MGCP side. */
-
- /* Prepare parameters with the information we got during the assignment */
- memset(&conn_peer, 0, sizeof(conn_peer));
- bsc_subscr_pick_codec(&conn_peer, conn);
- addr.s_addr = osmo_ntohl(lchan->abis_ip.bound_ip);
- osmo_strlcpy(conn_peer.addr, inet_ntoa(addr), sizeof(conn_peer.addr));
- conn_peer.port = lchan->abis_ip.bound_port;
- conn_peer.ptime = 20;
-
- /* (Pre)Change state and modify the connection */
- conn_fsm_state_chg(ST_WAIT_MDCX_BTS);
- rc = mgcp_conn_modify(conn->user_plane.fi_bts, GSCON_EV_MGW_MDCX_RESP_BTS, &conn_peer);
- if (rc != 0) {
- assignment_failed(fi, GSM0808_CAUSE_EQUIPMENT_FAILURE);
- return;
- }
- break;
- case GSM48_CMODE_SIGN:
- /* Confirm the successful assignment on BSSMAP and
- * change back into active state */
- send_ass_compl(lchan, fi, false);
- conn_fsm_state_chg(ST_ACTIVE);
- break;
- default:
- /* Unsupported modes should have been already filtered
- * by gscon_fsm_active(). If we reach the default
- * section here anyway than some unsupported mode must
- * have made it into the FSM, this would be a bug, so
- * we fire an assertion here */
- OSMO_ASSERT(false);
- break;
- }
+ case GSCON_EV_HANDOVER_END:
+ osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0);
+ gscon_dtap_queue_flush(conn, 1);
+ return;
- break;
- case GSCON_EV_RR_ASS_FAIL:
- {
- enum gsm0808_cause cause = GSM0808_CAUSE_RQSTED_TERRESTRIAL_RESOURCE_UNAVAILABLE;
- if (data)
- cause = *((enum gsm0808_cause*)data);
- assignment_failed(fi, cause);
- }
- break;
case GSCON_EV_MO_DTAP:
forward_dtap(conn, (struct msgb *)data, fi);
break;
case GSCON_EV_MT_DTAP:
- submit_dtap(conn, (struct msgb *)data, fi);
+ /* cache until handover is done */
+ submit_dtap(conn, (struct msgb *)data);
break;
case GSCON_EV_TX_SCCP:
- sigtran_send(conn, (struct msgb *)data, fi);
+ gscon_sigtran_send(conn, (struct msgb *)data);
break;
default:
OSMO_ASSERT(false);
- break;
}
}
-/* We are waiting for the MGW response to the MDCX */
-static void gscon_fsm_wait_mdcx_bts(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+static bool same_mgw_info(const struct mgcp_conn_peer *a, const struct mgcp_conn_peer *b)
{
- struct gsm_subscriber_connection *conn = fi->priv;
- struct mgcp_conn_peer conn_peer;
- struct sockaddr_in *sin = NULL;
+ if (!a || !b)
+ return false;
+ if (a == b)
+ return true;
+ if (strcmp(a->addr, b->addr))
+ return false;
+ if (a->port != b->port)
+ return false;
+ if (a->call_id != b->call_id)
+ return false;
+ return true;
+}
- switch (event) {
- case GSCON_EV_MGW_MDCX_RESP_BTS:
-
- /* Prepare parameters with the connection information we got
- * with the assignment command */
- memset(&conn_peer, 0, sizeof(conn_peer));
- bsc_subscr_pick_codec(&conn_peer, conn);
- conn_peer.call_id = conn->sccp.conn_id;
- sin = (struct sockaddr_in *)&conn->user_plane.aoip_rtp_addr_remote;
- conn_peer.port = osmo_ntohs(sin->sin_port);
- osmo_strlcpy(conn_peer.addr, inet_ntoa(sin->sin_addr), sizeof(conn_peer.addr));
- conn_peer.ptime = 20;
-
- /* Make sure we use the same endpoint where we created the
- * BTS connection. */
- osmo_strlcpy(conn_peer.endpoint, conn->user_plane.mgw_endpoint, sizeof(conn_peer.endpoint));
-
- switch (conn->sccp.msc->a.asp_proto) {
- case OSMO_SS7_ASP_PROT_IPA:
- /* Send assignment complete message to the MSC */
- send_ass_compl(conn->lchan, fi, true);
- conn_fsm_state_chg(ST_ACTIVE);
- break;
- default:
- /* (Pre)Change state and create the connection */
- conn_fsm_state_chg(ST_WAIT_CRCX_MSC);
- conn->user_plane.fi_msc = mgcp_conn_create(conn->network->mgw.client, fi,
- GSCON_EV_MGW_FAIL_MSC,
- GSCON_EV_MGW_CRCX_RESP_MSC, &conn_peer);
- if (!conn->user_plane.fi_msc) {
- assignment_failed(fi, GSM0808_CAUSE_EQUIPMENT_FAILURE);
- return;
- }
- break;
- }
+/* Make sure a conn->user_plane.mgw_endpoint is allocated with the proper mgw endpoint name. For
+ * SCCPlite, pass in msc_assigned_cic the CIC received upon BSSMAP Assignment Command or BSSMAP Handover
+ * Request form the MSC (which is only stored in conn->user_plane after success). Ignored for AoIP. */
+struct mgw_endpoint *gscon_ensure_mgw_endpoint(struct gsm_subscriber_connection *conn,
+ uint16_t msc_assigned_cic)
+{
+ if (conn->user_plane.mgw_endpoint)
+ return conn->user_plane.mgw_endpoint;
- break;
- case GSCON_EV_MO_DTAP:
- forward_dtap(conn, (struct msgb *)data, fi);
- break;
- case GSCON_EV_MT_DTAP:
- submit_dtap(conn, (struct msgb *)data, fi);
- break;
- case GSCON_EV_TX_SCCP:
- sigtran_send(conn, (struct msgb *)data, fi);
- break;
- default:
- OSMO_ASSERT(false);
- break;
+ if (gscon_is_sccplite(conn)) {
+ /* derive endpoint name from CIC on A interface side */
+ conn->user_plane.mgw_endpoint =
+ mgw_endpoint_alloc(conn->fi, GSCON_EV_FORGET_MGW_ENDPOINT,
+ conn->network->mgw.client, conn->fi->id,
+ "%x@mgw", msc_assigned_cic);
+ LOGPFSML(conn->fi, LOGL_DEBUG, "MGW endpoint name derived from CIC 0x%x: %s\n",
+ msc_assigned_cic, mgw_endpoint_name(conn->user_plane.mgw_endpoint));
+
+ } else if (gscon_is_aoip(conn)) {
+ /* use dynamic RTPBRIDGE endpoint allocation in MGW */
+ conn->user_plane.mgw_endpoint =
+ mgw_endpoint_alloc(conn->fi, GSCON_EV_FORGET_MGW_ENDPOINT,
+ conn->network->mgw.client, conn->fi->id,
+ "rtpbridge/*@mgw");
+ } else {
+ LOGPFSML(conn->fi, LOGL_ERROR, "Conn is neither SCCPlite nor AoIP!?\n");
+ return NULL;
}
+
+ return conn->user_plane.mgw_endpoint;
}
-static void gscon_fsm_wait_crcx_msc(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+bool gscon_connect_mgw_to_msc(struct gsm_subscriber_connection *conn,
+ struct gsm_lchan *for_lchan,
+ const char *addr, uint16_t port,
+ struct osmo_fsm_inst *notify,
+ uint32_t event_success, uint32_t event_failure,
+ void *notify_data,
+ struct mgwep_ci **created_ci)
{
- struct gsm_subscriber_connection *conn = fi->priv;
- struct mgcp_conn_peer *conn_peer = NULL;
- struct gsm_lchan *lchan = conn->lchan;
- struct sockaddr_in *sin = NULL;
-
- switch (event) {
- case GSCON_EV_MGW_CRCX_RESP_MSC:
- conn_peer = data;
+ int rc;
+ struct mgwep_ci *ci;
+ struct mgcp_conn_peer mgw_info;
+ enum mgcp_verb verb;
- /* Store address information we got in response from the CRCX command. */
- sin = (struct sockaddr_in *)&conn->user_plane.aoip_rtp_addr_local;
- sin->sin_family = AF_INET;
- sin->sin_addr.s_addr = inet_addr(conn_peer->addr);
- sin->sin_port = osmo_ntohs(conn_peer->port);
+ if (created_ci)
+ *created_ci = NULL;
- /* Send assignment complete message to the MSC */
- send_ass_compl(lchan, fi, true);
+ if (gscon_is_sccplite(conn)) {
+ /* SCCPlite connection uses an MGW endpoint created by the MSC, so there is nothing to do
+ * here. */
+ if (notify)
+ osmo_fsm_inst_dispatch(notify, event_success, notify_data);
+ return true;
+ }
- conn_fsm_state_chg(ST_ACTIVE);
+ mgw_info = (struct mgcp_conn_peer){
+ .port = port,
+ .call_id = conn->sccp.conn_id,
+ .ptime = 20,
+ };
+ mgcp_pick_codec(&mgw_info, for_lchan);
- break;
- case GSCON_EV_MO_DTAP:
- forward_dtap(conn, (struct msgb *)data, fi);
- break;
- case GSCON_EV_MT_DTAP:
- submit_dtap(conn, (struct msgb *)data, fi);
- break;
- case GSCON_EV_TX_SCCP:
- sigtran_send(conn, (struct msgb *)data, fi);
- break;
- default:
- OSMO_ASSERT(false);
- break;
+ rc = osmo_strlcpy(mgw_info.addr, addr, sizeof(mgw_info.addr));
+ if (rc <= 0 || rc >= sizeof(mgw_info.addr)) {
+ LOGPFSML(conn->fi, LOGL_ERROR, "Failed to compose MGW endpoint address for MGW -> MSC\n");
+ return false;
}
-}
-static void gscon_fsm_clearing(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct gsm_subscriber_connection *conn = fi->priv;
- struct msgb *resp;
+ ci = conn->user_plane.mgw_endpoint_ci_msc;
+ if (ci) {
+ const struct mgcp_conn_peer *prev_crcx_info = mgwep_ci_get_rtp_info(ci);
- switch (event) {
- case GSCON_EV_RSL_CLEAR_COMPL:
- resp = gsm0808_create_clear_complete();
- sigtran_send(conn, resp, fi);
- /* we cannot terminate the FSM here, as that would send N-DISCCONNET.req
- * and 3GPP TS 48.006 Section 9.2 clearly states that SCCP connections must
- * always be released from the MSC side*/
- break;
- default:
- OSMO_ASSERT(false);
- break;
- }
-}
+ if (!conn->user_plane.mgw_endpoint) {
+ LOGPFSML(conn->fi, LOGL_ERROR, "Internal error: conn has a CI but no endoint\n");
+ return false;
+ }
-/* Wait for the handover logic to tell us whether the handover completed,
- * failed or has timed out */
-static void gscon_fsm_wait_ho_compl(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct gsm_subscriber_connection *conn = fi->priv;
- struct mgcp_conn_peer conn_peer;
- struct gsm_lchan *lchan = conn->lchan;
- struct in_addr addr;
- struct msgb *resp;
- int rc;
+ if (!prev_crcx_info) {
+ LOGPFSML(conn->fi, LOGL_ERROR, "There already is an MGW connection for the MSC side,"
+ " but it seems to be broken. Will not CRCX another one (%s)\n",
+ mgwep_ci_name(ci));
+ return false;
+ }
- switch (event) {
- case GSCON_EV_HO_COMPL:
- /* The handover logic informs us that the handover has been
- * completet. Now we have to tell the MGW the IP/Port on the
- * new BTS so that the uplink RTP traffic can be redirected
- * there. */
-
- /* Prepare parameters with the information we got during the
- * handover procedure (via IPACC) */
- memset(&conn_peer, 0, sizeof(conn_peer));
- bsc_subscr_pick_codec(&conn_peer, conn);
- addr.s_addr = osmo_ntohl(lchan->abis_ip.bound_ip);
- osmo_strlcpy(conn_peer.addr, inet_ntoa(addr), sizeof(conn_peer.addr));
- conn_peer.port = lchan->abis_ip.bound_port;
- conn_peer.ptime = 20;
-
- /* (Pre)Change state and modify the connection */
- conn_fsm_state_chg(ST_WAIT_MDCX_BTS_HO);
- rc = mgcp_conn_modify(conn->user_plane.fi_bts, GSCON_EV_MGW_MDCX_RESP_BTS, &conn_peer);
- if (rc != 0) {
- resp = gsm0808_create_clear_rqst(GSM0808_CAUSE_EQUIPMENT_FAILURE);
- sigtran_send(conn, resp, fi);
- conn_fsm_state_chg(ST_CLEARING);
- return;
+ if (same_mgw_info(&mgw_info, prev_crcx_info)) {
+ LOGPFSML(conn->fi, LOGL_DEBUG,
+ "MSC side MGW endpoint ci is already configured to %s\n",
+ mgwep_ci_name(ci));
+ return true;
}
- break;
- case GSCON_EV_HO_TIMEOUT:
- case GSCON_EV_HO_FAIL:
- /* The handover logic informs us that the handover failed for
- * some reason. This means the phone stays on the TS/BTS on
- * which it currently is. We will change back to the active
- * state again as there are no further operations needed */
- conn_fsm_state_chg(ST_ACTIVE);
- break;
- default:
- OSMO_ASSERT(false);
- break;
- }
-}
-/* Wait for the MGW to confirm handover related modification of the connection
- * parameters */
-static void gscon_fsm_wait_mdcx_bts_ho(struct osmo_fsm_inst *fi, uint32_t event, void *data)
-{
- struct gsm_subscriber_connection *conn = fi->priv;
+ verb = MGCP_VERB_MDCX;
+ } else
+ verb = MGCP_VERB_CRCX;
- switch (event) {
- case GSCON_EV_MGW_MDCX_RESP_BTS:
- /* The MGW has confirmed the handover MDCX, and the handover
- * is now also done on the RTP side. We may now change back
- * to the active state. */
- conn_fsm_state_chg(ST_ACTIVE);
- break;
- case GSCON_EV_MO_DTAP:
- forward_dtap(conn, (struct msgb *)data, fi);
- break;
- case GSCON_EV_MT_DTAP:
- submit_dtap(conn, (struct msgb *)data, fi);
- break;
- case GSCON_EV_TX_SCCP:
- sigtran_send(conn, (struct msgb *)data, fi);
- break;
- default:
- OSMO_ASSERT(false);
- break;
+ gscon_ensure_mgw_endpoint(conn, for_lchan->activate.msc_assigned_cic);
+
+ if (!conn->user_plane.mgw_endpoint) {
+ LOGPFSML(conn->fi, LOGL_ERROR, "Unable to allocate endpoint info\n");
+ return false;
+ }
+
+ if (!ci) {
+ ci = mgw_endpoint_ci_add(conn->user_plane.mgw_endpoint, "to-MSC");
+ if (created_ci)
+ *created_ci = ci;
+ conn->user_plane.mgw_endpoint_ci_msc = ci;
}
+ if (!ci) {
+ LOGPFSML(conn->fi, LOGL_ERROR, "Unable to allocate endpoint CI info\n");
+ return false;
+ }
+
+ mgw_endpoint_ci_request(ci, verb, &mgw_info, notify, event_success, event_failure, notify_data);
+ return true;
}
#define EV_TRANSPARENT_SCCP S(GSCON_EV_TX_SCCP) | S(GSCON_EV_MO_DTAP) | S(GSCON_EV_MT_DTAP)
@@ -866,8 +551,9 @@ static void gscon_fsm_wait_mdcx_bts_ho(struct osmo_fsm_inst *fi, uint32_t event,
static const struct osmo_fsm_state gscon_fsm_states[] = {
[ST_INIT] = {
.name = "INIT",
- .in_event_mask = S(GSCON_EV_A_CONN_REQ) | S(GSCON_EV_A_CONN_IND),
- .out_state_mask = S(ST_WAIT_CC),
+ .in_event_mask = S(GSCON_EV_A_CONN_REQ) | S(GSCON_EV_A_CONN_IND)
+ | S(GSCON_EV_HANDOVER_END),
+ .out_state_mask = S(ST_WAIT_CC) | S(ST_ACTIVE) | S(ST_CLEARING),
.action = gscon_fsm_init,
},
[ST_WAIT_CC] = {
@@ -878,133 +564,135 @@ static const struct osmo_fsm_state gscon_fsm_states[] = {
},
[ST_ACTIVE] = {
.name = "ACTIVE",
- .in_event_mask = EV_TRANSPARENT_SCCP | S(GSCON_EV_A_ASSIGNMENT_CMD) |
- S(GSCON_EV_A_HO_REQ) | S(GSCON_EV_HO_START),
- .out_state_mask = S(ST_CLEARING) | S(ST_WAIT_CRCX_BTS) | S(ST_WAIT_ASS_CMPL) |
- S(ST_WAIT_MO_HO_CMD) | S(ST_WAIT_HO_COMPL),
+ .in_event_mask = EV_TRANSPARENT_SCCP | S(GSCON_EV_ASSIGNMENT_START) |
+ S(GSCON_EV_HANDOVER_START),
+ .out_state_mask = S(ST_CLEARING) | S(ST_ASSIGNMENT) |
+ S(ST_HANDOVER),
+ .onenter = gscon_fsm_active_onenter,
.action = gscon_fsm_active,
},
- [ST_WAIT_CRCX_BTS] = {
- .name = "WAIT_CRCX_BTS",
- .in_event_mask = EV_TRANSPARENT_SCCP | S(GSCON_EV_MGW_CRCX_RESP_BTS),
- .out_state_mask = S(ST_ACTIVE) | S(ST_WAIT_ASS_CMPL),
- .action = gscon_fsm_wait_crcx_bts,
+ [ST_ASSIGNMENT] = {
+ .name = "ASSIGNMENT",
+ .in_event_mask = EV_TRANSPARENT_SCCP | S(GSCON_EV_ASSIGNMENT_END),
+ .out_state_mask = S(ST_ACTIVE) | S(ST_CLEARING),
+ .action = gscon_fsm_assignment,
},
- [ST_WAIT_ASS_CMPL] = {
- .name = "WAIT_ASS_CMPL",
- .in_event_mask = EV_TRANSPARENT_SCCP | S(GSCON_EV_RR_ASS_COMPL) | S(GSCON_EV_RR_ASS_FAIL),
- .out_state_mask = S(ST_ACTIVE) | S(ST_WAIT_MDCX_BTS),
- .action = gscon_fsm_wait_ass_cmpl,
- },
- [ST_WAIT_MDCX_BTS] = {
- .name = "WAIT_MDCX_BTS",
- .in_event_mask = EV_TRANSPARENT_SCCP | S(GSCON_EV_MGW_MDCX_RESP_BTS),
- .out_state_mask = S(ST_ACTIVE) | S(ST_WAIT_CRCX_MSC),
- .action = gscon_fsm_wait_mdcx_bts,
- },
- [ST_WAIT_CRCX_MSC] = {
- .name = "WAIT_CRCX_MSC",
- .in_event_mask = EV_TRANSPARENT_SCCP | S(GSCON_EV_MGW_CRCX_RESP_MSC),
- .out_state_mask = S(ST_ACTIVE),
- .action = gscon_fsm_wait_crcx_msc,
+ [ST_HANDOVER] = {
+ .name = "HANDOVER",
+ .in_event_mask = EV_TRANSPARENT_SCCP | S(GSCON_EV_HANDOVER_END),
+ .out_state_mask = S(ST_ACTIVE) | S(ST_CLEARING),
+ .action = gscon_fsm_handover,
},
[ST_CLEARING] = {
.name = "CLEARING",
- .in_event_mask = S(GSCON_EV_RSL_CLEAR_COMPL),
- .action = gscon_fsm_clearing,
+ /* dead end state */
},
+};
- /* TODO: external handover, probably it makes sense to break up the
- * program flow in handover_logic.c a bit and handle some of the logic
- * here? */
- [ST_WAIT_MT_HO_ACC] = {
- .name = "WAIT_MT_HO_ACC",
- },
- [ST_WAIT_MT_HO_COMPL] = {
- .name = "WAIT_MT_HO_COMPL",
- },
- [ST_WAIT_MO_HO_CMD] = {
- .name = "WAIT_MO_HO_CMD",
- },
- [ST_MO_HO_PROCEEDING] = {
- .name = "MO_HO_PROCEEDING",
- },
+void gscon_change_primary_lchan(struct gsm_subscriber_connection *conn, struct gsm_lchan *new_lchan)
+{
+ /* On release, do not receive release events that look like the primary lchan is gone. */
+ struct gsm_lchan *old_lchan = conn->lchan;
- /* Internal handover */
- [ST_WAIT_HO_COMPL] = {
- .name = "WAIT_HO_COMPL",
- .in_event_mask = S(GSCON_EV_HO_COMPL) | S(GSCON_EV_HO_FAIL) | S(GSCON_EV_HO_TIMEOUT),
- .out_state_mask = S(ST_ACTIVE) | S(ST_WAIT_MDCX_BTS_HO) | S(ST_CLEARING),
- .action = gscon_fsm_wait_ho_compl,
- },
- [ST_WAIT_MDCX_BTS_HO] = {
- .name = "WAIT_MDCX_BTS_HO",
- .in_event_mask = EV_TRANSPARENT_SCCP | S(GSCON_EV_MGW_MDCX_RESP_BTS),
- .action = gscon_fsm_wait_mdcx_bts_ho,
- .out_state_mask = S(ST_ACTIVE),
- },
-};
+ conn->lchan = new_lchan;
+ conn->lchan->conn = conn;
+
+ if (old_lchan) {
+ lchan_forget_conn(old_lchan);
+ lchan_release(old_lchan, false, false, 0);
+ }
+}
+
+void gscon_lchan_releasing(struct gsm_subscriber_connection *conn, struct gsm_lchan *lchan)
+{
+ if (!lchan)
+ return;
+ if (conn->assignment.new_lchan == lchan) {
+ if (conn->assignment.fi)
+ osmo_fsm_inst_dispatch(conn->assignment.fi, ASSIGNMENT_EV_LCHAN_ERROR, lchan);
+ lchan_forget_conn(conn->assignment.new_lchan);
+ conn->assignment.new_lchan = NULL;
+ }
+ if (conn->ho.new_lchan == lchan) {
+ if (conn->ho.fi)
+ osmo_fsm_inst_dispatch(conn->ho.fi, HO_EV_LCHAN_ERROR, lchan);
+ }
+ if (conn->lchan == lchan) {
+ lchan_forget_conn(conn->lchan);
+ conn->lchan = NULL;
+ }
+ if (!conn->lchan) {
+ osmo_fsm_inst_state_chg(conn->fi, ST_CLEARING, 60, 999);
+ gscon_bssmap_clear(conn, GSM0808_CAUSE_EQUIPMENT_FAILURE);
+ }
+}
+
+/* An lchan was deallocated. */
+void gscon_forget_lchan(struct gsm_subscriber_connection *conn, struct gsm_lchan *lchan)
+{
+ if (!lchan)
+ return;
+ if (conn->assignment.new_lchan == lchan)
+ conn->assignment.new_lchan = NULL;
+ if (conn->ho.new_lchan == lchan)
+ conn->ho.new_lchan = NULL;
+ if (conn->lchan == lchan)
+ conn->lchan = NULL;
+ if (!conn->lchan)
+ gscon_bssmap_clear(conn, GSM0808_CAUSE_EQUIPMENT_FAILURE);
+}
+
+static void gscon_forget_mgw_endpoint(struct gsm_subscriber_connection *conn)
+{
+ conn->user_plane.mgw_endpoint = NULL;
+ conn->user_plane.mgw_endpoint_ci_msc = NULL;
+ lchan_forget_mgw_endpoint(conn->lchan);
+ lchan_forget_mgw_endpoint(conn->assignment.new_lchan);
+ lchan_forget_mgw_endpoint(conn->ho.new_lchan);
+}
+
+void gscon_forget_mgw_endpoint_ci(struct gsm_subscriber_connection *conn, struct mgwep_ci *ci)
+{
+ if (ci != conn->user_plane.mgw_endpoint_ci_msc)
+ return;
+ conn->user_plane.mgw_endpoint_ci_msc = NULL;
+}
static void gscon_fsm_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_subscriber_connection *conn = fi->priv;
- struct msgb *resp = NULL;
-
- /* When a connection on the MGW fails, make sure that the reference
- * in our book-keeping is erased. */
- switch (event) {
- case GSCON_EV_MGW_FAIL_BTS:
- conn->user_plane.fi_bts = NULL;
- break;
- case GSCON_EV_MGW_FAIL_MSC:
- conn->user_plane.fi_msc = NULL;
- break;
- }
/* Regular allstate event processing */
switch (event) {
- case GSCON_EV_MGW_FAIL_BTS:
- case GSCON_EV_MGW_FAIL_MSC:
- /* Note: An MGW connection die per definition at any time.
- * However, if it dies during the assignment we must return
- * with an assignment failure */
- OSMO_ASSERT(fi->state != ST_INIT && fi->state != ST_WAIT_CC);
- if (fi->state == ST_WAIT_CRCX_BTS || fi->state == ST_WAIT_ASS_CMPL || fi->state == ST_WAIT_MDCX_BTS
- || fi->state == ST_WAIT_CRCX_MSC)
- assignment_failed(fi, GSM0808_CAUSE_EQUIPMENT_FAILURE);
- break;
case GSCON_EV_A_CLEAR_CMD:
/* MSC tells us to cleanly shut down */
- conn_fsm_state_chg(ST_CLEARING);
- gsm0808_clear(conn);
+ osmo_fsm_inst_state_chg(fi, ST_CLEARING, 60, 999);
+ LOGPFSML(fi, LOGL_DEBUG, "Releasing all lchans (if any) after BSSMAP Clear Command\n");
+ gscon_release_lchans(conn, true);
/* FIXME: Release all terestrial resources in ST_CLEARING */
/* According to 3GPP 48.008 3.1.9.1. "The BSS need not wait for the radio channel
* release to be completed or for the guard timer to expire before returning the
* CLEAR COMPLETE message" */
/* Close MGCP connections */
- toss_mgcp_conn(conn, fi);
+ mgw_endpoint_clear(conn->user_plane.mgw_endpoint);
- /* FIXME: Question: Is this a hack to force a clear complete from internel?
- * nobody seems to send the event from outside? */
- osmo_fsm_inst_dispatch(conn->fi, GSCON_EV_RSL_CLEAR_COMPL, NULL);
+ gscon_sigtran_send(conn, gsm0808_create_clear_complete());
break;
case GSCON_EV_A_DISC_IND:
/* MSC or SIGTRAN network has hard-released SCCP connection,
* terminate the FSM now. */
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, data);
break;
- case GSCON_EV_RLL_REL_IND:
- /* BTS reports that one of the LAPDm data links was released */
- /* send proper clear request to MSC */
- LOGPFSML(fi, LOGL_DEBUG, "Tx BSSMAP CLEAR REQUEST to MSC\n");
- resp = gsm0808_create_clear_rqst(GSM0808_CAUSE_RADIO_INTERFACE_MESSAGE_FAILURE);
- sigtran_send(conn, resp, fi);
+ case GSCON_EV_FORGET_MGW_ENDPOINT:
+ gscon_forget_mgw_endpoint(conn);
break;
case GSCON_EV_RSL_CONN_FAIL:
- LOGPFSML(fi, LOGL_DEBUG, "Tx BSSMAP CLEAR REQUEST to MSC\n");
- resp = gsm0808_create_clear_rqst(GSM0808_CAUSE_RADIO_INTERFACE_FAILURE);
- sigtran_send(conn, resp, fi);
+ if (conn->lchan) {
+ conn->lchan->release_in_error = true;
+ conn->lchan->rsl_error_cause = data ? *(uint8_t*)data : RSL_ERR_IE_ERROR;
+ }
+ gscon_bssmap_clear(conn, GSM0808_CAUSE_RADIO_INTERFACE_FAILURE);
break;
case GSCON_EV_MGW_MDCX_RESP_MSC:
LOGPFSML(fi, LOGL_DEBUG, "Rx MDCX of MSC side (LCLS?)\n");
@@ -1021,22 +709,9 @@ static void gscon_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cau
{
struct gsm_subscriber_connection *conn = fi->priv;
- if (conn->ho) {
- LOGPFSML(fi, LOGL_DEBUG, "Releasing handover state\n");
- bsc_clear_handover(conn, 1);
- conn->ho = NULL;
- }
-
- if (conn->secondary_lchan) {
- LOGPFSML(fi, LOGL_DEBUG, "Releasing secondary_lchan\n");
- lchan_release(conn->secondary_lchan, 0, RSL_REL_LOCAL_END);
- conn->secondary_lchan = NULL;
- }
- if (conn->lchan) {
- LOGPFSML(fi, LOGL_DEBUG, "Releasing lchan\n");
- lchan_release(conn->lchan, 0, RSL_REL_LOCAL_END);
- conn->lchan = NULL;
- }
+ lchan_forget_conn(conn->lchan);
+ lchan_forget_conn(conn->assignment.new_lchan);
+ lchan_forget_conn(conn->ho.new_lchan);
if (conn->sccp.state != SUBSCR_SCCP_ST_NONE) {
LOGPFSML(fi, LOGL_DEBUG, "Disconnecting SCCP\n");
@@ -1046,11 +721,6 @@ static void gscon_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cau
conn->sccp.state = SUBSCR_SCCP_ST_NONE;
}
- /* drop pending messages */
- gscon_dtap_queue_flush(conn, 0);
-
- penalty_timers_free(&conn->hodec2.penalty_timers);
-
if (conn->bsub) {
LOGPFSML(fi, LOGL_DEBUG, "Putting bsc_subscr\n");
bsc_subscr_put(conn->bsub);
@@ -1059,21 +729,27 @@ static void gscon_cleanup(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cau
llist_del(&conn->entry);
talloc_free(conn);
- fi->priv = NULL;
}
static void gscon_pre_term(struct osmo_fsm_inst *fi, enum osmo_fsm_term_cause cause)
{
struct gsm_subscriber_connection *conn = fi->priv;
- /* Make sure all possibly still open MGCP connections get closed */
- toss_mgcp_conn(conn, fi);
+ mgw_endpoint_clear(conn->user_plane.mgw_endpoint);
if (conn->lcls.fi) {
/* request termination of LCLS FSM */
osmo_fsm_inst_term(conn->lcls.fi, cause, NULL);
conn->lcls.fi = NULL;
}
+
+ LOGPFSML(fi, LOGL_DEBUG, "Releasing all lchans (if any) because this conn is terminating\n");
+ gscon_release_lchans(conn, false);
+
+ /* drop pending messages */
+ gscon_dtap_queue_flush(conn, 0);
+
+ penalty_timers_free(&conn->hodec2.penalty_timers);
}
static int gscon_timer_cb(struct osmo_fsm_inst *fi)
@@ -1082,6 +758,8 @@ static int gscon_timer_cb(struct osmo_fsm_inst *fi)
switch (fi->T) {
case 993210:
+ lchan_release(conn->lchan, false, true, RSL_ERR_INTERWORKING);
+
/* MSC has not responded/confirmed connection with CC, this
* could indicate a bad SCCP connection. We now inform the the
* FSM that controls the BSSMAP reset about the event. Maybe
@@ -1093,16 +771,16 @@ static int gscon_timer_cb(struct osmo_fsm_inst *fi)
* gscon_cleanup() above) */
osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL);
break;
- case 10: /* Assignment Failed */
- assignment_failed(fi, GSM0808_CAUSE_RADIO_INTERFACE_FAILURE);
- break;
- case MGCP_MGW_TIMEOUT_TIMER_NR: /* Assignment failed (no response from MGW) */
- assignment_failed(fi, GSM0808_CAUSE_EQUIPMENT_FAILURE);
- break;
- case MGCP_MGW_HO_TIMEOUT_TIMER_NR: /* Handover failed (no response from MGW) */
- conn_fsm_state_chg(ST_ACTIVE);
+ case 999:
+ /* The MSC has sent a BSSMAP Clear Command, we acknowledged that, but the conn was never
+ * disconnected. */
+ LOGPFSML(fi, LOGL_ERROR, "Long after a BSSMAP Clear Command, the conn is still not"
+ " released. For sanity, discarding this conn now.\n");
+ a_reset_conn_fail(conn->sccp.msc->a.reset_fsm);
+ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
break;
default:
+ LOGPFSML(fi, LOGL_ERROR, "Unknown timer %d expired\n", fi->T);
OSMO_ASSERT(false);
}
return 0;
@@ -1113,8 +791,9 @@ static struct osmo_fsm gscon_fsm = {
.states = gscon_fsm_states,
.num_states = ARRAY_SIZE(gscon_fsm_states),
.allstate_event_mask = S(GSCON_EV_A_DISC_IND) | S(GSCON_EV_A_CLEAR_CMD) | S(GSCON_EV_RSL_CONN_FAIL) |
- S(GSCON_EV_RLL_REL_IND) | S(GSCON_EV_MGW_FAIL_BTS) | S(GSCON_EV_MGW_FAIL_MSC) |
- S(GSCON_EV_MGW_MDCX_RESP_MSC) | S(GSCON_EV_LCLS_FAIL),
+ S(GSCON_EV_LCLS_FAIL) |
+ S(GSCON_EV_FORGET_LCHAN) |
+ S(GSCON_EV_FORGET_MGW_ENDPOINT),
.allstate_action = gscon_fsm_allstate,
.cleanup = gscon_cleanup,
.pre_term = gscon_pre_term,
@@ -1123,17 +802,16 @@ static struct osmo_fsm gscon_fsm = {
.event_names = gscon_fsm_event_names,
};
+void bsc_subscr_conn_fsm_init()
+{
+ OSMO_ASSERT(osmo_fsm_register(&gscon_fsm) == 0);
+ OSMO_ASSERT(osmo_fsm_register(&lcls_fsm) == 0);
+}
+
/* Allocate a subscriber connection and its associated FSM */
struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_network *net)
{
struct gsm_subscriber_connection *conn;
- static bool g_initialized = false;
-
- if (!g_initialized) {
- osmo_fsm_register(&gscon_fsm);
- osmo_fsm_register(&lcls_fsm);
- g_initialized = true;
- }
conn = talloc_zero(net, struct gsm_subscriber_connection);
if (!conn)
@@ -1190,25 +868,20 @@ static void gscon_dtap_queue_add(struct gsm_subscriber_connection *conn, struct
msgb_enqueue(&conn->dtap_queue, msg);
}
-void gscon_dtap_queue_flush(struct gsm_subscriber_connection *conn, int send)
+static void gscon_dtap_queue_flush(struct gsm_subscriber_connection *conn, int send)
{
struct msgb *msg;
unsigned int flushed_count = 0;
- if (conn->secondary_lchan || conn->ho) {
- LOGP(DMSC, LOGL_ERROR, "%s: Cannot send queued DTAP messages, handover/assignment is still ongoing\n",
- bsc_subscr_name(conn->bsub));
- send = 0;
- }
-
while ((msg = msgb_dequeue(&conn->dtap_queue))) {
conn->dtap_queue_len --;
flushed_count ++;
if (send) {
int link_id = (int)msg->cb[GSCON_DTAP_QUEUE_MSGB_CB_LINK_ID];
bool allow_sacch = !!msg->cb[GSCON_DTAP_QUEUE_MSGB_CB_ALLOW_SACCH];
- LOGP(DMSC, LOGL_DEBUG, "%s: Sending queued DTAP message after handover/assignment (%u/%u)\n",
- bsc_subscr_name(conn->bsub), flushed_count, conn->dtap_queue_len);
+ LOGPFSML(conn->fi, LOGL_DEBUG,
+ "%s: Sending queued DTAP message after handover/assignment (%u/%u)\n",
+ bsc_subscr_name(conn->bsub), flushed_count, conn->dtap_queue_len);
gsm0808_send_rsl_dtap(conn, msg, link_id, allow_sacch);
} else
msgb_free(msg);
@@ -1290,7 +963,7 @@ static void gsm0808_send_rsl_dtap(struct gsm_subscriber_connection *conn,
failed_to_send:
LOGPFSML(conn->fi, LOGL_ERROR, "Tx BSSMAP CLEAR REQUEST to MSC\n");
resp = gsm0808_create_clear_rqst(GSM0808_CAUSE_EQUIPMENT_FAILURE);
- sigtran_send(conn, resp, conn->fi);
+ gscon_sigtran_send(conn, resp);
osmo_fsm_inst_state_chg(conn->fi, ST_ACTIVE, 0, 0);
}
@@ -1298,10 +971,48 @@ void gscon_submit_rsl_dtap(struct gsm_subscriber_connection *conn,
struct msgb *msg, int link_id, int allow_sacch)
{
/* buffer message during assignment / handover */
- if (conn->secondary_lchan || conn->ho) {
+ if (conn->fi->state != ST_ACTIVE) {
gscon_dtap_queue_add(conn, msg, link_id, !! allow_sacch);
return;
}
gsm0808_send_rsl_dtap(conn, msg, link_id, allow_sacch);
}
+
+/* Compose an FSM ID, if possible from the current subscriber information */
+void gscon_update_id(struct gsm_subscriber_connection *conn)
+{
+ osmo_fsm_inst_update_id_f(conn->fi, "conn%u%s%s",
+ conn->sccp.conn_id,
+ conn->bsub? "_" : "",
+ conn->bsub? bsc_subscr_id(conn->bsub) : "");
+}
+
+bool gscon_is_aoip(struct gsm_subscriber_connection *conn)
+{
+ if (!conn || !conn->sccp.msc)
+ return false;
+
+ switch (conn->sccp.msc->a.asp_proto) {
+ case OSMO_SS7_ASP_PROT_SUA:
+ case OSMO_SS7_ASP_PROT_M3UA:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+bool gscon_is_sccplite(struct gsm_subscriber_connection *conn)
+{
+ if (!conn || !conn->sccp.msc)
+ return false;
+
+ switch (conn->sccp.msc->a.asp_proto) {
+ case OSMO_SS7_ASP_PROT_IPA:
+ return true;
+
+ default:
+ return false;
+ }
+}