/* ITU-T G.964 Section 14.3.3 V5.1-intterface Layer 1 FSM - AN and LE */ /* (C) 2022 by Andreas Eversberg * * All Rights Reserved * * SPDX-License-Identifier: GPL-2.0+ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * */ /***********************************************************************/ /* internal data structures */ /***********************************************************************/ #include #include #include #include "v5x_internal.h" #include "v5x_protocol.h" #include "v52_le_lcp_fsm.h" #include "layer1.h" #include "v5x_l1_fsm.h" #include "logging.h" #define S(x) (1 << (x)) #define TIMEOUT 1 /***********************************************************************/ /* state names, event names, primitives, ... */ /***********************************************************************/ enum v5x_l1_fsm_state { V5X_L1FSM_S_ANLE1_NORMAL, V5X_L1FSM_S_ANLE2_LOCALLY_DET_FAIL, V5X_L1FSM_S_ANLE3_REMOTELY_DET_FAIL, V5X_L1FSM_S_ANLE4_INTERNAL_FAIL, V5X_L1FSM_S_ANLE51_LINK_ID_SENDING, V5X_L1FSM_S_ANLE52_LINK_ID_RECEIVED, }; enum v5x_l1_fsm_event { /* V5.x events */ V5X_L1FSM_E_MPH_stop, /* Request to stop with error report */ V5X_L1FSM_E_MPH_proceed,/* Request to proceed with error report */ V5X_L1FSM_E_NORMAL, /* Normal frames, Sa7 == 1 */ V5X_L1FSM_E_LOS, /* Loss of signal/frame */ V5X_L1FSM_E_RAI, /* Remote alarm indication */ V5X_L1FSM_E_AIS, /* Alarm indication signal */ V5X_L1FSM_E_INTERNAL_F, /* Internal failure */ V5X_L1FSM_E_INTERNAL_D, /* Internal failure disappears */ V5X_L1FSM_E_TIMEOUT, /* Persistence check timer fired */ /* V5.2 events */ V5X_L1FSM_E_MPH_ID, /* Send link identification signal */ V5X_L1FSM_E_MPH_NOR, /* Remove link identification signal */ V5X_L1FSM_E_MPH_IDR, /* Link identification request */ V5X_L1FSM_E_NORMAL_Sa7, /* Normal frames, Sa7 == 0 */ }; static const struct value_string v5x_l1_fsm_event_names[] = { { V5X_L1FSM_E_MPH_stop, "Request to stop with error report" }, { V5X_L1FSM_E_MPH_proceed, "Request to proceed with error report" }, { V5X_L1FSM_E_NORMAL, "Normal frames, Sa7 = ONE" }, { V5X_L1FSM_E_LOS, "Loss of signal/frame" }, { V5X_L1FSM_E_RAI, "Remote alarm indication" }, { V5X_L1FSM_E_AIS, "Alarm indication signal" }, { V5X_L1FSM_E_INTERNAL_F, "Internal failure" }, { V5X_L1FSM_E_INTERNAL_D, "Internal failure disappears" }, { V5X_L1FSM_E_TIMEOUT, "Expiry of persistence check timer" }, { V5X_L1FSM_E_MPH_ID, "Send link identification signal" }, { V5X_L1FSM_E_MPH_NOR, "Remove link identification signal" }, { V5X_L1FSM_E_MPH_IDR, "Link identification request" }, { V5X_L1FSM_E_NORMAL_Sa7, "Normal frames, Sa7 = ZERO" }, { 0, NULL } }; /***********************************************************************/ /* Messages to other layers */ /***********************************************************************/ /* send message to upper (LCP) layer */ static void mph_rcv(struct osmo_fsm_inst *fi, enum v5x_mph_prim prim) { struct v5x_l1_proto *l1 = fi->priv; struct v5x_link *v5l = l1->v5l; if (v5l->fi) v52_le_lcp_mph_rcv(v5l, prim); // FIXME: send to other protocols too } static void signal_snd(struct osmo_fsm_inst *fi, enum l1_signal_prim prim) { struct v5x_l1_proto *l1 = fi->priv; struct v5x_link *v5l = l1->v5l; v5x_l1_signal_snd(v5l, prim); } /***********************************************************************/ /* L1 state FSM */ /***********************************************************************/ static int v5x_l1_fsm_timer_cb(struct osmo_fsm_inst *fi) { osmo_fsm_inst_dispatch(fi, V5X_L1FSM_E_TIMEOUT, NULL); return 0; } static void l1_fsm_le1_normal(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data) { switch (event) { case V5X_L1FSM_E_NORMAL: /* ignore */ break; case V5X_L1FSM_E_LOS: /* Start timer */ osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE2_LOCALLY_DET_FAIL, TIMEOUT, 0); /* send MPH-EIa */ mph_rcv(fi, MPH_EIa); break; case V5X_L1FSM_E_RAI: /* Start timer */ osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE3_REMOTELY_DET_FAIL, TIMEOUT, 0); /* send MPH-EIb */ mph_rcv(fi, MPH_EIb); break; case V5X_L1FSM_E_AIS: /* Start timer */ osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE2_LOCALLY_DET_FAIL, TIMEOUT, 0); /* send MPH-EIc */ mph_rcv(fi, MPH_EIc); break; case V5X_L1FSM_E_INTERNAL_F: osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE4_INTERNAL_FAIL, 0, 0); /* send MPH-DI */ mph_rcv(fi, MPH_DI); /* send MPH-EId */ mph_rcv(fi, MPH_EId); break; case V5X_L1FSM_E_TIMEOUT: /* send MPH-AI */ mph_rcv(fi, MPH_AI); break; case V5X_L1FSM_E_MPH_ID: osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE51_LINK_ID_SENDING, 0, 0); /* send Sa7 = ZERO */ signal_snd(fi, L1_SIGNAL_SA7_0); break; case V5X_L1FSM_E_MPH_NOR: /* ignore */ break; case V5X_L1FSM_E_NORMAL_Sa7: osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE52_LINK_ID_RECEIVED, 0, 0); break; case V5X_L1FSM_E_MPH_IDR: /* send MPH-EIg */ mph_rcv(fi, MPH_EIg); break; default: OSMO_ASSERT(0); } } static void l1_fsm_le2_locally_det_fail(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data) { switch (event) { case V5X_L1FSM_E_NORMAL: /* Start timer */ osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE1_NORMAL, TIMEOUT, 0); break; case V5X_L1FSM_E_LOS: /* send MPH-EIa */ mph_rcv(fi, MPH_EIa); break; case V5X_L1FSM_E_RAI: osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE3_REMOTELY_DET_FAIL, 0, 0); /* send MPH-EIdr */ mph_rcv(fi, MPH_EIdr); /* send MPH-EIb */ mph_rcv(fi, MPH_EIb); break; case V5X_L1FSM_E_AIS: /* send MPH-EIc */ mph_rcv(fi, MPH_EIc); break; case V5X_L1FSM_E_INTERNAL_F: osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE4_INTERNAL_FAIL, 0, 0); /* send MPH-DI */ mph_rcv(fi, MPH_DI); /* send MPH-EId */ mph_rcv(fi, MPH_EId); break; case V5X_L1FSM_E_TIMEOUT: /* send MPH-DI */ mph_rcv(fi, MPH_DI); break; case V5X_L1FSM_E_MPH_ID: /* send MPH-DI */ mph_rcv(fi, MPH_DI); break; case V5X_L1FSM_E_MPH_NOR: /* send MPH-DI */ mph_rcv(fi, MPH_DI); break; case V5X_L1FSM_E_NORMAL_Sa7: /* Start timer */ osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE52_LINK_ID_RECEIVED, TIMEOUT, 0); break; case V5X_L1FSM_E_MPH_IDR: /* send MPH-DI */ mph_rcv(fi, MPH_DI); break; default: OSMO_ASSERT(0); } } static void l1_fsm_le3_remotely_det_fail(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data) { switch (event) { case V5X_L1FSM_E_NORMAL: /* Start timer */ osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE1_NORMAL, TIMEOUT, 0); break; case V5X_L1FSM_E_LOS: osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE2_LOCALLY_DET_FAIL, 0, 0); /* send MPH-EIa */ mph_rcv(fi, MPH_EIa); /* send MPH-EIbr */ mph_rcv(fi, MPH_EIbr); break; case V5X_L1FSM_E_RAI: /* ignore */ break; case V5X_L1FSM_E_AIS: osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE2_LOCALLY_DET_FAIL, 0, 0); /* send MPH-EIc */ mph_rcv(fi, MPH_EIc); /* send MPH-EIbr */ mph_rcv(fi, MPH_EIbr); break; case V5X_L1FSM_E_INTERNAL_F: osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE4_INTERNAL_FAIL, 0, 0); /* send MPH-DI */ mph_rcv(fi, MPH_DI); /* send MPH-EId */ mph_rcv(fi, MPH_EId); break; case V5X_L1FSM_E_TIMEOUT: /* send MPH-DI */ mph_rcv(fi, MPH_DI); break; case V5X_L1FSM_E_MPH_ID: /* send MPH-DI */ mph_rcv(fi, MPH_DI); break; case V5X_L1FSM_E_MPH_NOR: /* send MPH-DI */ mph_rcv(fi, MPH_DI); break; case V5X_L1FSM_E_NORMAL_Sa7: /* Start timer */ osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE52_LINK_ID_RECEIVED, TIMEOUT, 0); break; case V5X_L1FSM_E_MPH_IDR: /* send MPH-DI */ mph_rcv(fi, MPH_DI); break; default: OSMO_ASSERT(0); } } static void l1_fsm_le4_internal_fail(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data) { struct v5x_link *v5l = fi->priv; struct v5x_l1_proto *l1 = v5l->l1; switch (event) { case V5X_L1FSM_E_NORMAL: /* ignore */ break; case V5X_L1FSM_E_LOS: /* send MPH-EIa */ mph_rcv(fi, MPH_EIa); break; case V5X_L1FSM_E_RAI: /* ignore */ break; case V5X_L1FSM_E_AIS: /* send MPH-EIc */ mph_rcv(fi, MPH_EIc); break; case V5X_L1FSM_E_INTERNAL_F: /* ignore */ break; case V5X_L1FSM_E_INTERNAL_D: osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE3_REMOTELY_DET_FAIL, 0, 0); /* send MPH-EIbr */ mph_rcv(fi, MPH_EIbr); /* Go on to normal state, when no alarm persists. * We must do this, because there is no frame reception to trigger this. */ if (!l1->los && !l1->rai && !l1->ais) { if (l1->sa7 == 0) osmo_fsm_inst_dispatch(fi, V5X_L1FSM_E_NORMAL_Sa7, NULL); else osmo_fsm_inst_dispatch(fi, V5X_L1FSM_E_NORMAL, NULL); } break; case V5X_L1FSM_E_TIMEOUT: /* ignore */ break; case V5X_L1FSM_E_MPH_ID: /* send MPH-DI */ mph_rcv(fi, MPH_DI); break; case V5X_L1FSM_E_MPH_NOR: /* send MPH-DI */ mph_rcv(fi, MPH_DI); break; case V5X_L1FSM_E_NORMAL_Sa7: /* ignore */ break; case V5X_L1FSM_E_MPH_IDR: /* send MPH-DI */ mph_rcv(fi, MPH_DI); break; default: OSMO_ASSERT(0); } } static void l1_fsm_le51_link_id_sending(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data) { switch (event) { case V5X_L1FSM_E_NORMAL: /* ignore */ break; case V5X_L1FSM_E_LOS: /* Start timer */ osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE2_LOCALLY_DET_FAIL, TIMEOUT, 0); /* send Sa7 = ONE */ signal_snd(fi, L1_SIGNAL_SA7_1); /* send MPH-EIa */ mph_rcv(fi, MPH_EIa); break; case V5X_L1FSM_E_RAI: /* Start timer */ osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE3_REMOTELY_DET_FAIL, TIMEOUT, 0); /* send Sa7 = ONE */ signal_snd(fi, L1_SIGNAL_SA7_1); /* send MPH-EIb */ mph_rcv(fi, MPH_EIb); break; case V5X_L1FSM_E_AIS: /* Start timer */ osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE2_LOCALLY_DET_FAIL, TIMEOUT, 0); /* send Sa7 = ONE */ signal_snd(fi, L1_SIGNAL_SA7_1); /* send MPH-EIc */ mph_rcv(fi, MPH_EIc); break; case V5X_L1FSM_E_INTERNAL_F: osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE4_INTERNAL_FAIL, 0, 0); /* send Sa7 = ONE */ signal_snd(fi, L1_SIGNAL_SA7_1); /* send MPH-DI */ mph_rcv(fi, MPH_DI); /* send MPH-EId */ mph_rcv(fi, MPH_EId); break; case V5X_L1FSM_E_MPH_ID: /* ignore */ break; case V5X_L1FSM_E_MPH_NOR: osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE1_NORMAL, 0, 0); /* send Sa7 = ONE */ signal_snd(fi, L1_SIGNAL_SA7_1); break; case V5X_L1FSM_E_NORMAL_Sa7: /* ignore */ break; default: OSMO_ASSERT(0); } } static void l1_fsm_le52_link_id_received(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data) { switch (event) { case V5X_L1FSM_E_NORMAL: break; case V5X_L1FSM_E_LOS: /* Start timer */ osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE2_LOCALLY_DET_FAIL, TIMEOUT, 0); /* send MPH-EIa */ mph_rcv(fi, MPH_EIa); break; case V5X_L1FSM_E_RAI: /* Start timer */ osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE3_REMOTELY_DET_FAIL, TIMEOUT, 0); /* send MPH-EIb */ mph_rcv(fi, MPH_EIb); break; case V5X_L1FSM_E_AIS: /* Start timer */ osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE2_LOCALLY_DET_FAIL, TIMEOUT, 0); /* send MPH-EIc */ mph_rcv(fi, MPH_EIc); break; case V5X_L1FSM_E_INTERNAL_F: osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE4_INTERNAL_FAIL, 0, 0); /* send MPH-DI */ mph_rcv(fi, MPH_DI); /* send MPH-EId */ mph_rcv(fi, MPH_EId); break; case V5X_L1FSM_E_TIMEOUT: /* send MPH-DI */ mph_rcv(fi, MPH_AI); break; case V5X_L1FSM_E_MPH_ID: osmo_fsm_inst_state_chg(fi, V5X_L1FSM_S_ANLE51_LINK_ID_SENDING, 0, 0); /* send Sa7 = ZERO */ signal_snd(fi, L1_SIGNAL_SA7_0); break; case V5X_L1FSM_E_NORMAL_Sa7: /* ignore */ break; case V5X_L1FSM_E_MPH_IDR: /* send MPH-IDI */ mph_rcv(fi, MPH_IDI); break; default: OSMO_ASSERT(0); } } /* Table 12/G.965 */ static const struct osmo_fsm_state v5x_l1_fsm_states[] = { [V5X_L1FSM_S_ANLE1_NORMAL] = { .name = "AN/LE1 Normal", .in_event_mask = S(V5X_L1FSM_E_NORMAL) | S(V5X_L1FSM_E_LOS) | S(V5X_L1FSM_E_RAI) | S(V5X_L1FSM_E_AIS) | S(V5X_L1FSM_E_INTERNAL_F) | S(V5X_L1FSM_E_TIMEOUT) | S(V5X_L1FSM_E_MPH_ID) | S(V5X_L1FSM_E_MPH_NOR) | S(V5X_L1FSM_E_NORMAL_Sa7) | S(V5X_L1FSM_E_MPH_IDR), .out_state_mask = S(V5X_L1FSM_S_ANLE2_LOCALLY_DET_FAIL) | S(V5X_L1FSM_S_ANLE3_REMOTELY_DET_FAIL) | S(V5X_L1FSM_S_ANLE4_INTERNAL_FAIL) | S(V5X_L1FSM_S_ANLE51_LINK_ID_SENDING) | S(V5X_L1FSM_S_ANLE52_LINK_ID_RECEIVED), .action = l1_fsm_le1_normal, }, [V5X_L1FSM_S_ANLE2_LOCALLY_DET_FAIL] = { .name = "AN/LE2 Locally detected failure", .in_event_mask = S(V5X_L1FSM_E_NORMAL) | S(V5X_L1FSM_E_LOS) | S(V5X_L1FSM_E_RAI) | S(V5X_L1FSM_E_AIS) | S(V5X_L1FSM_E_INTERNAL_F) | S(V5X_L1FSM_E_TIMEOUT) | S(V5X_L1FSM_E_MPH_ID) | S(V5X_L1FSM_E_MPH_NOR) | S(V5X_L1FSM_E_NORMAL_Sa7) | S(V5X_L1FSM_E_MPH_IDR), .out_state_mask = S(V5X_L1FSM_S_ANLE1_NORMAL) | S(V5X_L1FSM_S_ANLE3_REMOTELY_DET_FAIL) | S(V5X_L1FSM_S_ANLE4_INTERNAL_FAIL) | S(V5X_L1FSM_S_ANLE52_LINK_ID_RECEIVED), .action = l1_fsm_le2_locally_det_fail, }, [V5X_L1FSM_S_ANLE3_REMOTELY_DET_FAIL] = { .name = "AN/LE3 Remotely detected failure", .in_event_mask = S(V5X_L1FSM_E_NORMAL) | S(V5X_L1FSM_E_LOS) | S(V5X_L1FSM_E_RAI) | S(V5X_L1FSM_E_AIS) | S(V5X_L1FSM_E_INTERNAL_F) | S(V5X_L1FSM_E_TIMEOUT) | S(V5X_L1FSM_E_MPH_ID) | S(V5X_L1FSM_E_MPH_NOR) | S(V5X_L1FSM_E_NORMAL_Sa7) | S(V5X_L1FSM_E_MPH_IDR), .out_state_mask = S(V5X_L1FSM_S_ANLE1_NORMAL) | S(V5X_L1FSM_S_ANLE2_LOCALLY_DET_FAIL) | S(V5X_L1FSM_S_ANLE4_INTERNAL_FAIL) | S(V5X_L1FSM_S_ANLE52_LINK_ID_RECEIVED), .action = l1_fsm_le3_remotely_det_fail, }, [V5X_L1FSM_S_ANLE4_INTERNAL_FAIL] = { .name = "AN/LE4 Internal failure", .in_event_mask = S(V5X_L1FSM_E_NORMAL) | S(V5X_L1FSM_E_LOS) | S(V5X_L1FSM_E_RAI) | S(V5X_L1FSM_E_AIS) | S(V5X_L1FSM_E_INTERNAL_F) | S(V5X_L1FSM_E_INTERNAL_D) | S(V5X_L1FSM_E_TIMEOUT) | S(V5X_L1FSM_E_MPH_ID) | S(V5X_L1FSM_E_MPH_NOR) | S(V5X_L1FSM_E_NORMAL_Sa7) | S(V5X_L1FSM_E_MPH_IDR), .out_state_mask = S(V5X_L1FSM_S_ANLE3_REMOTELY_DET_FAIL), .action = l1_fsm_le4_internal_fail, }, [V5X_L1FSM_S_ANLE51_LINK_ID_SENDING] = { .name = "AN/LE5.1 Link ID sending", .in_event_mask = S(V5X_L1FSM_E_NORMAL) | S(V5X_L1FSM_E_LOS) | S(V5X_L1FSM_E_RAI) | S(V5X_L1FSM_E_AIS) | S(V5X_L1FSM_E_INTERNAL_F) | S(V5X_L1FSM_E_MPH_ID) | S(V5X_L1FSM_E_MPH_NOR) | S(V5X_L1FSM_E_NORMAL_Sa7), .out_state_mask = S(V5X_L1FSM_S_ANLE1_NORMAL) | S(V5X_L1FSM_S_ANLE2_LOCALLY_DET_FAIL) | S(V5X_L1FSM_S_ANLE3_REMOTELY_DET_FAIL) | S(V5X_L1FSM_S_ANLE4_INTERNAL_FAIL), .action = l1_fsm_le51_link_id_sending, }, [V5X_L1FSM_S_ANLE52_LINK_ID_RECEIVED] = { .name = "AN/LE5.2 Link ID received", .in_event_mask = S(V5X_L1FSM_E_NORMAL) | S(V5X_L1FSM_E_LOS) | S(V5X_L1FSM_E_RAI) | S(V5X_L1FSM_E_AIS) | S(V5X_L1FSM_E_INTERNAL_F) | S(V5X_L1FSM_E_TIMEOUT) | S(V5X_L1FSM_E_MPH_ID) | S(V5X_L1FSM_E_NORMAL_Sa7) | S(V5X_L1FSM_E_MPH_IDR), .out_state_mask = S(V5X_L1FSM_S_ANLE1_NORMAL) | S(V5X_L1FSM_S_ANLE2_LOCALLY_DET_FAIL) | S(V5X_L1FSM_S_ANLE3_REMOTELY_DET_FAIL) | S(V5X_L1FSM_S_ANLE4_INTERNAL_FAIL) | S(V5X_L1FSM_S_ANLE51_LINK_ID_SENDING), .action = l1_fsm_le52_link_id_received, }, }; struct osmo_fsm v5x_l1_fsm = { .name = "V5X_L1", .states = v5x_l1_fsm_states, .num_states = ARRAY_SIZE(v5x_l1_fsm_states), .timer_cb = v5x_l1_fsm_timer_cb, .log_subsys = DV5L1, .event_names = v5x_l1_fsm_event_names, }; struct v5x_l1_proto *v5x_l1_fsm_create(void *ctx, struct v5x_link *v5l, uint8_t id) { struct v5x_l1_proto *l1; OSMO_ASSERT(v5l); l1 = talloc_zero(ctx, struct v5x_l1_proto); if (!l1) return NULL; l1->v5l = v5l; l1->sa7 = -1; l1->fi = osmo_fsm_inst_alloc(&v5x_l1_fsm, l1, l1, LOGL_DEBUG, NULL); if (!l1->fi) return NULL; osmo_fsm_inst_update_id_f(l1->fi, "%s-L%u", v5x_interface_name(v5l->interface), id); return l1; } void v5x_l1_fsm_destroy(struct v5x_l1_proto *l1) { if (l1->fi) { osmo_fsm_inst_free(l1->fi); } talloc_free(l1); } const char *v5x_l1_fsm_state_name(struct v5x_l1_proto *l1) { return v5x_l1_fsm_states[l1->fi->state].name; } void v5x_l1_init(void) { int rc; rc = osmo_fsm_register(&v5x_l1_fsm); OSMO_ASSERT(!rc); LOGP(DV5L1, LOGL_NOTICE, "Using V5x L1 protocol\n"); } bool v5x_l1_is_up(struct v5x_l1_proto *l1) { return l1->fi->state == V5X_L1FSM_S_ANLE1_NORMAL || l1->fi->state == V5X_L1FSM_S_ANLE51_LINK_ID_SENDING || l1->fi->state == V5X_L1FSM_S_ANLE52_LINK_ID_RECEIVED; } /*********************************************************************** * Messages from other layers ***********************************************************************/ /* receive primitive from physical E1 layer */ int v5x_l1_signal_rcv(struct v5x_link *v5l, enum l1_signal_prim prim) { struct v5x_l1_proto *l1 = v5l->l1; struct v5x_interface *v5if = v5l->interface; struct osmo_fsm_inst *fi = l1->fi; int state_changed = 0; switch (prim) { case L1_SIGNAL_LOS: LOGV5L(v5l, DV5L1, LOGL_DEBUG, "Signal LOS detected on link %d of interface %d.\n", v5l->id, v5if->id); if (!l1->los) { l1->los = 1; osmo_fsm_inst_dispatch(fi, V5X_L1FSM_E_LOS, NULL); } break; case L1_SIGNAL_NO_LOS: LOGV5L(v5l, DV5L1, LOGL_DEBUG, "Signal LOS gone on link %d of interface %d.\n", v5l->id, v5if->id); if (l1->los) { l1->los = 0; state_changed = 1; } break; case L1_SIGNAL_RAI: LOGV5L(v5l, DV5L1, LOGL_DEBUG, "Signal RAI detected on link %d of interface %d.\n", v5l->id, v5if->id); if (!l1->rai) { l1->rai = 1; osmo_fsm_inst_dispatch(fi, V5X_L1FSM_E_RAI, NULL); } break; case L1_SIGNAL_NO_RAI: LOGV5L(v5l, DV5L1, LOGL_DEBUG, "Signal RAI gone on link %d of interface %d.\n", v5l->id, v5if->id); if (l1->rai) { l1->rai = 0; state_changed = 1; } break; case L1_SIGNAL_AIS: LOGV5L(v5l, DV5L1, LOGL_DEBUG, "Signal AIS detected on link %d of interface %d.\n", v5l->id, v5if->id); if (!l1->ais) { l1->ais = 1; osmo_fsm_inst_dispatch(fi, V5X_L1FSM_E_AIS, NULL); } break; case L1_SIGNAL_NO_AIS: LOGV5L(v5l, DV5L1, LOGL_DEBUG, "Signal AIS gone on link %d of interface %d.\n", v5l->id, v5if->id); if (l1->ais) { l1->ais = 0; state_changed = 1; } break; case L1_SIGNAL_SA7_0: if (l1->sa7 != 0) { LOGV5L(v5l, DV5L1, LOGL_DEBUG, "Bit Sa7=0 on link %d of interface %d.\n", v5l->id, v5if->id); l1->sa7 = 0; state_changed = 1; } break; case L1_SIGNAL_SA7_1: if (l1->sa7 != 1) { LOGV5L(v5l, DV5L1, LOGL_DEBUG, "Bit Sa7=1 on link %d of interface %d.\n", v5l->id, v5if->id); l1->sa7 = 1; state_changed = 1; } break; default: LOGV5L(v5l, DV5L1, LOGL_NOTICE, "Invalid L1 primitive %d receied from physical layer.\n", prim); return -EINVAL; } /* Go on to normal state, when no alarm persists. * We must do this, because there is no frame reception to trigger this. */ if (state_changed && !l1->los && !l1->rai && !l1->ais) { if (l1->sa7 == 0) osmo_fsm_inst_dispatch(fi, V5X_L1FSM_E_NORMAL_Sa7, NULL); else osmo_fsm_inst_dispatch(fi, V5X_L1FSM_E_NORMAL, NULL); } return 0; } /* receive message from upper (LCP) layer */ void v5x_l1_mph_snd(struct v5x_link *v5l, enum v5x_mph_prim prim) { struct v5x_l1_proto *l1 = v5l->l1; enum v5x_l1_fsm_event event; switch (prim) { case MPH_ID: event = V5X_L1FSM_E_MPH_ID; break; case MPH_NOR: event = V5X_L1FSM_E_MPH_NOR; break; case MPH_IDR: event = V5X_L1FSM_E_MPH_IDR; break; case MPH_stop: // FIXME: do we need this? break; case MPH_proceed: // FIXME: do we need this? break; default: LOGV5L(v5l, DV5PORT, LOGL_NOTICE, "Got invalid prim %d at this protocol\n", prim); return; } /* send event to FSM */ osmo_fsm_inst_dispatch(l1->fi, event, NULL); }