2021-12-28 10:32:53 +00:00
|
|
|
/* ITU-T G.964 Section 14.3.3 V5.1-intterface Layer 1 FSM - AN and LE */
|
|
|
|
|
2022-12-23 20:50:47 +00:00
|
|
|
/* (C) 2022 by Andreas Eversberg <jolly@eversberg.eu>
|
2021-12-28 10:32:53 +00:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2022-12-23 20:50:47 +00:00
|
|
|
/***********************************************************************/
|
|
|
|
/* internal data structures */
|
|
|
|
/***********************************************************************/
|
|
|
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
#include <osmocom/core/linuxlist.h>
|
|
|
|
#include <osmocom/gsm/tlv.h>
|
2021-12-28 10:32:53 +00:00
|
|
|
|
|
|
|
#include "v5x_internal.h"
|
2022-12-23 20:50:47 +00:00
|
|
|
#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, ... */
|
|
|
|
/***********************************************************************/
|
2021-12-28 10:32:53 +00:00
|
|
|
|
2022-12-16 17:32:31 +00:00
|
|
|
enum v5x_l1_fsm_state {
|
2022-12-23 20:50:47 +00:00
|
|
|
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) {
|
2022-12-28 19:36:31 +00:00
|
|
|
if (l1->sa7 == 0)
|
2022-12-23 20:50:47 +00:00
|
|
|
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,
|
|
|
|
},
|
2021-12-28 10:32:53 +00:00
|
|
|
};
|
|
|
|
|
2022-12-23 20:50:47 +00:00
|
|
|
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;
|
2022-12-28 19:36:31 +00:00
|
|
|
l1->sa7 = -1;
|
2022-12-23 20:50:47 +00:00
|
|
|
|
|
|
|
l1->fi = osmo_fsm_inst_alloc(&v5x_l1_fsm, l1, l1, LOGL_DEBUG, NULL);
|
|
|
|
if (!l1->fi)
|
|
|
|
return NULL;
|
2023-08-13 12:17:40 +00:00
|
|
|
osmo_fsm_inst_update_id_f(l1->fi, "%s-L%u", v5x_interface_name(v5l->interface), id);
|
2022-12-23 20:50:47 +00:00
|
|
|
|
|
|
|
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);
|
2022-12-28 19:36:31 +00:00
|
|
|
LOGP(DV5L1, LOGL_NOTICE, "Using V5x L1 protocol\n");
|
2022-12-23 20:50:47 +00:00
|
|
|
}
|
|
|
|
|
2023-02-21 06:17:58 +00:00
|
|
|
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;
|
|
|
|
}
|
2022-12-23 20:50:47 +00:00
|
|
|
|
|
|
|
/***********************************************************************
|
|
|
|
* 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;
|
2023-04-15 15:50:21 +00:00
|
|
|
struct v5x_interface *v5if = v5l->interface;
|
2022-12-23 20:50:47 +00:00
|
|
|
struct osmo_fsm_inst *fi = l1->fi;
|
|
|
|
int state_changed = 0;
|
|
|
|
|
|
|
|
switch (prim) {
|
|
|
|
case L1_SIGNAL_LOS:
|
2023-04-15 15:50:21 +00:00
|
|
|
LOGP(DV5L1, LOGL_DEBUG, "Signal LOS detected on link %d of interface %d.\n", v5l->id, v5if->id);
|
2022-12-23 20:50:47 +00:00
|
|
|
if (!l1->los) {
|
|
|
|
l1->los = 1;
|
|
|
|
osmo_fsm_inst_dispatch(fi, V5X_L1FSM_E_LOS, NULL);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case L1_SIGNAL_NO_LOS:
|
2023-04-15 15:50:21 +00:00
|
|
|
LOGP(DV5L1, LOGL_DEBUG, "Signal LOS gone on link %d of interface %d.\n", v5l->id, v5if->id);
|
2022-12-23 20:50:47 +00:00
|
|
|
if (l1->los) {
|
|
|
|
l1->los = 0;
|
|
|
|
state_changed = 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case L1_SIGNAL_RAI:
|
2023-04-15 15:50:21 +00:00
|
|
|
LOGP(DV5L1, LOGL_DEBUG, "Signal RAI detected on link %d of interface %d.\n", v5l->id, v5if->id);
|
2022-12-23 20:50:47 +00:00
|
|
|
if (!l1->rai) {
|
|
|
|
l1->rai = 1;
|
|
|
|
osmo_fsm_inst_dispatch(fi, V5X_L1FSM_E_RAI, NULL);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case L1_SIGNAL_NO_RAI:
|
2023-04-15 15:50:21 +00:00
|
|
|
LOGP(DV5L1, LOGL_DEBUG, "Signal RAI gone on link %d of interface %d.\n", v5l->id, v5if->id);
|
2022-12-23 20:50:47 +00:00
|
|
|
if (l1->rai) {
|
|
|
|
l1->rai = 0;
|
|
|
|
state_changed = 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case L1_SIGNAL_AIS:
|
2023-04-15 15:50:21 +00:00
|
|
|
LOGP(DV5L1, LOGL_DEBUG, "Signal AIS detected on link %d of interface %d.\n", v5l->id, v5if->id);
|
2022-12-23 20:50:47 +00:00
|
|
|
if (!l1->ais) {
|
|
|
|
l1->ais = 1;
|
|
|
|
osmo_fsm_inst_dispatch(fi, V5X_L1FSM_E_AIS, NULL);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case L1_SIGNAL_NO_AIS:
|
2023-04-15 15:50:21 +00:00
|
|
|
LOGP(DV5L1, LOGL_DEBUG, "Signal AIS gone on link %d of interface %d.\n", v5l->id, v5if->id);
|
2022-12-23 20:50:47 +00:00
|
|
|
if (l1->ais) {
|
|
|
|
l1->ais = 0;
|
|
|
|
state_changed = 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case L1_SIGNAL_SA7_0:
|
2022-12-28 19:36:31 +00:00
|
|
|
|
|
|
|
if (l1->sa7 != 0) {
|
2023-04-15 15:50:21 +00:00
|
|
|
LOGP(DV5L1, LOGL_DEBUG, "Bit Sa7=0 on link %d of interface %d.\n", v5l->id, v5if->id);
|
2022-12-28 19:36:31 +00:00
|
|
|
l1->sa7 = 0;
|
2022-12-23 20:50:47 +00:00
|
|
|
state_changed = 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case L1_SIGNAL_SA7_1:
|
2022-12-28 19:36:31 +00:00
|
|
|
if (l1->sa7 != 1) {
|
2023-04-15 15:50:21 +00:00
|
|
|
LOGP(DV5L1, LOGL_DEBUG, "Bit Sa7=1 on link %d of interface %d.\n", v5l->id, v5if->id);
|
2022-12-28 19:36:31 +00:00
|
|
|
l1->sa7 = 1;
|
2022-12-23 20:50:47 +00:00
|
|
|
state_changed = 1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2022-12-28 19:36:31 +00:00
|
|
|
LOGP(DV5L1, LOGL_NOTICE, "Invalid L1 primitive %d receied from physical layer.\n", prim);
|
2022-12-23 20:50:47 +00:00
|
|
|
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) {
|
2022-12-28 19:36:31 +00:00
|
|
|
if (l1->sa7 == 0)
|
2022-12-23 20:50:47 +00:00
|
|
|
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:
|
|
|
|
LOGP(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);
|
|
|
|
}
|