forked from retronetworking/osmo-v5
2544 lines
83 KiB
C
2544 lines
83 KiB
C
/* Management information forwarding between AN <-> LE */
|
|
|
|
/* (C) 2022 by Andreas Eversberg <jolly@eversberg.eu>
|
|
*
|
|
* 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.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* Part 1 does all the startup procedures and handles port blocking/unblocking.
|
|
* Three state machines handle system startup, PSTN DL startup and PSTN restart.
|
|
* Depending on the dialect, individual ports are blocked/unblocked or all
|
|
* ports are blocked/unblocked with the accellerated alignment procedure.
|
|
*
|
|
* To understand it, please read C.13 of both G.964 and G.965 carefully.
|
|
*
|
|
* Notes:
|
|
*
|
|
* To prevent blocking/unblocking when PH-sockets get connected, there is a
|
|
* startup delay. If unblocking would be performed during startup, AN may
|
|
* send blocking indications that have been queued before. This results in an
|
|
* inconsistency between LE and AN. The AN will have the port unblocked, but
|
|
* LE will still think it is blocked.
|
|
*
|
|
* Part 2 handles link blocking/unblocking.
|
|
*
|
|
* Part 3 forwards messages between AN and PSTN/ISDN protocol (NAT protocol)
|
|
*
|
|
* Part 4 handles bearer channel allocation and activation.
|
|
*
|
|
* Part 5 handles switch over of protection protocol
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <errno.h>
|
|
|
|
#include "v5x_internal.h"
|
|
#include "v5x_protocol.h"
|
|
#include "v5x_le_ctrl_fsm.h"
|
|
#include "v5x_le_port_fsm.h"
|
|
#include "v5x_le_pstn_fsm.h"
|
|
#include "v52_le_lcp_fsm.h"
|
|
#include "v52_le_bcc_fsm.h"
|
|
#include "v52_le_pp_fsm.h"
|
|
#include "v5x_le_management.h"
|
|
#include "layer1.h"
|
|
#include "lapv5.h"
|
|
#include "logging.h"
|
|
|
|
#define S(x) (1 << (x))
|
|
|
|
struct variant_interface_id {
|
|
uint8_t variant;
|
|
uint32_t interface_id;
|
|
};
|
|
|
|
/*
|
|
* Part 1: startup of common control + PSTN link, blocking/unblocking
|
|
*/
|
|
|
|
/* send (un)blocking to socket and store (un)blocking state of AN */
|
|
static void block_unblock_socket(struct v5x_user_port *v5up, bool block)
|
|
{
|
|
uint8_t ctrl;
|
|
|
|
if (block && v5up->an_unblocked) {
|
|
v5up->an_unblocked = false;
|
|
ctrl = PH_CTRL_BLOCK;
|
|
ph_socket_tx_msg(&v5up->ph_socket, v5up->type == V5X_USER_TYPE_ISDN ? 3 : 0, PH_PRIM_CTRL_IND, &ctrl,
|
|
1);
|
|
return;
|
|
}
|
|
if (!block && !v5up->an_unblocked) {
|
|
v5up->an_unblocked = true;
|
|
ctrl = PH_CTRL_UNBLOCK;
|
|
ph_socket_tx_msg(&v5up->ph_socket, v5up->type == V5X_USER_TYPE_ISDN ? 3 : 0, PH_PRIM_CTRL_IND, &ctrl,
|
|
1);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* (silently) block all ports */
|
|
static void block_all_ports(struct v5x_interface *v5if, bool silently, bool isdn, bool pstn)
|
|
{
|
|
struct v5x_user_port *v5up;
|
|
bool any = false;
|
|
|
|
LOGP(DV5MGMT, LOGL_INFO, "%slocking %s%s%s ports:\n", (silently) ? "Silently b" : "B",
|
|
(isdn) ? "ISDN" : "", (isdn && pstn) ? "+" : "", (pstn) ? "PSTN" : "");
|
|
llist_for_each_entry(v5up, &v5if->user_ports, list) {
|
|
if (!isdn && !v5up->pstn.proto)
|
|
continue;
|
|
if (!pstn && v5up->pstn.proto)
|
|
continue;
|
|
LOGP(DV5MGMT, LOGL_INFO, " -> Set %s port %d into blocked state.\n",
|
|
(v5up->type == V5X_USER_TYPE_PSTN) ? "PSTN" : "ISDN", v5up->nr);
|
|
any = true;
|
|
if (silently)
|
|
block_unblock_socket(v5up, true);
|
|
if (!silently)
|
|
v5x_port_mph_snd(v5up, MPH_BI);
|
|
if (v5up->pstn.proto) {
|
|
if (silently)
|
|
v5x_le_port_pstn_block(v5up->port_fi);
|
|
LOGP(DV5MGMT, LOGL_DEBUG, " -> Send port blocked control to PSTN port %d.\n", v5up->nr);
|
|
v5x_le_pstn_mdu_snd(v5up, MDU_CTRL_port_blocked);
|
|
} else {
|
|
if (silently)
|
|
v5x_le_port_isdn_block(v5up->port_fi);
|
|
}
|
|
}
|
|
|
|
if (!any)
|
|
LOGP(DV5MGMT, LOGL_INFO, " -> Not any port to unblock.\n");
|
|
}
|
|
|
|
/* (silently) unblock all ports */
|
|
static void unblock_all_ports(struct v5x_interface *v5if, bool silently, bool isdn, bool pstn)
|
|
{
|
|
struct v5x_user_port *v5up;
|
|
bool any = false;
|
|
|
|
LOGP(DV5MGMT, LOGL_INFO, "%snblocking %s%s%s ports:\n", (silently) ? "Silently u" : "U",
|
|
(isdn) ? "ISDN" : "", (isdn && pstn) ? "+" : "", (pstn) ? "PSTN" : "");
|
|
llist_for_each_entry(v5up, &v5if->user_ports, list) {
|
|
if (!v5up->le_unblocked)
|
|
continue;
|
|
if (!isdn && !v5up->pstn.proto)
|
|
continue;
|
|
if (!pstn && v5up->pstn.proto)
|
|
continue;
|
|
LOGP(DV5MGMT, LOGL_INFO, " -> Set %s port %d into unblocked state.\n",
|
|
(v5up->type == V5X_USER_TYPE_PSTN) ? "PSTN" : "ISDN", v5up->nr);
|
|
any = true;
|
|
if (silently)
|
|
block_unblock_socket(v5up, false);
|
|
if (!silently)
|
|
v5x_port_mph_snd(v5up, MPH_UBR);
|
|
if (v5up->pstn.proto) {
|
|
if (silently)
|
|
v5x_le_port_pstn_unblock(v5up->port_fi);
|
|
LOGP(DV5MGMT, LOGL_DEBUG, " -> Send port unblocked control to PSTN port %d.\n", v5up->nr);
|
|
v5x_le_pstn_mdu_snd(v5up, MDU_CTRL_port_unblocked);
|
|
} else {
|
|
if (silently)
|
|
v5x_le_port_isdn_unblock(v5up->port_fi);
|
|
}
|
|
}
|
|
|
|
if (!any)
|
|
LOGP(DV5MGMT, LOGL_INFO, " -> Not any port to unblock.\n");
|
|
}
|
|
|
|
/* align all ports */
|
|
static void align_all_ports(struct v5x_interface *v5if)
|
|
{
|
|
struct v5x_user_port *v5up;
|
|
bool any = false;
|
|
|
|
LOGP(DV5MGMT, LOGL_INFO, "Aligning all ports:\n");
|
|
llist_for_each_entry(v5up, &v5if->user_ports, list) {
|
|
if (v5up->le_unblocked) {
|
|
LOGP(DV5MGMT, LOGL_INFO, " -> Set %s port %d into unblocked state.\n",
|
|
(v5up->type == V5X_USER_TYPE_PSTN) ? "PSTN" : "ISDN", v5up->nr);
|
|
any = true;
|
|
v5x_port_mph_snd(v5up, MPH_UBR);
|
|
if (v5up->pstn.proto) {
|
|
LOGP(DV5MGMT, LOGL_DEBUG, " -> Send port unblocked control to PSTN port %d.\n",
|
|
v5up->nr);
|
|
v5x_le_pstn_mdu_snd(v5up, MDU_CTRL_port_unblocked);
|
|
}
|
|
} else {
|
|
LOGP(DV5MGMT, LOGL_INFO, " -> Set %s port %d into blocked state.\n",
|
|
(v5up->type == V5X_USER_TYPE_PSTN) ? "PSTN" : "ISDN", v5up->nr);
|
|
any = true;
|
|
v5x_port_mph_snd(v5up, MPH_BI);
|
|
if (v5up->pstn.proto) {
|
|
LOGP(DV5MGMT, LOGL_DEBUG, " -> Send port blocked control to PSTN port %d.\n",
|
|
v5up->nr);
|
|
v5x_le_pstn_mdu_snd(v5up, MDU_CTRL_port_blocked);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!any)
|
|
LOGP(DV5MGMT, LOGL_INFO, " -> Not any port to align.\n");
|
|
}
|
|
|
|
/***********************************************************************/
|
|
/* state names, event names, primitives, ... */
|
|
/***********************************************************************/
|
|
|
|
/* C.17 States in the system management */
|
|
enum v5x_le_system_state {
|
|
V5X_LE_SYSTEM_S_LESYS0_SYSTEM_STARTUP, /* LESYS0 */
|
|
V5X_LE_SYSTEM_S_LESYS1_WAIT_VARIANT_AND_ID, /* LESYS1 */
|
|
V5X_LE_SYSTEM_S_LESYS2_IN_SERVICE, /* LESYS2 */
|
|
V5X_LE_SYSTEM_S_LESYS3_CONTROL_DL_RELEASED_1, /* LESYS3 */
|
|
V5X_LE_SYSTEM_S_LESYS4_CONTROL_DL_RELEASED_2, /* LESYS4 */
|
|
V5X_LE_SYSTEM_S_LESYS5_SWITCH_OVER, /* LESYS5 */
|
|
V5X_LE_SYSTEM_S_LESYS6_AWAIT_V5_IF_INIT, /* LESYS6 */
|
|
V5X_LE_SYSTEM_S_LESYS7_FORCE_SYSTEM_RESTART, /* LESYS7 */
|
|
V5X_LE_SYSTEM_S_LESYS8_SYSTEM_DEACTIVATED, /* LESYS8 */
|
|
};
|
|
|
|
enum v5x_le_pstn_dl_state {
|
|
V5X_LE_PSTN_DL_S_LEPDL0_PSTN_NULL, /* LEPDL0 */
|
|
V5X_LE_PSTN_DL_S_LEPDL1_ACTIVATE_PSTN, /* LEPDL1 */
|
|
V5X_LE_PSTN_DL_S_LEPDL2_PSTN_IN_SERVICE, /* LEPDL2 */
|
|
V5X_LE_PSTN_DL_S_LEPDL3_PSTN_DL_RELEASED_1, /* LEPDL3 */
|
|
V5X_LE_PSTN_DL_S_LEPDL4_PSTN_DL_RELEASED_2, /* LEPDL4 */
|
|
};
|
|
|
|
enum v5x_le_pstn_restart_state {
|
|
V5X_LE_PSTN_RS_S_LEPRS0_RESTART_NULL, /* LEPRS0 */
|
|
V5X_LE_PSTN_RS_S_LEPRS1_RESTART, /* LEPRS1 */
|
|
};
|
|
|
|
/* these states are not described, but derived from C.28 description */
|
|
enum v5x_le_unblk_all_state {
|
|
V5X_LE_UNBLK_ALL_S_NULL,
|
|
V5X_LE_UNBLK_ALL_S_REQUESTED, /* request sent, wait for accepted */
|
|
V5X_LE_UNBLK_ALL_S_COMPLETED, /* completed sent, wait for completed */
|
|
};
|
|
|
|
/* C.13 - C.16 Events for system management */
|
|
enum v5x_le_system_fsm_event {
|
|
V5X_LE_SYSTEM_E_START,
|
|
V5X_LE_SYSTEM_E_CONTROL_DL_ESTABLISHED,
|
|
V5X_LE_SYSTEM_E_CONTROL_DL_RELEASED,
|
|
V5X_LE_SYSTEM_E_VARIANT_AND_ID,
|
|
V5X_LE_SYSTEM_E_TIMEOUT_TV1,
|
|
V5X_LE_SYSTEM_E_TIMEOUT_TC1,
|
|
V5X_LE_SYSTEM_E_TIMEOUT_TC2,
|
|
V5X_LE_SYSTEM_E_TIMEOUT_TC8,
|
|
V5X_LE_SYSTEM_E_TIMEOUT_TC9,
|
|
};
|
|
|
|
static const struct value_string v5x_le_system_fsm_event_names[] = {
|
|
{ V5X_LE_SYSTEM_E_START, "Start" },
|
|
{ V5X_LE_SYSTEM_E_CONTROL_DL_ESTABLISHED, "Control DL establish" },
|
|
{ V5X_LE_SYSTEM_E_CONTROL_DL_RELEASED, "Control DL release" },
|
|
{ V5X_LE_SYSTEM_E_VARIANT_AND_ID, "Got variant and interface ID" },
|
|
{ V5X_LE_SYSTEM_E_TIMEOUT_TV1, "Timeout TV1" },
|
|
{ V5X_LE_SYSTEM_E_TIMEOUT_TC1, "Timeout TC1" },
|
|
{ V5X_LE_SYSTEM_E_TIMEOUT_TC2, "Timeout TC2" },
|
|
{ V5X_LE_SYSTEM_E_TIMEOUT_TC8, "Timeout TC8" },
|
|
{ V5X_LE_SYSTEM_E_TIMEOUT_TC9, "Timeout TC9" },
|
|
{ 0, NULL }
|
|
};
|
|
|
|
enum v5x_le_pstn_dl_fsm_event {
|
|
V5X_LE_PSTN_DL_E_START,
|
|
V5X_LE_PSTN_DL_E_PSTN_DL_ESTABLISHED,
|
|
V5X_LE_PSTN_DL_E_PSTN_DL_RELEASED,
|
|
V5X_LE_PSTN_DL_E_TIMEOUT_TC3,
|
|
};
|
|
|
|
static const struct value_string v5x_le_pstn_dl_fsm_event_names[] = {
|
|
{ V5X_LE_PSTN_DL_E_START, "Start" },
|
|
{ V5X_LE_PSTN_DL_E_PSTN_DL_ESTABLISHED, "PSTN DL establish" },
|
|
{ V5X_LE_PSTN_DL_E_PSTN_DL_RELEASED, "PSTN DL release" },
|
|
{ V5X_LE_PSTN_DL_E_TIMEOUT_TC3, "Timeout TC3" },
|
|
{ 0, NULL }
|
|
};
|
|
|
|
enum v5x_le_pstn_rs_fsm_event {
|
|
V5X_LE_PSTN_RS_E_MDU_CTRL_restart_request,
|
|
V5X_LE_PSTN_RS_E_MDU_CTRL_restart_complete,
|
|
V5X_LE_PSTN_RS_E_Request_PSTN_Restart,
|
|
V5X_LE_PSTN_RS_E_Complete_PSTN_Restart,
|
|
V5X_LE_PSTN_RS_E_TIMEOUT_TR1,
|
|
V5X_LE_PSTN_RS_E_TIMEOUT_TR2,
|
|
};
|
|
|
|
static const struct value_string v5x_le_pstn_rs_fsm_event_names[] = {
|
|
{ V5X_LE_PSTN_RS_E_MDU_CTRL_restart_request, "MDU-CTRL (restart request)" },
|
|
{ V5X_LE_PSTN_RS_E_MDU_CTRL_restart_complete, "MDU-CTRL (restart complete)" },
|
|
{ V5X_LE_PSTN_RS_E_Request_PSTN_Restart, "Request restart by system mgmt" },
|
|
{ V5X_LE_PSTN_RS_E_Complete_PSTN_Restart, "Completed restart of all PSTN" },
|
|
{ V5X_LE_PSTN_RS_E_TIMEOUT_TR1, "Timeout TR1" },
|
|
{ V5X_LE_PSTN_RS_E_TIMEOUT_TR2, "Timeout TR2" },
|
|
{ 0, NULL }
|
|
};
|
|
|
|
enum v5x_le_unblk_all_fsm_event {
|
|
V5X_LE_UNBLK_ALL_E_Request_Unblock_Block,
|
|
V5X_LE_UNBLK_ALL_E_REQUEST,
|
|
V5X_LE_UNBLK_ALL_E_REJECTED,
|
|
V5X_LE_UNBLK_ALL_E_ACCEPTED,
|
|
V5X_LE_UNBLK_ALL_E_COMPLETED,
|
|
V5X_LE_UNBLK_ALL_E_TIMEOUT_TU1,
|
|
V5X_LE_UNBLK_ALL_E_TIMEOUT_TU2,
|
|
};
|
|
|
|
static const struct value_string v5x_le_unblk_all_fsm_event_names[] = {
|
|
{ V5X_LE_UNBLK_ALL_E_Request_Unblock_Block, "Request Unblock/Block" },
|
|
{ V5X_LE_UNBLK_ALL_E_REQUEST, "REQUEST received" },
|
|
{ V5X_LE_UNBLK_ALL_E_REJECTED, "REJECTED received" },
|
|
{ V5X_LE_UNBLK_ALL_E_ACCEPTED, "ACCEPTED received" },
|
|
{ V5X_LE_UNBLK_ALL_E_COMPLETED, "COMPLETED received" },
|
|
{ V5X_LE_UNBLK_ALL_E_TIMEOUT_TU1, "Timeout TU1" },
|
|
{ V5X_LE_UNBLK_ALL_E_TIMEOUT_TU2, "Timeout TU2" },
|
|
{ 0, NULL }
|
|
};
|
|
|
|
/***********************************************************************/
|
|
/* timers of all states FSM */
|
|
/***********************************************************************/
|
|
|
|
#define TIMEOUT_TU1 100
|
|
#define TIMEOUT_TU2 60
|
|
|
|
static void start_timer_system(struct v5x_mgmt_proto *mgmt, int delay)
|
|
{
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Start timer for system startup delay.\n");
|
|
osmo_timer_schedule(&mgmt->timer_system, delay, 0);
|
|
}
|
|
|
|
static void start_timer_tr1(struct v5x_mgmt_proto *mgmt)
|
|
{
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Start timer TR1.\n");
|
|
osmo_timer_schedule(&mgmt->timer_tr1, 100, 0);
|
|
}
|
|
|
|
static void start_timer_tr2(struct v5x_mgmt_proto *mgmt)
|
|
{
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Start timer TR2.\n");
|
|
osmo_timer_schedule(&mgmt->timer_tr2, 120, 0);
|
|
}
|
|
|
|
static void start_timer_tc1(struct v5x_mgmt_proto *mgmt)
|
|
{
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Start timer TC1.\n");
|
|
osmo_timer_schedule(&mgmt->timer_tc1, 15, 0);
|
|
}
|
|
|
|
static void start_timer_tc2(struct v5x_mgmt_proto *mgmt)
|
|
{
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Start timer TC2.\n");
|
|
osmo_timer_schedule(&mgmt->timer_tc2, 60, 0);
|
|
}
|
|
|
|
static void start_timer_tc3(struct v5x_mgmt_proto *mgmt)
|
|
{
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Start timer TC3.\n");
|
|
osmo_timer_schedule(&mgmt->timer_tc3, 15, 0);
|
|
}
|
|
|
|
static void start_timer_tc8(struct v5x_mgmt_proto *mgmt)
|
|
{
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Start timer TC8.\n");
|
|
osmo_timer_schedule(&mgmt->timer_tc8, 20, 0);
|
|
}
|
|
|
|
static void start_timer_tc9(struct v5x_mgmt_proto *mgmt)
|
|
{
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Start timer TC9.\n");
|
|
osmo_timer_schedule(&mgmt->timer_tc9, 95, 0);
|
|
}
|
|
|
|
static void start_timer_tv1(struct v5x_mgmt_proto *mgmt)
|
|
{
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Start timer TV1.\n");
|
|
osmo_timer_schedule(&mgmt->timer_tv1, 15, 0);
|
|
}
|
|
|
|
static void stop_timer_system(struct v5x_mgmt_proto *mgmt)
|
|
{
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Stop timer for system startup delay.\n");
|
|
osmo_timer_del(&mgmt->timer_system);
|
|
}
|
|
|
|
static void stop_timer_tr1(struct v5x_mgmt_proto *mgmt)
|
|
{
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Stop timer TR1.\n");
|
|
osmo_timer_del(&mgmt->timer_tr1);
|
|
}
|
|
|
|
static void stop_timer_tr2(struct v5x_mgmt_proto *mgmt)
|
|
{
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Stop timer TR2.\n");
|
|
osmo_timer_del(&mgmt->timer_tr2);
|
|
}
|
|
|
|
static void stop_timer_tc1(struct v5x_mgmt_proto *mgmt)
|
|
{
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Stop timer TC1.\n");
|
|
osmo_timer_del(&mgmt->timer_tc1);
|
|
}
|
|
|
|
static void stop_timer_tc2(struct v5x_mgmt_proto *mgmt)
|
|
{
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Stop timer TC2.\n");
|
|
osmo_timer_del(&mgmt->timer_tc2);
|
|
}
|
|
|
|
static void stop_timer_tc3(struct v5x_mgmt_proto *mgmt)
|
|
{
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Stop timer TC3.\n");
|
|
osmo_timer_del(&mgmt->timer_tc3);
|
|
}
|
|
|
|
static void stop_timer_tc8(struct v5x_mgmt_proto *mgmt)
|
|
{
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Stop timer TC8.\n");
|
|
osmo_timer_del(&mgmt->timer_tc8);
|
|
}
|
|
|
|
static void stop_timer_tc9(struct v5x_mgmt_proto *mgmt)
|
|
{
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Stop timer TC9.\n");
|
|
osmo_timer_del(&mgmt->timer_tc9);
|
|
}
|
|
|
|
static void stop_timer_tv1(struct v5x_mgmt_proto *mgmt)
|
|
{
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Stop timer TV1.\n");
|
|
osmo_timer_del(&mgmt->timer_tv1);
|
|
}
|
|
|
|
static void timeout_system(void *data)
|
|
{
|
|
struct v5x_mgmt_proto *mgmt = data;
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Timeout timer for system startup delay.\n");
|
|
v5x_le_mgmt_start(mgmt->interface);
|
|
}
|
|
|
|
static void timeout_tr1(void *data)
|
|
{
|
|
struct v5x_mgmt_proto *mgmt = data;
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Timeout TR1.\n");
|
|
osmo_fsm_inst_dispatch(mgmt->pstn_rs_fi, V5X_LE_PSTN_RS_E_TIMEOUT_TR1, NULL);
|
|
}
|
|
|
|
static void timeout_tr2(void *data)
|
|
{
|
|
struct v5x_mgmt_proto *mgmt = data;
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Timeout TR2.\n");
|
|
osmo_fsm_inst_dispatch(mgmt->pstn_rs_fi, V5X_LE_PSTN_RS_E_TIMEOUT_TR2, NULL);
|
|
}
|
|
|
|
static void timeout_tc1(void *data)
|
|
{
|
|
struct v5x_mgmt_proto *mgmt = data;
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Timeout TC1.\n");
|
|
osmo_fsm_inst_dispatch(mgmt->system_fi, V5X_LE_SYSTEM_E_TIMEOUT_TC1, NULL);
|
|
}
|
|
|
|
static void timeout_tc2(void *data)
|
|
{
|
|
struct v5x_mgmt_proto *mgmt = data;
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Timeout TC2.\n");
|
|
osmo_fsm_inst_dispatch(mgmt->system_fi, V5X_LE_SYSTEM_E_TIMEOUT_TC2, NULL);
|
|
}
|
|
|
|
static void timeout_tc3(void *data)
|
|
{
|
|
struct v5x_mgmt_proto *mgmt = data;
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Timeout TC3.\n");
|
|
osmo_fsm_inst_dispatch(mgmt->pstn_dl_fi, V5X_LE_PSTN_DL_E_TIMEOUT_TC3, NULL);
|
|
}
|
|
|
|
static void timeout_tc8(void *data)
|
|
{
|
|
struct v5x_mgmt_proto *mgmt = data;
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Timeout TC8.\n");
|
|
osmo_fsm_inst_dispatch(mgmt->system_fi, V5X_LE_SYSTEM_E_TIMEOUT_TC8, NULL);
|
|
}
|
|
|
|
static void timeout_tc9(void *data)
|
|
{
|
|
struct v5x_mgmt_proto *mgmt = data;
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Timeout TC9.\n");
|
|
osmo_fsm_inst_dispatch(mgmt->system_fi, V5X_LE_SYSTEM_E_TIMEOUT_TC9, NULL);
|
|
}
|
|
|
|
static void timeout_tv1(void *data)
|
|
{
|
|
struct v5x_mgmt_proto *mgmt = data;
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Timeout TV1.\n");
|
|
osmo_fsm_inst_dispatch(mgmt->system_fi, V5X_LE_SYSTEM_E_TIMEOUT_TV1, NULL);
|
|
}
|
|
|
|
static void reset_all_timers(struct v5x_mgmt_proto *mgmt)
|
|
{
|
|
stop_timer_tr1(mgmt);
|
|
stop_timer_tr2(mgmt);
|
|
stop_timer_tc1(mgmt);
|
|
stop_timer_tc2(mgmt);
|
|
stop_timer_tc3(mgmt);
|
|
stop_timer_tc8(mgmt);
|
|
stop_timer_tc9(mgmt);
|
|
stop_timer_tv1(mgmt);
|
|
}
|
|
|
|
/***********************************************************************/
|
|
/* system startup state FSM */
|
|
/***********************************************************************/
|
|
|
|
static void disable_lapv5(struct v5x_mgmt_proto *mgmt)
|
|
{
|
|
struct v5x_interface *v5if = mgmt->interface;
|
|
|
|
lapv5_set_enabled(v5if->control.li, false);
|
|
v5if->control.established = 0;
|
|
if (v5if->pstn.li) {
|
|
lapv5_set_enabled(v5if->pstn.li, false);
|
|
v5if->pstn.established = 0;
|
|
}
|
|
if (v5if->lcp.li) {
|
|
lapv5_set_enabled(v5if->lcp.li, false);
|
|
v5if->lcp.established = 0;
|
|
}
|
|
if (v5if->bcc.li) {
|
|
lapv5_set_enabled(v5if->bcc.li, false);
|
|
v5if->bcc.established = 0;
|
|
}
|
|
if (v5if->protection.li[0]) {
|
|
lapv5_set_enabled(v5if->protection.li[0], false);
|
|
v5if->protection.established[0] = 0;
|
|
}
|
|
if (v5if->protection.li[1]) {
|
|
lapv5_set_enabled(v5if->protection.li[1], false);
|
|
v5if->protection.established[1] = 0;
|
|
}
|
|
}
|
|
|
|
static void system_startup(struct v5x_mgmt_proto *mgmt)
|
|
{
|
|
struct v5x_interface *v5if = mgmt->interface;
|
|
struct v5x_link *v5l;
|
|
struct v5x_user_port *v5up;
|
|
|
|
/* enable LAPV5 DLs except PSTN */
|
|
lapv5_set_enabled(v5if->control.li, true);
|
|
if (v5if->pstn.li)
|
|
lapv5_set_enabled(v5if->pstn.li, false);
|
|
if (v5if->lcp.li)
|
|
lapv5_set_enabled(v5if->lcp.li, true);
|
|
if (v5if->bcc.li)
|
|
lapv5_set_enabled(v5if->bcc.li, true);
|
|
if (v5if->protection.li[0])
|
|
lapv5_set_enabled(v5if->protection.li[0], true);
|
|
if (v5if->protection.li[1])
|
|
lapv5_set_enabled(v5if->protection.li[1], true);
|
|
|
|
/* no pending restart */
|
|
mgmt->pstn_rs_pending = false;
|
|
|
|
/* reset state machines */
|
|
if (v5if->control.ctrl)
|
|
v5x_le_ctrl_stop(v5if->control.ctrl);
|
|
llist_for_each_entry(v5l, &v5if->links, list) {
|
|
if (v5l->ctrl)
|
|
v5x_le_ctrl_stop(v5l->ctrl);
|
|
}
|
|
llist_for_each_entry(v5up, &v5if->user_ports, list) {
|
|
if (v5up->ctrl)
|
|
v5x_le_ctrl_stop(v5up->ctrl);
|
|
}
|
|
|
|
/* block ISDN and PSTN ports */
|
|
block_all_ports(v5if, true, true, true);
|
|
|
|
/* reset timers */
|
|
reset_all_timers(mgmt);
|
|
|
|
/* reset management state machines */
|
|
mgmt->pstn_dl_fi->state = V5X_LE_PSTN_DL_S_LEPDL0_PSTN_NULL;
|
|
mgmt->pstn_rs_fi->state = V5X_LE_PSTN_RS_S_LEPRS0_RESTART_NULL;
|
|
|
|
/* establish CONTROL DL and other DLs, except PSTN DL */
|
|
if (mgmt->do_est) {
|
|
lapv5_dl_est_req(v5if->control.li, V5X_DLADDR_CTRL);
|
|
if (v5if->pstn.li)
|
|
lapv5_dl_est_req(v5if->pstn.li, V5X_DLADDR_PSTN);
|
|
if (v5if->lcp.li)
|
|
lapv5_dl_est_req(v5if->lcp.li, V52_DLADDR_LCP);
|
|
if (v5if->bcc.li)
|
|
lapv5_dl_est_req(v5if->bcc.li, V52_DLADDR_BCC);
|
|
if (v5if->protection.li[0])
|
|
lapv5_dl_est_req(v5if->protection.li[0], V52_DLADDR_PROTECTION);
|
|
if (v5if->protection.li[1])
|
|
lapv5_dl_est_req(v5if->protection.li[1], V52_DLADDR_PROTECTION);
|
|
}
|
|
}
|
|
|
|
static void start_traffic(struct v5x_mgmt_proto *mgmt)
|
|
{
|
|
struct v5x_interface *v5if = mgmt->interface;
|
|
struct v5x_user_port *v5up;
|
|
|
|
/* COMMON_CONTROL */
|
|
v5x_le_ctrl_start(v5if->control.ctrl);
|
|
/* PORT_CONTROL */
|
|
llist_for_each_entry(v5up, &v5if->user_ports, list)
|
|
v5x_le_ctrl_start(v5up->ctrl);
|
|
}
|
|
|
|
static void stop_traffic(struct v5x_mgmt_proto *mgmt)
|
|
{
|
|
struct v5x_interface *v5if = mgmt->interface;
|
|
struct v5x_user_port *v5up;
|
|
|
|
/* COMMON_CONTROL */
|
|
v5x_le_ctrl_stop(v5if->control.ctrl);
|
|
/* PORT_CONTROL */
|
|
llist_for_each_entry(v5up, &v5if->user_ports, list)
|
|
v5x_le_ctrl_stop(v5up->ctrl);
|
|
}
|
|
|
|
static void system_lesys0_system_startup(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data)
|
|
{
|
|
struct v5x_mgmt_proto *mgmt = fi->priv;
|
|
struct v5x_interface *v5if = mgmt->interface;
|
|
|
|
switch (event) {
|
|
case V5X_LE_SYSTEM_E_START:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Perform system startup\n");
|
|
system_startup(mgmt);
|
|
break;
|
|
case V5X_LE_SYSTEM_E_CONTROL_DL_ESTABLISHED:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Establishment of control DL success.\n");
|
|
/* new state */
|
|
osmo_fsm_inst_state_chg(fi, V5X_LE_SYSTEM_S_LESYS1_WAIT_VARIANT_AND_ID, 0, 0);
|
|
/* start traffic to CONTROL_COMMON and CONTROL_PORT FSM */
|
|
start_traffic(mgmt);
|
|
/* start timer TV1 */
|
|
start_timer_tv1(mgmt);
|
|
/* request variant and ID */
|
|
LOGP(DV5MGMT, LOGL_INFO, "Requesting variant and interface ID.\n");
|
|
v5x_le_ctrl_common_snd(v5if, V5X_CTRL_ID_REQUEST_VARIANT_AND_INTERFACE_ID, NULL, NULL, NULL);
|
|
break;
|
|
case V5X_LE_SYSTEM_E_CONTROL_DL_RELEASED:
|
|
LOGP(DV5MGMT, LOGL_ERROR, "Establishment of control DL failed.\n");
|
|
/* re-establish DL */
|
|
if (mgmt->do_est)
|
|
lapv5_dl_est_req(v5if->control.li, V5X_DLADDR_CTRL);
|
|
break;
|
|
default:
|
|
OSMO_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
static void system_lesys1_wait_variant_and_id(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
|
{
|
|
struct v5x_mgmt_proto *mgmt = fi->priv;
|
|
struct variant_interface_id *vi;
|
|
struct v5x_interface *v5if = mgmt->interface;
|
|
|
|
switch (event) {
|
|
case V5X_LE_SYSTEM_E_VARIANT_AND_ID:
|
|
vi = data;
|
|
LOGP(DV5MGMT, LOGL_INFO, "AN replies variant and interface ID. (variant = %d, interface_id = %d)\n",
|
|
vi->variant, vi->interface_id);
|
|
/* stop timer TV1 */
|
|
stop_timer_tv1(mgmt);
|
|
/* handle variant and ID */
|
|
v5if->variant_remote = vi->variant;
|
|
v5if->variant_remote_valid = true;
|
|
v5if->id_remote = vi->interface_id;
|
|
v5if->id_remote_valid = true;
|
|
if (v5if->variant_remote != v5if->variant_local) {
|
|
LOGP(DV5MGMT, LOGL_ERROR, "Variant does not match: AN(%d) != LE(%d)\n",
|
|
v5if->variant_remote, v5if->variant_local);
|
|
}
|
|
if (v5if->id_remote != v5if->id_local) {
|
|
LOGP(DV5MGMT, LOGL_ERROR, "Interface ID does not match: AN(%d) != LE(%d)\n",
|
|
v5if->id_remote, v5if->id_local);
|
|
}
|
|
if (v5if->variant_remote != v5if->variant_local || v5if->id_remote != v5if->id_local) {
|
|
LOGP(DV5MGMT, LOGL_ERROR, "************************************************************\n");
|
|
LOGP(DV5MGMT, LOGL_ERROR, "You must set matching Variant and Interface ID at AN and LE!\n");
|
|
LOGP(DV5MGMT, LOGL_ERROR, "************************************************************\n");
|
|
/* disable LAPV5 DLs */
|
|
disable_lapv5(mgmt);
|
|
/* new state */
|
|
osmo_fsm_inst_state_chg(fi, V5X_LE_SYSTEM_S_LESYS7_FORCE_SYSTEM_RESTART, 0, 0);
|
|
/* start timer TC9 */
|
|
start_timer_tc9(mgmt);
|
|
break;
|
|
}
|
|
/* new state */
|
|
osmo_fsm_inst_state_chg(fi, V5X_LE_SYSTEM_S_LESYS2_IN_SERVICE, 0, 0);
|
|
if (mgmt->do_align) {
|
|
/* unblock all ISDN user ports */
|
|
if (mgmt->acc_align && v5if->dialect == V5X_DIALECT_V52)
|
|
osmo_fsm_inst_dispatch(mgmt->unblk_all_fi[UNBLK_ALL_ISDN],
|
|
V5X_LE_UNBLK_ALL_E_Request_Unblock_Block, (void *)UNBLK_ALL_ISDN);
|
|
else
|
|
unblock_all_ports(v5if, false, true, false);
|
|
}
|
|
if (v5if->pstn.li) {
|
|
/* enable PSTN DL and trigger PSTN DL startup */
|
|
lapv5_set_enabled(v5if->pstn.li, true);
|
|
osmo_fsm_inst_dispatch(mgmt->pstn_dl_fi, V5X_LE_PSTN_DL_E_START, NULL);
|
|
}
|
|
break;
|
|
case V5X_LE_SYSTEM_E_TIMEOUT_TV1:
|
|
/* start timer TV1 */
|
|
start_timer_tv1(mgmt);
|
|
/* request variant and ID again */
|
|
v5x_le_ctrl_common_snd(v5if, V5X_CTRL_ID_REQUEST_VARIANT_AND_INTERFACE_ID, NULL, NULL, NULL);
|
|
break;
|
|
case V5X_LE_SYSTEM_E_CONTROL_DL_ESTABLISHED:
|
|
/* ignore */
|
|
break;
|
|
case V5X_LE_SYSTEM_E_CONTROL_DL_RELEASED:
|
|
LOGP(DV5MGMT, LOGL_ERROR, "Establishment of control DL failed.\n");
|
|
/* stop timer TV1 */
|
|
stop_timer_tv1(mgmt);
|
|
/* new state */
|
|
osmo_fsm_inst_state_chg(fi, V5X_LE_SYSTEM_S_LESYS0_SYSTEM_STARTUP, 0, 0);
|
|
/* stop traffic to CONTROL_COMMON and CONTROL_PORT FSM */
|
|
stop_traffic(mgmt);
|
|
break;
|
|
default:
|
|
OSMO_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
static void system_lesys2_in_service(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data)
|
|
{
|
|
struct v5x_mgmt_proto *mgmt = fi->priv;
|
|
struct v5x_interface *v5if = mgmt->interface;
|
|
|
|
switch (event) {
|
|
case V5X_LE_SYSTEM_E_CONTROL_DL_ESTABLISHED:
|
|
/* ignore */
|
|
break;
|
|
case V5X_LE_SYSTEM_E_CONTROL_DL_RELEASED:
|
|
LOGP(DV5MGMT, LOGL_ERROR, "Establishment of control DL failed.\n");
|
|
/* start timer TC1 */
|
|
start_timer_tc1(mgmt);
|
|
/* new state */
|
|
osmo_fsm_inst_state_chg(fi, V5X_LE_SYSTEM_S_LESYS3_CONTROL_DL_RELEASED_1, 0, 0);
|
|
/* re-establish DL */
|
|
if (mgmt->do_est)
|
|
lapv5_dl_est_req(v5if->control.li, V5X_DLADDR_CTRL);
|
|
break;
|
|
default:
|
|
OSMO_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
static void system_lesys3_control_dl_released_1(struct osmo_fsm_inst *fi, uint32_t event,
|
|
void __attribute__((unused)) *data)
|
|
{
|
|
struct v5x_mgmt_proto *mgmt = fi->priv;
|
|
struct v5x_interface *v5if = mgmt->interface;
|
|
|
|
switch (event) {
|
|
case V5X_LE_SYSTEM_E_CONTROL_DL_ESTABLISHED:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Establishment of control DL success.\n");
|
|
/* stop timer TC1 */
|
|
stop_timer_tc1(mgmt);
|
|
/* new state */
|
|
osmo_fsm_inst_state_chg(fi, V5X_LE_SYSTEM_S_LESYS2_IN_SERVICE, 0, 0);
|
|
break;
|
|
case V5X_LE_SYSTEM_E_CONTROL_DL_RELEASED:
|
|
/* re-establish DL */
|
|
if (mgmt->do_est)
|
|
lapv5_dl_est_req(v5if->control.li, V5X_DLADDR_CTRL);
|
|
break;
|
|
case V5X_LE_SYSTEM_E_TIMEOUT_TC1:
|
|
LOGP(DV5MGMT, LOGL_ERROR, "Blocking all ports and wait for establishment of control DL.\n");
|
|
/* stop traffic to CONTROL_COMMON and CONTROL_PORT FSM */
|
|
stop_traffic(mgmt);
|
|
/* block all ISDN user ports */
|
|
block_all_ports(v5if, true, true, false);
|
|
/* start timer TC2 */
|
|
start_timer_tc2(mgmt);
|
|
/* new state */
|
|
osmo_fsm_inst_state_chg(fi, V5X_LE_SYSTEM_S_LESYS4_CONTROL_DL_RELEASED_2, 0, 0);
|
|
break;
|
|
default:
|
|
OSMO_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
static void system_lesys4_control_dl_released_2(struct osmo_fsm_inst *fi, uint32_t event,
|
|
void __attribute__((unused)) *data)
|
|
{
|
|
struct v5x_mgmt_proto *mgmt = fi->priv;
|
|
struct v5x_interface *v5if = mgmt->interface;
|
|
|
|
switch (event) {
|
|
case V5X_LE_SYSTEM_E_CONTROL_DL_ESTABLISHED:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Establishment of control DL success.\n");
|
|
/* stop timer TC2 */
|
|
stop_timer_tc2(mgmt);
|
|
/* start traffic to CONTROL_COMMON and CONTROL_PORT FSM */
|
|
start_traffic(mgmt);
|
|
/* new state */
|
|
osmo_fsm_inst_state_chg(fi, V5X_LE_SYSTEM_S_LESYS2_IN_SERVICE, 0, 0);
|
|
if (mgmt->do_align) {
|
|
/* unblock all ISDN user ports */
|
|
if (mgmt->acc_align && v5if->dialect == V5X_DIALECT_V52)
|
|
osmo_fsm_inst_dispatch(mgmt->unblk_all_fi[UNBLK_ALL_ISDN],
|
|
V5X_LE_UNBLK_ALL_E_Request_Unblock_Block, (void *)UNBLK_ALL_ISDN);
|
|
else
|
|
unblock_all_ports(v5if, false, true, false);
|
|
}
|
|
/* trigger PSTN DL restart */
|
|
if (mgmt->pstn_rs_pending && v5if->pstn.li) {
|
|
mgmt->pstn_rs_pending = false;
|
|
osmo_fsm_inst_dispatch(mgmt->pstn_rs_fi, V5X_LE_PSTN_RS_E_Request_PSTN_Restart, NULL);
|
|
}
|
|
break;
|
|
case V5X_LE_SYSTEM_E_CONTROL_DL_RELEASED:
|
|
/* re-establish DL */
|
|
if (mgmt->do_est)
|
|
lapv5_dl_est_req(v5if->control.li, V5X_DLADDR_CTRL);
|
|
break;
|
|
case V5X_LE_SYSTEM_E_TIMEOUT_TC2:
|
|
LOGP(DV5MGMT, LOGL_ERROR, "Could not establish control DL, shutting down.\n");
|
|
/* disable LAPV5 DLs */
|
|
disable_lapv5(mgmt);
|
|
/* block all PSTN user ports */
|
|
block_all_ports(v5if, true, false, true);
|
|
/* start timer TC8 */
|
|
start_timer_tc8(mgmt);
|
|
/* new state */
|
|
osmo_fsm_inst_state_chg(fi, V5X_LE_SYSTEM_S_LESYS7_FORCE_SYSTEM_RESTART, 0, 0);
|
|
break;
|
|
default:
|
|
OSMO_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
static void system_lesys5_switch_over(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data)
|
|
{
|
|
switch (event) {
|
|
default:
|
|
OSMO_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
static void system_lesys6_await_v5_if_init(struct osmo_fsm_inst *fi, uint32_t event,
|
|
void __attribute__((unused)) *data)
|
|
{
|
|
switch (event) {
|
|
default:
|
|
OSMO_ASSERT(0);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static void system_lesys7_force_system_restart(struct osmo_fsm_inst *fi, uint32_t event,
|
|
void __attribute__((unused)) *data)
|
|
{
|
|
struct v5x_mgmt_proto *mgmt = fi->priv;
|
|
struct v5x_interface *v5if = mgmt->interface;
|
|
|
|
switch (event) {
|
|
case V5X_LE_SYSTEM_E_CONTROL_DL_ESTABLISHED:
|
|
case V5X_LE_SYSTEM_E_CONTROL_DL_RELEASED:
|
|
/* ignore */
|
|
break;
|
|
case V5X_LE_SYSTEM_E_TIMEOUT_TC8:
|
|
case V5X_LE_SYSTEM_E_TIMEOUT_TC9:
|
|
/* new state */
|
|
osmo_fsm_inst_state_chg(fi, V5X_LE_SYSTEM_S_LESYS8_SYSTEM_DEACTIVATED, 0, 0);
|
|
if (mgmt->auto_restart) {
|
|
/* start automatically */
|
|
v5x_le_mgmt_start(v5if);
|
|
}
|
|
break;
|
|
default:
|
|
OSMO_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
static void system_lesys8_system_deactivated(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data)
|
|
{
|
|
struct v5x_mgmt_proto *mgmt = fi->priv;
|
|
|
|
switch (event) {
|
|
case V5X_LE_SYSTEM_E_CONTROL_DL_ESTABLISHED:
|
|
case V5X_LE_SYSTEM_E_CONTROL_DL_RELEASED:
|
|
/* ignore */
|
|
break;
|
|
case V5X_LE_SYSTEM_E_START:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Perform system restart\n");
|
|
/* new state */
|
|
osmo_fsm_inst_state_chg(fi, V5X_LE_SYSTEM_S_LESYS0_SYSTEM_STARTUP, 0, 0);
|
|
system_startup(mgmt);
|
|
break;
|
|
default:
|
|
OSMO_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
/* There is no table, it is described in text at clause C.13 and C.15 of ITU-T G.964 */
|
|
static const struct osmo_fsm_state v5x_le_system_fsm_states[] = {
|
|
[V5X_LE_SYSTEM_S_LESYS0_SYSTEM_STARTUP] = {
|
|
.name = "SYSTEM STARTUP (LESYS0)",
|
|
.in_event_mask = S(V5X_LE_SYSTEM_E_START) |
|
|
S(V5X_LE_SYSTEM_E_CONTROL_DL_ESTABLISHED) |
|
|
S(V5X_LE_SYSTEM_E_CONTROL_DL_RELEASED),
|
|
.out_state_mask = S(V5X_LE_SYSTEM_S_LESYS1_WAIT_VARIANT_AND_ID),
|
|
.action = system_lesys0_system_startup,
|
|
},
|
|
[V5X_LE_SYSTEM_S_LESYS1_WAIT_VARIANT_AND_ID] = {
|
|
.name = "WAIT FOR VARIANT & INTERFACE ID (LESYS1)",
|
|
.in_event_mask = S(V5X_LE_SYSTEM_E_VARIANT_AND_ID) |
|
|
S(V5X_LE_SYSTEM_E_TIMEOUT_TV1) |
|
|
S(V5X_LE_SYSTEM_E_CONTROL_DL_ESTABLISHED) |
|
|
S(V5X_LE_SYSTEM_E_CONTROL_DL_RELEASED),
|
|
.out_state_mask = S(V5X_LE_SYSTEM_S_LESYS2_IN_SERVICE) |
|
|
S(V5X_LE_SYSTEM_S_LESYS7_FORCE_SYSTEM_RESTART) |
|
|
S(V5X_LE_SYSTEM_S_LESYS0_SYSTEM_STARTUP),
|
|
.action = system_lesys1_wait_variant_and_id,
|
|
},
|
|
[V5X_LE_SYSTEM_S_LESYS2_IN_SERVICE] = {
|
|
.name = "IN SERVICE (LESYS2)",
|
|
.in_event_mask = S(V5X_LE_SYSTEM_E_CONTROL_DL_ESTABLISHED) |
|
|
S(V5X_LE_SYSTEM_E_CONTROL_DL_RELEASED),
|
|
.out_state_mask = S(V5X_LE_SYSTEM_S_LESYS3_CONTROL_DL_RELEASED_1),
|
|
.action = system_lesys2_in_service,
|
|
},
|
|
[V5X_LE_SYSTEM_S_LESYS3_CONTROL_DL_RELEASED_1] = {
|
|
.name = "CONTROL DL RELEASE 1 (LESYS3)",
|
|
.in_event_mask = S(V5X_LE_SYSTEM_E_CONTROL_DL_ESTABLISHED) |
|
|
S(V5X_LE_SYSTEM_E_CONTROL_DL_RELEASED) |
|
|
S(V5X_LE_SYSTEM_E_TIMEOUT_TC1),
|
|
.out_state_mask = S(V5X_LE_SYSTEM_S_LESYS2_IN_SERVICE) |
|
|
S(V5X_LE_SYSTEM_S_LESYS4_CONTROL_DL_RELEASED_2),
|
|
.action = system_lesys3_control_dl_released_1,
|
|
},
|
|
[V5X_LE_SYSTEM_S_LESYS4_CONTROL_DL_RELEASED_2] = {
|
|
.name = "CONTROL DL RELEASE 2 (LESYS4)",
|
|
.in_event_mask = S(V5X_LE_SYSTEM_E_CONTROL_DL_ESTABLISHED) |
|
|
S(V5X_LE_SYSTEM_E_CONTROL_DL_RELEASED) |
|
|
S(V5X_LE_SYSTEM_E_TIMEOUT_TC2),
|
|
.out_state_mask = S(V5X_LE_SYSTEM_S_LESYS7_FORCE_SYSTEM_RESTART) |
|
|
S(V5X_LE_SYSTEM_S_LESYS2_IN_SERVICE),
|
|
.action = system_lesys4_control_dl_released_2,
|
|
},
|
|
#if 0
|
|
[V5X_LE_SYSTEM_S_LESYS5_SWITCH_OVER] = {
|
|
.name = "SWITCH OVER (LESYS5)",
|
|
.action = system_lesys5_switch_over,
|
|
},
|
|
[V5X_LE_SYSTEM_S_LESYS6_AWAIT_V5_IF_INIT] = {
|
|
.name = "AWAIT V5 INTERFACE INIT (LESYS6)",
|
|
.action = system_lesys6_await_v5_if_init,
|
|
},
|
|
#endif
|
|
[V5X_LE_SYSTEM_S_LESYS7_FORCE_SYSTEM_RESTART] = {
|
|
.name = "FORCE SYSTEM RESTART (LESYS7)",
|
|
.in_event_mask = S(V5X_LE_SYSTEM_E_CONTROL_DL_ESTABLISHED) |
|
|
S(V5X_LE_SYSTEM_E_CONTROL_DL_RELEASED) |
|
|
S(V5X_LE_SYSTEM_E_TIMEOUT_TC8) |
|
|
S(V5X_LE_SYSTEM_E_TIMEOUT_TC9),
|
|
.out_state_mask = S(V5X_LE_SYSTEM_S_LESYS8_SYSTEM_DEACTIVATED),
|
|
.action = system_lesys7_force_system_restart,
|
|
},
|
|
[V5X_LE_SYSTEM_S_LESYS8_SYSTEM_DEACTIVATED] = {
|
|
.name = "SYSTEM DEACTIVATED (LESYS8)",
|
|
.in_event_mask = S(V5X_LE_SYSTEM_E_CONTROL_DL_ESTABLISHED) |
|
|
S(V5X_LE_SYSTEM_E_CONTROL_DL_RELEASED) |
|
|
S(V5X_LE_SYSTEM_E_START),
|
|
.out_state_mask = S(V5X_LE_SYSTEM_S_LESYS0_SYSTEM_STARTUP),
|
|
.action = system_lesys8_system_deactivated,
|
|
},
|
|
};
|
|
|
|
struct osmo_fsm v5x_le_system_fsm = {
|
|
.name = "System_startup",
|
|
.states = v5x_le_system_fsm_states,
|
|
.num_states = ARRAY_SIZE(v5x_le_system_fsm_states),
|
|
.allstate_event_mask = 0,
|
|
.allstate_action = NULL,
|
|
.cleanup = NULL,
|
|
.timer_cb = NULL,
|
|
.log_subsys = DV5MGMT,
|
|
.event_names = v5x_le_system_fsm_event_names,
|
|
};
|
|
|
|
const char *v5x_le_system_fsm_state_name(struct osmo_fsm_inst *fi)
|
|
{
|
|
return v5x_le_system_fsm_states[fi->state].name;
|
|
}
|
|
|
|
/***********************************************************************/
|
|
/* PSTN DL startup state FSM */
|
|
/***********************************************************************/
|
|
|
|
static void pstn_lepdl0_pstn_null(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data)
|
|
{
|
|
struct v5x_mgmt_proto *mgmt = fi->priv;
|
|
struct v5x_interface *v5if = mgmt->interface;
|
|
|
|
switch (event) {
|
|
case V5X_LE_PSTN_DL_E_START:
|
|
/* new state */
|
|
osmo_fsm_inst_state_chg(fi, V5X_LE_PSTN_DL_S_LEPDL1_ACTIVATE_PSTN, 0, 0);
|
|
/* establish DL */
|
|
if (mgmt->do_est && !v5if->pstn.established)
|
|
lapv5_dl_est_req(v5if->pstn.li, V5X_DLADDR_PSTN);
|
|
break;
|
|
case V5X_LE_PSTN_DL_E_PSTN_DL_ESTABLISHED:
|
|
/* ignore */
|
|
break;
|
|
case V5X_LE_PSTN_DL_E_PSTN_DL_RELEASED:
|
|
/* ignore */
|
|
break;
|
|
default:
|
|
OSMO_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
static void pstn_lepdl1_active_pstn(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data)
|
|
{
|
|
struct v5x_mgmt_proto *mgmt = fi->priv;
|
|
struct v5x_interface *v5if = mgmt->interface;
|
|
|
|
switch (event) {
|
|
case V5X_LE_PSTN_DL_E_PSTN_DL_ESTABLISHED:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Establishment of PSTN DL success.\n");
|
|
/* new state */
|
|
osmo_fsm_inst_state_chg(fi, V5X_LE_PSTN_DL_S_LEPDL2_PSTN_IN_SERVICE, 0, 0);
|
|
if (mgmt->do_align) {
|
|
/* unblock all PSTN user ports */
|
|
if (mgmt->acc_align && v5if->dialect == V5X_DIALECT_V52)
|
|
osmo_fsm_inst_dispatch(mgmt->unblk_all_fi[UNBLK_ALL_PSTN],
|
|
V5X_LE_UNBLK_ALL_E_Request_Unblock_Block, (void *)UNBLK_ALL_PSTN);
|
|
else
|
|
unblock_all_ports(v5if, false, false, true);
|
|
}
|
|
break;
|
|
case V5X_LE_PSTN_DL_E_PSTN_DL_RELEASED:
|
|
LOGP(DV5MGMT, LOGL_ERROR, "Establishment of PSTN DL failed.\n");
|
|
/* re-establish DL */
|
|
if (mgmt->do_est)
|
|
lapv5_dl_est_req(v5if->pstn.li, V5X_DLADDR_PSTN);
|
|
break;
|
|
default:
|
|
OSMO_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
static void pstn_lepdl2_pstn_in_service(struct osmo_fsm_inst *fi, uint32_t event, __attribute__((unused)) void *data)
|
|
{
|
|
struct v5x_mgmt_proto *mgmt = fi->priv;
|
|
struct v5x_interface *v5if = mgmt->interface;
|
|
|
|
switch (event) {
|
|
case V5X_LE_PSTN_DL_E_PSTN_DL_ESTABLISHED:
|
|
/* ignore */
|
|
break;
|
|
case V5X_LE_PSTN_DL_E_PSTN_DL_RELEASED:
|
|
LOGP(DV5MGMT, LOGL_ERROR, "Establishment of PSTN DL failed.\n");
|
|
/* start timer TC3 */
|
|
start_timer_tc3(mgmt);
|
|
/* new state */
|
|
osmo_fsm_inst_state_chg(fi, V5X_LE_PSTN_DL_S_LEPDL3_PSTN_DL_RELEASED_1, 0, 0);
|
|
/* re-establish DL */
|
|
if (mgmt->do_est)
|
|
lapv5_dl_est_req(v5if->pstn.li, V5X_DLADDR_PSTN);
|
|
break;
|
|
default:
|
|
OSMO_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
static void pstn_lepdl3_pstn_dl_released_1(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data)
|
|
{
|
|
struct v5x_mgmt_proto *mgmt = fi->priv;
|
|
struct v5x_interface *v5if = mgmt->interface;
|
|
|
|
switch (event) {
|
|
case V5X_LE_PSTN_DL_E_PSTN_DL_ESTABLISHED:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Establishment of PSTN DL success.\n");
|
|
/* stop timer TC3 */
|
|
stop_timer_tc3(mgmt);
|
|
/* new state */
|
|
osmo_fsm_inst_state_chg(fi, V5X_LE_PSTN_DL_S_LEPDL2_PSTN_IN_SERVICE, 0, 0);
|
|
break;
|
|
case V5X_LE_PSTN_DL_E_PSTN_DL_RELEASED:
|
|
/* re-establish DL */
|
|
if (mgmt->do_est)
|
|
lapv5_dl_est_req(v5if->pstn.li, V5X_DLADDR_PSTN);
|
|
break;
|
|
case V5X_LE_PSTN_DL_E_TIMEOUT_TC3:
|
|
/* new state */
|
|
osmo_fsm_inst_state_chg(fi, V5X_LE_PSTN_DL_S_LEPDL4_PSTN_DL_RELEASED_2, 0, 0);
|
|
/* block all PSTN user ports */
|
|
block_all_ports(v5if, true, false, true);
|
|
break;
|
|
default:
|
|
OSMO_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
static void pstn_lepdl4_pstn_dl_released_2(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data)
|
|
{
|
|
struct v5x_mgmt_proto *mgmt = fi->priv;
|
|
struct v5x_interface *v5if = mgmt->interface;
|
|
|
|
switch (event) {
|
|
case V5X_LE_PSTN_DL_E_PSTN_DL_ESTABLISHED:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Establishment of PSTN DL success.\n");
|
|
/* new state */
|
|
osmo_fsm_inst_state_chg(fi, V5X_LE_PSTN_DL_S_LEPDL2_PSTN_IN_SERVICE, 0, 0);
|
|
/* start PSTN restart process */
|
|
if (mgmt->system_fi->state == V5X_LE_SYSTEM_S_LESYS4_CONTROL_DL_RELEASED_2)
|
|
mgmt->pstn_rs_pending = true;
|
|
else
|
|
osmo_fsm_inst_dispatch(mgmt->pstn_rs_fi, V5X_LE_PSTN_RS_E_Request_PSTN_Restart, NULL);
|
|
break;
|
|
case V5X_LE_PSTN_DL_E_PSTN_DL_RELEASED:
|
|
/* re-establish DL */
|
|
if (mgmt->do_est)
|
|
lapv5_dl_est_req(v5if->pstn.li, V5X_DLADDR_PSTN);
|
|
break;
|
|
default:
|
|
OSMO_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
static const struct osmo_fsm_state v5x_le_pstn_dl_fsm_states[] = {
|
|
[V5X_LE_PSTN_DL_S_LEPDL0_PSTN_NULL] = {
|
|
.name = "PSTN NULL (LEPDL0)",
|
|
.in_event_mask = S(V5X_LE_PSTN_DL_E_START) |
|
|
S(V5X_LE_PSTN_DL_E_PSTN_DL_ESTABLISHED) |
|
|
S(V5X_LE_PSTN_DL_E_PSTN_DL_RELEASED),
|
|
.out_state_mask = S(V5X_LE_PSTN_DL_S_LEPDL1_ACTIVATE_PSTN),
|
|
.action = pstn_lepdl0_pstn_null,
|
|
},
|
|
[V5X_LE_PSTN_DL_S_LEPDL1_ACTIVATE_PSTN] = {
|
|
.name = "ACTIVE PSTN (LEPDL1)",
|
|
.in_event_mask = S(V5X_LE_PSTN_DL_E_PSTN_DL_ESTABLISHED) |
|
|
S(V5X_LE_PSTN_DL_E_PSTN_DL_RELEASED),
|
|
.out_state_mask = S(V5X_LE_PSTN_DL_S_LEPDL2_PSTN_IN_SERVICE),
|
|
.action = pstn_lepdl1_active_pstn,
|
|
},
|
|
[V5X_LE_PSTN_DL_S_LEPDL2_PSTN_IN_SERVICE] = {
|
|
.name = "PSTN IN SERVICE (LEPDL2)",
|
|
.in_event_mask = S(V5X_LE_PSTN_DL_E_PSTN_DL_ESTABLISHED) |
|
|
S(V5X_LE_PSTN_DL_E_PSTN_DL_RELEASED),
|
|
.out_state_mask = S(V5X_LE_PSTN_DL_S_LEPDL3_PSTN_DL_RELEASED_1),
|
|
.action = pstn_lepdl2_pstn_in_service,
|
|
},
|
|
[V5X_LE_PSTN_DL_S_LEPDL3_PSTN_DL_RELEASED_1] = {
|
|
.name = "PSTN DL RELEASED 1 (LEPDL3)",
|
|
.in_event_mask = S(V5X_LE_PSTN_DL_E_PSTN_DL_ESTABLISHED) |
|
|
S(V5X_LE_PSTN_DL_E_PSTN_DL_RELEASED) |
|
|
S(V5X_LE_PSTN_DL_E_TIMEOUT_TC3),
|
|
.out_state_mask = S(V5X_LE_PSTN_DL_S_LEPDL2_PSTN_IN_SERVICE) |
|
|
S(V5X_LE_PSTN_DL_S_LEPDL4_PSTN_DL_RELEASED_2),
|
|
.action = pstn_lepdl3_pstn_dl_released_1,
|
|
},
|
|
[V5X_LE_PSTN_DL_S_LEPDL4_PSTN_DL_RELEASED_2] = {
|
|
.name = "PSTN DL RELEASED 2 (LEPDL4)",
|
|
.in_event_mask = S(V5X_LE_PSTN_DL_E_PSTN_DL_ESTABLISHED) |
|
|
S(V5X_LE_PSTN_DL_E_PSTN_DL_RELEASED),
|
|
.out_state_mask = S(V5X_LE_PSTN_DL_S_LEPDL2_PSTN_IN_SERVICE),
|
|
.action = pstn_lepdl4_pstn_dl_released_2,
|
|
},
|
|
};
|
|
|
|
struct osmo_fsm v5x_le_pstn_dl_fsm = {
|
|
.name = "PSTN_DL_startup",
|
|
.states = v5x_le_pstn_dl_fsm_states,
|
|
.num_states = ARRAY_SIZE(v5x_le_pstn_dl_fsm_states),
|
|
.allstate_event_mask = 0,
|
|
.allstate_action = NULL,
|
|
.cleanup = NULL,
|
|
.timer_cb = NULL,
|
|
.log_subsys = DV5MGMT,
|
|
.event_names = v5x_le_pstn_dl_fsm_event_names,
|
|
};
|
|
|
|
const char *v5x_le_pstn_dl_fsm_state_name(struct osmo_fsm_inst *fi)
|
|
{
|
|
return v5x_le_pstn_dl_fsm_states[fi->state].name;
|
|
}
|
|
|
|
/***********************************************************************/
|
|
/* PSTN Restart state FSM */
|
|
/***********************************************************************/
|
|
|
|
static void restart_all_pstn_ports(struct v5x_interface *v5if)
|
|
{
|
|
struct v5x_user_port *v5up;
|
|
|
|
llist_for_each_entry(v5up, &v5if->user_ports, list) {
|
|
if (v5up->pstn.proto) {
|
|
LOGP(DV5MGMT, LOGL_INFO, "Send restart request to PSTN port %d.\n", v5up->nr);
|
|
v5x_le_pstn_mdu_snd(v5up, MDU_CTRL_port_restart_req);
|
|
v5x_le_pstn_mdu_snd(v5up, MDU_CTRL_port_restart_compl);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void pstn_leprs0_restart_null(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data)
|
|
{
|
|
struct v5x_mgmt_proto *mgmt = fi->priv;
|
|
struct v5x_interface *v5if = mgmt->interface;
|
|
|
|
switch (event) {
|
|
case V5X_LE_PSTN_RS_E_Request_PSTN_Restart:
|
|
LOGP(DV5MGMT, LOGL_INFO, "PSTN Restart was requested locally.\n");
|
|
/* send restart request to COMMON_CONTROL */
|
|
v5x_le_ctrl_common_snd(v5if, V5X_CTRL_ID_RESTART_REQUEST, NULL, NULL, NULL);
|
|
/* fall through */
|
|
case V5X_LE_PSTN_RS_E_MDU_CTRL_restart_request:
|
|
LOGP(DV5MGMT, LOGL_INFO, "PSTN Restart was requested by remote.\n");
|
|
/* new state */
|
|
osmo_fsm_inst_state_chg(fi, V5X_LE_PSTN_RS_S_LEPRS1_RESTART, 0, 0);
|
|
/* start TR1 & TR2 */
|
|
start_timer_tr1(mgmt);
|
|
start_timer_tr2(mgmt);
|
|
/* send restart to all PSTN ports, they are always complete */
|
|
restart_all_pstn_ports(v5if);
|
|
/* as the PSTN restart is complete, proceed directly */
|
|
osmo_fsm_inst_dispatch(mgmt->pstn_rs_fi, V5X_LE_PSTN_RS_E_Complete_PSTN_Restart, NULL);
|
|
break;
|
|
case V5X_LE_PSTN_RS_E_MDU_CTRL_restart_complete:
|
|
/* ignore */
|
|
break;
|
|
default:
|
|
OSMO_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
static void pstn_leprs1_restart(struct osmo_fsm_inst *fi, uint32_t event, void __attribute__((unused)) *data)
|
|
{
|
|
struct v5x_mgmt_proto *mgmt = fi->priv;
|
|
struct v5x_interface *v5if = mgmt->interface;
|
|
|
|
switch (event) {
|
|
case V5X_LE_PSTN_RS_E_MDU_CTRL_restart_complete:
|
|
/* if timer TR2 is not running */
|
|
if (!osmo_timer_pending(&mgmt->timer_tr2))
|
|
break;
|
|
/* stop timer TR2 */
|
|
stop_timer_tr2(mgmt);
|
|
/* if restart of local PSTN ports is complete (always) or timer TR1 is not running */
|
|
if (1 || !osmo_timer_pending(&mgmt->timer_tr1)) {
|
|
/* stop timer TR1 */
|
|
stop_timer_tr1(mgmt);
|
|
/* new state */
|
|
osmo_fsm_inst_state_chg(fi, V5X_LE_PSTN_RS_S_LEPRS0_RESTART_NULL, 0, 0);
|
|
/* MDU-CTRL (restart complete) to PSTN ports was already performed */
|
|
}
|
|
break;
|
|
case V5X_LE_PSTN_RS_E_Complete_PSTN_Restart:
|
|
/* if timer TR1 is not running */
|
|
if (!osmo_timer_pending(&mgmt->timer_tr1))
|
|
break;
|
|
/* stop timer TR1 */
|
|
stop_timer_tr1(mgmt);
|
|
/* if restart of local PSTN ports is complete (always) */
|
|
if (1) {
|
|
/* stop timer TR2 */
|
|
stop_timer_tr2(mgmt);
|
|
/* new state */
|
|
osmo_fsm_inst_state_chg(fi, V5X_LE_PSTN_RS_S_LEPRS0_RESTART_NULL, 0, 0);
|
|
}
|
|
/* send restart complete to COMMON_CONTROL */
|
|
v5x_le_ctrl_common_snd(v5if, V5X_CTRL_ID_RESTART_COMPLETE, NULL, NULL, NULL);
|
|
if (mgmt->do_align) {
|
|
/* unblock all PSTN user ports */
|
|
if (mgmt->acc_align && v5if->dialect == V5X_DIALECT_V52)
|
|
osmo_fsm_inst_dispatch(mgmt->unblk_all_fi[UNBLK_ALL_PSTN],
|
|
V5X_LE_UNBLK_ALL_E_Request_Unblock_Block, (void *)UNBLK_ALL_PSTN);
|
|
else
|
|
unblock_all_ports(v5if, false, false, true);
|
|
}
|
|
break;
|
|
case V5X_LE_PSTN_RS_E_TIMEOUT_TR1:
|
|
LOGP(DV5MGMT, LOGL_ERROR, "Local PSTN Restart timed out.\n");
|
|
/* if restart of local PSTN ports is complete (always) */
|
|
if (1) {
|
|
/* stop timer TR2 */
|
|
stop_timer_tr2(mgmt);
|
|
/* new state */
|
|
osmo_fsm_inst_state_chg(fi, V5X_LE_PSTN_RS_S_LEPRS0_RESTART_NULL, 0, 0);
|
|
}
|
|
/* send restart complete to COMMON_CONTROL */
|
|
v5x_le_ctrl_common_snd(v5if, V5X_CTRL_ID_RESTART_COMPLETE, NULL, NULL, NULL);
|
|
/* unblock all PSTN user ports */
|
|
unblock_all_ports(v5if, false, false, true);
|
|
break;
|
|
case V5X_LE_PSTN_RS_E_TIMEOUT_TR2:
|
|
LOGP(DV5MGMT, LOGL_ERROR, "Remote PSTN Restart timed out.\n");
|
|
/* new state */
|
|
osmo_fsm_inst_state_chg(fi, V5X_LE_PSTN_RS_S_LEPRS0_RESTART_NULL, 0, 0);
|
|
/* send restart complete to COMMON_CONTROL */
|
|
v5x_le_ctrl_common_snd(v5if, V5X_CTRL_ID_RESTART_COMPLETE, NULL, NULL, NULL);
|
|
if (mgmt->do_align) {
|
|
/* unblock all PSTN user ports */
|
|
if (mgmt->acc_align && v5if->dialect == V5X_DIALECT_V52)
|
|
osmo_fsm_inst_dispatch(mgmt->unblk_all_fi[UNBLK_ALL_PSTN],
|
|
V5X_LE_UNBLK_ALL_E_Request_Unblock_Block, (void *)UNBLK_ALL_PSTN);
|
|
else
|
|
unblock_all_ports(v5if, false, false, true);
|
|
}
|
|
break;
|
|
case V5X_LE_PSTN_RS_E_Request_PSTN_Restart:
|
|
LOGP(DV5MGMT, LOGL_INFO, "PSTN Restart was requested locally.\n");
|
|
/* ignore */
|
|
break;
|
|
case V5X_LE_PSTN_RS_E_MDU_CTRL_restart_request:
|
|
LOGP(DV5MGMT, LOGL_INFO, "PSTN Restart was requested by remote.\n");
|
|
/* ignore */
|
|
break;
|
|
default:
|
|
OSMO_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
static const struct osmo_fsm_state v5x_le_pstn_rs_fsm_states[] = {
|
|
[V5X_LE_PSTN_RS_S_LEPRS0_RESTART_NULL] = {
|
|
.name = "RESTART NULL (LEPRS0)",
|
|
.in_event_mask = S(V5X_LE_PSTN_RS_E_MDU_CTRL_restart_request) |
|
|
S(V5X_LE_PSTN_RS_E_MDU_CTRL_restart_complete) |
|
|
S(V5X_LE_PSTN_RS_E_Request_PSTN_Restart),
|
|
.out_state_mask = S(V5X_LE_PSTN_RS_S_LEPRS1_RESTART),
|
|
.action = pstn_leprs0_restart_null,
|
|
},
|
|
[V5X_LE_PSTN_RS_S_LEPRS1_RESTART] = {
|
|
.name = "RESTART (LEPRS1)",
|
|
.in_event_mask = S(V5X_LE_PSTN_RS_E_MDU_CTRL_restart_complete) |
|
|
S(V5X_LE_PSTN_RS_E_Complete_PSTN_Restart) |
|
|
S(V5X_LE_PSTN_RS_E_TIMEOUT_TR1) |
|
|
S(V5X_LE_PSTN_RS_E_TIMEOUT_TR2) |
|
|
S(V5X_LE_PSTN_RS_E_MDU_CTRL_restart_request) |
|
|
S(V5X_LE_PSTN_RS_E_Request_PSTN_Restart),
|
|
.out_state_mask = S(V5X_LE_PSTN_RS_S_LEPRS0_RESTART_NULL),
|
|
.action = pstn_leprs1_restart,
|
|
},
|
|
};
|
|
|
|
struct osmo_fsm v5x_le_pstn_rs_fsm = {
|
|
.name = "PSTN_restart",
|
|
.states = v5x_le_pstn_rs_fsm_states,
|
|
.num_states = ARRAY_SIZE(v5x_le_pstn_rs_fsm_states),
|
|
.allstate_event_mask = 0,
|
|
.allstate_action = NULL,
|
|
.cleanup = NULL,
|
|
.timer_cb = NULL,
|
|
.log_subsys = DV5MGMT,
|
|
.event_names = v5x_le_pstn_rs_fsm_event_names,
|
|
};
|
|
|
|
const char *v5x_le_pstn_rs_fsm_state_name(struct osmo_fsm_inst *fi)
|
|
{
|
|
return v5x_le_pstn_rs_fsm_states[fi->state].name;
|
|
}
|
|
|
|
/***********************************************************************/
|
|
/* UNBLK/BLK ALL state FSM (5 instances used accelerated alignment) */
|
|
/***********************************************************************/
|
|
|
|
static int v5x_le_unblk_all_timer_cb(struct osmo_fsm_inst *fi)
|
|
{
|
|
if (fi->T == 1)
|
|
osmo_fsm_inst_dispatch(fi, V5X_LE_PSTN_RS_E_TIMEOUT_TR1, NULL);
|
|
if (fi->T == 2)
|
|
osmo_fsm_inst_dispatch(fi, V5X_LE_PSTN_RS_E_TIMEOUT_TR2, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void do_accelerated_alignment(struct v5x_interface *v5if, int ins)
|
|
{
|
|
switch (ins) {
|
|
case UNBLK_ALL_PSTN_ISDN:
|
|
unblock_all_ports(v5if, true, true, true);
|
|
break;
|
|
case UNBLK_ALL_PSTN:
|
|
unblock_all_ports(v5if, true, false, true);
|
|
break;
|
|
case UNBLK_ALL_ISDN:
|
|
unblock_all_ports(v5if, true, true, false);
|
|
break;
|
|
case BLK_ALL_PSTN:
|
|
block_all_ports(v5if, true, false, true);
|
|
break;
|
|
case BLK_ALL_ISDN:
|
|
block_all_ports(v5if, true, true, false);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void complete_accelerated_alignment(struct v5x_interface *v5if, int ins)
|
|
{
|
|
struct v5x_user_port *v5up;
|
|
|
|
if (ins == BLK_ALL_PSTN || ins == BLK_ALL_ISDN)
|
|
return;
|
|
|
|
llist_for_each_entry(v5up, &v5if->user_ports, list) {
|
|
if (ins == UNBLK_ALL_PSTN && !v5up->pstn.proto)
|
|
continue;
|
|
if (ins == UNBLK_ALL_ISDN && v5up->pstn.proto)
|
|
continue;
|
|
/* a complete set (ISDN/PSTN) was was unblocked, now we send blocking of actual blocked ports */
|
|
if ((ins == UNBLK_ALL_PSTN_ISDN || ins == UNBLK_ALL_PSTN || ins == UNBLK_ALL_ISDN)
|
|
&& !v5up->le_unblocked) {
|
|
LOGP(DV5MGMT, LOGL_INFO, " -> Send block indication to %s port %d.\n",
|
|
(v5up->type == V5X_USER_TYPE_PSTN) ? "PSTN" : "ISDN", v5up->nr);
|
|
v5x_port_mph_snd(v5up, MPH_BI);
|
|
}
|
|
/* a complete set (ISDN/PSTN) was was blocked, now we send unblocking of actual unblocked ports */
|
|
if ((ins == BLK_ALL_PSTN || ins == BLK_ALL_ISDN) && v5up->le_unblocked) {
|
|
LOGP(DV5MGMT, LOGL_INFO, " -> Send unblock request to %s port %d.\n",
|
|
(v5up->type == V5X_USER_TYPE_PSTN) ? "PSTN" : "ISDN", v5up->nr);
|
|
v5x_port_mph_snd(v5up, MPH_UBR);
|
|
}
|
|
}
|
|
}
|
|
static void unblk_all_null(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
|
{
|
|
struct v5x_mgmt_proto *mgmt = fi->priv;
|
|
struct v5x_interface *v5if = mgmt->interface;
|
|
/* instance */
|
|
long ins = (long)data;
|
|
|
|
switch (event) {
|
|
case V5X_LE_UNBLK_ALL_E_REQUEST:
|
|
if (!mgmt->acc_align) {
|
|
LOGP(DV5MGMT, LOGL_INFO, "Rejecting multiple port (un)blocking, It is not enabled!\n");
|
|
v5x_le_ctrl_common_snd(v5if, V5X_CTRL_ID_UNBLK_ALL_REL_PSTN_ISDN_REJECTED + ins*4,
|
|
NULL, NULL, NULL);
|
|
break;
|
|
}
|
|
/* sending accepted */
|
|
LOGP(DV5MGMT, LOGL_INFO, "Accepting multiple port (un)blocking, sending 'ACCEPTED' and perform it.\n");
|
|
v5x_le_ctrl_common_snd(v5if, V5X_CTRL_ID_UNBLK_ALL_REL_PSTN_ISDN_ACCEPTED + ins*4,
|
|
NULL, NULL, NULL);
|
|
/* start timer 2 and change state */
|
|
osmo_fsm_inst_state_chg(fi, V5X_LE_UNBLK_ALL_S_COMPLETED, TIMEOUT_TU2, 2);
|
|
/* silently align user ports */
|
|
do_accelerated_alignment(v5if, ins);
|
|
/* sending completed */
|
|
LOGP(DV5MGMT, LOGL_INFO, "Completed multiple port (un)blocking, sending 'COMPLETED'.\n");
|
|
v5x_le_ctrl_common_snd(v5if, V5X_CTRL_ID_UNBLK_ALL_REL_PSTN_ISDN_COMPLETED + ins*4,
|
|
NULL, NULL, NULL);
|
|
break;
|
|
case V5X_LE_UNBLK_ALL_E_Request_Unblock_Block:
|
|
/* start timer 1 and change state */
|
|
osmo_fsm_inst_state_chg(fi, V5X_LE_UNBLK_ALL_S_REQUESTED, TIMEOUT_TU1, 1);
|
|
/* sending request */
|
|
LOGP(DV5MGMT, LOGL_INFO, "Requesting multiple port (un)blocking, sending 'REQUEST'.\n");
|
|
v5x_le_ctrl_common_snd(v5if, V5X_CTRL_ID_UNBLK_ALL_REL_PSTN_ISDN_REQUEST + ins*4,
|
|
NULL, NULL, NULL);
|
|
break;
|
|
default:
|
|
OSMO_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
static void unblk_all_requested(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
|
{
|
|
struct v5x_mgmt_proto *mgmt = fi->priv;
|
|
struct v5x_interface *v5if = mgmt->interface;
|
|
/* instance */
|
|
long ins = (long)data;
|
|
|
|
switch (event) {
|
|
case V5X_LE_UNBLK_ALL_E_REQUEST:
|
|
/* sending accepted */
|
|
LOGP(DV5MGMT, LOGL_INFO, "Collision of multiple port (un)blocking, sending 'ACCEPTED'.\n");
|
|
v5x_le_ctrl_common_snd(v5if, V5X_CTRL_ID_UNBLK_ALL_REL_PSTN_ISDN_ACCEPTED + ins*4,
|
|
NULL, NULL, NULL);
|
|
break;
|
|
case V5X_LE_UNBLK_ALL_E_ACCEPTED:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Remote accepted multiple port (un)blocking, perform it.\n");
|
|
/* start timer 2 and change state */
|
|
osmo_fsm_inst_state_chg(fi, V5X_LE_UNBLK_ALL_S_COMPLETED, TIMEOUT_TU2, 2);
|
|
/* silently align user ports */
|
|
do_accelerated_alignment(v5if, ins);
|
|
/* sending completed */
|
|
LOGP(DV5MGMT, LOGL_INFO, "Completed multiple port (un)blocking, sending 'COMPLETED'.\n");
|
|
v5x_le_ctrl_common_snd(v5if, V5X_CTRL_ID_UNBLK_ALL_REL_PSTN_ISDN_COMPLETED + ins*4,
|
|
NULL, NULL, NULL);
|
|
break;
|
|
case V5X_LE_UNBLK_ALL_E_REJECTED:
|
|
LOGP(DV5MGMT, LOGL_ERROR, "Multiple port (un)blocking was rejected, aborting.\n");
|
|
/* strop timer 1 and change state */
|
|
osmo_fsm_inst_state_chg(fi, V5X_LE_UNBLK_ALL_S_NULL, 0, 0);
|
|
break;
|
|
case V5X_LE_UNBLK_ALL_E_TIMEOUT_TU1:
|
|
LOGP(DV5MGMT, LOGL_ERROR, "Multiple port (un)blocking timed out, aborting.\n");
|
|
/* change state */
|
|
osmo_fsm_inst_state_chg(fi, V5X_LE_UNBLK_ALL_S_NULL, 0, 0);
|
|
break;
|
|
default:
|
|
OSMO_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
static void unblk_all_completed(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
|
{
|
|
struct v5x_mgmt_proto *mgmt = fi->priv;
|
|
struct v5x_interface *v5if = mgmt->interface;
|
|
/* instance */
|
|
long ins = (long)data;
|
|
|
|
switch (event) {
|
|
case V5X_LE_UNBLK_ALL_E_COMPLETED:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Multiple port (un)blocking completed by remote.\n");
|
|
/* stop timer 2 and change state */
|
|
osmo_fsm_inst_state_chg(fi, V5X_LE_UNBLK_ALL_S_NULL, 0, 0);
|
|
/* send MPH-UBR/BI to align individual ports */
|
|
complete_accelerated_alignment(v5if, ins);
|
|
break;
|
|
case V5X_LE_UNBLK_ALL_E_TIMEOUT_TU2:
|
|
LOGP(DV5MGMT, LOGL_NOTICE, "Multiple port (un)blocking completed due to timeout.\n");
|
|
/* change state */
|
|
osmo_fsm_inst_state_chg(fi, V5X_LE_UNBLK_ALL_S_NULL, 0, 0);
|
|
/* send MPH-UBR/BI to align individual ports */
|
|
complete_accelerated_alignment(v5if, ins);
|
|
break;
|
|
default:
|
|
OSMO_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
static const struct osmo_fsm_state v5x_le_unblk_all_fsm_states[] = {
|
|
[V5X_LE_UNBLK_ALL_S_NULL] = {
|
|
.name = "NULL",
|
|
.in_event_mask = S(V5X_LE_UNBLK_ALL_E_REQUEST) |
|
|
S(V5X_LE_UNBLK_ALL_E_Request_Unblock_Block),
|
|
.out_state_mask = S(V5X_LE_UNBLK_ALL_S_REQUESTED) |
|
|
S(V5X_LE_UNBLK_ALL_S_COMPLETED),
|
|
.action = unblk_all_null,
|
|
},
|
|
[V5X_LE_UNBLK_ALL_S_REQUESTED] = {
|
|
.name = "REQUESTED",
|
|
.in_event_mask = S(V5X_LE_UNBLK_ALL_E_REQUEST) |
|
|
S(V5X_LE_UNBLK_ALL_E_ACCEPTED) |
|
|
S(V5X_LE_UNBLK_ALL_E_REJECTED) |
|
|
S(V5X_LE_UNBLK_ALL_E_TIMEOUT_TU1),
|
|
.out_state_mask = S(V5X_LE_UNBLK_ALL_S_COMPLETED) |
|
|
S(V5X_LE_UNBLK_ALL_S_NULL),
|
|
.action = unblk_all_requested,
|
|
},
|
|
[V5X_LE_UNBLK_ALL_S_COMPLETED] = {
|
|
.name = "COMPLETED",
|
|
.in_event_mask = S(V5X_LE_UNBLK_ALL_E_COMPLETED) |
|
|
S(V5X_LE_UNBLK_ALL_E_TIMEOUT_TU2),
|
|
.out_state_mask = S(V5X_LE_UNBLK_ALL_S_NULL),
|
|
.action = unblk_all_completed,
|
|
},
|
|
};
|
|
|
|
struct osmo_fsm v5x_le_unblk_all_fsm = {
|
|
.name = "UNBLOCK_BLOCK_ALL",
|
|
.states = v5x_le_unblk_all_fsm_states,
|
|
.num_states = ARRAY_SIZE(v5x_le_unblk_all_fsm_states),
|
|
.allstate_event_mask = 0,
|
|
.allstate_action = NULL,
|
|
.cleanup = NULL,
|
|
.timer_cb = v5x_le_unblk_all_timer_cb,
|
|
.log_subsys = DV5MGMT,
|
|
.event_names = v5x_le_unblk_all_fsm_event_names,
|
|
};
|
|
|
|
const char *v5x_le_unblk_all_fsm_state_name(struct osmo_fsm_inst *fi)
|
|
{
|
|
return v5x_le_unblk_all_fsm_states[fi->state].name;
|
|
}
|
|
|
|
/***********************************************************************/
|
|
/* create and destroy of all state FSMs */
|
|
/***********************************************************************/
|
|
|
|
void v5x_le_mgmt_init(void)
|
|
{
|
|
int rc;
|
|
|
|
rc = osmo_fsm_register(&v5x_le_system_fsm);
|
|
OSMO_ASSERT(!rc);
|
|
rc = osmo_fsm_register(&v5x_le_pstn_dl_fsm);
|
|
OSMO_ASSERT(!rc);
|
|
rc = osmo_fsm_register(&v5x_le_pstn_rs_fsm);
|
|
OSMO_ASSERT(!rc);
|
|
rc = osmo_fsm_register(&v5x_le_unblk_all_fsm);
|
|
OSMO_ASSERT(!rc);
|
|
LOGP(DV5MGMT, LOGL_NOTICE, "Using V5x management\n");
|
|
}
|
|
|
|
struct v5x_mgmt_proto *v5x_le_mgmt_create(struct v5x_interface *v5if)
|
|
{
|
|
struct v5x_mgmt_proto *mgmt;
|
|
int i;
|
|
|
|
mgmt = talloc_zero(v5if, struct v5x_mgmt_proto);
|
|
if (!mgmt)
|
|
return NULL;
|
|
mgmt->interface = v5if;
|
|
mgmt->auto_restart = true;
|
|
|
|
/* accellerated alignment when using V5.2 */
|
|
if (v5if->dialect == V5X_DIALECT_V52)
|
|
mgmt->acc_align = true;
|
|
|
|
mgmt->system_fi = osmo_fsm_inst_alloc(&v5x_le_system_fsm, mgmt, mgmt, LOGL_DEBUG, NULL);
|
|
if (!mgmt->system_fi)
|
|
goto error;
|
|
mgmt->pstn_dl_fi = osmo_fsm_inst_alloc(&v5x_le_pstn_dl_fsm, mgmt, mgmt, LOGL_DEBUG, NULL);
|
|
if (!mgmt->pstn_dl_fi)
|
|
goto error;
|
|
mgmt->pstn_rs_fi = osmo_fsm_inst_alloc(&v5x_le_pstn_rs_fsm, mgmt, mgmt, LOGL_DEBUG, NULL);
|
|
if (!mgmt->pstn_rs_fi)
|
|
goto error;
|
|
if (v5if->dialect == V5X_DIALECT_V52) {
|
|
for (i = 0; i < 5; i++) {
|
|
mgmt->unblk_all_fi[i] = osmo_fsm_inst_alloc(&v5x_le_unblk_all_fsm, mgmt, mgmt, LOGL_DEBUG,
|
|
NULL);
|
|
if (!mgmt->unblk_all_fi[i])
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
/* timer events */
|
|
mgmt->timer_system.data = mgmt;
|
|
mgmt->timer_system.cb = timeout_system;
|
|
mgmt->timer_tr1.data = mgmt;
|
|
mgmt->timer_tr1.cb = timeout_tr1;
|
|
mgmt->timer_tr2.data = mgmt;
|
|
mgmt->timer_tr2.cb = timeout_tr2;
|
|
mgmt->timer_tc1.data = mgmt;
|
|
mgmt->timer_tc1.cb = timeout_tc1;
|
|
mgmt->timer_tc2.data = mgmt;
|
|
mgmt->timer_tc2.cb = timeout_tc2;
|
|
mgmt->timer_tc3.data = mgmt;
|
|
mgmt->timer_tc3.cb = timeout_tc3;
|
|
mgmt->timer_tc8.data = mgmt;
|
|
mgmt->timer_tc8.cb = timeout_tc8;
|
|
mgmt->timer_tc9.data = mgmt;
|
|
mgmt->timer_tc9.cb = timeout_tc9;
|
|
mgmt->timer_tv1.data = mgmt;
|
|
mgmt->timer_tv1.cb = timeout_tv1;
|
|
|
|
return mgmt;
|
|
|
|
error:
|
|
v5x_le_mgmt_destroy(mgmt);
|
|
return NULL;
|
|
}
|
|
|
|
void v5x_le_mgmt_destroy(struct v5x_mgmt_proto *mgmt)
|
|
{
|
|
int i = 0;
|
|
|
|
if (mgmt->system_fi)
|
|
osmo_fsm_inst_free(mgmt->system_fi);
|
|
if (mgmt->pstn_dl_fi)
|
|
osmo_fsm_inst_free(mgmt->pstn_dl_fi);
|
|
if (mgmt->pstn_rs_fi)
|
|
osmo_fsm_inst_free(mgmt->pstn_rs_fi);
|
|
for (i = 0; i < 5; i++) {
|
|
if (mgmt->unblk_all_fi[i])
|
|
osmo_fsm_inst_free(mgmt->unblk_all_fi[i]);
|
|
}
|
|
|
|
stop_timer_system(mgmt);
|
|
stop_timer_tr1(mgmt);
|
|
stop_timer_tr2(mgmt);
|
|
stop_timer_tc1(mgmt);
|
|
stop_timer_tc2(mgmt);
|
|
stop_timer_tc3(mgmt);
|
|
stop_timer_tc8(mgmt);
|
|
stop_timer_tc9(mgmt);
|
|
stop_timer_tv1(mgmt);
|
|
|
|
talloc_free(mgmt);
|
|
}
|
|
|
|
/***********************************************************************/
|
|
/* trigger processes */
|
|
/***********************************************************************/
|
|
|
|
/* do system startup after given delay */
|
|
void v5x_le_mgmt_start_delay(struct v5x_interface *v5if, int delay)
|
|
{
|
|
start_timer_system(v5if->mgmt, delay);
|
|
}
|
|
|
|
/* do system startup now */
|
|
int v5x_le_mgmt_start(struct v5x_interface *v5if)
|
|
{
|
|
if (v5if->mgmt->system_fi->state != V5X_LE_SYSTEM_S_LESYS0_SYSTEM_STARTUP
|
|
&& v5if->mgmt->system_fi->state != V5X_LE_SYSTEM_S_LESYS8_SYSTEM_DEACTIVATED)
|
|
return -EINVAL;
|
|
|
|
osmo_fsm_inst_dispatch(v5if->mgmt->system_fi, V5X_LE_SYSTEM_E_START, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* do PSTN restart */
|
|
int v5x_le_pstn_restart(struct v5x_interface *v5if)
|
|
{
|
|
if (v5if->mgmt->system_fi->state != V5X_LE_SYSTEM_S_LESYS2_IN_SERVICE)
|
|
return -EINVAL;
|
|
|
|
osmo_fsm_inst_dispatch(v5if->mgmt->pstn_rs_fi, V5X_LE_PSTN_RS_E_Request_PSTN_Restart, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* do alignment of all ports */
|
|
int v5x_le_align_ports(struct v5x_interface *v5if, int accelerated)
|
|
{
|
|
if (v5if->mgmt->system_fi->state != V5X_LE_SYSTEM_S_LESYS2_IN_SERVICE)
|
|
return -EINVAL;
|
|
|
|
/* unblock all ISDN user ports */
|
|
if (accelerated && v5if->dialect == V5X_DIALECT_V52)
|
|
osmo_fsm_inst_dispatch(v5if->mgmt->unblk_all_fi[UNBLK_ALL_PSTN_ISDN],
|
|
V5X_LE_UNBLK_ALL_E_Request_Unblock_Block, (void *)UNBLK_ALL_PSTN_ISDN);
|
|
else
|
|
align_all_ports(v5if->mgmt->interface);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/***********************************************************************/
|
|
/* messages from other layers */
|
|
/***********************************************************************/
|
|
|
|
static bool v5x_le_mgmt_port_is_in_service(struct v5x_user_port *v5up)
|
|
{
|
|
if (v5up->interface->mgmt->system_fi->state != V5X_LE_SYSTEM_S_LESYS2_IN_SERVICE)
|
|
return false;
|
|
if (v5up->interface->mgmt->pstn_dl_fi->state != V5X_LE_PSTN_DL_S_LEPDL2_PSTN_IN_SERVICE)
|
|
return false;
|
|
if (v5up->interface->mgmt->pstn_rs_fi->state != V5X_LE_PSTN_RS_S_LEPRS0_RESTART_NULL)
|
|
return false;
|
|
return v5x_le_ctrl_is_in_service(v5up->ctrl);
|
|
}
|
|
|
|
/* messages from data link layer */
|
|
void v5x_le_mdl_rcv(struct v5x_interface *v5if, struct v5x_link *v5l, enum v5x_mgmt_prim prim, uint16_t dladdr)
|
|
{
|
|
switch (prim) {
|
|
case MDL_ESTABLISH_ind:
|
|
case MDL_ESTABLISH_cnf:
|
|
switch (dladdr) {
|
|
case V5X_DLADDR_CTRL:
|
|
v5if->control.established = true;
|
|
osmo_fsm_inst_dispatch(v5if->mgmt->system_fi, V5X_LE_SYSTEM_E_CONTROL_DL_ESTABLISHED, NULL);
|
|
break;
|
|
case V5X_DLADDR_PSTN:
|
|
v5if->pstn.established = true;
|
|
osmo_fsm_inst_dispatch(v5if->mgmt->pstn_dl_fi, V5X_LE_PSTN_DL_E_PSTN_DL_ESTABLISHED, NULL);
|
|
break;
|
|
case V52_DLADDR_LCP:
|
|
v5if->lcp.established = true;
|
|
/* start CONTROL_LINK FSM */
|
|
llist_for_each_entry(v5l, &v5if->links, list)
|
|
v5x_le_ctrl_start(v5l->ctrl);
|
|
break;
|
|
case V52_DLADDR_BCC:
|
|
v5if->bcc.established = true;
|
|
break;
|
|
case V52_DLADDR_PROTECTION:
|
|
if (v5l == v5if->primary_link)
|
|
v5if->protection.established[0] = true;
|
|
if (v5l == v5if->secondary_link)
|
|
v5if->protection.established[1] = true;
|
|
break;
|
|
}
|
|
break;
|
|
case MDL_RELEASE_ind:
|
|
switch (dladdr) {
|
|
case V5X_DLADDR_CTRL:
|
|
v5if->control.established = false;
|
|
osmo_fsm_inst_dispatch(v5if->mgmt->system_fi, V5X_LE_SYSTEM_E_CONTROL_DL_RELEASED, NULL);
|
|
break;
|
|
case V5X_DLADDR_PSTN:
|
|
v5if->pstn.established = false;
|
|
osmo_fsm_inst_dispatch(v5if->mgmt->pstn_dl_fi, V5X_LE_PSTN_DL_E_PSTN_DL_RELEASED, NULL);
|
|
break;
|
|
case V52_DLADDR_LCP:
|
|
if (v5if->dialect != V5X_DIALECT_V52)
|
|
break;
|
|
v5if->lcp.established = false;
|
|
/* re-establish DL */
|
|
if (v5if->mgmt->do_est)
|
|
lapv5_dl_est_req(v5if->lcp.li, V52_DLADDR_LCP);
|
|
/* stop CONTROL_LINK FSM */
|
|
llist_for_each_entry(v5l, &v5if->links, list)
|
|
v5x_le_ctrl_stop(v5l->ctrl);
|
|
break;
|
|
case V52_DLADDR_BCC:
|
|
if (v5if->dialect != V5X_DIALECT_V52)
|
|
break;
|
|
v5if->bcc.established = false;
|
|
/* re-establish DL */
|
|
if (v5if->mgmt->do_est)
|
|
lapv5_dl_est_req(v5if->bcc.li, V52_DLADDR_BCC);
|
|
break;
|
|
case V52_DLADDR_PROTECTION:
|
|
if (v5if->dialect != V5X_DIALECT_V52)
|
|
break;
|
|
if (v5l == v5if->primary_link) {
|
|
v5if->protection.established[0] = false;
|
|
/* re-establish DL */
|
|
if (v5if->mgmt->do_est)
|
|
lapv5_dl_est_req(v5if->protection.li[0], V52_DLADDR_PROTECTION);
|
|
}
|
|
if (v5l == v5if->secondary_link) {
|
|
v5if->protection.established[1] = false;
|
|
/* re-establish DL */
|
|
if (v5if->mgmt->do_est)
|
|
lapv5_dl_est_req(v5if->protection.li[1], V52_DLADDR_PROTECTION);
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
case MDL_ERROR_ind:
|
|
case MDL_LAYER_1_FAILURE_ind:
|
|
break;
|
|
default:
|
|
LOGP(DV5MGMT, LOGL_NOTICE, "Ignoring data link message %d from address = %d\n", prim, dladdr);
|
|
}
|
|
}
|
|
|
|
/* messages from common control layer */
|
|
void v5x_le_common_ctrl_rcv(struct v5x_interface *v5if, uint8_t cfi, uint8_t variant, uint8_t rej_cause,
|
|
uint32_t interface_id)
|
|
{
|
|
struct variant_interface_id vi;
|
|
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Received common control message from control protocol. "
|
|
"(cfi = %d, variant = %d, rej_cause = %d, interface_id = %d)\n", cfi, variant, rej_cause, interface_id);
|
|
switch (cfi) {
|
|
/* variant & interface ID */
|
|
case V5X_CTRL_ID_REQUEST_VARIANT_AND_INTERFACE_ID:
|
|
LOGP(DV5MGMT, LOGL_INFO, "AN requests variant and interface ID, replying.\n");
|
|
v5x_le_ctrl_common_snd(v5if, V5X_CTRL_ID_VARIANT_AND_INTERFACE_ID, NULL, &v5if->variant_local,
|
|
&v5if->id_local);
|
|
break;
|
|
case V5X_CTRL_ID_VARIANT_AND_INTERFACE_ID:
|
|
vi.variant = variant;
|
|
vi.interface_id = interface_id;
|
|
osmo_fsm_inst_dispatch(v5if->mgmt->system_fi, V5X_LE_SYSTEM_E_VARIANT_AND_ID, &vi);
|
|
break;
|
|
/* PSTN restart */
|
|
case V5X_CTRL_ID_RESTART_REQUEST:
|
|
osmo_fsm_inst_dispatch(v5if->mgmt->pstn_rs_fi, V5X_LE_PSTN_RS_E_MDU_CTRL_restart_request, NULL);
|
|
break;
|
|
case V5X_CTRL_ID_RESTART_COMPLETE:
|
|
osmo_fsm_inst_dispatch(v5if->mgmt->pstn_rs_fi, V5X_LE_PSTN_RS_E_MDU_CTRL_restart_complete, NULL);
|
|
break;
|
|
/* accelerated aligment: request */
|
|
case V5X_CTRL_ID_UNBLK_ALL_REL_PSTN_ISDN_REQUEST:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Unblock of all related PSTN and ISDN ports received.\n");
|
|
osmo_fsm_inst_dispatch(v5if->mgmt->unblk_all_fi[UNBLK_ALL_PSTN_ISDN], V5X_LE_UNBLK_ALL_E_REQUEST,
|
|
(void *)UNBLK_ALL_PSTN_ISDN);
|
|
break;
|
|
case V5X_CTRL_ID_UNBLK_ALL_REL_PSTN_REQUEST:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Unblock of all related PSTN ports received.\n");
|
|
osmo_fsm_inst_dispatch(v5if->mgmt->unblk_all_fi[UNBLK_ALL_PSTN], V5X_LE_UNBLK_ALL_E_REQUEST,
|
|
(void *)UNBLK_ALL_PSTN);
|
|
break;
|
|
case V5X_CTRL_ID_UNBLK_ALL_REL_ISDN_REQUEST:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Unblock of all related ISDN ports received.\n");
|
|
osmo_fsm_inst_dispatch(v5if->mgmt->unblk_all_fi[UNBLK_ALL_ISDN], V5X_LE_UNBLK_ALL_E_REQUEST,
|
|
(void *)UNBLK_ALL_ISDN);
|
|
break;
|
|
case V5X_CTRL_ID_BLK_ALL_PSTN_REQUEST:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Block of all related PSTN ports received.\n");
|
|
osmo_fsm_inst_dispatch(v5if->mgmt->unblk_all_fi[BLK_ALL_PSTN], V5X_LE_UNBLK_ALL_E_REQUEST,
|
|
(void *)BLK_ALL_PSTN);
|
|
break;
|
|
case V5X_CTRL_ID_BLK_ALL_ISDN_REQUEST:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Block of all related ISDN ports received.\n");
|
|
osmo_fsm_inst_dispatch(v5if->mgmt->unblk_all_fi[BLK_ALL_ISDN], V5X_LE_UNBLK_ALL_E_REQUEST,
|
|
(void *)BLK_ALL_ISDN);
|
|
break;
|
|
/* accelerated aligment: accepted */
|
|
case V5X_CTRL_ID_UNBLK_ALL_REL_PSTN_ISDN_ACCEPTED:
|
|
osmo_fsm_inst_dispatch(v5if->mgmt->unblk_all_fi[UNBLK_ALL_PSTN_ISDN], V5X_LE_UNBLK_ALL_E_ACCEPTED,
|
|
(void *)UNBLK_ALL_PSTN_ISDN);
|
|
break;
|
|
case V5X_CTRL_ID_UNBLK_ALL_REL_PSTN_ACCEPTED:
|
|
osmo_fsm_inst_dispatch(v5if->mgmt->unblk_all_fi[UNBLK_ALL_PSTN], V5X_LE_UNBLK_ALL_E_ACCEPTED,
|
|
(void *)UNBLK_ALL_PSTN);
|
|
break;
|
|
case V5X_CTRL_ID_UNBLK_ALL_REL_ISDN_ACCEPTED:
|
|
osmo_fsm_inst_dispatch(v5if->mgmt->unblk_all_fi[UNBLK_ALL_ISDN], V5X_LE_UNBLK_ALL_E_ACCEPTED,
|
|
(void *)UNBLK_ALL_ISDN);
|
|
break;
|
|
case V5X_CTRL_ID_BLK_ALL_PSTN_ACCEPTED:
|
|
osmo_fsm_inst_dispatch(v5if->mgmt->unblk_all_fi[BLK_ALL_PSTN], V5X_LE_UNBLK_ALL_E_ACCEPTED,
|
|
(void *)BLK_ALL_PSTN);
|
|
break;
|
|
case V5X_CTRL_ID_BLK_ALL_ISDN_ACCEPTED:
|
|
osmo_fsm_inst_dispatch(v5if->mgmt->unblk_all_fi[BLK_ALL_ISDN], V5X_LE_UNBLK_ALL_E_ACCEPTED,
|
|
(void *)BLK_ALL_ISDN);
|
|
break;
|
|
/* accelerated aligment: rejected */
|
|
case V5X_CTRL_ID_UNBLK_ALL_REL_PSTN_ISDN_REJECTED:
|
|
osmo_fsm_inst_dispatch(v5if->mgmt->unblk_all_fi[UNBLK_ALL_PSTN_ISDN], V5X_LE_UNBLK_ALL_E_REJECTED,
|
|
(void *)UNBLK_ALL_PSTN_ISDN);
|
|
break;
|
|
case V5X_CTRL_ID_UNBLK_ALL_REL_PSTN_REJECTED:
|
|
osmo_fsm_inst_dispatch(v5if->mgmt->unblk_all_fi[UNBLK_ALL_PSTN], V5X_LE_UNBLK_ALL_E_REJECTED,
|
|
(void *)UNBLK_ALL_PSTN);
|
|
break;
|
|
case V5X_CTRL_ID_UNBLK_ALL_REL_ISDN_REJECTED:
|
|
osmo_fsm_inst_dispatch(v5if->mgmt->unblk_all_fi[UNBLK_ALL_ISDN], V5X_LE_UNBLK_ALL_E_REJECTED,
|
|
(void *)UNBLK_ALL_ISDN);
|
|
break;
|
|
case V5X_CTRL_ID_BLK_ALL_PSTN_REJECTED:
|
|
osmo_fsm_inst_dispatch(v5if->mgmt->unblk_all_fi[BLK_ALL_PSTN], V5X_LE_UNBLK_ALL_E_REJECTED,
|
|
(void *)BLK_ALL_PSTN);
|
|
break;
|
|
case V5X_CTRL_ID_BLK_ALL_ISDN_REJECTED:
|
|
osmo_fsm_inst_dispatch(v5if->mgmt->unblk_all_fi[BLK_ALL_ISDN], V5X_LE_UNBLK_ALL_E_REJECTED,
|
|
(void *)BLK_ALL_ISDN);
|
|
break;
|
|
/* accelerated aligment: completed */
|
|
case V5X_CTRL_ID_UNBLK_ALL_REL_PSTN_ISDN_COMPLETED:
|
|
osmo_fsm_inst_dispatch(v5if->mgmt->unblk_all_fi[UNBLK_ALL_PSTN_ISDN], V5X_LE_UNBLK_ALL_E_COMPLETED,
|
|
(void *)UNBLK_ALL_PSTN_ISDN);
|
|
break;
|
|
case V5X_CTRL_ID_UNBLK_ALL_REL_PSTN_COMPLETED:
|
|
osmo_fsm_inst_dispatch(v5if->mgmt->unblk_all_fi[UNBLK_ALL_PSTN], V5X_LE_UNBLK_ALL_E_COMPLETED,
|
|
(void *)UNBLK_ALL_PSTN);
|
|
break;
|
|
case V5X_CTRL_ID_UNBLK_ALL_REL_ISDN_COMPLETED:
|
|
osmo_fsm_inst_dispatch(v5if->mgmt->unblk_all_fi[UNBLK_ALL_ISDN], V5X_LE_UNBLK_ALL_E_COMPLETED,
|
|
(void *)UNBLK_ALL_ISDN);
|
|
break;
|
|
case V5X_CTRL_ID_BLK_ALL_PSTN_COMPLETED:
|
|
osmo_fsm_inst_dispatch(v5if->mgmt->unblk_all_fi[BLK_ALL_PSTN], V5X_LE_UNBLK_ALL_E_COMPLETED,
|
|
(void *)BLK_ALL_PSTN);
|
|
break;
|
|
case V5X_CTRL_ID_BLK_ALL_ISDN_COMPLETED:
|
|
osmo_fsm_inst_dispatch(v5if->mgmt->unblk_all_fi[BLK_ALL_ISDN], V5X_LE_UNBLK_ALL_E_COMPLETED,
|
|
(void *)BLK_ALL_ISDN);
|
|
break;
|
|
default:
|
|
LOGP(DV5MGMT, LOGL_NOTICE, "Ignoring common control message with cfi = %d\n", cfi);
|
|
}
|
|
}
|
|
|
|
void v5x_le_mgmt_port_unblock(struct v5x_user_port *v5up)
|
|
{
|
|
v5up->le_unblocked = true;
|
|
if (v5x_le_mgmt_port_is_in_service(v5up)) {
|
|
v5x_port_mph_snd(v5up, MPH_UBR);
|
|
if (v5up->pstn.proto && v5up->an_unblocked) {
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Send port unblocked control to PSTN port %d.\n",
|
|
v5up->nr);
|
|
v5x_le_pstn_mdu_snd(v5up, MDU_CTRL_port_unblocked);
|
|
}
|
|
}
|
|
if (!v5up->an_unblocked) {
|
|
LOGP(DV5MGMT, LOGL_INFO, "Reply with blocking, because AN is blocked.\n");
|
|
uint8_t ctrl = PH_CTRL_BLOCK;
|
|
ph_socket_tx_msg(&v5up->ph_socket, v5up->type == V5X_USER_TYPE_ISDN ? 3 : 0,
|
|
PH_PRIM_CTRL_IND, &ctrl, 1);
|
|
}
|
|
}
|
|
|
|
void v5x_le_mgmt_port_block(struct v5x_user_port *v5up)
|
|
{
|
|
v5up->le_unblocked = false;
|
|
if (v5x_le_mgmt_port_is_in_service(v5up)) {
|
|
v5x_port_mph_snd(v5up, MPH_BI);
|
|
if (v5up->pstn.proto) {
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Send port blocked control to PSTN port %d.\n",
|
|
v5up->nr);
|
|
v5x_le_pstn_mdu_snd(v5up, MDU_CTRL_port_blocked);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* received management primitives from LE (NAT protocol via PH-socket) */
|
|
void v5x_le_nat_ph_rcv(struct v5x_user_port *v5up, uint8_t prim, uint8_t *data, int length)
|
|
{
|
|
ph_socket_t *s = &v5up->ph_socket;
|
|
|
|
switch (prim) {
|
|
case PH_PRIM_CTRL_REQ:
|
|
if (length && *data == PH_CTRL_UNBLOCK) {
|
|
LOGP(DV5MGMT, LOGL_INFO, "Received unblocking from LE of %s port %d.\n",
|
|
(v5up->type == V5X_USER_TYPE_PSTN) ? "PSTN" : "ISDN", v5up->nr);
|
|
v5x_le_mgmt_port_unblock(v5up);
|
|
}
|
|
if (length && *data == PH_CTRL_BLOCK) {
|
|
LOGP(DV5MGMT, LOGL_INFO, "Received blocking from LE of %s port %d.\n",
|
|
(v5up->type == V5X_USER_TYPE_PSTN) ? "PSTN" : "ISDN", v5up->nr);
|
|
v5x_le_mgmt_port_block(v5up);
|
|
}
|
|
break;
|
|
case PH_PRIM_ACT_REQ:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Received activation from LE of %s port %d.\n",
|
|
(v5up->type == V5X_USER_TYPE_PSTN) ? "PSTN" : "ISDN", v5up->nr);
|
|
if (!v5up->an_unblocked) {
|
|
ph_socket_tx_msg(s, 3, PH_PRIM_DACT_IND, NULL, 0);
|
|
break;
|
|
}
|
|
v5x_port_mph_snd(v5up, MPH_AR);
|
|
break;
|
|
case PH_PRIM_DACT_REQ:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Received deactivation from LE of %s port %d.\n",
|
|
(v5up->type == V5X_USER_TYPE_PSTN) ? "PSTN" : "ISDN", v5up->nr);
|
|
v5x_port_mph_snd(v5up, MPH_DR);
|
|
break;
|
|
default:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Received unknown prim %d from LE for %s port %d.\n", prim,
|
|
(v5up->type == V5X_USER_TYPE_PSTN) ? "PSTN" : "ISDN", v5up->nr);
|
|
}
|
|
}
|
|
|
|
/* received management primitives from AN (via port control protocol) */
|
|
void v5x_le_port_mph_rcv(struct v5x_user_port *v5up, enum v5x_mph_prim prim, uint8_t perf_grading)
|
|
{
|
|
switch (prim) {
|
|
case MPH_UBR:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Received unblocking request from AN of %s port %d.\n",
|
|
(v5up->type == V5X_USER_TYPE_PSTN) ? "PSTN" : "ISDN", v5up->nr);
|
|
if (v5up->le_unblocked) {
|
|
LOGP(DV5MGMT, LOGL_INFO, "Reply with unblocking request, because port available at LE.\n");
|
|
v5x_port_mph_snd(v5up, MPH_UBR);
|
|
block_unblock_socket(v5up, false);
|
|
if (v5up->pstn.proto) {
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Send port unblocked control to PSTN port %d.\n", v5up->nr);
|
|
v5x_le_pstn_mdu_snd(v5up, MDU_CTRL_port_unblocked);
|
|
}
|
|
} else {
|
|
LOGP(DV5MGMT, LOGL_INFO, "Reply with blocking indication, because port unavailable at LE.\n");
|
|
v5up->an_unblocked = false;
|
|
v5x_port_mph_snd(v5up, MPH_BI);
|
|
}
|
|
break;
|
|
case MPH_UBI:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Received unblocking indication from AN of %s port %d.\n",
|
|
(v5up->type == V5X_USER_TYPE_PSTN) ? "PSTN" : "ISDN", v5up->nr);
|
|
block_unblock_socket(v5up, false);
|
|
if (v5up->pstn.proto) {
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Send port unblocked control to PSTN port %d.\n", v5up->nr);
|
|
v5x_le_pstn_mdu_snd(v5up, MDU_CTRL_port_unblocked);
|
|
}
|
|
break;
|
|
case MPH_BR:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Received blocking request from AN of %s port %d.\n",
|
|
(v5up->type == V5X_USER_TYPE_PSTN) ? "PSTN" : "ISDN", v5up->nr);
|
|
LOGP(DV5MGMT, LOGL_INFO, "Reply with blocking indication.\n");
|
|
v5x_port_mph_snd(v5up, MPH_BI);
|
|
block_unblock_socket(v5up, true);
|
|
if (v5up->pstn.proto) {
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Send port blocked control to PSTN port %d.\n", v5up->nr);
|
|
v5x_le_pstn_mdu_snd(v5up, MDU_CTRL_port_blocked);
|
|
}
|
|
break;
|
|
case MPH_BI:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Received blocking indication from AN of %s port %d.\n",
|
|
(v5up->type == V5X_USER_TYPE_PSTN) ? "PSTN" : "ISDN", v5up->nr);
|
|
block_unblock_socket(v5up, true);
|
|
if (v5up->pstn.proto) {
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Send port blocked control to PSTN port %d.\n", v5up->nr);
|
|
v5x_le_pstn_mdu_snd(v5up, MDU_CTRL_port_blocked);
|
|
}
|
|
break;
|
|
case MPH_AI:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Received activation indication from AN of ISDN port %d.\n", v5up->nr);
|
|
ph_socket_tx_msg(&v5up->ph_socket, 3, PH_PRIM_ACT_IND, NULL, 0);
|
|
break;
|
|
case MPH_AWI:
|
|
break;
|
|
case MPH_DSAI:
|
|
break;
|
|
case MPH_DI:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Received deactivation indication from AN of ISDN port %d.\n", v5up->nr);
|
|
ph_socket_tx_msg(&v5up->ph_socket, 3, PH_PRIM_DACT_IND, NULL, 0);
|
|
break;
|
|
case MPH_GI:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Received performance grading from AN for ISDN port %d: %s\n", v5up->nr,
|
|
(perf_grading) ? "Degraded" : "Normal grade");
|
|
break;
|
|
default:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Received unknown prim %d from AN for %s port %d.\n", prim, (v5up->type == V5X_USER_TYPE_PSTN) ? "PSTN" : "ISDN", v5up->nr);
|
|
}
|
|
}
|
|
|
|
/* receive MDU primitives from PSTN protocol */
|
|
void v5x_le_pstn_mdu_rcv(struct v5x_user_port *v5up, enum v5x_mgmt_prim prim)
|
|
{
|
|
switch (prim) {
|
|
case MDU_error_ind:
|
|
LOGP(DV5MGMT, LOGL_NOTICE, "Received MDU-error_indication from PSTN protocol of port %d.\n", v5up->nr);
|
|
break;
|
|
case MDU_CTRL_port_restart_ack:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Received restart acknowledge from PSTN protocol of port %d.\n", v5up->nr);
|
|
break;
|
|
default:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Received unknown prim %d from PSTN protocol for port %d.\n", prim, v5up->nr);
|
|
}
|
|
}
|
|
|
|
/* flush all channels from non-operational link */
|
|
static void flush_link(struct v5x_link *v5l)
|
|
{
|
|
struct v5x_user_port *v5up;
|
|
int t;
|
|
|
|
for (t = 1; t <= 31; t++) {
|
|
if (!v5l->ts[t].b_channel)
|
|
continue;
|
|
if ((v5up = v5l->ts[t].v5up)) {
|
|
if (v5up->ts[0] == &v5l->ts[t]) {
|
|
v5up->ts[0] = NULL;
|
|
ph_deactivate_req(&v5l->ts[t]);
|
|
}
|
|
if (v5up->ts[1] == &v5l->ts[t]) {
|
|
v5up->ts[1] = NULL;
|
|
ph_deactivate_req(&v5l->ts[t]);
|
|
}
|
|
v5l->ts[t].v5up = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Part 2: LCP handling
|
|
*/
|
|
|
|
/* receive MDU primitives from LCP protocol */
|
|
void v52_le_lcp_mdu_rcv(struct v5x_link *v5l, enum v5x_mgmt_prim prim)
|
|
{
|
|
switch (prim) {
|
|
case MDU_AI:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Link %d is active.\n", v5l->id);
|
|
break;
|
|
case MDU_DI:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Link %d is inactive.\n", v5l->id);
|
|
break;
|
|
case MDU_LAI:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Link %d becomes active in blocked state, try unblocking.\n", v5l->id);
|
|
v52_le_lcp_mdu_snd(v5l, MDU_LUBR);
|
|
break;
|
|
case MDU_IDReq:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Remote side perform link ID check of link %d, replying.\n", v5l->id);
|
|
v52_le_lcp_mdu_snd(v5l, MDU_IDAck);
|
|
break;
|
|
case MDU_IDAck:
|
|
break;
|
|
case MDU_IDRel:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Remote side ID check done.\n");
|
|
break;
|
|
case MDU_IDRej:
|
|
break;
|
|
case MDU_EIg:
|
|
break;
|
|
case MDU_LUBR:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Remote side unblocks link ID %d, replying.\n", v5l->id);
|
|
v52_le_lcp_mdu_snd(v5l, MDU_LUBR);
|
|
break;
|
|
case MDU_LUBI:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Remote side link ID %d is now unblocked.\n", v5l->id);
|
|
break;
|
|
case MDU_LBI:
|
|
LOGP(DV5MGMT, LOGL_INFO, "LCP indicated blocked link ID %d.\n", v5l->id);
|
|
flush_link(v5l);
|
|
break;
|
|
case MDU_LBR:
|
|
case MDU_LBRN:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Remote request blocking of link link ID %d, replying.\n", v5l->id);
|
|
flush_link(v5l);
|
|
v52_le_lcp_mdu_snd(v5l, MDU_LBI);
|
|
break;
|
|
default:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Received unknown prim %d from LCP protocol for link ID %d.\n", prim, v5l->id);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Part 3: forwarding messages between PSTN and gateway application
|
|
*/
|
|
|
|
static const struct value_string v5x_fe_prim_names[] = {
|
|
{ FE_establish_req, "FE-establish_req" },
|
|
{ FE_establish_ack_ind, "FE-establish_ack_ind" },
|
|
{ FE_establish_ind, "FE-establish_ind" },
|
|
{ FE_establish_ack_req, "FE-establish_ack_req" },
|
|
{ FE_line_signal_req, "FE-line_signal_req" },
|
|
{ FE_line_signal_ind, "FE-line_signal_ind" },
|
|
{ FE_protocol_param_req, "FE-protocol_param_req" },
|
|
{ FE_disconnect_req, "FE-disconnect_req" },
|
|
{ FE_disconnect_compl_req, "FE-disconnect_compl_req" },
|
|
{ FE_disconnect_compl_ind, "FE-disconnect_compl_ind" },
|
|
{ 0, NULL }
|
|
};
|
|
|
|
/* receive FE-message from NAT (PH-socket) and forward it to the PSTN protocol */
|
|
void v5x_le_nat_fe_rcv(struct v5x_user_port *v5up, struct msgb *msg)
|
|
{
|
|
enum v5x_fe_prim prim;
|
|
|
|
prim = *msg->data;
|
|
msgb_pull(msg, 1);
|
|
|
|
if (!v5x_le_port_pstn_is_operational(v5up->port_fi)) {
|
|
LOGP(DV5MGMT, LOGL_NOTICE, "Dropping (NAT->LE) message of non-operational PSTN port for L3Addr %d.\n",
|
|
v5up->nr);
|
|
msgb_free(msg);
|
|
return;
|
|
}
|
|
LOGP(DV5MGMT, LOGL_INFO, "FE message: %s NAT -> PSTN_LE of PSTN port %d.\n",
|
|
get_value_string(v5x_fe_prim_names, prim), v5up->nr);
|
|
v5x_le_pstn_fe_snd(v5up, prim, msg);
|
|
}
|
|
|
|
/* receive FE-message from PSTN protocol and forward it to NAT (PH-socket) */
|
|
void v5x_le_pstn_fe_rcv(struct v5x_user_port *v5up, enum v5x_fe_prim prim, struct msgb *msg)
|
|
{
|
|
*msgb_push(msg, 1) = prim;
|
|
|
|
if (!v5x_le_port_pstn_is_operational(v5up->port_fi) && prim != FE_disconnect_compl_ind) {
|
|
LOGP(DV5MGMT, LOGL_NOTICE, "Dropping (AN->NAT) message of non-operational PSTN port for L3Addr %d.\n",
|
|
v5up->nr);
|
|
msgb_free(msg);
|
|
return;
|
|
}
|
|
LOGP(DV5MGMT, LOGL_INFO, "FE message: %s PSTN_LE -> NAT of PSTN port %d.\n",
|
|
get_value_string(v5x_fe_prim_names, prim), v5up->nr);
|
|
ph_socket_tx_msg(&v5up->ph_socket, 0, PH_PRIM_DATA_IND, msg->data, msg->len);
|
|
|
|
msgb_free(msg);
|
|
}
|
|
|
|
/*
|
|
* Part 4: BCC channel handling, also activation/deactivation for V5.1
|
|
*/
|
|
|
|
/* assign channel */
|
|
void v5x_le_channel_assign(struct v5x_user_port *v5up, int channel)
|
|
{
|
|
struct v5x_interface *v5if = v5up->interface;
|
|
struct v5x_link *v5l = NULL;
|
|
int found, t;
|
|
|
|
OSMO_ASSERT(channel >= 1 && channel <= 2);
|
|
|
|
/* reset echo canceler state */
|
|
v5x_echo_reset(&v5up->ep[channel - 1], v5up->use_line_echo);
|
|
|
|
/* the channels are pre-assigned with V5.1, so only activation state is set */
|
|
if (v5if->dialect == V5X_DIALECT_V51) {
|
|
if (!v5up->ts[channel - 1]) {
|
|
LOGP(DV5MGMT, LOGL_ERROR, "Cannot activate channel, because no TS is assigned for %s port "
|
|
"%d.\n", (v5up->type == V5X_USER_TYPE_PSTN) ? "PSTN" : "ISDN", v5l->id);
|
|
return;
|
|
}
|
|
if (v5up->ts[channel - 1]->b_activated)
|
|
return;
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Activating channel %d for %s port %d at TS %d of link %d.\n",
|
|
channel, (v5up->type == V5X_USER_TYPE_PSTN) ? "PSTN" : "ISDN", v5up->nr,
|
|
v5up->ts[channel - 1]->nr, v5up->ts[channel - 1]->link->id);
|
|
ph_activate_req(v5up->ts[channel - 1]);
|
|
return;
|
|
}
|
|
|
|
/* channel already assigned, just activate it, if not already */
|
|
if (v5up->ts[channel - 1]) {
|
|
ph_activate_req(v5up->ts[channel - 1]);
|
|
return;
|
|
}
|
|
|
|
/* hunt for a channel */
|
|
found = 0;
|
|
llist_for_each_entry(v5l, &v5if->links, list) {
|
|
if (!v52_le_lcp_is_operational(v5l->fi)) {
|
|
LOGP(DV5MGMT, LOGL_NOTICE, "While searching for a bearer channel: Skipping link with ID %d, "
|
|
"because it is not operational.\n", v5l->id);
|
|
continue;
|
|
}
|
|
for (t = 1; t <= 31; t++) {
|
|
if (!v5l->ts[t].b_channel)
|
|
continue;
|
|
if (!v5l->ts[t].v5up) {
|
|
found = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (found)
|
|
break;
|
|
}
|
|
if (!found) {
|
|
LOGP(DV5MGMT, LOGL_NOTICE, "No free channel found.\n");
|
|
return;
|
|
}
|
|
|
|
/* assign channel */
|
|
v5l->ts[t].v5up = v5up;
|
|
v5up->ts[channel - 1] = &v5l->ts[t];
|
|
|
|
/* request BCC to allocate channel on AN */
|
|
v52_le_bcc_mdu_snd(v5if, v5up->nr, (v5up->type == V5X_USER_TYPE_ISDN), v5up->ts[channel - 1]->link->id,
|
|
v5up->ts[channel - 1]->nr, 1, channel, NULL, (v5if->use_capability) ? &v5if->capability : NULL,
|
|
MDU_BCC_allocation_req);
|
|
}
|
|
|
|
/* unassign channel */
|
|
void v5x_le_channel_unassign(struct v5x_user_port *v5up, int channel)
|
|
{
|
|
struct v5x_interface *v5if = v5up->interface;
|
|
|
|
/* the channels are pre-assigned with V5.1, so only activation state is set */
|
|
if (v5if->dialect == V5X_DIALECT_V51) {
|
|
if (!v5up->ts[channel - 1]) {
|
|
LOGP(DV5MGMT, LOGL_ERROR, "Cannot deactivate channel, because no TS is assigned for %s port "
|
|
"%d.\n", (v5up->type == V5X_USER_TYPE_PSTN) ? "PSTN" : "ISDN", v5up->nr);
|
|
return;
|
|
}
|
|
if (!v5up->ts[channel - 1]->b_activated)
|
|
return;
|
|
LOGP(DV5MGMT, LOGL_DEBUG, "Deactivating channel %d for %s port %d at TS %d of link %d.\n",
|
|
channel, (v5up->type == V5X_USER_TYPE_PSTN) ? "PSTN" : "ISDN", v5up->nr,
|
|
v5up->ts[channel - 1]->nr, v5up->ts[channel - 1]->link->id);
|
|
ph_deactivate_req(v5up->ts[channel - 1]);
|
|
return;
|
|
}
|
|
|
|
/* if not assigned */
|
|
if (!v5up->ts[channel - 1])
|
|
return;
|
|
|
|
/* deactivate */
|
|
ph_deactivate_req(v5up->ts[channel - 1]);
|
|
|
|
/* unassign or abort ongoing BCC allocation */
|
|
v52_le_bcc_mdu_snd(v5if, v5up->nr, v5up->type == V5X_USER_TYPE_ISDN, v5up->ts[channel - 1]->link->id,
|
|
v5up->ts[channel - 1]->nr, 1, channel, NULL, 0, MDU_BCC_deallocation_req);
|
|
}
|
|
|
|
int v52_le_bcc_mdu_rcv(struct v5x_interface *v5if, uint8_t link_id, uint8_t ts,
|
|
uint8_t __attribute__((unused)) *isdn_multislot, enum v5x_mgmt_prim prim, const uint8_t *cause,
|
|
uint8_t cause_len)
|
|
{
|
|
struct v5x_link *v5l;
|
|
struct v5x_user_port *v5up;
|
|
int channel = 0;
|
|
|
|
if (ts > 31) {
|
|
LOGP(DV5MGMT, LOGL_ERROR, "TS %d out of range.\n", ts);
|
|
return -EINVAL;
|
|
}
|
|
|
|
v5l = v5x_link_find_id(v5if, link_id);
|
|
if (!v5l) {
|
|
LOGP(DV5MGMT, LOGL_ERROR, "Invalid link ID %d.\n", link_id);
|
|
return -EINVAL;
|
|
}
|
|
v5up = v5l->ts[ts].v5up;
|
|
if (!v5up) {
|
|
LOGP(DV5MGMT, LOGL_NOTICE, "TS %d of link ID %d is not assigned.\n", ts, link_id);
|
|
return 0;
|
|
}
|
|
if (v5up->ts[0] == &v5l->ts[ts])
|
|
channel = 1;
|
|
if (v5up->ts[1] == &v5l->ts[ts])
|
|
channel = 2;
|
|
OSMO_ASSERT(channel);
|
|
|
|
switch (prim) {
|
|
case MDU_BCC_allocation_conf:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Assigned channel %d for %s port %d at TS %d of link %d.\n",
|
|
channel, (v5up->type == V5X_USER_TYPE_PSTN) ? "PSTN" : "ISDN", v5up->nr, ts, link_id);
|
|
ph_activate_req(v5up->ts[channel - 1]);
|
|
break;
|
|
case MDU_BCC_deallocation_conf:
|
|
LOGP(DV5MGMT, LOGL_INFO, "Unassigned channel %d for %s port %d at TS %d of link %d.\n",
|
|
channel, (v5up->type == V5X_USER_TYPE_PSTN) ? "PSTN" : "ISDN", v5up->nr, ts, link_id);
|
|
v5l->ts[ts].v5up = NULL;
|
|
v5up->ts[0] = NULL;
|
|
break;
|
|
case MDU_BCC_allocation_reject_ind:
|
|
case MDU_BCC_deallocation_reject_ind:
|
|
if (cause_len < 1)
|
|
break;
|
|
LOGP(DV5MGMT, LOGL_ERROR, "Failed to (un)assign channel %d for %s port %d at TS %d of link %d: %s.\n",
|
|
channel, (v5up->type == V5X_USER_TYPE_PSTN) ? "PSTN" : "ISDN", v5up->nr, ts, link_id,
|
|
get_value_string(v52_bcc_reject_cause_type_str, cause[0] & 0x7f));
|
|
if (cause_len > 1)
|
|
LOGP(DV5MGMT, LOGL_ERROR, " -> Diagnostic: %s\n", osmo_hexdump(cause + 1, cause_len - 1));
|
|
v5l->ts[ts].v5up = NULL;
|
|
v5up->ts[0] = NULL;
|
|
break;
|
|
case MDU_BCC_protocol_error_ind:
|
|
if (cause_len < 1)
|
|
break;
|
|
LOGP(DV5MGMT, LOGL_ERROR, "Failed to (un)assign channel %d for %s port %d at TS %d of link %d: %s.\n",
|
|
channel, (v5up->type == V5X_USER_TYPE_PSTN) ? "PSTN" : "ISDN", v5up->nr, ts, link_id,
|
|
get_value_string(v5x_cause_type_str, cause[0] & 0x7f));
|
|
if (cause_len > 1)
|
|
LOGP(DV5MGMT, LOGL_ERROR, " -> Diagnostic: %s\n", osmo_hexdump(cause + 1, cause_len - 1));
|
|
if (cause_len >= 3 && cause[1] == 0x20 && cause[2] == 0x47) {
|
|
LOGP(DV5MGMT, LOGL_ERROR, "AN rejects information-transfer-capability information element, "
|
|
"please disable via VTY!\n");
|
|
}
|
|
v5l->ts[ts].v5up = NULL;
|
|
v5up->ts[0] = NULL;
|
|
break;
|
|
default:
|
|
LOGP(DV5MGMT, LOGL_ERROR, "Failed to (un)assign channel %d for %s port %d at TS %d of link %d.\n",
|
|
channel, (v5up->type == V5X_USER_TYPE_PSTN) ? "PSTN" : "ISDN", v5up->nr, ts, link_id);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Part 5: Protection protocol handling
|
|
*/
|
|
|
|
static void switchover_request_link(struct v5x_link *v5l, uint8_t link_id, uint8_t ts)
|
|
{
|
|
LOGP(DV5MGMT, LOGL_NOTICE, "Switch over request from AN to link %d and TS %d.\n", link_id, ts);
|
|
if (!v5l) {
|
|
LOGP(DV5MGMT, LOGL_ERROR, " -> Rejecting, because requested link is not primary, nor secondary.\n");
|
|
v52_le_pp_mdu_snd(v5l->interface, MDU_Protection_switch_over_rej, link_id, ts,
|
|
V52_PP_REJECT_CAUSE_T_TARGET_CC_NOT_PROV);
|
|
return;
|
|
}
|
|
if (ts != v5l->c_channel[0].ts->nr) {
|
|
LOGP(DV5MGMT, LOGL_ERROR, " -> Rejecting, because requested TS is not a C-channel.\n");
|
|
v52_le_pp_mdu_snd(v5l->interface, MDU_Protection_switch_over_rej, link_id, ts,
|
|
V52_PP_REJECT_CAUSE_T_TARGET_CC_NOT_PROV);
|
|
return;
|
|
}
|
|
if (!v52_le_lcp_is_operational(v5l->fi)) {
|
|
LOGP(DV5MGMT, LOGL_ERROR, " -> Rejecting, because requested link is not operational.\n");
|
|
v52_le_pp_mdu_snd(v5l->interface, MDU_Protection_switch_over_rej, link_id, ts,
|
|
V52_PP_REJECT_CAUSE_T_TARGET_CC_NOT_OPER);
|
|
return;
|
|
}
|
|
if (v5l == v5l->interface->cc_link) {
|
|
LOGP(DV5MGMT, LOGL_ERROR, " -> Rejecting, because requested allocation exists already.\n");
|
|
v52_le_pp_mdu_snd(v5l->interface, MDU_Protection_switch_over_rej, link_id, ts,
|
|
V52_PP_REJECT_CAUSE_T_ALLOC_EXISTS_ALREADY);
|
|
return;
|
|
}
|
|
LOGP(DV5MGMT, LOGL_NOTICE, "Sending switch over command to AN.\n");
|
|
v52_le_pp_mdu_snd(v5l->interface, MDU_Protection_switch_over_com, link_id, ts, 0);
|
|
}
|
|
|
|
static void switchover_request(struct v5x_interface *v5if)
|
|
{
|
|
LOGP(DV5MGMT, LOGL_NOTICE, "Switch over request from AN, due to link failure.\n");
|
|
if (v5if->primary_link == v5if->cc_link) {
|
|
LOGP(DV5MGMT, LOGL_NOTICE, "Sending switch over command to AN. (go to secondary link)\n");
|
|
v52_le_pp_mdu_snd(v5if, MDU_Protection_switch_over_com, v5if->secondary_link->id, 16, 0);
|
|
}
|
|
if (v5if->secondary_link == v5if->cc_link) {
|
|
LOGP(DV5MGMT, LOGL_NOTICE, "Sending switch over command to AN. (go to primary link)\n");
|
|
v52_le_pp_mdu_snd(v5if, MDU_Protection_switch_over_com, v5if->primary_link->id, 16, 0);
|
|
}
|
|
}
|
|
|
|
int v52_le_pp_mdu_rcv(struct v5x_interface *v5if, enum v5x_mgmt_prim prim, uint8_t link_id, uint8_t ts,
|
|
const uint8_t *cause, uint8_t cause_len)
|
|
{
|
|
struct v5x_link *v5l = NULL;
|
|
|
|
/* get link from what the AN is talking about */
|
|
if (link_id == v5if->primary_link->id)
|
|
v5l = v5if->primary_link;
|
|
if (link_id == v5if->secondary_link->id)
|
|
v5l = v5if->secondary_link;
|
|
|
|
switch (prim) {
|
|
case MDU_Protection_switch_over_ack:
|
|
LOGP(DV5MGMT, LOGL_NOTICE, "Switch over request from LE to link %d and TS %d was acked.\n", link_id,
|
|
ts);
|
|
LOGP(DV5MGMT, LOGL_NOTICE, "Performing switch over.\n");
|
|
if (v5l)
|
|
v5if->cc_link = v5l;
|
|
break;
|
|
case MDU_Protection_switch_over_req:
|
|
if (ts)
|
|
switchover_request_link(v5l, link_id, ts);
|
|
else
|
|
switchover_request(v5if);
|
|
break;
|
|
case MDU_Protection_switch_over_reject_ind:
|
|
if (cause_len < 1)
|
|
break;
|
|
LOGP(DV5MGMT, LOGL_ERROR, "Switch over to link %d and TS %d was rejected by AN: %s.\n", link_id, ts,
|
|
get_value_string(v52_pp_reject_cause_type_str, cause[0] & 0x7f));
|
|
break;
|
|
case MDU_Protection_switch_over_error_ind:
|
|
LOGP(DV5MGMT, LOGL_NOTICE, "Switch over request from LE to link %d and TS %d failed.\n", link_id, ts);
|
|
break;
|
|
case MDU_Protection_reset_SN_ind:
|
|
LOGP(DV5MGMT, LOGL_NOTICE, "AN resets serial numbers at Protection protocol.\n");
|
|
break;
|
|
case MDU_Protection_reset_SN_com:
|
|
LOGP(DV5MGMT, LOGL_NOTICE, "LE resets serial numbers at Protection protocol.\n");
|
|
break;
|
|
case MDU_Protection_reset_SN_ack:
|
|
LOGP(DV5MGMT, LOGL_NOTICE, "AN acknowledges reset serial numbers at Protection protocol.\n");
|
|
break;
|
|
case MDU_Protection_reset_SN_error_ind:
|
|
LOGP(DV5MGMT, LOGL_NOTICE, "Switch over request failed, due to serial numbers reset at Protection "
|
|
"protocol.\n");
|
|
break;
|
|
case MDU_Protection_protocol_error_ind:
|
|
if (cause_len < 1)
|
|
break;
|
|
LOGP(DV5MGMT, LOGL_ERROR, "Protocol error: %s.\n",
|
|
get_value_string(v5x_cause_type_str, cause[0] & 0x7f));
|
|
if (cause_len > 1)
|
|
LOGP(DV5MGMT, LOGL_ERROR, " -> Diagnostic: %s\n", osmo_hexdump(cause + 1, cause_len - 1));
|
|
break;
|
|
default:
|
|
LOGP(DV5MGMT, LOGL_ERROR, "Unknown message primitive %d.\n", prim);
|
|
}
|
|
|
|
return 0;
|
|
}
|