/* ITu-T G.965 Section 16.2 V5.2-interface Link control FSM - LE side */ /* (C) 2021 by Harald Welte * (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 #include "v5x_internal.h" #include "v5x_protocol.h" #include "layer1.h" #include "v5x_l1_fsm.h" #include "v5x_le_ctrl_fsm.h" #include "v5x_le_management.h" #include "v52_le_lcp_fsm.h" #include "logging.h" #define S(x) (1 << (x)) /***********************************************************************/ /* state names, event names, primitives, ... */ /***********************************************************************/ /* 16.2.4.2.2 */ enum v52_lcp_fsm_state { V52_LCPFSM_S_LE01_NOP_LINK_FAILURE, V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, V52_LCPFSM_S_LE11_NOP_LOCAL_LINK_UNBLOCK, V52_LCPFSM_S_LE12_NOP_REMOTE_LINK_UNBLOCK, V52_LCPFSM_S_LE20_OP_OPERATIONAL, V52_LCPFSM_S_LE21_OP_REMOTE_LINK_ID, V52_LCPFSM_S_LE22_OP_LOCAL_LINK_ID, }; enum v52_lcp_fsm_event { V52_LCPFSM_E_MPH_AI, /* Activate Indication (L1 link operational) */ V52_LCPFSM_E_MPH_DI, /* Deactivate Indication (L1 link not operational) */ V52_LCPFSM_E_MDU_IDReq, /* Identification Request */ V52_LCPFSM_E_FE_IDAck, V52_LCPFSM_E_MPH_IDI, /* Identification Indication */ V52_LCPFSM_E_MPH_EIg, /* Identification Failure */ V52_LCPFSM_E_FE_IDReq, V52_LCPFSM_E_MDU_IDAck, /* Send Link ID ACK */ V52_LCPFSM_E_FE_IDRel, V52_LCPFSM_E_MDU_IDRej, /* Link ID Reject */ V52_LCPFSM_E_FE_IDRej, V52_LCPFSM_E_MDU_LUBR, /* Link Unblock Request */ V52_LCPFSM_E_MDU_LBI, /* Link Block Indication */ V52_LCPFSM_E_FE302, /* AN Initiated unblocking */ V52_LCPFSM_E_FE304, /* AN initiated link block */ V52_LCPFSM_E_FE305, /* deferred link block request */ V52_LCPFSM_E_FE306, /* non-deferred link block request */ }; static const struct value_string v52_lcp_fsm_event_names[] = { { V52_LCPFSM_E_MPH_AI, "MPH-AI" }, { V52_LCPFSM_E_MPH_DI, "MPH-DI" }, { V52_LCPFSM_E_MDU_IDReq, "MDU-IDReq" }, { V52_LCPFSM_E_FE_IDAck, "FE-IDAck" }, { V52_LCPFSM_E_MPH_IDI, "MPH-IDI" }, { V52_LCPFSM_E_MPH_EIg, "MPH-EIg" }, { V52_LCPFSM_E_FE_IDReq, "FE-IDReq" }, { V52_LCPFSM_E_MDU_IDAck, "MDU-IDack" }, { V52_LCPFSM_E_FE_IDRel, "FE-IDRel" }, { V52_LCPFSM_E_MDU_IDRej, "MDU-IDRej" }, { V52_LCPFSM_E_FE_IDRej, "FE-IDRej" }, { V52_LCPFSM_E_MDU_LUBR, "MDU-LUBR" }, { V52_LCPFSM_E_MDU_LBI, "MDU-LBI" }, { V52_LCPFSM_E_FE302, "FE302" }, { V52_LCPFSM_E_FE304, "FE304" }, { V52_LCPFSM_E_FE305, "FE305" }, { V52_LCPFSM_E_FE306, "FE306" }, { 0, NULL } }; /***********************************************************************/ /* Messages to other layers */ /***********************************************************************/ /* send message to upper (management) layer */ static void rcv_mdu(struct osmo_fsm_inst *fi, enum v5x_mgmt_prim prim) { struct v5x_link *v5l = fi->priv; v52_le_lcp_mdu_rcv(v5l, prim); } /* send message to lower (L1 FSM) layer */ static void snd_mph(struct osmo_fsm_inst *fi, enum v5x_mph_prim prim) { struct v5x_link *v5l = fi->priv; v5x_l1_mph_snd(v5l, prim); } /* send message to lower (CTRL) layer */ void snd_fe(struct osmo_fsm_inst *fi, enum v52_link_ctrl_func lcf) { struct v5x_link *v5l = fi->priv; v52_le_ctrl_link_snd(v5l, lcf); } /***********************************************************************/ /* LCP state FSM */ /***********************************************************************/ static void lcp_fsm_le01_link_failure(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data) { switch (event) { case V52_LCPFSM_E_MPH_AI: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0); /* Send MDU-LAI */ rcv_mdu(fi, MDU_LAI); break; case V52_LCPFSM_E_MPH_DI: /* ignore */ break; case V52_LCPFSM_E_MDU_IDReq: /* Send MDU-DI */ rcv_mdu(fi, MDU_DI); break; case V52_LCPFSM_E_FE_IDReq: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED, 0, 0); /* Send FE303 */ snd_fe(fi, V52_LCP_FE_303_304_LINK_BLOCK); break; case V52_LCPFSM_E_FE_IDRel: case V52_LCPFSM_E_FE_IDRej: /* ignore */ break; case V52_LCPFSM_E_MDU_LUBR: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED, 0, 0); /* Send FE303 */ snd_fe(fi, V52_LCP_FE_303_304_LINK_BLOCK); /* Send MDU-DI */ rcv_mdu(fi, MDU_DI); break; case V52_LCPFSM_E_MDU_LBI: case V52_LCPFSM_E_FE302: case V52_LCPFSM_E_FE305: case V52_LCPFSM_E_FE306: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED, 0, 0); /* Send FE303 */ snd_fe(fi, V52_LCP_FE_303_304_LINK_BLOCK); break; case V52_LCPFSM_E_FE304: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED, 0, 0); break; default: OSMO_ASSERT(0); } } static void lcp_fsm_le02_link_failure_blocked(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data) { switch (event) { case V52_LCPFSM_E_MPH_AI: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0); /* Send MDU-LBI */ rcv_mdu(fi, MDU_LBI); /* Send MDU-LAI */ rcv_mdu(fi, MDU_LAI); break; case V52_LCPFSM_E_MPH_DI: /* ignore */ break; case V52_LCPFSM_E_MDU_IDReq: /* Send MDU-DI */ rcv_mdu(fi, MDU_DI); break; case V52_LCPFSM_E_FE_IDReq: /* Send FE303 */ snd_fe(fi, V52_LCP_FE_303_304_LINK_BLOCK); break; case V52_LCPFSM_E_MDU_LUBR: /* Send FE303 */ snd_fe(fi, V52_LCP_FE_303_304_LINK_BLOCK); /* Send MDU-DI */ rcv_mdu(fi, MDU_DI); break; case V52_LCPFSM_E_MDU_LBI: case V52_LCPFSM_E_FE302: case V52_LCPFSM_E_FE305: case V52_LCPFSM_E_FE306: /* Send FE303 */ snd_fe(fi, V52_LCP_FE_303_304_LINK_BLOCK); break; case V52_LCPFSM_E_FE304: /* ignore */ break; default: OSMO_ASSERT(0); } } static void lcp_fsm_le10_link_blocked(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data) { switch (event) { case V52_LCPFSM_E_MPH_AI: /* Send MDU-LAI */ rcv_mdu(fi, MDU_LAI); break; case V52_LCPFSM_E_MPH_DI: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED, 0, 0); /* Send MDU-DI */ rcv_mdu(fi, MDU_DI); break; case V52_LCPFSM_E_MDU_IDReq: /* Send MDU-LBI */ rcv_mdu(fi, MDU_LBI); break; case V52_LCPFSM_E_FE_IDReq: /* Send FE303 */ snd_fe(fi, V52_LCP_FE_303_304_LINK_BLOCK); break; case V52_LCPFSM_E_FE_IDRel: case V52_LCPFSM_E_FE_IDRej: /* ignore */ break; case V52_LCPFSM_E_MDU_LUBR: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE11_NOP_LOCAL_LINK_UNBLOCK, 0, 0); /* Send FE301 */ snd_fe(fi, V52_LCP_FE_301_302_LINK_UNBLOCK); break; case V52_LCPFSM_E_MDU_LBI: case V52_LCPFSM_E_FE305: case V52_LCPFSM_E_FE306: /* Send FE303 */ snd_fe(fi, V52_LCP_FE_303_304_LINK_BLOCK); break; case V52_LCPFSM_E_FE302: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE12_NOP_REMOTE_LINK_UNBLOCK, 0, 0); /* Send MDL-LUBR */ rcv_mdu(fi, MDU_LUBR); break; case V52_LCPFSM_E_FE304: /* ignore */ break; default: OSMO_ASSERT(0); } } static void lcp_fsm_le11_local_link_unblock(struct osmo_fsm_inst *fi, uint32_t event, __attribute__((unused)) void *data) { switch (event) { case V52_LCPFSM_E_MPH_AI: /* ignore */ break; case V52_LCPFSM_E_MPH_DI: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED, 0, 0); /* Send MDU-DI */ rcv_mdu(fi, MDU_DI); break; case V52_LCPFSM_E_MDU_IDReq: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0); /* Send MDU-LBI */ rcv_mdu(fi, MDU_LBI); break; case V52_LCPFSM_E_FE_IDReq: /* Send FE-IDRej */ snd_fe(fi, V52_LCP_FE_IDRej); break; case V52_LCPFSM_E_FE_IDRel: case V52_LCPFSM_E_FE_IDRej: /* ignore */ break; case V52_LCPFSM_E_MDU_LUBR: /* Send FE301 */ snd_fe(fi, V52_LCP_FE_301_302_LINK_UNBLOCK); break; case V52_LCPFSM_E_MDU_LBI: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0); /* Send FE303 */ snd_fe(fi, V52_LCP_FE_303_304_LINK_BLOCK); break; case V52_LCPFSM_E_FE302: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0); /* Send MDU-LUBI */ rcv_mdu(fi, MDU_LUBI); break; case V52_LCPFSM_E_FE304: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0); /* Send MDU-LBI */ rcv_mdu(fi, MDU_LBI); break; case V52_LCPFSM_E_FE305: case V52_LCPFSM_E_FE306: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0); /* Send FE303 */ snd_fe(fi, V52_LCP_FE_303_304_LINK_BLOCK); /* Send MDU-LBI */ rcv_mdu(fi, MDU_LBI); break; default: OSMO_ASSERT(0); } } static void lcp_fsm_le12_remote_link_unblock(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data) { switch (event) { case V52_LCPFSM_E_MPH_AI: /* ignore */ break; case V52_LCPFSM_E_MPH_DI: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED, 0, 0); /* Send MDU-DI */ rcv_mdu(fi, MDU_DI); break; case V52_LCPFSM_E_MDU_IDReq: /* Send MDU-IDRej */ rcv_mdu(fi, MDU_IDRej); /* Send MDU-LUBR */ rcv_mdu(fi, MDU_LUBR); break; case V52_LCPFSM_E_FE_IDReq: /* Send FE-IDRej */ snd_fe(fi, V52_LCP_FE_IDRej); break; case V52_LCPFSM_E_MDU_LUBR: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0); /* Send FE301 */ snd_fe(fi, V52_LCP_FE_301_302_LINK_UNBLOCK); /* Send MDU-LUBI */ rcv_mdu(fi, MDU_LUBI); break; case V52_LCPFSM_E_MDU_LBI: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0); /* Send FE303 */ snd_fe(fi, V52_LCP_FE_303_304_LINK_BLOCK); break; case V52_LCPFSM_E_FE302: /* Send MDU-LUBR */ rcv_mdu(fi, MDU_LUBR); break; case V52_LCPFSM_E_FE304: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0); /* Send MDU-LBI */ rcv_mdu(fi, MDU_LBI); break; case V52_LCPFSM_E_FE305: case V52_LCPFSM_E_FE306: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0); /* Send FE303 */ snd_fe(fi, V52_LCP_FE_303_304_LINK_BLOCK); /* Send MDU-LBI */ rcv_mdu(fi, MDU_LBI); break; default: OSMO_ASSERT(0); } } static void lcp_fsm_le20_operational(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data) { switch (event) { case V52_LCPFSM_E_MPH_AI: /* ignore */ break; case V52_LCPFSM_E_MPH_DI: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE01_NOP_LINK_FAILURE, 0, 0); /* Send MDU-DI */ rcv_mdu(fi, MDU_DI); break; case V52_LCPFSM_E_MDU_IDReq: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE22_OP_LOCAL_LINK_ID, 0, 0); /* Send FE-IDReq */ snd_fe(fi, V52_LCP_FE_IDReq); break; case V52_LCPFSM_E_FE_IDReq: /* Send MDU-IDReq */ rcv_mdu(fi, MDU_IDReq); break; case V52_LCPFSM_E_MDU_IDAck: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE21_OP_REMOTE_LINK_ID, 0, 0); /* Send FE-IDAck */ snd_fe(fi, V52_LCP_FE_IDAck); /* Send MPH-ID */ snd_mph(fi, MPH_ID); break; case V52_LCPFSM_E_MDU_IDRej: /* Send FE-IDRej */ snd_fe(fi, V52_LCP_FE_IDRej); break; case V52_LCPFSM_E_MDU_LUBR: /* Send FE301 */ snd_fe(fi, V52_LCP_FE_301_302_LINK_UNBLOCK); break; case V52_LCPFSM_E_MDU_LBI: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0); /* Send FE303 */ snd_fe(fi, V52_LCP_FE_303_304_LINK_BLOCK); break; case V52_LCPFSM_E_FE302: /* Send MDU-LUBI */ rcv_mdu(fi, MDU_LUBI); break; case V52_LCPFSM_E_FE304: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0); /* Send MDU-LBI */ rcv_mdu(fi, MDU_LBI); break; case V52_LCPFSM_E_FE305: /* Send MDU-LBR */ rcv_mdu(fi, MDU_LBR); break; case V52_LCPFSM_E_FE306: /* Send MDU-LBRN */ rcv_mdu(fi, MDU_LBRN); break; default: OSMO_ASSERT(0); } } static void lcp_fsm_le21_op_remote_link_id(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data) { switch (event) { case V52_LCPFSM_E_MPH_AI: /* ignore */ break; case V52_LCPFSM_E_MPH_DI: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE01_NOP_LINK_FAILURE, 0, 0); /* Send MPH-NOR */ snd_mph(fi, MPH_NOR); /* Send MDU-DI */ rcv_mdu(fi, MDU_DI); break; case V52_LCPFSM_E_MDU_IDReq: /* Send MDU-IDRej */ rcv_mdu(fi, MDU_IDRej); break; case V52_LCPFSM_E_FE_IDReq: /* ignore */ break; case V52_LCPFSM_E_MDU_IDAck: /* ignore */ break; case V52_LCPFSM_E_FE_IDRel: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0); /* Send MDU-IDRel */ rcv_mdu(fi, MDU_IDRel); /* Send MPH-NOR */ snd_mph(fi, MPH_NOR); break; case V52_LCPFSM_E_MDU_IDRej: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0); /* Send FE-IDRej */ rcv_mdu(fi, MDU_IDRej); /* Send MPH-NOR */ snd_mph(fi, MPH_NOR); break; case V52_LCPFSM_E_MDU_LUBR: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0); /* Send FE301 */ snd_fe(fi, V52_LCP_FE_301_302_LINK_UNBLOCK); /* Send MPH-NOR */ snd_mph(fi, MPH_NOR); break; case V52_LCPFSM_E_MDU_LBI: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0); /* Send FE303 */ snd_fe(fi, V52_LCP_FE_303_304_LINK_BLOCK); /* Send MPH-NOR */ snd_mph(fi, MPH_NOR); break; case V52_LCPFSM_E_FE302: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0); /* Send MPH-NOR */ snd_mph(fi, MPH_NOR); /* Send MDU-IDRel */ rcv_mdu(fi, MDU_IDRel); /* Send MDU-LUBI */ rcv_mdu(fi, MDU_LUBI); break; case V52_LCPFSM_E_FE304: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0); /* Send MPH-NOR */ snd_mph(fi, MPH_NOR); /* Send MDU-LBI */ rcv_mdu(fi, MDU_LBI); break; case V52_LCPFSM_E_FE305: /* Send MDU-LBR */ rcv_mdu(fi, MDU_LBR); break; case V52_LCPFSM_E_FE306: /* Send MDU-LBRN */ rcv_mdu(fi, MDU_LBRN); break; default: OSMO_ASSERT(0); } } static void lcp_fsm_le22_op_local_link_id(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data) { switch (event) { case V52_LCPFSM_E_MPH_AI: /* ignore */ break; case V52_LCPFSM_E_MPH_DI: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE01_NOP_LINK_FAILURE, 0, 0); /* Send FE-IDRel */ snd_fe(fi, V52_LCP_FE_IDRel); /* Send MDU-DI */ rcv_mdu(fi, MDU_DI); break; case V52_LCPFSM_E_MDU_IDReq: /* ignore */ break; case V52_LCPFSM_E_FE_IDAck: /* Send MPH-IDR */ snd_mph(fi, MPH_IDR); break; case V52_LCPFSM_E_MPH_IDI: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0); /* Send FE-IDRel */ snd_fe(fi, V52_LCP_FE_IDRel); /* Send MDU-AI */ rcv_mdu(fi, MDU_AI); break; case V52_LCPFSM_E_MPH_EIg: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0); /* Send FE-IDRel */ snd_fe(fi, V52_LCP_FE_IDRel); /* Send MDU-EIg */ rcv_mdu(fi, MDU_EIg); break; case V52_LCPFSM_E_FE_IDReq: /* Send FE-IDRej */ snd_fe(fi, V52_LCP_FE_IDRej); break; case V52_LCPFSM_E_FE_IDRej: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0); /* Send MDU-IDRej */ rcv_mdu(fi, MDU_IDRej); break; case V52_LCPFSM_E_MDU_LUBR: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0); /* Send FE301 */ snd_fe(fi, V52_LCP_FE_301_302_LINK_UNBLOCK); break; case V52_LCPFSM_E_MDU_LBI: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0); /* Send FE303 */ snd_fe(fi, V52_LCP_FE_303_304_LINK_BLOCK); break; case V52_LCPFSM_E_FE302: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0); /* Send MDU-IDRej */ rcv_mdu(fi, MDU_IDRej); /* Send MDU-LUBI */ rcv_mdu(fi, MDU_LUBI); break; case V52_LCPFSM_E_FE304: osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0); /* Send MDU-LBI */ rcv_mdu(fi, MDU_LBI); break; case V52_LCPFSM_E_FE305: /* Send MDU-LBR */ rcv_mdu(fi, MDU_LBR); break; case V52_LCPFSM_E_FE306: /* Send MDU-LBRN */ rcv_mdu(fi, MDU_LBRN); break; default: OSMO_ASSERT(0); } } /* Table 17G.965 */ static const struct osmo_fsm_state v52_le_lcp_fsm_states[] = { [V52_LCPFSM_S_LE01_NOP_LINK_FAILURE] = { .name = "Link failure (LE0.1)", .in_event_mask = S(V52_LCPFSM_E_MPH_AI) | S(V52_LCPFSM_E_MPH_DI) | S(V52_LCPFSM_E_MDU_IDReq) | S(V52_LCPFSM_E_FE_IDReq) | S(V52_LCPFSM_E_FE_IDRel) | S(V52_LCPFSM_E_FE_IDRej) | S(V52_LCPFSM_E_MDU_LUBR) | S(V52_LCPFSM_E_MDU_LBI) | S(V52_LCPFSM_E_FE302) | S(V52_LCPFSM_E_FE304) | S(V52_LCPFSM_E_FE305) | S(V52_LCPFSM_E_FE306), .out_state_mask = S(V52_LCPFSM_S_LE20_OP_OPERATIONAL) | S(V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED), .action = lcp_fsm_le01_link_failure, }, [V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED] = { .name = "Link failure and blocked(LE0.2)", .in_event_mask = S(V52_LCPFSM_E_MPH_AI) | S(V52_LCPFSM_E_MPH_DI) | S(V52_LCPFSM_E_MDU_IDReq) | S(V52_LCPFSM_E_FE_IDReq) | S(V52_LCPFSM_E_MDU_LUBR) | S(V52_LCPFSM_E_MDU_LBI) | S(V52_LCPFSM_E_FE302) | S(V52_LCPFSM_E_FE304) | S(V52_LCPFSM_E_FE305) | S(V52_LCPFSM_E_FE306), .out_state_mask = S(V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED), .action = lcp_fsm_le02_link_failure_blocked, }, [V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED] = { .name = "Link blocked (LE1.0)", .in_event_mask = S(V52_LCPFSM_E_MPH_AI) | S(V52_LCPFSM_E_MPH_DI) | S(V52_LCPFSM_E_MDU_IDReq) | S(V52_LCPFSM_E_FE_IDReq) | S(V52_LCPFSM_E_FE_IDRel) | S(V52_LCPFSM_E_FE_IDRej) | S(V52_LCPFSM_E_MDU_LUBR) | S(V52_LCPFSM_E_MDU_LBI) | S(V52_LCPFSM_E_FE302) | S(V52_LCPFSM_E_FE304) | S(V52_LCPFSM_E_FE305) | S(V52_LCPFSM_E_FE306), .out_state_mask = S(V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED) | S(V52_LCPFSM_S_LE11_NOP_LOCAL_LINK_UNBLOCK) | S(V52_LCPFSM_S_LE12_NOP_REMOTE_LINK_UNBLOCK), .action = lcp_fsm_le10_link_blocked, }, [V52_LCPFSM_S_LE11_NOP_LOCAL_LINK_UNBLOCK] = { .name = "Local link unblock (LE1.1)", .in_event_mask = S(V52_LCPFSM_E_MPH_AI) | S(V52_LCPFSM_E_MPH_DI) | S(V52_LCPFSM_E_MDU_IDReq) | S(V52_LCPFSM_E_FE_IDReq) | S(V52_LCPFSM_E_FE_IDRel) | S(V52_LCPFSM_E_FE_IDRej) | S(V52_LCPFSM_E_MDU_LUBR) | S(V52_LCPFSM_E_MDU_LBI) | S(V52_LCPFSM_E_FE302) | S(V52_LCPFSM_E_FE304) | S(V52_LCPFSM_E_FE305) | S(V52_LCPFSM_E_FE306), .out_state_mask = S(V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED) | S(V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED) | S(V52_LCPFSM_S_LE20_OP_OPERATIONAL), .action = lcp_fsm_le11_local_link_unblock, }, [V52_LCPFSM_S_LE12_NOP_REMOTE_LINK_UNBLOCK] = { .name = "Remote link unblocke (LE1.2)", .in_event_mask = S(V52_LCPFSM_E_MPH_AI) | S(V52_LCPFSM_E_MPH_DI) | S(V52_LCPFSM_E_MDU_IDReq) | S(V52_LCPFSM_E_FE_IDReq) | S(V52_LCPFSM_E_MDU_LUBR) | S(V52_LCPFSM_E_MDU_LBI) | S(V52_LCPFSM_E_FE302) | S(V52_LCPFSM_E_FE304) | S(V52_LCPFSM_E_FE305) | S(V52_LCPFSM_E_FE306), .out_state_mask = S(V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED) | S(V52_LCPFSM_S_LE20_OP_OPERATIONAL) | S(V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED), .action = lcp_fsm_le12_remote_link_unblock, }, [V52_LCPFSM_S_LE20_OP_OPERATIONAL] = { .name = "Link Operational (LE2.0)", .in_event_mask = S(V52_LCPFSM_E_MPH_AI) | S(V52_LCPFSM_E_MPH_DI) | S(V52_LCPFSM_E_MDU_IDReq) | S(V52_LCPFSM_E_FE_IDReq) | S(V52_LCPFSM_E_MDU_IDAck) | S(V52_LCPFSM_E_MDU_IDRej) | S(V52_LCPFSM_E_MDU_LUBR) | S(V52_LCPFSM_E_MDU_LBI) | S(V52_LCPFSM_E_FE302) | S(V52_LCPFSM_E_FE304) | S(V52_LCPFSM_E_FE305) | S(V52_LCPFSM_E_FE306), .out_state_mask = S(V52_LCPFSM_S_LE01_NOP_LINK_FAILURE) | S(V52_LCPFSM_S_LE22_OP_LOCAL_LINK_ID) | S(V52_LCPFSM_S_LE21_OP_REMOTE_LINK_ID) | S(V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED), .action = lcp_fsm_le20_operational, }, [V52_LCPFSM_S_LE21_OP_REMOTE_LINK_ID] = { .name = "Remote link identification (LE2.1)", .in_event_mask = S(V52_LCPFSM_E_MPH_AI) | S(V52_LCPFSM_E_MPH_DI) | S(V52_LCPFSM_E_MDU_IDReq) | S(V52_LCPFSM_E_FE_IDReq) | S(V52_LCPFSM_E_MDU_IDAck) | S(V52_LCPFSM_E_FE_IDRel) | S(V52_LCPFSM_E_MDU_IDRej) | S(V52_LCPFSM_E_MDU_LUBR) | S(V52_LCPFSM_E_MDU_LBI) | S(V52_LCPFSM_E_FE302) | S(V52_LCPFSM_E_FE304) | S(V52_LCPFSM_E_FE305) | S(V52_LCPFSM_E_FE306), .out_state_mask = S(V52_LCPFSM_S_LE01_NOP_LINK_FAILURE) | S(V52_LCPFSM_S_LE20_OP_OPERATIONAL) | S(V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED), .action = lcp_fsm_le21_op_remote_link_id, }, [V52_LCPFSM_S_LE22_OP_LOCAL_LINK_ID] = { .name = "Local link identification (LE2.2)", .in_event_mask = S(V52_LCPFSM_E_MPH_AI) | S(V52_LCPFSM_E_MPH_DI) | S(V52_LCPFSM_E_MDU_IDReq) | S(V52_LCPFSM_E_FE_IDAck) | S(V52_LCPFSM_E_MPH_IDI) | S(V52_LCPFSM_E_MPH_EIg) | S(V52_LCPFSM_E_FE_IDReq) | S(V52_LCPFSM_E_FE_IDRej) | S(V52_LCPFSM_E_MDU_LUBR) | S(V52_LCPFSM_E_MDU_LBI) | S(V52_LCPFSM_E_FE302) | S(V52_LCPFSM_E_FE304) | S(V52_LCPFSM_E_FE305) | S(V52_LCPFSM_E_FE306), .out_state_mask = S(V52_LCPFSM_S_LE01_NOP_LINK_FAILURE) | S(V52_LCPFSM_S_LE20_OP_OPERATIONAL) | S(V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED), .action =lcp_fsm_le22_op_local_link_id, }, }; struct osmo_fsm v52_le_lcp_fsm = { .name = "V52_LE_LCP", .states = v52_le_lcp_fsm_states, .num_states = ARRAY_SIZE(v52_le_lcp_fsm_states), .timer_cb = NULL, .log_subsys = DV5LCP, .event_names = v52_lcp_fsm_event_names, }; struct osmo_fsm_inst *v52_le_lcp_create(void *ctx, struct v5x_link *v5l, uint8_t id) { struct osmo_fsm_inst *fi; OSMO_ASSERT(v5l); fi = osmo_fsm_inst_alloc(&v52_le_lcp_fsm, ctx, v5l, LOGL_DEBUG, NULL); if (!fi) return NULL; osmo_fsm_inst_update_id_f(fi, "%d", id); /* initial state for links that are not in failure state */ fi->state = V52_LCPFSM_S_LE20_OP_OPERATIONAL; return fi; } void v52_le_lcp_destroy(struct osmo_fsm_inst *fi) { if (fi) osmo_fsm_inst_free(fi); } bool v52_le_lcp_is_operational(struct osmo_fsm_inst *fi) { return (fi->state >= V52_LCPFSM_S_LE20_OP_OPERATIONAL); } const char *v52_le_lcp_state_name(struct osmo_fsm_inst *fi) { return v52_le_lcp_fsm_states[fi->state].name; } void v52_le_lcp_init(void) { int rc; rc = osmo_fsm_register(&v52_le_lcp_fsm); OSMO_ASSERT(!rc); LOGP(DV5LCP, LOGL_NOTICE, "Using V52 link control protocol\n"); } /*********************************************************************** * Messages from other layers ***********************************************************************/ /* receive message from lower (L1 FSM) layer */ int v52_le_lcp_mph_rcv(struct v5x_link *v5l, enum v5x_mph_prim prim) { enum v52_lcp_fsm_event event = 9999; switch (prim) { case MPH_AI: LOGP(DV5LCP, LOGL_NOTICE, "Received link activation indication from L1 (link ID %d).\n", v5l->id); event = V52_LCPFSM_E_MPH_AI; break; case MPH_DI: LOGP(DV5LCP, LOGL_NOTICE, "Received link deactivation indication from L1 (link ID %d).\n", v5l->id); event = V52_LCPFSM_E_MPH_DI; break; case MPH_EIa: LOGP(DV5LCP, LOGL_DEBUG, "Received link error indication from L1 (link ID %d): LOS\n", v5l->id); return 0; case MPH_EIb: LOGP(DV5LCP, LOGL_DEBUG, "Received link error indication from L1 (link ID %d): RAI\n", v5l->id); return 0; case MPH_EIc: LOGP(DV5LCP, LOGL_DEBUG, "Received link error indication from L1 (link ID %d): AIS\n", v5l->id); return 0; case MPH_EId: LOGP(DV5LCP, LOGL_DEBUG, "Received link error indication from L1 (link ID %d): Internal failure\n", v5l->id); return 0; case MPH_EIe: LOGP(DV5LCP, LOGL_DEBUG, "Received link error indication from L1 (link ID %d): CRC error\n", v5l->id); return 0; case MPH_EIf: LOGP(DV5LCP, LOGL_DEBUG, "Received link error indication from L1 (link ID %d): " "Remote CRC error indication\n", v5l->id); return 0; case MPH_IDI: LOGP(DV5LCP, LOGL_NOTICE, "Received link identification indication from L1 (link ID %d).\n", v5l->id); event = V52_LCPFSM_E_MPH_IDI; break; case MPH_EIg: LOGP(DV5LCP, LOGL_NOTICE, "Received link identification failure from L1 (link ID %d).\n", v5l->id); event = V52_LCPFSM_E_MPH_EIg; break; case MPH_EIdr: return 0; case MPH_EIbr: return 0; default: LOGP(DV5LCP, LOGL_NOTICE, "Invalid LCP primitive %d receied from L1/AN (link ID %d).\n", prim, v5l->id); return -EINVAL; } OSMO_ASSERT(event < 9999); osmo_fsm_inst_dispatch(v5l->fi, event, NULL); return 0; } /* receive message from lower (CTRL) layer */ int v52_le_lcp_fe_rcv(struct v5x_link *v5l, enum v52_link_ctrl_func lcf) { enum v52_lcp_fsm_event event; switch (lcf) { case V52_LCP_FE_IDReq: event = V52_LCPFSM_E_FE_IDReq; break; case V52_LCP_FE_IDAck: event = V52_LCPFSM_E_FE_IDAck; break; case V52_LCP_FE_IDRel: event = V52_LCPFSM_E_FE_IDRel; break; case V52_LCP_FE_IDRej: event = V52_LCPFSM_E_FE_IDRej; break; case V52_LCP_FE_301_302_LINK_UNBLOCK: event = V52_LCPFSM_E_FE302; break; case V52_LCP_FE_303_304_LINK_BLOCK: event = V52_LCPFSM_E_FE304; break; case V52_LCP_FE_305_DEF_LINK_BLOCK_REQ: event = V52_LCPFSM_E_FE305; break; case V52_LCP_FE_306_NON_DEF_LINK_BLOCK_REQ: event = V52_LCPFSM_E_FE306; break; default: LOGP(DV5LCP, LOGL_NOTICE, "Invalid link control function primitive %d receied from AN.\n", lcf); return -EINVAL; } osmo_fsm_inst_dispatch(v5l->fi, event, NULL); return 0; } int v52_le_lcp_mdu_snd(struct v5x_link *v5l, enum v5x_mgmt_prim prim) { enum v52_lcp_fsm_event event; switch (prim) { case MDU_IDReq: event = V52_LCPFSM_E_MDU_IDReq; break; case MDU_IDAck: event = V52_LCPFSM_E_MDU_IDAck; break; case MDU_IDRej: event = V52_LCPFSM_E_MDU_IDRej; break; case MDU_LUBR: event = V52_LCPFSM_E_MDU_LUBR; break; case MDU_LBI: event = V52_LCPFSM_E_MDU_LBI; break; default: LOGP(DV5LCP, LOGL_NOTICE, "Invalid MDU primitive %d receied from management.\n", prim); return -EINVAL; } osmo_fsm_inst_dispatch(v5l->fi, event, NULL); return 0; }