summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2017-01-28 22:23:35 (UTC)
committerHarald Welte <laforge@gnumonks.org>2017-02-13 03:11:26 (UTC)
commit50e931338dfa1ad570734b80cce065ab611929aa (patch)
tree453e0b5f0b6f1ea482100ca99de0909c651a2545
parent20d00f7c9cd5fa328be7eef0257e5acb6945b1a6 (diff)
downloadlibosmo-sccp-50e931338dfa1ad570734b80cce065ab611929aa.zip
libosmo-sccp-50e931338dfa1ad570734b80cce065ab611929aa.tar.gz
libosmo-sccp-50e931338dfa1ad570734b80cce065ab611929aa.tar.bz2
libosmo-sccp-50e931338dfa1ad570734b80cce065ab611929aa.tar.xz
WIP: implement xUA ASP and AS state machine as osmo_fsm
Change-Id: Iae808a23a17e675f2b9eb31f8ef3d789651daecc
-rw-r--r--include/osmocom/sigtran/sigtran_sap.h1
-rw-r--r--src/sua.c46
-rw-r--r--src/xua_as_fsm.c143
-rw-r--r--src/xua_asp_fsm.c445
-rw-r--r--src/xua_asp_fsm.h43
5 files changed, 676 insertions, 2 deletions
diff --git a/include/osmocom/sigtran/sigtran_sap.h b/include/osmocom/sigtran/sigtran_sap.h
index 544fc44..fc02a43 100644
--- a/include/osmocom/sigtran/sigtran_sap.h
+++ b/include/osmocom/sigtran/sigtran_sap.h
@@ -3,4 +3,5 @@
enum osmo_sigtran_sap {
SCCP_SAP_USER = _SAP_SS7_BASE,
+ XUA_SAP_LM,
};
diff --git a/src/sua.c b/src/sua.c
index 36e31fd..f6ef482 100644
--- a/src/sua.c
+++ b/src/sua.c
@@ -29,6 +29,7 @@
#include <osmocom/core/logging.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/socket.h>
+#include <osmocom/core/fsm.h>
#include <osmocom/netif/stream.h>
#include <osmocom/sigtran/xua_msg.h>
@@ -37,6 +38,8 @@
#include <osmocom/sigtran/protocol/sua.h>
#include <osmocom/sigtran/sua.h>
+#include "xua_asp_fsm.h"
+
#define SUA_MSGB_SIZE 1500
/* Appendix C.4 of Q.714 (all in milliseconds) */
@@ -61,6 +64,8 @@ struct osmo_sccp_user {
/* user call-back function in case of incoming primitives */
osmo_prim_cb prim_cb;
void *priv;
+ /* Application Server FSM Instance */
+ struct osmo_fsm_inst *as_fi;
};
struct osmo_sccp_link {
@@ -74,6 +79,8 @@ struct osmo_sccp_link {
uint32_t next_id;
int is_server;
void *data;
+ /* Application Server Proces FSM Instance */
+ struct osmo_fsm_inst *asp_fi;
};
enum sua_connection_state {
@@ -1140,6 +1147,39 @@ static int sua_rx_co(struct osmo_sccp_link *link,
return rc;
}
+/* map from SUA ASPSM/ASPTM to xua_asp_fsm event */
+static const struct xua_aspxm_map sua_aspxm_map[] = {
+ { SUA_MSGC_ASPSM, SUA_ASPSM_UP, XUA_ASP_E_ASPSM_ASPUP },
+ { SUA_MSGC_ASPSM, SUA_ASPSM_DOWN, XUA_ASP_E_ASPSM_ASPDN },
+ { SUA_MSGC_ASPSM, SUA_ASPSM_BEAT, XUA_ASP_E_ASPSM_BEAT },
+ { SUA_MSGC_ASPSM, SUA_ASPSM_UP_ACK, XUA_ASP_E_ASPSM_ASPUP_ACK },
+ { SUA_MSGC_ASPSM, SUA_ASPSM_DOWN_ACK, XUA_ASP_E_ASPSM_ASPDN_ACK },
+ { SUA_MSGC_ASPSM, SUA_ASPSM_BEAT_ACK, XUA_ASP_E_ASPSM_BEAT_ACK },
+ { SUA_MSGC_ASPTM, SUA_ASPTM_ACTIVE, XUA_ASP_E_ASPTM_ASPAC },
+ { SUA_MSGC_ASPTM, SUA_ASPTM_INACTIVE, XUA_ASP_E_ASPTM_ASPIA },
+ { SUA_MSGC_ASPTM, SUA_ASPTM_ACTIVE_ACK, XUA_ASP_E_ASPTM_ASPAC_ACK },
+ { SUA_MSGC_ASPTM, SUA_ASPTM_INACTIVE_ACK, XUA_ASP_E_ASPTM_ASPIA_ACK },
+};
+
+static int sua_rx_asp(struct osmo_sccp_link *link,
+ struct xua_msg *xua, struct msgb *msg)
+{
+#if 0
+ int event;
+
+ /* map from the M3UA message class and message type to the XUA
+ * ASP FSM event number */
+ event = xua_asp_event_map(xua, sua_aspxm_map,
+ ARRAY_SIZE(sua_aspxm_map));
+ if (event < 0)
+ return event;
+
+ /* deliver that event to the ASP FSM */
+ osmo_fsm_inst_dispatch(fi, event, xua);
+#endif
+ return 0;
+}
+
/* process SUA message received from socket */
static int sua_rx_msg(struct osmo_sccp_link *link, struct msgb *msg)
{
@@ -1163,10 +1203,12 @@ static int sua_rx_msg(struct osmo_sccp_link *link, struct msgb *msg)
case SUA_MSGC_CO:
rc = sua_rx_co(link, xua, msg);
break;
- case SUA_MSGC_MGMT:
- case SUA_MSGC_SNM:
case SUA_MSGC_ASPSM:
case SUA_MSGC_ASPTM:
+ rc = sua_rx_asp(link, xua, msg);
+ break;
+ case SUA_MSGC_MGMT:
+ case SUA_MSGC_SNM:
case SUA_MSGC_RKM:
/* FIXME */
default:
diff --git a/src/xua_as_fsm.c b/src/xua_as_fsm.c
new file mode 100644
index 0000000..d7a64a4
--- /dev/null
+++ b/src/xua_as_fsm.c
@@ -0,0 +1,143 @@
+/* SCCP M3UA / SUA AS osmo_fsm according to RFC3868 4.3.1 */
+/* (C) Copyright 2017 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights reserved.
+ *
+ * Based on Erlang implementation xua_as_fsm.erl in osmo-ss7.git
+ */
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/utils.h>
+
+enum xua_as_state {
+ XUA_AS_S_DOWN,
+ XUA_AS_S_INACTIVE,
+ XUA_AS_S_ACTIVE,
+ XUA_AS_S_PENDING,
+}
+
+enum xua_as_event {
+ XUA_ASPAS_ASP_INACTIVE_IND,
+ XUA_ASPAS_ASP_DOWN_IND,
+ XUA_ASPAS_ASP_ACTIVE_IND,
+};
+
+static const struct value_string xua_as_event_names[] = {
+ { XUA_ASPAS_ASP_INACTIVE_IND, "ASPAS-ASP_INACTIVE.ind" },
+ { XUA_ASPAS_ASP_DOWN_IND, "ASPAS-ASP_DOWN.ind" },
+ { XUA_ASPAS_ASP_ACTIVE_IND, "ASPAS-ASP_ACTIVE.ind" },
+ { 0, NULL }
+};
+
+static void xua_as_fsm_down(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case XUA_ASPAS_ASP_INACTIVE_IND:
+ /* one ASP transitions into ASP-INACTIVE */
+ osmo_fsm_state_chg(fi, XUA_AS_S_INACTIVE, 0, 0);
+ break;
+ case XUA_ASPAS_ASP_DOWN_IND:
+ /* ignore */
+ break;
+ }
+}
+
+static void xua_as_fsm_inactive(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case XUA_ASPAS_ASP_DOWN_IND:
+ /* one ASP transitions into ASP-DOWN */
+ if (check_any_other_asp_not_down()) {
+ /* ignore, we stay AS_INACTIVE */
+ } else
+ osmo_fsm_state_chg(fi, XUA_AS_S_DOWN, 0, 0);
+ break;
+ case XUA_ASPAS_ASP_ACTIVE_IND:
+ /* one ASP transitions into ASP-ACTIVE */
+ osmo_fsm_state_chg(fi, XUA_AS_S_ACTIVE, 0, 0);
+ break;
+ case XUA_ASPAS_ASP_INACTIVE_IND:
+ /* ignore */
+ break;
+ }
+}
+
+static void xua_as_fsm_active(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case XUA_ASPAS_ASP_DOWN_IND:
+ case XUA_ASPAS_ASP_INACTIVE_IND:
+ if (check_any_other_asp_in_active()) {
+ /* ignore, we stay AS_ACTIVE */
+ } else
+ osmo_fsm_state_chg(fi, XUA_AS_S_ACTIVE, 0, 0);
+ break;
+ case XUA_ASPAS_ASP_ACTIVE_IND:
+ /* ignore */
+ break;
+ }
+}
+
+static void xua_as_fsm_pending(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case XUA_ASPAS_ASP_ACTIVE_IND:
+ /* one ASP transitions into ASP-ACTIVE */
+ osmo_fsm_state_chg(fi, XUA_AS_S_ACTIVE, 0, 0);
+ break;
+ case XUA_ASPAS_ASP_INACTIVE_IND:
+ /* ignore */
+ break;
+ case XUA_ASPAS_ASP_DOWN_IND:
+ /* ignore */
+ break;
+ }
+}
+
+static const struct osmo_fsm_state xua_as_fsm_states[] = {
+ [XUA_AS_S_DOWN] = {
+ .in_event_mask = S(XUA_ASPAS_ASP_INACTIVE_IND) |
+ S(XUA_ASPAS_ASP_DOWN_IND),
+ .out_state_mask = S(XUA_AS_S_DOWN) |
+ S(XUA_AS_S_INACTIVE),
+ .name = "AS_DOWN",
+ },
+ [XUA_AS_S_INACTIVE] = {
+ .in_event_mask = S(XUA_ASPAS_ASP_DOWN_IND) |
+ S(XUA_ASPAS_ASP_ACTIVE_IND) |
+ S(XUA_ASPAS_ASP_INACTIVE_IND),
+ .out_state_mask = S(XUA_AS_S_DOWN) |
+ S(XUA_AS_S_INACTIVE) |
+ S(XUA_AS_S_ACTIVE),
+ .name = "AS_INACTIVE",
+ },
+ [XUA_AS_S_ACTIVE] = {
+ .in_event_mask = S(XUA_ASPAS_ASP_DOWN_IND) |
+ S(XUA_ASPAS_ASP_INACTIVE_IND) |
+ S(XUA_ASPAS_ASP_ACTIVE_IND),
+ .out_state_mask = S(XUA_AS_S_ACTIVE) |
+ S(XUA_AS_S_PENDING),
+ .name = "AS_ACTIVE",
+ },
+ [XUA_AS_S_PENDING] = {
+ .in_event_mask = S(XUA_ASPAS_ASP_INACTIVE_IND) |
+ S(XUA_ASPAS_ASP_DOWN_IND) |
+ S(XUA_ASPAS_ASP_ACTIVE_IND),
+ .out_state_mask = S(XUA_AS_S_DOWN) |
+ S(XUA_AS_S_INACTIVE) |
+ S(XUA_AS_S_ACTIVE) |
+ S(XUA_AS_S_PENDING),
+ .name = "AS_PENDING",
+ },
+};
+
+struct osmo_fsm xua_as_fsm = {
+ .name = "XUA_AS",
+ .states = xua_as_states,
+ .num_states = ARRAY_SIZE(xua_as_states),
+ .allstate_event_mask =,
+ .allstate_action =,
+ .log_subsys = DXUA,
+ .event_names = xua_as_event_names,
+};
+
diff --git a/src/xua_asp_fsm.c b/src/xua_asp_fsm.c
new file mode 100644
index 0000000..40f2c2e
--- /dev/null
+++ b/src/xua_asp_fsm.c
@@ -0,0 +1,445 @@
+/* SCCP M3UA / SUA ASP osmo_fsm according to RFC3868 4.3.1 */
+/* (C) Copyright 2017 by Harald Welte <laforge@gnumonks.org>
+ *
+ * All Rights reserved.
+ *
+ * Based on my earlier Erlang implementation xua_asp_fsm.erl in
+ * osmo-ss7.git
+ */
+
+#include <osmocom/core/fsm.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/timer.h>
+
+/* The general idea is:
+ * * translate incoming SUA/M3UA msg_class/msg_type to xua_asp_event
+ * * propagate state transitions to XUA_AS_FSM via _onenter functiosn
+ * * notify the Layer Management of any relevant changes
+ * *
+ */
+
+/* According to RFC3868 Section 8 */
+#define XUA_T_A_SEC 2
+#define XUA_T_R_SEC 2
+#define XUA_T_ACK_SEC 2
+#define XUA_T_BEAT_SEC 30
+#define SUA_T_IAS_SEC (7*60) /* SUA only */
+#define SUA_T_IAR_SEC (15*60) /* SUA only */
+
+static const struct value_string xua_asp_role_names[] = {
+ { XUA_ASPFSM_ROLE_ASP, "ASP" },
+ { XUA_ASPFSM_ROLE_SG, "SG" },
+ { XUA_ASPFSM_ROLE_IPSP, "IPSP" },
+ { 0, NULL }
+};
+
+static const struct value_string xua_asp_event_names[] = {
+ { XUA_ASP_E_M_ASP_UP_REQ, "M-ASP_UP.req" },
+ { XUA_ASP_E_M_ASP_ACTIVE_REQ, "M-ASP_ACTIVE.req" },
+ { XUA_ASP_E_M_ASP_DOWN_REQ, "M-ASP_DOWN.req" },
+ { XUA_ASP_E_M_ASP_INACTIVE_REQ, "M-ASP_INACTIVE.req" },
+
+ { XUA_ASP_E_ASPSM_ASPUP, "ASPSM-ASP_UP" },
+ { XUA_ASP_E_ASPSM_ASPUP_ACK, "ASPSM-ASP_UP_ACK" },
+ { XUA_ASP_E_ASPTM_ASPAC, "ASPTM-ASP_AC" },
+ { XUA_ASP_E_ASPTM_ASPAC_ACK, "ASPTM-ASP_AC_ACK" },
+ { XUA_ASP_E_ASPSM_ASPDN, "ASPSM-ASP_DN" },
+ { XUA_ASP_E_ASPSM_ASPDN_ACK, "ASPSM-ASP_DN_ACK" },
+ { XUA_ASP_E_ASPTM_ASPIA, "ASPTM-ASP_IA" },
+ { XUA_ASP_E_ASPTM_ASPIA_ACK, "ASPTM_ASP_IA_ACK" },
+ { 0, NULL }
+};
+
+struct xua_layer_manager {
+ //prim_cb(struct osmo_prim_hdr *oph );
+};
+
+/* private data structure for each FSM instance */
+struct xua_asp_fsm_priv {
+ enum xua_asp_role role;
+ uint32_t asp_identifier;
+ struct osmo_fsm_inst *as_fi;
+ struct xua_layer_manager *lm;
+
+ /* routing context[s]: list of 32bit integers */
+ /* ACTIVE: traffic mode type, tid label, drn label ? */
+
+ struct {
+ struct osmo_timer_list timer;
+ int out_event;
+ } t_ack;
+};
+
+/* Send a XUA LM Primitive to the XUA Layer Manager (LM) */
+static int send_xlm_prim(struct osmo_fsm_inst *fi,
+ enum osmo_xlm_prim_type prim,
+ enum osmo_prim_operation op,
+ const uint8_t *data, unsigned int data_len)
+{
+ struct xua_asp_fsm_priv *xafp = fi->priv;
+ struct msgb *xlmsg;
+ struct osmo_xlm_prim *prim;
+ struct xua_layer_manager *lm = xafp->lm;
+
+ if (!lm || !lm->prim_cb)
+ return 0;
+
+ xlmsg = xua_msgb_alloc();
+ if (!xlmsg)
+ return -ENOMEM;
+ prim = (struct osmo_xlm_prim *) msgb_put(xlmsg, sizeof(*prim));
+ osmo_prim_init(&prim->oph, XUA_SAP_LM, prim, op, xlmsg);
+
+ lm->prim_cb(&prim->oph, FIXME);
+
+ return 0;
+}
+
+/* wrapper around send_xlm_prim for primitives without data */
+static int send_xlm_prim_simple(struct osmo_fsm_inst *fi,
+ enum osmo_xlm_prim_type prim,
+ enum osmo_prim_operation op)
+{
+ return send_xlm_prim(fi, prim, op, NULL, 0);
+}
+
+/* ask the xUA implementation to transmit a specific message */
+static int peer_send(struct osmo_fsm_inst *fi, int out_event)
+{
+ struct xua_asp_fsm_priv *xafp = fi->priv;
+
+ /* FIXME! */
+}
+
+static void xua_t_ack_cb(void *data)
+{
+ struct osmo_fsm_inst *fi = data;
+ struct xua_asp_fsm_priv *xafp = fi->priv;
+
+ /* Re-transmit message */
+ peer_send(fi, xafp->t_ack.out_event);
+
+ /* Re-start the timer */
+ osmo_timer_start(&fi->t_ack.timer, XUA_T_ACK_SEC, 0)
+}
+
+static int peer_send_and_start_t_ack(struct osmo_fsm_inst *fi,
+ int out_event)
+{
+ struct xua_asp_fsm_priv *xafp = fi->priv;
+ int rc;
+
+ rc = peer_send(fi, out_event);
+ if (rc < 0)
+ return rc;
+
+ xafp->t_ack.out_event = out_event;
+ xafp->t_ack.cb = xua_t_ack_cb,
+ xafp->t_ack.data = fi;
+
+ osmo_timer_start(&fi->t_ack.timer, XUA_T_ACK_SEC, 0)
+
+ return rc;
+}
+
+#define ENSURE_ASP_OR_IPSP(fi, event) \
+ do { \
+ struct xua_asp_fsm_priv *_xafp = fi->priv; \
+ if (_xafp->role != XUA_ASPFSM_ROLE_ASP && \
+ _xafp->role != XUA_ASPFSM_ROLE_IPSP) { \
+ LOGPfSML(fi, LOGL_ERROR, "event %s not permitted " \
+ "in role %s\n", \
+ osmo_fsm_event_name(fi->fsm, event), \
+ get_value_string(xua_asp_role_names, _xafp->role));\
+ return; \
+ } \
+ } while(0)
+
+#define ENSURE_SG_OR_IPSP(fi, event) \
+ do { \
+ struct xua_asp_fsm_priv *_xafp = fi->priv; \
+ if (_xafp->role != XUA_ASPFSM_ROLE_SG && \
+ _xafp->role != XUA_ASPFSM_ROLE_IPSP) { \
+ LOGPfSML(fi, LOGL_ERROR, "event %s not permitted " \
+ "in role %s\n", \
+ osmo_fsm_event_name(fi->fsm, event), \
+ get_value_string(xua_asp_role_names, _xafp->role));\
+ return; \
+ } \
+ } while(0)
+
+static void xua_asp_fsm_down(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct xua_asp_fsm_priv *xafp = fi->priv;
+ switch (event) {
+ case XUA_ASP_E_M_ASP_UP_REQ:
+ /* only if role ASP */
+ ENSURE_ASP_OR_IPSP(fi, event);
+ /* Send M3UA_MSGT_ASPSM_ASPUP and start t_ack */
+ peer_send_and_start_t_ack(fi, XUA_ASP_E_ASPSM_ASPUP);
+ break;
+ case XUA_ASP_E_ASPSM_ASPUP_ACK:
+ /* only if role ASP */
+ ENSURE_ASP_OR_IPSP(fi, event);
+ osmo_fsm_state_chg(fi, XUA_ASP_S_INACTIVE, 0, 0);
+ /* inform layer manager */
+ send_xlm_prim_simple(fi, OSMO_XLM_PRIM_M_ASP_UP,
+ PRIM_OP_COFIRM);
+ break;
+ case XUA_ASP_E_ASPSM_ASPUP:
+ /* only if role SG */
+ ENSURE_SG_OR_IPSP(fi, event);
+ /* send ACK */
+ osmo_fsm_state_chg(fi, XUA_ASP_S_INACTIVE, 0, 0);
+ send_xlm_prim_simple(fi, OSMO_XLM_PRIM_M_ASP_UP,
+ PRIM_OP_INDICATION);
+ break;
+ case XUA_ASP_E_ASPSM_ASPDN:
+ /* only if role SG */
+ ENSURE_SG_OR_IPSP(fi, event);
+ /* The SGP MUST send an ASP Down Ack message in response
+ * to a received ASP Down message from the ASP even if
+ * the ASP is already marked as ASP-DOWN at the SGP. */
+ peer_send(fi, XUA_ASP_E_ASPSM_ASPDN_ACK);
+ break;
+ }
+}
+
+static void xua_asp_fsm_down_onenter(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct xua_asp_fsm_priv *xafp = fi->priv;
+ osmo_fsm_inst_dispatch(xafp->as_fi, XUA_ASPAS_ASP_DOWN_IND, fi);
+}
+
+static void xua_asp_fsm_inactive(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case XUA_ASP_E_M_ASP_ACTIVE_REQ:
+ /* send M3UA_MSGT_ASPTM_ASPAC and start t_ack */
+ peer_send_and_start_t_ack(fi, XUA_ASP_E_ASPTM_ASPAC);
+ break;
+ case XUA_ASP_E_M_ASP_DOWN_REQ:
+ /* send M3UA_MSGT_ASPSM_ASPDN and start t_ack */
+ peer_send_and_start_t_ack(fi, XUA_ASP_E_ASPSM_ASPDN);
+ break;
+ case XUA_ASP_E_ASPTM_ASPAC_ACK:
+ /* only in role ASP */
+ ENSURE_ASP_OR_IPSP(fi, event);
+ /* inform layer manager */
+ send_xlm_prim_simple(fi, OSMO_XLM_PRIM_M_ASP_ACTIVE,
+ PRIM_OP_CONFIRM);
+ break;
+ case XUA_ASP_E_ASPSM_ASPDN_ACK:
+ /* only in role ASP */
+ ENSURE_ASP_OR_IPSP(fi, event);
+ /* inform layer manager */
+ send_xlm_prim_simple(fi, OSMO_XLM_PRIM_M_ASP_DOWN,
+ PRIM_OP_CONFIRM);
+ break;
+ case XUA_ASP_E_ASPTM_ASPAC:
+ /* only in role SG */
+ ENSURE_SG_OR_IPSP(fi, event);
+ /* send ACK */
+ peer_send(fi, XUA_ASP_E_ASPTM_ASPAC_ACK);
+ /* transition state and inform layer manager */
+ osmo_fsm_state_chg(fi, XUA_ASP_S_ACTIVE, 0, 0);
+ send_xlm_prim_simple(fi, OSMO_XLM_PRIM_M_ASP_ACTIVE,
+ PRIM_OP_INDICATION);
+ break;
+ case XUA_ASP_E_ASPSM_ASPDN:
+ /* only in role SG */
+ ENSURE_SG_OR_IPSP(fi, event);
+ /* send ACK */
+ peer_send(fi, XUA_ASP_E_ASPSM_ASPDN_ACK);
+ /* transition state and inform layer manager */
+ osmo_fsm_state_chg(fi, XUA_ASP_S_DOWN, 0, 0);
+ send_xlm_prim_simple(fi, OSMO_XLM_PRIM_M_ASP_DOWN,
+ PRIM_OP_INDICATION);
+ break;
+ case XUA_ASP_E_ASPSM_ASPUP:
+ /* only if role SG */
+ ENSURE_SG_OR_IPSP(fi, event);
+ /* If an ASP Up message is received and internally the
+ * remote ASP is already in the ASP-INACTIVE state, an
+ * ASP Up Ack message is returned and no further action
+ * is taken. */
+ peer_send(fi, XUA_ASP_E_ASPSM_ASPUP_ACK);
+ break;
+ }
+}
+
+static void xua_asp_fsm_inactive_onenter(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct xua_asp_fsm_priv *xafp = fi->priv;
+ osmo_fsm_inst_dispatch(xafp->as_fi, XUA_ASPAS_ASP_INACTIVE_IND, fi);
+}
+
+static void xua_asp_fsm_active(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ switch (event) {
+ case XUA_ASP_E_ASPSM_ASPDN_ACK:
+ /* only in role ASP */
+ ENSURE_ASP_OR_IPSP(fi, event);
+ osmo_fsm_state_chg(fi, XUA_ASP_S_DOWN, 0, 0);
+ /* inform layer manager */
+ send_xlm_prim_simple(fi, OSMO_XLM_PRIM_M_ASP_DOWN,
+ PRIM_OP_CONFIRM);
+ break;
+ case M3UA_MSGT_ASPTM_ASPIA_ACK:
+ /* only in role ASP */
+ ENSURE_ASP_OR_IPSP(fi, event);
+ osmo_fsm_state_chg(fi, XUA_ASP_S_INACTIVE, 0, 0);
+ /* inform layer manager */
+ send_xlm_prim_simple(fi, OSMO_XLM_PRIM_M_ASP_INACTIVE,
+ PRIM_OP_CONFIRM);
+ break;
+ case XUA_ASP_E_M_ASP_DOWN_REQ:
+ /* only in role ASP */
+ ENSURE_ASP_OR_IPSP(fi, event);
+ /* send M3UA_MSGT_ASPSM_ASPDN and star t_ack */
+ peer_send_and_start_t_ack(fi, XUA_ASP_E_ASPSM_ASPDN);
+ break;
+ case XUA_ASP_E_M_ASP_INACTIVE_REQ:
+ /* only in role ASP */
+ ENSURE_ASP_OR_IPSP(fi, event);
+ /* send M3UA_MSGT_ASPTM_ASPIA and star t_ack */
+ peer_send_and_start_t_ack(fi, XUA_ASP_E_ASPTM_ASPIA);
+ break;
+ case XUA_ASP_E_ASPTM_ASPIA:
+ /* only in role SG */
+ ENSURE_SG_OR_IPSP(fi, event);
+ /* send ACK */
+ peer_send(fi, XUA_ASP_E_ASPTM_ASPIA_ACK);
+ /* transition state and inform layer manager */
+ osmo_fsm_state_chg(fi, XUA_ASP_S_INACTIVE, 0, 0);
+ send_xlm_prim_simple(fi, OSMO_XLM_PRIM_M_ASP_INACTIVE,
+ PRIM_OP_INDICATION);
+ break;
+ case XUA_ASP_E_ASPSM_ASPDN:
+ /* only in role SG */
+ ENSURE_SG_OR_IPSP(fi, event);
+ /* send ACK */
+ peer_send(fi, XUA_ASP_E_ASPTM_ASPDN_ACK);
+ /* transition state and inform layer manager */
+ osmo_fsm_state_chg(fi, XUA_ASP_S_DOWN, 0, 0);
+ send_xlm_prim_simple(fi, OSMO_XLM_PRIM_M_ASP_DOWN,
+ PRIM_OP_INDICATION);
+ break;
+ case XUA_ASP_E_ASPSM_ASPUP:
+ /* only if role SG */
+ ENSURE_SG_OR_IPSP(fi, event);
+ /* an ASP Up Ack message is returned, as well as
+ * an Error message ("Unexpected Message), and the
+ * remote ASP state is changed to ASP-INACTIVE in all
+ * relevant Application Servers */
+ /* FIXME: Send ERROR message */
+ osmo_fsm_state_chg(fi, XUA_ASP_S_INACTIVE, 0, 0);
+ send_xlm_prim_simple(fi, OSMO_XLM_PRIM_M_ASP_INACTIVE,
+ PRIM_OP_INDICATION);
+ break;
+ }
+}
+
+static void xua_asp_fsm_active_onenter(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct xua_asp_fsm_priv *xafp = fi->priv;
+ osmo_fsm_inst_dispatch(xafp->as_fi, XUA_ASPAS_ASP_ACTIVE_IND, fi);
+}
+
+static int xua_asp_fsm_timer_cb(struct osmo_fsm_inst *fi)
+{
+}
+
+static const struct osmo_fsm_state xua_asp_states[] = {
+ [XUA_ASP_S_DOWN] = {
+ .in_event_mask = S(XUA_ASP_E_M_ASP_UP_REQ) |
+ S(XUA_ASP_E_ASPSM_ASPUP) |
+ S(XUA_ASP_E_ASPSM_ASPUP_ACK |
+ S(XUA_ASP_E_ASPSM_ASPDN),
+ .out_state_mask = S(XUA_ASP_S_INACTIVE),
+ .name = "ASP_DOWN",
+ .action = xua_asp_fsm_down,
+ .onenter = xua_asp_fsm_down_onenter,
+ },
+ [XUA_ASP_S_INACTIVE] = {
+ .in_event_mask = S(XUA_ASP_E_M_ASP_ACTIVE_REQ) |
+ S(XUA_ASP_E_M_ASP_DOWN_REQ) |
+ S(XUA_ASP_E_ASPTM_ASPAC) |
+ S(XUA_ASP_E_ASPTM_ASPAC_ACK) |
+ S(XUA_ASP_E_ASPSM_ASPDN) |
+ S(XUA_ASP_E_ASPSM_ASPDN_ACK |
+ S(XUA_ASP_E_ASPSM_ASPUP),
+ .out_state_mask = S(XUA_ASP_S_DOWN) |
+ S(XUA_ASP_S_ACTIVE),
+ .name = "ASP_INACTIVE",
+ .action = xua_asp_fsm_inactive,
+ .onenter = xua_asp_fsm_inactive_onenter,
+ },
+ [XUA_ASP_S_ACTIVE] = {
+ .in_event_mask = S(XUA_ASP_E_ASPSM_ASPDN) |
+ S(XUA_ASP_E_ASPSM_ASPDN_ACK) |
+ S(XUA_ASP_E_ASPSM_ASPUP) |
+ S(XUA_ASP_E_ASPTM_ASPIA) |
+ S(XUA_ASP_E_ASPTM_ASPIA_ACK) |
+ S(XUA_ASP_E_M_ASP_DOWN_REQ) |
+ S(XUA_ASP_E_M_ASP_INACTIVE_REQ),
+ .out_state_mask = S(XUA_ASP_S_INACTIVE) |
+ S(XUA_ASP_S_DOWN),
+ .name = "ASP_ACTIVE",
+ .action = xua_asp_fsm_active,
+ .onenter = xua_asp_fsm_active_onenter,
+ },
+};
+
+
+struct osmo_fsm xua_asp_fsm = {
+ .name = "XUA_ASP",
+ .states = xua_asp_states,
+ .num_states = ARRAY_SIZE(xua_asp_states),
+ .allstate_event_mask =,
+ .allstate_action =,
+ .timer_cb = xua_asp_fsm_timer_cb,
+ .log_subsys = DXUA,
+ .event_names = xua_asp_event_names,
+};
+
+
+enum xua_asp_event xua_asp_event_map(const struct xua_msg *xua,
+ const struct xua_aspxm_map *maps,
+ unsigned int num_maps)
+{
+ int i;
+
+ for (i= 0; i < ARRAY_SIZE(sua_aspxm_map); i++) {
+ const struct xua_aspxm_map *map = maps[i];
+ if (xua->hdr.msg_class == map->msg_class &&
+ xua->hdr.msg_type == map->msg_type) {
+ return map->event;
+ }
+ }
+ return -1;
+}
+
+struct osmo_fsm_inst *xua_asp_fsm_start(struct osmo_fsm_inst *as_fi,
+ enum xua_asp_role role,
+ uint32_t asp_id)
+{
+ struct osmo_fsm_inst *fi;
+ struct xua_asp_fsm_priv *xafp;
+
+ /* allocate as child of AS? */
+ fi = osmo_fsm_inst_alloc(&xua_asp_fsm, as_fi, NULL, log_level, id);
+
+ xafp = talloc_zero(fi, struct xua_asp_fsm_priv);
+ if (!xafp) {
+ osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL);
+ return NULL;
+ }
+ xafp->role = role;
+ xafp->asp_identifier = asp_id;
+ xafp->as_fi = as_fi;
+
+ fi->priv = xafp;
+
+ return fi;
+}
diff --git a/src/xua_asp_fsm.h b/src/xua_asp_fsm.h
new file mode 100644
index 0000000..1b075f9
--- /dev/null
+++ b/src/xua_asp_fsm.h
@@ -0,0 +1,43 @@
+#pragma once
+
+enum xua_asp_state {
+ XUA_ASP_S_DOWN,
+ XUA_ASP_S_INACTIVE,
+ XUA_ASP_S_ACTIVE,
+};
+
+enum xua_asp_event {
+ XUA_ASP_E_M_ASP_UP_REQ,
+ XUA_ASP_E_M_ASP_ACTIVE_REQ,
+ XUA_ASP_E_M_ASP_DOWN_REQ,
+ XUA_ASP_E_M_ASP_INACTIVE_REQ,
+
+ XUA_ASP_E_ASPSM_ASPUP,
+ XUA_ASP_E_ASPSM_ASPUP_ACK,
+ XUA_ASP_E_ASPTM_ASPAC,
+ XUA_ASP_E_ASPTM_ASPAC_ACK,
+ XUA_ASP_E_ASPSM_ASPDN,
+ XUA_ASP_E_ASPSM_ASPDN_ACK,
+ XUA_ASP_E_ASPTM_ASPIA,
+ XUA_ASP_E_ASPTM_ASPIA_ACK,
+
+ XUA_ASP_E_ASPSM_BEAT,
+ XUA_ASP_E_ASPSM_BEAT_ACK,
+};
+
+enum xua_asp_role {
+ XUA_ASPFSM_ROLE_ASP,
+ XUA_ASPFSM_ROLE_SG,
+ XUA_ASPFSM_ROLE_IPSP,
+};
+
+struct xua_aspxm_map {
+ uint8_t msg_class;
+ uint8_t msg_type;
+ enum xua_asp_event event;
+};
+
+
+enum xua_asp_event xua_asp_event_map(const struct xua_msg *xua,
+ const struct xua_aspxm_map *maps,
+ unsigned int num_maps);