aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/mtp2_iac_fsm.c355
-rw-r--r--src/mtp2_iac_fsm.h16
-rw-r--r--src/mtp2_lsc_fsm.c512
-rw-r--r--src/mtp2_lsc_fsm.h26
-rw-r--r--src/mtp2_txc_fsm.c191
-rw-r--r--src/mtp2_txc_fsm.h30
-rw-r--r--src/sccp_scoc.c26
7 files changed, 1156 insertions, 0 deletions
diff --git a/src/mtp2_iac_fsm.c b/src/mtp2_iac_fsm.c
new file mode 100644
index 0000000..e356876
--- /dev/null
+++ b/src/mtp2_iac_fsm.c
@@ -0,0 +1,355 @@
+
+#include <osmocom/core/fsm.h>
+#include "mtp2_iac_fsm.h"
+
+/* Implementation of the ITU-T Q.703 (MTP@) Initial alignment control FSM as
+ * described [primarily] in Figure 9/Q.703 */
+
+enum mtp2_iac_fsm_state {
+ MTP2_IAC_S_IDLE,
+ MTP2_IAC_S_NOT_ALIGNED,
+ MTP2_IAC_S_ALIGNED,
+ MTP2_IAC_S_PROVING,
+};
+
+static const struct value_string mtp2_iac_event_names[] = {
+ { MTP2_IAC_E_EMERGENCY, "LSC2IAC_EMERGENCY" },
+ { MTP2_IAC_E_START, "LSC2IAC_START" },
+ { MTP2_IAC_E_STOP, "LSC2IAC_STOP" },
+ { MTP2_IAC_E_RX_SIO, "RC2IAC_RX_SIO" },
+ { MTP2_IAC_E_RX_SIOS, "RC2IAC_RX_SIOS" },
+ { MTP2_IAC_E_RX_SIN, "RC2IAC_RX_SIN" },
+ { MTP2_IAC_E_RX_SIE, "RC2IAC_RX_SIE" },
+ { MTP2_IAC_E_CORRECT_SU, "DAEDR2IAC_CORRECT_SU" },
+ { MTP2_IAC_E_ABORT_PROVING, "AERM2IAC_ABORT_PROVING" },
+ { 0, NULL }
+};
+
+/* Section 12.3/Q.703 */
+#define MTP2_T1_64_MS 45000
+#define MTP2_T2_MS 60000
+#define MTP2_T3_MS 1500
+#define MTP2_T4e_64_MS 500
+#define MTP2_T4n_64_MS 8200
+#define MTP2_T5_MS 100
+#define MTP2_T6_64_MS 5000
+#define MTP2_T7_64_MS 1000
+
+struct iac_fsm_data {
+ bool emergency;
+ bool further_proving;
+ uint32_t t4_ms;
+};
+
+static void mtp2_iac_fsm_idle(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+
+ struct iac_fsm_data *ifd = fi->priv;
+
+ switch (event) {
+ case MTP2_IAC_E_EMERGENCY:
+ /* Mark emergency */
+ ifd->emergency = true;
+ return;
+ case MTP2_IAC_E_START:
+ /* IAC -> TXC: Send SIO */
+ /* Start T2 */
+ osmo_fsm_inst_state_chg(fi, MTP2_IAC_S_NOT_ALIGNED, 0, 2);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void mtp2_iac_fsm_not_aligned(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct iac_fsm_data *ifd = fi->priv;
+
+ switch (event) {
+ case MTP2_IAC_E_STOP:
+ /* Stop T2 (implicit below) */
+ /* Cancel emergency */
+ ifd->emergency = false;
+ osmo_fsm_inst_state_chg(fi, MTP2_IAC_S_IDLE, 0, 0);
+ case MTP2_IAC_E_RX_SIO:
+ case MTP2_IAC_E_RX_SIN:
+ /* Stop T2 (implicit below) */
+ if (emergency) {
+ /* Set T4 to Pe */
+ ifd->t4_ms = MTP2_T4e_64_MS;
+ /* IAC -> TXC: Send SIE */
+ } else {
+ /* Set T4 to Pn */
+ ifd->t4_ms = MTP2_T4n_64_MS;
+ /* IAC -> TXC: Send SIN */
+ }
+ /* Start T3 */
+ osmo_fsm_inst_state_chg_ms(fi, MTP2_IAC_S_ALIGNED, MTP2_T3_MS, 3);
+ break;
+ case MTP2_IAC_E_RX_SIE:
+ /* Stop T2 (implicit below) */
+ if (emergency) {
+ /* Set T4 to Pe */
+ ifd->t4_ms = MTP2_T4e_64_MS;
+ /* IAC -> TXC: Send SIE */
+ } else {
+ /* Set T4 to Pe */
+ ifd->t4_ms = MTP2_T4e_64_MS;
+ /* IAC -> TXC: Send SIN */
+ }
+ /* Start T3 */
+ osmo_fsm_inst_state_chg_ms(fi, MTP2_IAC_S_ALIGNED, MTP2_T3_MS, 3);
+ break;
+ case MTP2_IAC_E_EMERGENCY:
+ ifd->emergency = true;
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void mtp2_iac_fsm_aligned(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct iac_fsm_data *ifd = fi->priv;
+ struct osmo_fsm_inst *lsc_fi = fi->proc.parent;
+
+ switch (event) {
+ case MTP2_IAC_E_RX_SIE:
+ /* Set T4 to Pe */
+ ifd->t4_ms = MTP2_T4e_64_MS;
+ /* fall-through */
+ case MTP2_IAC_E_RX_SIN:
+ /* Stop T3 (implicit below) */
+ if (t4 == Pe) {
+ /* IAC -> AERM: set i to ie */
+ }
+ /* IAC -> AERM: Start */
+ /* Start T4 (implicit below) */
+ /* Cp := 0 */
+ /* cancel further proving */
+ ifd->further_proving = false;
+ osmo_fsm_inst_state_chg_ms(fi, MTP2_IAC_S_PROVING, ifd->t4_ms, 4);
+ break;
+ case MTP2_IAC_E_EMERGENCY:
+ /* IAC -> TXC: Send SIE */
+ /* Set T4 to Pe */
+ ifd->t4_ms = MTP2_T4e_64_MS;
+ osmo_fsm_inst_state_chg(fi, MTP2_IAC_S_ALIGNED, 0, 0);
+ break;
+ case MTP2_IAC_E_STOP:
+ /* Stop T3 (implicit below) */
+ /* Cancel emergency */
+ ifd->emergency = false;
+ osmo_fsm_inst_state_chg(fi, MTP2_IAC_S_IDLE, 0, 0);
+ break;
+ case MTP2_IAC_E_RX_SIOS:
+ /* IAC -> LSC: Alignment not possible */
+ osmo_fsm_inst_dispatch(lsc_fi, MTP_LSC_E_ALIGNMENT_NOT_POSSIBLE, NULL);
+ /* Stop T3 (implicit below) */
+ /* Cancel emergency */
+ ifd->emergency = false;
+ osmo_fsm_inst_state_chg(fi, MTP2_IAC_S_IDLE, 0, 0);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void mtp2_iac_fsm_proving(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct iac_fsm_data *ifd = fi->priv;
+ struct osmo_fsm_inst *lsc_fi = fi->proc.parent;
+
+ switch (event) {
+ case MTP2_IAC_E_RX_SIO:
+ /* Stop T4 (implicit below) */
+ /* IAC -> AERM: Stop */
+ /* Start T3 */
+ osmo_fsm_inst_state_chg_ms(fi, MGP2_IAC_S_ALIGNED, MTP2_T3_MS, 3);
+ break;
+ case MTP2_IAC_E_CORRECT_SU:
+ if (ifd->further_proving) {
+ /* Stop T4 (implicit below) */
+ /* 5 in-line below */
+ /* IAC -> AERM: Start */
+ /* Cancel further proving */
+ ifd->further_proving = false;
+ /* Start T4 */
+ osmo_fsm_inst_state_chg_ms(fi, MTP2_IAC_S_PROVING, ifd->t4_ms, 4);
+ } else {
+ /* 6: empty */
+ }
+ break;
+ case MTP2_IAC_E_RX_SIOS:
+ /* Stop T4 (implicit below) */
+ /* IAC -> LSC: Alignment not possible */
+ osmo_fsm_inst_dispatch(lsc_fi, MTP_LSC_E_ALIGNMENT_NOT_POSSIBLE, NULL);
+ /* 4 in-line below */
+ /* IAC -> AERM: Stop */
+ /* cancel emergency */
+ ifd->emergency = false;
+ osmo_fsm_inst_state_chg(fi, MTP2_IAC_S_IDLE, 0, 0);
+ break;
+ case MTP2_IAC_E_STOP:
+ /* Stop T4 (implicit below) */
+ /* 4 in-line below */
+ /* IAC -> AERM: Stop */
+ /* cancel emergency */
+ ifd->emergency = false;
+ osmo_fsm_inst_state_chg(fi, MTP2_IAC_S_IDLE, 0, 0);
+ break;
+ case MTP2_IAC_E_ABORT_PROVING:
+ /* Cp := Cp + 1 */
+ if (id->cp == 5) {
+ /* IAC -> LSC: Alignment not possible */
+ osmo_fsm_inst_dispatch(lsc_fi, MTP_LSC_E_ALIGNMENT_NOT_POSSIBLE, NULL);
+ /* Stop T4 (implicit below) */
+ /* IAC -> AERM: Stop */
+ /* cancel emergency */
+ ifd->emergency = false;
+ osmo_fsm_inst_state_chg(fi, MTP2_IAC_S_IDLE, 0, 0);
+ } else{
+ /* Mark further proving */
+ ifd->further_proving = true;
+ }
+ break;
+ case MTP2_IAC_E_EMERGENCY:
+ /* IAC -> TXC: Send SIE */
+ /* Stop T4 (implicit below) */
+ /* Set T4 to Pe */
+ ifd->t4_ms = MTP2_T4e_64_MS;
+ /* IAC -> AERM: Stop */
+ /* Set Ti to Tie */
+ /* IAC -> AERM: Start */
+ /* Cancel further proving */
+ ifd->further_proving = false;
+ /* Start T4 */
+ osmo_fsm_inst_state_chg_ms(fi, MTP2_IAC_S_PROVING, ifd->t4_ms, 4);
+ break;
+ case MTP2_IAC_E_RX_SIE:
+ if (t4 == Pe)
+ break;
+ /* Stop T4 (implicit below) */
+ /* Set T4 to Pe */
+ ifd->t4_ms = MTP2_T4e_64_MS;
+ /* IAC -> AERM: Stop */
+ /* Set Ti to Tie */
+ /* IAC -> AERM: Start */
+ /* Cancel further proving */
+ ifd->further_proving = false;
+ /* Start T4 */
+ osmo_fsm_inst_state_chg_ms(fi, MTP2_IAC_S_PROVING, ifd->t4_ms, 4);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static const struct osmo_fsm_state mtp2_iac_states[] = {
+ [MTP2_IAC_S_IDLE] = {
+ .name = "IDLE",
+ .in_event_mask = S(MTP2_IAC_E_EMERGENCY) |
+ S(MTP2_IAC_E_START),
+ .out_state_mask = S(MTP2_IAC_S_IDLE) |
+ S(MTP2_IAC_S_NOT_ALIGNED),
+ .action = mtp2_iac_fsm_idle,
+ },
+ [MTP2_IAC_S_NOT_ALIGNED] = {
+ .name = "NOT_ALIGNED",
+ .in_event_mask = S(MTP2_IAC_E_STOP) |
+ S(MTP2_IAC_E_SIO) |
+ S(MTP2_IAC_E_SIN) |
+ S(MTP2_IAC_E_SIE) |
+ S(MTP2_IAC_E_EMERGENCY),
+ .out_state_mask = S(MTP2_IAC_S_IDLE) |
+ S(MTP2_IAC_S_NOT_ALIGNED) |
+ S(MTP2_IAC_S_ALIGNED),
+ .action = mtp2_iac_fsm_not_aligned,
+ },
+ [MTP2_IAC_S_ALIGNED] = {
+ .name = "ALIGNED",
+ .in_event_mask = S(MTP2_IAC_E_SIE) |
+ S(MTP2_IAC_E_SIN) |
+ S(MTP2_IAC_E_EMERGENCY) |
+ S(MTP2_IAC_E_STOP) |
+ S(MTP2_IAC_E_RX_SIOS),
+ .out_state_mask = S(MTP2_IAC_S_ALIGNED) |
+ S(MTP2_IAC_S_PROVING) |
+ S(MTP2_IAC_S_IDLE),
+ .action = mtp2_iac_fsm_aligned,
+ },
+ [MTP2_IAC_S_PROVING] = {
+ .name = "PROVING",
+ .in_event_mask = S(MTP2_IAC_E_RX_SIO) |
+ S(MTP2_IAC_E_CORRECT_SU) |
+ S(MTP2_IAC_E_RX_SIOS) |
+ S(MTP2_IAC_E_STOP) |
+ S(MTP2_IAC_E_ABORT_PROVING) |
+ S(MTP2_IAC_E_EMERGENCY) |
+ S(MTP2_IAC_E_RX_SIE),
+ .out_state_mask = S(MTP2_IAC_S_ALIGNED) |
+ S(MTP2_IAC_S_IDLE) |
+ S(MTP2_IAC_S_PROVING),
+ .action = mtp2_iac_fsm_proving,
+ },
+};
+
+static int mtp2_iac_fsm_timer_cb(struct osmo_fsm_inst *fi)
+{
+ struct iac_fsm_data *ifd = fi->priv;
+ struct osmo_fsm_inst *lsc_fi = fi->proc.parent;
+
+ switch (fi->T) {
+ case 2:
+ /* Figure 9/Q.703 (sheet 1 of 6) */
+ OSMO_ASSERT(fi->state == MTP2_IAC_S_NOT_ALIGNED);
+ /* IAC -> LSC: Alignment not possible */
+ osmo_fsm_inst_dispatch(lsc_fi, MTP_LSC_E_ALIGNMENT_NOT_POSSIBLE, NULL);
+ /* Cancel emergency */
+ ifd->emergency = false;
+ osmo_fsm_inst_state_chg(fi, MTP2_IAC_S_IDLE, 0, 0);
+ break;
+ case 3:
+ /* Figure 9/Q.703 (sheet 4 of 6) */
+ OSMO_ASSERT(fi->state == MTP2_IAC_S_ALIGNED);
+ /* IAC -> LSC: Alignment not possible */
+ osmo_fsm_inst_dispatch(lsc_fi, MTP_LSC_E_ALIGNMENT_NOT_POSSIBLE, NULL);
+ /* Cancel emergency */
+ ifd->emergency = false;
+ osmo_fsm_inst_state_chg(fi, MTP2_IAC_S_IDLE, 0, 0);
+ break;
+ case 4:
+ /* Figure 9/Q.703 (sheet 5 of 6) */
+ OSMO_ASSERT(fi->state == MTP2_IAC_S_PROVING);
+ if (ifd->further_proving) {
+ /* 5 in-line below */
+ /* IAC -> AERM: Start */
+ /* Cancel further proving */
+ ifd->further_proving = false;
+ /* Start T4 */
+ osmo_fsm_inst_state_chg_ms(fi, MTP2_IAC_S_PROVING, ifd->t4_ms, 4);
+ } else {
+ /* 6: empty */
+ }
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+struct osmo_fsm mtp2_iac_fsm = {
+ .name = "MTP2_IAC",
+ .states = mtp2_iac_states,
+ .num_states = ARRAY_SIZE(mtp2_iac_states),
+ .timer_cb = mtp2_iac_fsm_timer_cb,
+ .log_subsys = DLMTP2,
+ .event_names = mtp2_iac_event_names,
+};
+
+struct osmo_fsm_inst *mtp2_iac_fsm_alloc(struct osmo_fsm_inst *lsc_fsm)
+{
+ struct osmo_fsm_inst *fi;
+ struct mtp2_iac_fsm_priv *ifp;
+
+ fi = osmo_fsm_inst_alloc_child
+}
+
diff --git a/src/mtp2_iac_fsm.h b/src/mtp2_iac_fsm.h
new file mode 100644
index 0000000..7604009
--- /dev/null
+++ b/src/mtp2_iac_fsm.h
@@ -0,0 +1,16 @@
+#pragma once
+#include <osmocom/core/fsm.h>
+
+enum mtp2_iac_fsm_event {
+ MTP2_IAC_E_EMERGENCY,
+ MTP2_IAC_E_START,
+ MTP2_IAC_E_STOP,
+ MTP2_IAC_E_RX_SIO,
+ MTP2_IAC_E_RX_SIOS,
+ MTP2_IAC_E_RX_SIN,
+ MTP2_IAC_E_RX_SIE,
+ MTP2_IAC_E_CORRECT_SU,
+ MTP2_IAC_E_ABORT_PROVING,
+};
+
+extern struct osmo_fsm mtp2_iac_fsm;
diff --git a/src/mtp2_lsc_fsm.c b/src/mtp2_lsc_fsm.c
new file mode 100644
index 0000000..d14b0f0
--- /dev/null
+++ b/src/mtp2_lsc_fsm.c
@@ -0,0 +1,512 @@
+/* Implementation of the ITU-T Q.703 (MTP2) Link State Control FSM as
+ * described [primarily] in Figure 8/Q.703 */
+
+#include <osmocom/core/fsm.h>
+
+#include "mtp2_lsc_fsm.h"
+#include "mtp2_iac_fsm.h"
+
+enum mtp2_lsc_fsm_state {
+ MTP2_LSC_S_POWER_OFF,
+ MTP2_LSC_S_OUT_OF_SERVICE,
+ MTP2_LSC_S_INITIAL_ALIGNMENT,
+ MTP2_LSC_S_ALIGNED_READY,
+ MTP2_LSC_S_ALIGNED_NOT_READY,
+ MTP2_LSC_S_IN_SERVICE,
+ MTP2_LSC_S_PROCESSOR_OUTAGE,
+};
+
+static const struct value_string mtp2_lsc_event_names[] = {
+ { MTP_LSC_E_POWER_ON, "MGMT2LSC_POWER_ON" },
+ { MTP_LSC_E_START, "L32LSC_START" },
+ { MTP_LSC_E_EMERGENCY, "L32LSC_EMERGENCY" },
+ { MTP_LSC_E_EMERGENCY_CEASES, "L32LSC_EMERGENCY_CEASES" },
+ { MTP_LSC_E_LOCAL_PROC_OUTAGE, "MGMT2LSC_LOCAL_PROCESSOR_OUTAGE" },
+ { MTP_LSC_E_LOCAL_PROC_OUTAGE_RECOVD, "MGMT2LSC_LOCAL_PROCESSOR_OUTAGE_RECOVERED" },
+ { MTP_LSC_E_LEVEL3_FAILURE, "MGMT2LSC_LEVEL3_FAILURE" },
+ { MTP_LSC_E_ALIGNMENT_COMPLETE, "IAC2LSC_ALIGNMENT_COMPLETE" },
+ { MTP_LSC_E_STOP, "L32LSC_STOP" },
+ { MTP_LSC_E_LINK_FAILURE, "RC2LSC_LINK_FAILURE" },
+ { MTP_LSC_E_ALIGNMENT_NOT_POSSIBLE, "IAC2LSC_ALIGNMENT_NOT_POSSIBLE" },
+ { MTP_LSC_E_RX_SIO_SIOS, "RC2LSC_SIO_SIOS" },
+ { MTP_LSC_E_RX_SIN_SIE, "RC2LSC_SIN_SIE" },
+ { MTP_LSC_E_RX_SIPO, "RC2LSC_SIPO" },
+ { MTP_LSC_E_RX_FISU_MSU, "RC2LSC_FISU_MSU" },
+ { MTP_LSC_E_FLUSH_BUFFERS, "L32LSC_FLUSH_BUFFERS" },
+ { MTP_LSC_E_CONTINUE, "L32LSC_CONTINUE" },
+ { MTP_LSC_E_NO_PROC_OUTAGE, "POC2LSC_NO_PROCESSOR_OUTAGE" },
+ { MTP_LSC_E_T1_EXP, "T1_EXPIRED" },
+ { 0, NULL }
+};
+
+struct lsc_fsm_data {
+ /*! Initial Alignment Control FSM */
+ struct osmo_fsm_inst *iac_fi;
+
+ bool local_proc_outage;
+
+ bool l3_indication_received;
+ bool processor_outage; /* remote? */
+};
+
+/* Figure 8/Q.703 (sheet 1 of 14) */
+static void mtp2_lsc_fsm_power_off(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct lsc_fsm_data *lfd = fi->priv;
+
+ switch (event) {
+ case MTP_LSC_E_POWER_ON:
+ /* LSC -> TXC: Start */
+ /* LSC -> TXC: Send SIOS */
+ /* LSC -> AERM: Set Ti to Tin */
+ /* Cancel local processor outage */
+ lfd->local_proc_outage = false;
+ /* Cancel emergency */
+ lfd->emergency = false;
+ osmo_fsm_inst_state_chg(fi, MTP2_LSC_S_OUT_OF_SERVICE, 0, 0);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+/* Figure 8/Q.703 (sheet 2+3 of 14) */
+static void mtp2_lsc_fsm_out_of_service(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct lsc_fsm_data *lfd = fi->priv;
+
+ switch (event) {
+ case MTP_LSC_E_START:
+ /* LSC -> RC: Start */
+ /* LSC -> TXC: Start */
+ if (ldf->emergency) {
+ /* LSC -> IAC: Emergency */
+ osmo_fsm_inst_dispatch(lfd->iac_fi, MTP2_IAC_E_EMERGENCY, NULL);
+ }
+ /* LSC -> IAC: Start */
+ osmo_fsm_inst_dispatch(lfd->iac_fi, MTP2_IAC_E_START, NULL);
+ /* LSC -> RC: Stop */
+ osmo_fsm_inst_state_chg(fi, MTP2_LSC_S_INITIAL_ALIGNMENT, 0, 0);
+ break;
+ case MTP_LSC_E_EMERGENCY:
+ lfd->emergency = true;
+ break;
+ case MTP_LSC_E_EMERGENCY_CEASES:
+ lfd->emergency = false;
+ break;
+ case MTP_LSC_E_LOCAL_PROC_OUTAGE:
+ lfd->local_proc_outage = true;
+ break;
+ case MTP_LSC_E_LOCAL_PROC_OUTAGE_RECOVD:
+ lfd->local_proc_outage = false;
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+/* Figure 8/Q.703 (sheet 4+5 of 14) */
+static void mtp2_lsc_fsm_initial_alignment(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct lsc_fsm_data *lfd = fi->priv;
+
+ switch (event) {
+ case MTP_LSC_E_LOCAL_PROC_OUTAGE:
+ /* fall-through */
+ case MTP_LSC_E_LEVEL3_FAILURE:
+ lfd->local_proc_outage = true;
+ break;
+ case MTP_LSC_E_LOCAL_PROC_OUTAGE_RECOVD:
+ lfd->local_proc_outage = false;
+ break;
+ case MTP_LSC_E_EMERGENCY:
+ lfd->emergency = true;
+ /* LSC -> IAC: Emergency */
+ osmo_fsm_inst_dispatch(lfd->iac_fi, MTP2_IAC_E_EMERGENCY, NULL);
+ break;
+ case MTP_LSC_E_ALIGNMENT_COMPLETE:
+ /* LSC -> SUERM: Start */
+ /* Start T1 */
+ if (lfd->local_proc_outage) {
+ /* LSC -> POC: Local Processor Outage */
+ /* LSC -> TXC: Send SIPO */
+ /* LSC -> RC: Reject MSU/FISU */
+ osmo_fsm_inst_state_chg_ms(fi, MTP3_LSC_S_ALIGNED_NOT_READY, MTP2_T1_64_MS, 1);
+ } else {
+ /* LSC -> TXC: Send FISU */
+ /* LSC -> RC: Accept MSU/FISU */
+ osmo_fsm_inst_state_chg_ms(fi, MTP2_LSC_S_ALIGNED_READY, MTP2_T1_64_MS, 1);
+ }
+ break;
+ case MTP_LSC_E_LINK_FAILURE:
+ /* LSC -> L3: Out of service */
+ /* fall-through */
+ case MTP_LSC_E_STOP:
+ /* LSC -> IAC: Stop */
+ osmo_fsm_inst_dispatch(lfd->iac_fi, MTP2_IAC_E_STOP, NULL);
+ /* LSC -> RC: Stop */
+ /* LSC -> TRX: Send SIOS */
+ /* Cancel local processor outage */
+ lfd->local_proc_outage = false;
+ /* Cancel emergency */
+ lfd->emergency = false;
+ osmo_fsm_inst_state_chg(fi, MTP2_LSC_S_OUT_OF_SERVICE, 0, 0);
+ break;
+ case MTP_LSC_E_ALIGNMENT_NOT_POSSIBLE:
+ /* LSC -> L3: Out of service */
+ /* LSC -> RC: Stop */
+ /* LSC -> TRX: Send SIOS */
+ /* Cancel local processor outage */
+ lfd->local_proc_outage = false;
+ /* Cancel emergency */
+ lfd->emergency = false;
+ osmo_fsm_inst_state_chg(fi, MTP2_LSC_S_OUT_OF_SERVICE, 0, 0);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+/* Figure 8/Q.703 (sheet 6+7 of 14) */
+static void mtp2_lsc_fsm_aligned_ready(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct lsc_fsm_data *lfd = fi->priv;
+ switch (event) {
+ case MTP_LSC_E_LINK_FAILURE:
+ /* fall-through */
+ case MTP_LSC_E_RX_SIO_SIOS:
+ case MTP_LSC_E_T1_EXP:
+ /* LSC -> L3: Out of service */
+ /* fall-through */
+ case MTP_LSC_E_STOP:
+ /* Stop T1 */
+ /* LSC -> RC: Stop */
+ /* LSC -> SUERM: Stop */
+ /* LSC -> TXC: Send SIOS */
+ /* Cancel emergency */
+ lfd->emergency = false;
+ osmo_fsm_inst_state_chg(fi, MTP2_LSC_S_OUT_OF_SERVICE, 0, 0);
+ break;
+ case MTP_LSC_E_RX_SIPO:
+ /* Stop T1 */
+ /* LSC -> L3: Remote processor outage */
+ /* LSC -> POC: Remote processor outage */
+ osmo_fsm_inst_state_chg(fi, MTP2_LSC_S_PROCESSOR_OUTAGE, 0, 0);
+ break;
+ case MTP_LSC_E_RX_FISU_MSU:
+ /* LSC -> L3: In service */
+ /* Stop T1 */
+ /* LSC -> TXC: Send MSU */
+ osmo_fsm_inst_state_chg(fi, MTP2_LSC_S_IN_SERVICE, 0, 0);
+ break;
+ case MTP_LSC_E_LOCAL_PROC_OUTAGE:
+ /* fall-through */
+ case MTP_LSC_E_LEVEL3_FAILURE:
+ /* LSC -> POC: Local Processor outage */
+ /* LSC -> TXC: Send SIPO */
+ /* LSC -> RC: Reject MSU/FISU */
+ osmo_fsm_inst_state_chg(fi, MTP2_LSC_S_ALIGNED_NOT_READY, 0, 0);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+/* Figure 8/Q.703 (sheet 8+9 of 14) */
+static void mtp2_lsc_fsm_aligned_not_ready(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct lsc_fsm_data *lfd = fi->priv;
+ switch (event) {
+ case MTP_LSC_E_LINK_FAILURE:
+ /* fall-through */
+ case MTP_LSC_E_RX_SIO_SIOS:
+ case MTP_LSC_E_T1_EXP:
+ /* LSC -> L3: Out of service */
+ /* fall-through */
+ case MTP_LSC_E_STOP:
+ /* Stop T1 */
+ /* LSC -> RC: Stop */
+ /* LSC -> SUERM: Stop */
+ /* LSC -> TxC: Send SIOS */
+ /* Cancel emergency and local processor outage */
+ lfd->emergency = false;
+ lfd->local_proc_outage = false;
+ osmo_fsm_inst_state_chg(fi, MTP2_LSC_S_OUT_OF_SERVICE, 0, 0);
+ break;
+ case MTP_LSC_E_LOCAL_PROC_OUTAGE_RECOVD:
+ /* LSC -> POC: Local processor recovered */
+ /* Cancel local processor outage */
+ lfd->local_proc_outage = false;
+ /* LSC -> TXC: Send FISU */
+ /* LSC -> RC: Accept MSU/FISU */
+ osmo_fsm_inst_state_chg(fi, MTP2_LSC_S_ALIGNED_READY, 0, 0);
+ break;
+ case MTP_LSC_E_RX_FISU_MSU:
+ /* LSC -> L3: In service */
+ /* Stop T1 */
+ osmo_fsm_inst_state_chg(fi, MTP2_LSC_S_PROCESSOR_OUTAGE, 0, 0);
+ break;
+ case MTP_LSC_E_RX_SIPO:
+ /* LSC -> L3: Remote processor outage */
+ /* LSC -> POC: Remote processor outage */
+ /* Stop T1 */
+ osmo_fsm_inst_state_chg(fi, MTP2_LSC_S_PROCESSOR_OUTAGE, 0, 0);
+ break;
+ default:
+ OSMO_ASSERT(0);
+
+ }
+}
+
+/* Figure 8/Q.703 (sheet 10+11 of 14) */
+static void mtp2_lsc_fsm_in_service(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct lsc_fsm_data *lfd = fi->priv;
+ switch (event) {
+ case MTP_LSC_E_LINK_FAILURE:
+ /* fall-through */
+ case MTP_LSC_E_RX_SIO_SIOS:
+ /* fall-through */
+ case MTP_LSC_E_RX_SIN_SIE:
+ /* LSC -> L3: Out of service */
+ /* fall-through */
+ case MTP_LSC_E_STOP:
+ /* LSC -> SUERM: Stop */
+ /* LSC -> RC: Stop */
+ /* LSC -> TxC: Send SIOS */
+ /* Cancel emergency */
+ lfd->emergency = false;
+ osmo_fsm_inst_state_chg(fi, MTP2_LSC_S_OUT_OF_SERVICE, 0, 0);
+ break;
+ case MTP_LSC_E_LOCAL_PROC_OUTAGE:
+ /* fall-through */
+ case MTP_LSC_E_LEVEL3_FAILURE:
+ /* LSC -> POC: Local Processor outage */
+ /* LSC -> TXC: Send SIPO */
+ /* LSC -> RC: Reject MSU/FISU */
+ osmo_fsm_inst_state_chg(fi, MTP2_LSC_S_ALIGNED_NOT_READY, 0, 0);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+/* Figure 8/Q.703 (sheet 12-14 of 14) */
+static void mtp2_lsc_fsm_processor_outage(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct lsc_fsm_data *lfd = fi->priv;
+ switch (event) {
+ case MTP_LSC_E_RX_FISU_MSU:
+ /* LSC -> POC: Remote processor recovered */
+ /* LSC -> L3: Remote processor recovered */
+ break;
+ case MTP_LSC_E_LEVEL3_FAILURE:
+ /* fall-through */
+ case MTP_LSC_E_LOCAL_PROC_OUTAGE:
+ /* LSC -> POC: Local processor outage */
+ /* LSC -> TXC: Send SIPO */
+ break;
+ case MTP_LSC_E_RX_SIPO:
+ /* LSC -> L3: Remote processor outage */
+ /* LSC -> POC: Remote processor outage */
+ break;
+ case MTP_LSC_E_LOCAL_PROC_OUTAGE_RECOVD:
+ /* LSC -> POC: Local processor recovered */
+ /* LSC -> RC: Retrieve FSNX */
+ /* LSC -> TXC: Send FISU */
+ break;
+ case MTP_LSC_E_FLUSH_BUFFERS:
+ /* LSC -> TXC: Flush buffers */
+ /* fall-through */
+ case MTP_LSC_E_CONTINUE:
+ /* Mark L3 indication received */
+ lfd->l3_indication_received = true;
+ if (lfd->processor_outage)
+ break;
+ /* Cancel Level3 indication received */
+ /* LSC -> TXC: Send MSU/FISU */
+ /* Cancel local processor outage */
+ /* LSC -> RC: Accept MSU/FISU */
+ osmo_fsm_inst_state_chg(fi, MTP2_LSC_S_IN_SERVICE, 0, 0);
+ break;
+ case MTP_LSC_E_NO_PROC_OUTAGE:
+ /* Cancel processor outage */
+ lfd->processor_outage = false;
+ if (!lfd->l3_indication_received)
+ break;
+ /* Cancel Level3 indication received */
+ /* LSC -> TXC: Send MSU/FISU */
+ /* Cancel local processor outage */
+ /* LSC -> RC: Accept MSU/FISU */
+ osmo_fsm_inst_state_chg(fi, MTP2_LSC_S_IN_SERVICE, 0, 0);
+ break;
+ case MTP_LSC_E_LINK_FAILURE:
+ case MTP_LSC_E_RX_SIO_SIOS:
+ case MTP_LSC_E_RX_SIN_SIE:
+ /* LSC -> L3: Out of service */
+ /* fall-through */
+ case MTP_LSC_E_STOP:
+ /* LSC -> SUERM: Stop */
+ /* LSC -> RC: Stop */
+ /* LSC -> POC: Stop */
+ /* LSC -> TXC: Send SIOS */
+ /* Cancel emergency and local processor outage */
+ lfd->emergency = false;
+ lfd->local_proc_outage = false;
+ osmo_fsm_inst_state_chg(fi, MTP2_LSC_S_OUT_OF_SERVICE, 0, 0);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+
+static int mtp2_lsc_fsm_timer_cb(struct osmo_fsm_inst *fi)
+{
+ struct lsc_fsm_data *lfd = fi->priv;
+
+ switch (fi-T) {
+ case 1:
+ /* we handle the timer expiration in the action call-backs
+ * to better align with the SDL diagrams */
+ osmo_fsm_inst_dispatch(fi, MTP_LSC_E_T1_EXP, NULL);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+
+ return 0;
+}
+
+
+static const struct osmo_fsm_state mtp2_lsc_states[] = {
+ [MTP2_LSC_S_POWER_OFF] = {
+ .name = "POWER_OFF",
+ .in_event_mask = S(MTP_LSC_E_POWER_ON),
+ .out_state_mask = S(MTP2_LSC_S_OUT_OF_SERVICE),
+ .action = mtp2_lsc_fsm_power_off,
+ },
+ [MTP2_LSC_S_OUT_OF_SERVICE] = {
+ .name = "OUT_OF_SERVICE",
+ .in_event_mask = S(MTP_LSC_E_START) |
+ S(MTP_LSC_E_EMERGENCY) |
+ S(MTP_LSC_E_EMERGENCY_CEASES) |
+ S(MTP_LSC_E_LOCAL_PROC_OUTAGE) |
+ S(MTP_LSC_E_LOCAL_PROC_OUTAGE_RECOVD) |
+ S(MTP_LSC_E_LEVEL3_FAILURE),
+ .out_state_mask = S(MTP2_LSC_S_OUT_OF_SERVICE) |
+ S(MTP2_LSC_S_INITIAL_ALIGNMENT),
+ .action = mtp2_lsc_fsm_out_of_service,
+ },
+ [MTP2_LSC_S_INITIAL_ALIGNMENT] = {
+ .name = "INITIAL_ALIGNMENT",
+ .in_event_mask = S(MTP_LSC_E_EMERGENCY) |
+ S(MTP_LSC_E_LOCAL_PROC_OUTAGE) |
+ S(MTP_LSC_E_LOCAL_PROC_OUTAGE_RECOVD) |
+ S(MTP_LSC_E_LEVEL3_FAILURE) |
+ S(MTP_LSC_E_ALIGNMENT_COMPLETE) |
+ S(MTP_LSC_E_STOP) |
+ S(MTP_LSC_E_LINK_FAILURE) |
+ S(MTP_LSC_E_ALIGNMENT_NOT_POSSIBLE),
+ .out_state_mask = S(MTP2_LSC_S_INITIAL_ALIGNMENT) |
+ S(MTP2_LSC_S_ALIGNED_NOT_READY) |
+ S(MTP2_LSC_S_OUT_OF_SERVICE),
+ .action = mtp2_lsc_fsm_initial_alignment,
+ },
+ [MTP2_LSC_S_ALIGNED_READY] = {
+ .name = "ALIGNED_READY",
+ .in_event_mask = S(MTP_LSC_E_LINK_FAILURE) |
+ S(MTP_LSC_E_RX_SIO_SIOS) |
+ S(MTP_LSC_E_STOP) |
+ S(MTP_LSC_E_RX_SIPO) |
+ S(MTP_LSC_E_RX_FISU_MSU) |
+ S(MTP_LSC_E_LOCAL_PROC_OUTAGE) |
+ S(MTP_LSC_E_LEVEL3_FAILURE) |
+ S(MTP_LSC_E_T1_EXP),
+ .out_state_mask = S(MTP2_LSC_S_OUT_OF_SERVICE) |
+ S(MTP2_LSC_S_PROCESSOR_OUTAGE) |
+ S(MTP2_LSC_S_IN_SERVICE) |
+ S(MTP2_LSC_S_ALIGNED_NOT_READY),
+ .action = mtp2_lsc_fsm_aligned_ready,
+ },
+ [MTP2_LSC_S_ALIGNED_NOT_READY] = {
+ .name = "ALIGNED_NOT_READY",
+ .in_event_mask = S(MTP_LSC_E_LINK_FAILURE) |
+ S(MTP_LSC_E_RX_SIO_SIOS) |
+ S(MTP_LSC_E_STOP) |
+ S(MTP_LSC_E_LOCAL_PROC_OUTAGE_RECOVD) |
+ S(MTP_LSC_E_RX_FISU_MSU) |
+ S(MTP_LSC_E_RX_SIPO) |
+ S(MTP_LSC_E_T1_EXP),
+ .out_state_mask = S(MTP2_LSC_S_OUT_OF_SERVICE) |
+ S(MTP2_LSC_S_ALIGNED_READY) |
+ S(MTP2_LSC_S_PROCESSOR_OUTAGE),
+ .action = mtp2_lsc_fsm_aligned_not_ready,
+ },
+ [MTP2_LSC_S_IN_SERVICE] = {
+ .name = "IN_SERVICE",
+ .in_event_mask = S(MTP_LSC_E_LINK_FAILURE) |
+ S(MTP_LSC_E_RX_SIO_SIOS) |
+ S(MTP_LSC_E_RX_SIN_SIE) |
+ S(MTP_LSC_E_LOCAL_PROC_OUTAGE) |
+ S(MTP_LSC_E_LEVEL3_FAILURE) |
+ S(MTP_LSC_E_RX_SIPO),
+ .out_state_mask = S(MTP2_LSC_S_OUT_OF_SERVICE) |
+ S(MTP2_LSC_S_PROCESSOR_OUTAGE),
+ .action = mtp2_lsc_fsm_in_service,
+ },
+ [MTP2_LSC_S_PROCESSOR_OUTAGE] = {
+ .name = "PROCESSOR_OUTAGE",
+ .in_event_mask = S(MTP_LSC_E_RX_FISU_MSU) |
+ S(MTP_LSC_E_LEVEL3_FAILURE) |
+ S(MTP_LSC_E_LOCAL_PROC_OUTAGE) |
+ S(MTP_LSC_E_RX_SIPO) |
+ S(MTP_LSC_E_LOCAL_PROC_OUTAGE_RECOVD) |
+ S(MTP_LSC_E_FLUSH_BUFFERS) |
+ S(MTP_LSC_E_NO_PROC_OUTAGE) |
+ S(MTP_LSC_E_LINK_FAILURE) |
+ S(MTP_LSC_E_RX_SIO_SIOS) |
+ S(MTP_LSC_E_RX_SIN_SIE) |
+ S(MTP_LSC_E_STOP),
+ .out_state_mask = S(MTP2_LSC_S_PROCESSOR_OUTAGE) |
+ S(MTP2_LSC_S_IN_SERVICE) |
+ S(MTP2_LSC_S_OUT_OF_SERVICE),
+ .action = mtp2_lsc_fsm_processor_outage,
+ },
+};
+
+
+struct osmo_fsm mtp2_lsc_fsm = {
+ .name = "MTP2_LSC",
+ .states = mtp2_lsc_states,
+ .num_states = ARRAY_SIZE(mtp2_lsc_states),
+ .timer_cb = mtp2_lsc_fsm_timer_cb,
+ .log_subsys = DLMTP2,
+ .event_names = mtp2_lsc_event_names,
+ .allstate_event_mask = ,
+ .allstate_action = mtp2_lsc_allstate,
+};
+
+struct osmo_fsm_inst *mtp2_lxc_fsm_alloc(struct osmo_ss7_link *s7l, int log_level)
+{
+ struct osmo_fsm_inst *fi;
+ struct mtp2_lsc_fsm_priv *lfp;
+
+ fi = osmo_fsm_inst_alloc(&mtp2_lsc_fsm, s7l, NULL, log_level, s7l->name);
+
+ lfp = talloc_zero(fi, struct mtp2_lxc_fsm_priv);
+ if (!lfp) {
+ osmo_fsm_inst_term(fi, OSM_FSM_TERM_ERROR, NULL);
+ return NULL;
+ }
+ /* Initial Alignment Control FSM instance */
+ lfp->iac_fi = mtp2_iac_fsm_alloc(fi);
+
+ lfp->local_proc_outage = false;
+ lfp->l3_indication_received = false;
+ lfp->processor_outage = false;
+
+ fi->priv = lfp;
+
+ return fi;
+}
diff --git a/src/mtp2_lsc_fsm.h b/src/mtp2_lsc_fsm.h
new file mode 100644
index 0000000..9aa1e4d
--- /dev/null
+++ b/src/mtp2_lsc_fsm.h
@@ -0,0 +1,26 @@
+#pragma once
+#include <osmocom/core/fsm.h>
+
+enum mtp2_lsc_fsm_event {
+ MTP_LSC_E_POWER_ON, /* MGMT -> LSC */
+ MTP_LSC_E_START, /* L3 -> LSC */
+ MTP_LSC_E_EMERGENCY, /* L3 -> LSC */
+ MTP_LSC_E_EMERGENCY_CEASES, /* L3 -> LSC */
+ MTP_LSC_E_LOCAL_PROC_OUTAGE, /* MGMT -> LSC */
+ MTP_LSC_E_LOCAL_PROC_OUTAGE_RECOVD, /* MGMT -> LSC */
+ MTP_LSC_E_LEVEL3_FAILURE, /* MGMT -> LSC */
+ MTP_LSC_E_ALIGNMENT_COMPLETE, /* IAC -> LSC */
+ MTP_LSC_E_STOP, /* L3 -> LSC */
+ MTP_LSC_E_LINK_FAILURE, /* RC -> LSC */
+ MTP_LSC_E_ALIGNMENT_NOT_POSSIBLE, /* IAC -> LSC */
+ MTP_LSC_E_RX_SIO_SIOS, /* RC -> LSC */
+ MTP_LSC_E_RX_SIPO, /* RC -> LSC */
+ MTP_LSC_E_RX_FISU_MSU, /* RC -> LSC */
+ MTP_LSC_E_FLUSH_BUFFERS, /* L3 -> LSC */
+ MTP_LSC_E_CONTINUE, /* L3 -> LSC */
+ MTP_LSC_E_NO_PROC_OUTAGE, /* POC -> LSC */
+ MTP_LSC_E_T1_EXP, /* LSC -> LSC */
+
+};
+
+extern struct osmo_fsm mtp2_lsc_fsm;
diff --git a/src/mtp2_txc_fsm.c b/src/mtp2_txc_fsm.c
new file mode 100644
index 0000000..413b280
--- /dev/null
+++ b/src/mtp2_txc_fsm.c
@@ -0,0 +1,191 @@
+#include <osmocom/core/fsm.h>
+#include "mtp2_txc_fsm.h"
+
+struct txc_fsm_data {
+ /* Link Status Control FSM Instance */
+ struct osmo_fsm_inst *lsc_fi;
+ /* back-pointer to SS7 Link */
+ struct osmo_ss7_link *s7l;
+
+ /* various fsm-private state */
+ FIXME lssu_available;
+ bool sib_received;
+ bool rtb_full;
+ bool msu_inhibited;
+
+ uint32_t fnsl;
+ uint32_t fnst;
+ uint32_t fnsx;
+ uint32_t fib;
+ uint32_t bib;
+ uint32_t fsnf;
+ uint32_t cm;
+};
+
+static inline bool is_m2pa(const struct txc_fsm_data *tfd)
+{
+ return tfd->s7l->type == OSMO_SS7_LINK_TYPE_M2PA;
+}
+
+static void m2pa_txc_fsm_idle(struct osmo_fsm_inst *fi, uint32_t event ,void *data)
+{
+ struct txc_fsm_data *tfd = fi->priv;
+
+ switch (event) {
+ case MTP2_TXC_E_START:
+ /* TXC -> DAEDT: Start */
+ /* Cancel LSSU available */
+ tfd->lssu_available = false;
+ /* Cancel SIB received */
+ tfd->sib_received = false;
+ /* Cancel RTP full */
+ tfd->rtb_full = false;
+ /* Cancel MSU inhibited */
+ tfd->msu_inhibited = false;
+ /* initialize various variables */
+ tfd->fsnl = 127;
+ tfd->fsnt = 127;
+ tfd->fsnx = 0;
+ tfd->fib = tfd->bib = 1;
+ tfd->fsnf = 0;
+ tfd->cm = 0;
+ osmo_fsm_inst_state_chg(fi, MTP2_TXC_S_IN_SERVICE, 0, 0);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void m2pa_txc_fsm_in_service(struct osmo_fsm_inst *fi, uint32_t event ,void *data)
+{
+ struct txc_fsm_data *tfd = fi->priv;
+ struct osmo_m2pa_peer *m2p;
+
+ if (is_m2pa(tfd))
+ m2p = tfd->s7l->u.m2pa;
+ else
+ m2p = NULL;
+
+ switch (event) {
+ case MTP2_TXC_E_SEND_SIOS:
+ /* Stop T7 */
+ tfd->lssu_available = SIOS;
+ if (is_m2pa(tfd))
+ osmo_m2pa_peer_send_link_status(m2p, M2PA_LSTS_OUT_OF_SERVICE);
+ break;
+ case MTP2_TXC_E_SEND_SIPO:
+ /* Stop T7 */
+ tfd->lssu_available = SIPO;
+ if (is_m2pa(tfd))
+ osmo_m2pa_peer_send_link_status(m2p, M2PA_LSTS_PROCESSOR_OUTAGE);
+ break;
+ case MTP2_TXC_E_SEND_SIO:
+ tfd->lssu_available = SIO;
+ if (is_m2pa(tfd))
+ osmo_m2pa_peer_send_link_status(m2p, M2PA_LSTS_ALIGNMENT);
+ break;
+ case MTP2_TXC_E_SEND_SIN:
+ tfd->lssu_available = SIN;
+ if (is_m2pa(tfd))
+ osmo_m2pa_peer_send_link_status(m2p, M2PA_LSTS_PROVING_NORMAL);
+ break;
+ case MTP2_TXC_E_SEND_SIE:
+ tfd->lssu_available = SIE;
+ if (is_m2pa(tfd))
+ osmo_m2pa_peer_send_link_status(m2p, M2PA_LSTS_PROVING_EMERGENCY);
+ break;
+ case MTP2_TXC_E_SEND_SIB:
+ tfd->lssu_available = SIB;
+ if (is_m2pa(tfd))
+ osmo_m2pa_peer_send_link_status(m2p, M2PA_LSTS_BUSY);
+ break;
+ case MTP2_TXC_E_START:
+ /* Cancel SIB received */
+ tfd->sib_received = false;
+ /* Cancel RTP full */
+ tfd->rtb_full = false;
+ /* Cancel MSU inhibited */
+ tfd->msu_inhibited = false;
+ /* initialize various variables */
+ tfd->fsnl = 127;
+ tfd->fsnt = 127;
+ tfd->fsnx = 0;
+ tfd->fib = tfd->bib = 1;
+ tfd->fsnf = 0;
+ tfd->cm = 0;
+ osmo_fsm_inst_state_chg(fi, MTP2_TXC_S_IN_SERVICE, 0, 0);
+ break;
+ case MTP2_TXC_E_SEND_FISU:
+ /* Stop T7 */
+ /* Mark MSU inhibited */
+ tfd->msu_inhibited = true;
+ tfd->lssu_available = FISU;
+ if (is_m2pa(tfd))
+ osmo_m2pa_peer_send_link_status(m2p, M2PA_LSTS_READY);
+ break;
+ case MTP2_TXC_E_SEND_MSU:
+ if (tfd->fsnl != tfd->fsnf - 1) {
+ /* Start T7 */
+ }
+ /* Cancel MSU inhibited */
+ tfd->msu_inhibited = false;
+ tfd->lssu_available = NULL;
+ break;
+ case MTP2_TXC_E_NACK_TO_BE_SENT:
+ ftd->bib = !ftd->bib;
+ break;
+ case MTP2_TXC_E_SIB_RECEIVED:
+ if (!tfd->sib_received) {
+ /* Start T6 */
+ tfd->sib_received = true;
+ }
+ /* Start T7 */
+ break;
+ case MTP2_TXC_E_MSG_FOR_TX:
+ /* Store MSU in TB */
+ break;
+ case MTP2_TSC_E_FLUSH_BUFFERS:
+ /* Erase all MSUs in RTB and TB */
+ /* Cancel RTB full */
+ tfd->rtb_full = false;
+ tfd->cm = 0;
+ tfd->fsnf = tfd->bsnr + 1;
+ tfd->fsnl = tfd->bsnr;
+ tfd->fsnl = tfd->bsnr;
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+
+static const struct osmo_fsm_state m2pa_txc_fsm_states[] = {
+ [MTP2_TXC_S_IDLE] = {
+ .name = "IDLE",
+ .action = m2pa_txc_fsm_idle,
+ .in_event_mask = S(MTP2_TXC_E_START),
+ .out_state_mask = S(MTP2_TXC_S_IDLE) |
+ S(MTP2_TXC_S_IN_SERVICE),
+ },
+ [MTP2_TXC_S_IN_SERVICE] = {
+ .name = "IN_SERVICE",
+ .action = m2pa_txc_fsm_in_service,
+ .in_event_mask = S(MTP2_TXC_E_SEND_SIOS) |
+ S(MTP2_TXC_E_SEND_SIPO) |
+ S(MTP2_TXC_E_SEND_SIO) |
+ S(MTP2_TXC_E_SEND_SIN) |
+ S(MTP2_TXC_E_SEND_SIE) |
+ S(MTP2_TXC_E_SEND_SIB) |
+ S(MTP2_TXC_E_START) |
+ S(MTP2_TXC_E_SEND_FISU) |
+ S(MTP2_TXC_E_SEND_MSU) |
+ S(MTP2_TXC_E_NACK_TO_BE_SENT) |
+ S(MTP2_TXC_E_SIB_RECEIVED) |
+ S(MTP2_TXC_E_MSG_FOR_TX) |
+ S(MTP2_TSC_E_FLUSH_BUFFERS),
+ .out_state_mask = S(MTP2_TXC_S_IN_SERVICE),
+ },
+};
+
+struct osmo_fsm m2pa_txc_fsm = {
+};
diff --git a/src/mtp2_txc_fsm.h b/src/mtp2_txc_fsm.h
new file mode 100644
index 0000000..0513fd8
--- /dev/null
+++ b/src/mtp2_txc_fsm.h
@@ -0,0 +1,30 @@
+#pragma once
+#include <osmocom/core/fsm.h>
+
+enum mtp2_txc_event {
+ MTP2_TXC_E_START, /* LSC -> TXC: Start */
+ MTP2_TXC_E_SEND_SIOS, /* LSC -> TXC: Send SIOS */
+ MTP2_TXC_E_SEND_SIPO, /* LSC -> TXC: Send SIPO */
+ MTP2_TXC_E_SEND_FISU, /* LSC -> TXC: Send FISU */
+ MTP2_TXC_E_SEND_MSU, /* LSC -> TXC: Send MSU */
+ MTP2_TXC_E_FLUSH_BUFFERS, /* LSC -> TXC: Flush Buffers */
+ MTP2_TXC_E_SEND_MSU, /* LSC -> TXC: Send MSU/FISU */
+
+ MTP2_TXC_E_SEND_SIO, /* IAC -> TXC: Send SIO */
+ MTP2_TXC_E_SEND_SIE, /* IAC -> TXC: Send SIE */
+ MTP2_TXC_E_SEND_SIN, /* IAC -> TXC: Send SIN */
+
+ MTP2_TXC_E_SEND_SIB, /* CC -> TXC: Send SIB */
+
+ MTP2_TXC_E_NACK_TO_BE_SENT, /* RC -> TXC: NACK To be sent */
+ MTP2_TXC_E_SIB_RECEIVED, /* RC -> TXC: SIB Received */
+ MTP2_TXC_E_MSG_FOR_TX, /* L3 -> TXC: Message for transmission */
+ //MTP2_TXC_E_BSNR_AND_BIBR, /* RC -> TXC */
+ //MTP2_TXC_E_FSNX_VALUE, /* RC -> TXC */
+ MTP2_TSC_E_FLUSH_BUFFERS, /* LSC -> TXC: Flush buffers */
+};
+
+enum mtp2_txc_fsm_state {
+ MTP2_TXC_S_IDLE,
+ MTP2_TXC_S_IN_SERVICE,
+};
diff --git a/src/sccp_scoc.c b/src/sccp_scoc.c
index db1db23..d95ed63 100644
--- a/src/sccp_scoc.c
+++ b/src/sccp_scoc.c
@@ -992,6 +992,14 @@ static void scoc_fsm_idle(struct osmo_fsm_inst *fi, uint32_t event, void *data)
switch (event) {
case SCOC_E_SCU_N_CONN_REQ:
prim = data;
+ if (msgb_l2len(prim->oph.msg) > SCCP_CR_MAX_DATA_LEN) {
+ LOGPFSML(fi, LOGL_ERROR, "N-CONNECT.req with DATA length > %u "
+ "not permitted by Q.713\n", SCCP_CR_MAX_DATA_LEN);
+ /* FIXME: send empty CR and send data in later DT1 */
+ scu_gen_encode_and_send(conn, event, NULL, OSMO_SCU_PRIM_N_DISCONNECT,
+ PRIM_OP_INDICATION);
+ break;
+ }
uconp = &prim->u.connect;
/* copy relevant parameters from prim to conn */
conn->called_addr = uconp->called_addr;
@@ -1070,6 +1078,12 @@ static void scoc_fsm_conn_pend_in(struct osmo_fsm_inst *fi, uint32_t event, void
switch (event) {
case SCOC_E_SCU_N_CONN_RESP:
prim = data;
+ if (msgb_l2len(prim->oph.msg) > SCCP_CC_MAX_DATA_LEN) {
+ LOGPFSML(fi, LOGL_ERROR, "N-CONNECT.resp with DATA length > %u "
+ "not permitted by Q.713\n", SCCP_CC_MAX_DATA_LEN);
+ /* FIXME: send CC with empty body and use DT1 to transfer data afterwards! */
+ break;
+ }
/* FIXME: assign local reference (only now?) */
/* FIXME: assign sls, protocol class and credit */
xua_gen_encode_and_send(conn, event, prim, SUA_CO_COAK);
@@ -1080,6 +1094,12 @@ static void scoc_fsm_conn_pend_in(struct osmo_fsm_inst *fi, uint32_t event, void
break;
case SCOC_E_SCU_N_DISC_REQ:
prim = data;
+ if (msgb_l2len(prim->oph.msg) > SCCP_CREF_MAX_DATA_LEN) {
+ LOGPFSML(fi, LOGL_ERROR, "N-DISCONNECT.req with DATA length > %u "
+ "not permitted by Q.713\n", SCCP_CREF_MAX_DATA_LEN);
+ /* FIXME: refuse without payload! */
+ break;
+ }
/* release resources: implicit */
xua_gen_encode_and_send(conn, event, prim, SUA_CO_COREF);
/* N. B: we've ignored CREF sending errors as there's no recovery option anyway */
@@ -1230,6 +1250,12 @@ static void scoc_fsm_active(struct osmo_fsm_inst *fi, uint32_t event, void *data
/* fall-through */
case SCOC_E_SCU_N_DISC_REQ:
prim = data;
+ if (msgb_l2len(prim->oph.msg) > SCCP_RLSD_MAX_DATA_LEN) {
+ LOGPFSML(fi, LOGL_ERROR, "N-DISCONNECT.req with DATA length > %u "
+ "not permitted by Q.713\n", SCCP_RLSD_MAX_DATA_LEN);
+ /* FIXME: send DT1 followed by empty RLSD */
+ break;
+ }
/* stop inact timers */
conn_stop_inact_timers(conn);
/* send RLSD to SCRC */