From 595d1091bc3b223c90a5c304a843422e7d12452f Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Fri, 16 Dec 2022 18:44:30 +0100 Subject: [PATCH] Move v51_le_ctrl to src/v5x_le_ctrl_fsm Also changed serveral names of functions/structures/defintions. --- src/v5x_le_ctrl_fsm.c | 583 ++++++++++++++++++++++++++++++++++++++++++ src/v5x_le_ctrl_fsm.h | 10 + v51_le_ctrl.c | 582 ----------------------------------------- v51_le_ctrl.h | 10 - 4 files changed, 593 insertions(+), 592 deletions(-) create mode 100644 src/v5x_le_ctrl_fsm.c create mode 100644 src/v5x_le_ctrl_fsm.h delete mode 100644 v51_le_ctrl.c delete mode 100644 v51_le_ctrl.h diff --git a/src/v5x_le_ctrl_fsm.c b/src/v5x_le_ctrl_fsm.c new file mode 100644 index 0000000..45b8cb1 --- /dev/null +++ b/src/v5x_le_ctrl_fsm.c @@ -0,0 +1,583 @@ +/* (C) 2021 by Harald Welte + * (C) 2022 by Andreas Eversberg + * + * All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include "v5x_internal.h" +#include "v5x_protocol.h" +#include "v5x_le_ctrl_fsm.h" +#include "v5x_le_port_fsm.h" +#include "v5x_le_provisioning.h" +#include "logging.h" + +#define S(x) (1 << (x)) + +#define TIMEOUT 1 + +/* uncomment to test lost TX messages at the first transmission */ +//#define TEST_TX_FAILURE + +/* PSTN Protocol FSM */ +enum v5x_le_ctrl_fsm_state { + V5X_PSTN_PROT_S_OUT_OF_SERVICE, /* LE0 */ + V5X_PSTN_PROT_S_NULL, /* LE1 */ + V5X_PSTN_PROT_S_PATH_INIT_BY_LE, /* LE2 */ + V5X_PSTN_PROT_S_PATH_INIT_BY_AN, /* LE3 */ + V5X_PSTN_PROT_S_PATH_ACTIVE, /* LE4 */ + V5X_PSTN_PROT_S_PATH_DISC_REQ, /* LE5 */ + V5X_PSTN_PROT_S_PORT_BLOCKED, /* LE6 */ +}; + +/*********************************************************************** + * port/common CTRL protocol FSM / G.964 Section 14 + ***********************************************************************/ + +enum v5x_ctrl_fsm_state { + V5X_CTRL_ST_OUT_OF_SERVICE = 0, + V5X_CTRL_ST_IN_SERVICE, + V5X_CTRL_ST_AWAIT_ACK, +}; + +enum v5x_ctrl_fsm_event { + V5X_CTRL_EV_MDU_START_TRAFFIC, + V5X_CTRL_EV_MDU_STOP_TRAFFIC, + V5X_CTRL_EV_MDU_CTRL, + V5X_CTRL_EV_RX_CONTROL, + V5X_CTRL_EV_RX_CONTROL_ACK, +}; + +static const struct value_string v5x_ctrl_fsm_event_names[] = { + { V5X_CTRL_EV_MDU_START_TRAFFIC, "MDU-start_traffic" }, + { V5X_CTRL_EV_MDU_STOP_TRAFFIC, "MDU-stop_traffic" }, + { V5X_CTRL_EV_MDU_CTRL, "MDU-CTRL" }, + { V5X_CTRL_EV_RX_CONTROL, "CONTROL" }, + { V5X_CTRL_EV_RX_CONTROL_ACK, "CONTROL_ACK" }, + { 0, NULL } +}; + +static void v5x_ctrl_mdu(struct osmo_fsm_inst *fi, const struct tlv_parsed *tp) +{ + struct v5x_ctrl_proto *ctrl = fi->priv; + + if (ctrl->v5if) { + enum v5x_ctrl_func_id cfi = *TLVP_VAL(tp, V5X_CTRL_IEI_CTRL_F_ID) & 0x7f; + uint8_t variant = 0; + uint8_t rej_cause = 0; + uint32_t interface_id = 0; + + LOGP(DV5CTRL, LOGL_DEBUG, "MDU-CTRL received.\n"); + + if (TLVP_PRESENT(tp, V5X_CTRL_IEI_VARIANT)) + variant = *TLVP_VAL(tp, V5X_CTRL_IEI_VARIANT) & 0x7f; + if (TLVP_PRESENT(tp, V5X_CTRL_IEI_REJECTION_CAUSE)) + rej_cause = *TLVP_VAL(tp, V5X_CTRL_IEI_REJECTION_CAUSE) & 0x0f; + if (TLVP_PRESENT(tp, V5X_CTRL_IEI_INTERFACE_ID)) + interface_id = osmo_load32be_ext_2(TLVP_VAL(tp, V5X_CTRL_IEI_INTERFACE_ID), 3); + v5x_le_provisioning_rcv(ctrl->v5if, cfi, variant, rej_cause, interface_id); + } + if (ctrl->v5up) { + enum v5x_ctrl_func_id cfe = *TLVP_VAL(tp, V5X_CTRL_IEI_CTRL_F_ELEMENT) & 0x7f; + uint8_t perf_grading = 0; + + LOGP(DV5CTRL, LOGL_DEBUG, "FE received for address %d.\n", ctrl->v5up->nr); + + if (TLVP_PRESENT(tp, V5X_CTRL_IEI_PERFORMANCE_GRADING)) + perf_grading = *TLVP_VAL(tp, V5X_CTRL_IEI_PERFORMANCE_GRADING) & 0x0f; + switch (ctrl->v5up->type) { + case V5X_USER_TYPE_ISDN: + v5x_le_port_isdn_rcv(ctrl->v5up, cfe, perf_grading); + break; + case V5X_USER_TYPE_PSTN: + v5x_le_port_pstn_rcv(ctrl->v5up, cfe); + break; + } + } +} + +/* display MDU-error_indication */ +static void v5x_mdu_error(struct osmo_fsm_inst *fi, const char *error) +{ + LOGP(DV5CTRL, LOGL_NOTICE, "MDU-error_indication: %s\n", error); +} + +/* send control towards lower (DL) layer */ +static void v5x_ctrl_send(struct v5x_ctrl_proto *ctrl, struct msgb *msg) +{ + struct osmo_fsm_inst *fi = ctrl->fi; + + switch (fi->state) { + case V5X_CTRL_ST_IN_SERVICE: + LOGP(DV5CTRL, LOGL_DEBUG, "We are in service, so we send our message now.\n"); + /* no message, clone current message and store to be repeated */ + ctrl->tx_msg = msgb_copy(msg, NULL); + /* go to AWAIT_PENDING_ACK */ + osmo_fsm_inst_state_chg(fi, V5X_CTRL_ST_AWAIT_ACK, TIMEOUT, (ctrl->v5up) ? 1 : 2); + /* send message towards DL */ +#ifdef TEST_TX_FAILURE + msgb_free(msg); +#else + v5x_snd((ctrl->v5if) ? : ctrl->v5up->inst, V5X_DLADDR_CTRL, msg); +#endif + break; + case V5X_CTRL_ST_AWAIT_ACK: + LOGP(DV5CTRL, LOGL_DEBUG, "We are waiting for ack, so we queue our message.\n"); + /* pending message, save message in queue */ + msgb_enqueue(&ctrl->tx_queue, msg); + break; + } +} + +/* receive acknowledge */ +static void v5x_ctrl_ack(struct osmo_fsm_inst *fi, const struct tlv_parsed *tp) +{ + struct v5x_ctrl_proto *ctrl = fi->priv; + struct msgb *msg; + + LOGP(DV5CTRL, LOGL_DEBUG, "Received acknowledge, removing pending message, if any.\n"); + /* free pending copy of message, if acked before retry */ + if (ctrl->tx_msg) { + msgb_free(ctrl->tx_msg); + ctrl->tx_msg = NULL; + } + /* go to IN_SERVICE */ + osmo_fsm_inst_state_chg(fi, V5X_CTRL_ST_IN_SERVICE, 0, 0); + /* sending next pending message in queue */ + msg = msgb_dequeue(&ctrl->tx_queue); + if (msg) { + LOGP(DV5CTRL, LOGL_DEBUG, "Found pending message in queue.\n"); + v5x_ctrl_send(ctrl, msg); + } +} + +/* stop traffic */ +static void v5x_ctrl_stop(struct osmo_fsm_inst *fi) +{ + struct v5x_ctrl_proto *ctrl = fi->priv; + struct msgb *msg; + + /* flush pending messages */ + if (ctrl->tx_msg) { + msgb_free(ctrl->tx_msg); + ctrl->tx_msg = NULL; + } + while ((msg = msgb_dequeue(&ctrl->tx_queue))) + msgb_free(msg); + /* go to OUT_OF_SERVICE */ + osmo_fsm_inst_state_chg(fi, V5X_CTRL_ST_OUT_OF_SERVICE, 0, 0); +} + +/* T01 / T02 */ +static int v5x_ctrl_fsm_timer_cb(struct osmo_fsm_inst *fi) +{ + struct v5x_ctrl_proto *ctrl = fi->priv; + struct msgb *msg; + + if (ctrl->tx_msg) { + LOGP(DV5CTRL, LOGL_DEBUG, "Timer fired the first time, resending message.\n"); + /* first expiry: repeat CONTROL; re-start T01 */ + /* send message towards DL */ + osmo_timer_schedule(&fi->timer, TIMEOUT, 0); + msg = ctrl->tx_msg; + ctrl->tx_msg = NULL; + v5x_snd((ctrl->v5if) ? : ctrl->v5up->inst, V5X_DLADDR_CTRL, msg); + } else { + LOGP(DV5CTRL, LOGL_DEBUG, "Timer fired the second time, indicate an error.\n"); + /* second expiry: send MDU-error_ind; go to IN_SERVICE */ + v5x_mdu_error(fi, "Second timeout while waiting for CONTROL ACK."); + osmo_fsm_inst_state_chg(fi, V5X_CTRL_ST_IN_SERVICE, 0, 0); + /* sending next pending message in queue */ + msg = msgb_dequeue(&ctrl->tx_queue); + if (msg) { + LOGP(DV5CTRL, LOGL_DEBUG, "Found pending message in queue.\n"); + v5x_ctrl_send(ctrl, msg); + } + } + + return 0; +} + +/* events in state OUT OF SERVICE */ +static void v5x_ctrl_fsm_oos(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch (event) { + case V5X_CTRL_EV_MDU_START_TRAFFIC: + /* go to IN_SERVICE */ + osmo_fsm_inst_state_chg(fi, V5X_CTRL_ST_IN_SERVICE, 0, 0); + break; + case V5X_CTRL_EV_MDU_STOP_TRAFFIC: + v5x_mdu_error(fi, "Got MDU-stop, but traffic not started."); + break; + case V5X_CTRL_EV_MDU_CTRL: + msgb_free(data); + v5x_mdu_error(fi, "Got MDU-CTRL, but traffic not started."); + break; + case V5X_CTRL_EV_RX_CONTROL: + v5x_mdu_error(fi, "Received CONTROL, but traffic not started."); + break; + case V5X_CTRL_EV_RX_CONTROL_ACK: + v5x_mdu_error(fi, "Received CONTROL ACK, but traffic not started."); + break; + } +} + +/* events in state IN SERVICE */ +static void v5x_ctrl_fsm_ins(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch (event) { + case V5X_CTRL_EV_MDU_START_TRAFFIC: + break; + case V5X_CTRL_EV_MDU_STOP_TRAFFIC: + v5x_ctrl_stop(fi); + break; + case V5X_CTRL_EV_MDU_CTRL: + v5x_ctrl_send(fi->priv, data); + break; + case V5X_CTRL_EV_RX_CONTROL: + v5x_ctrl_mdu(fi, data); + break; + } +} + +/* events in state AWAIT PORT/COMMON ACK */ +static void v5x_ctrl_fsm_aa(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch (event) { + case V5X_CTRL_EV_MDU_START_TRAFFIC: + break; + case V5X_CTRL_EV_MDU_STOP_TRAFFIC: + v5x_ctrl_stop(fi); + break; + case V5X_CTRL_EV_MDU_CTRL: + v5x_ctrl_send(fi->priv, data); + break; + case V5X_CTRL_EV_RX_CONTROL: + v5x_ctrl_mdu(fi, data); + break; + case V5X_CTRL_EV_RX_CONTROL_ACK: + v5x_ctrl_ack(fi, data); + break; + } +} + +static const struct osmo_fsm_state v5x_ctrl_fsm_states[] = { + [V5X_CTRL_ST_OUT_OF_SERVICE] = { + .name = "OUT_OF_SERVICE", + .in_event_mask = S(V5X_CTRL_EV_MDU_START_TRAFFIC) | + S(V5X_CTRL_EV_MDU_STOP_TRAFFIC) | + S(V5X_CTRL_EV_MDU_CTRL) | + S(V5X_CTRL_EV_RX_CONTROL) | + S(V5X_CTRL_EV_RX_CONTROL_ACK), + .out_state_mask = S(V5X_CTRL_ST_IN_SERVICE), + .action = v5x_ctrl_fsm_oos, + }, + [V5X_CTRL_ST_IN_SERVICE] = { + .name = "IN_SERVICE", + .in_event_mask = S(V5X_CTRL_EV_MDU_START_TRAFFIC) | + S(V5X_CTRL_EV_MDU_STOP_TRAFFIC) | + S(V5X_CTRL_EV_MDU_CTRL) | + S(V5X_CTRL_EV_RX_CONTROL), + + .out_state_mask = S(V5X_CTRL_ST_OUT_OF_SERVICE) | + S(V5X_CTRL_ST_AWAIT_ACK), + .action = v5x_ctrl_fsm_ins, + }, + [V5X_CTRL_ST_AWAIT_ACK] = { + .name = "AWAIT_ACK", + .in_event_mask = S(V5X_CTRL_EV_MDU_START_TRAFFIC) | + S(V5X_CTRL_EV_MDU_STOP_TRAFFIC) | + S(V5X_CTRL_EV_MDU_CTRL) | + S(V5X_CTRL_EV_RX_CONTROL) | + S(V5X_CTRL_EV_RX_CONTROL_ACK), + .out_state_mask = S(V5X_CTRL_ST_OUT_OF_SERVICE) | + S(V5X_CTRL_ST_IN_SERVICE), + .action = v5x_ctrl_fsm_aa, + }, +}; + +struct osmo_fsm v5x_ctrl_fsm = { + .name = "V5X_CTRL", + .states = v5x_ctrl_fsm_states, + .num_states = ARRAY_SIZE(v5x_ctrl_fsm_states), + .allstate_event_mask = 0, + .allstate_action = NULL, + .cleanup = NULL, + .timer_cb = v5x_ctrl_fsm_timer_cb, + .log_subsys = DV5CTRL, + .event_names = v5x_ctrl_fsm_event_names, +}; + +void v5x_le_ctrl_init(void) +{ + int rc; + + rc = osmo_fsm_register(&v5x_ctrl_fsm); + OSMO_ASSERT(!rc); + LOGP(DV5CTRL, LOGL_NOTICE, "Using V5x control protocol\n"); +} + +struct v5x_ctrl_proto *v5x_le_ctrl_create(struct v5x_interface *v5if, struct v5x_user_port *v5up, uint16_t nr) +{ + struct v5x_ctrl_proto *ctrl; + + OSMO_ASSERT((v5if && !v5up) || (!v5if && v5up)); + + ctrl = talloc_zero((v5if) ? : v5up->inst, struct v5x_ctrl_proto); + if (!ctrl) + return NULL; + ctrl->v5if = v5if; + ctrl->v5up = v5up; + INIT_LLIST_HEAD(&ctrl->tx_queue); + + ctrl->fi = osmo_fsm_inst_alloc(&v5x_ctrl_fsm, ctrl, ctrl, LOGL_DEBUG, NULL); + if (!ctrl->fi) { + v5x_le_ctrl_destroy(ctrl); + return NULL; + } + osmo_fsm_inst_update_id_f(ctrl->fi, "%d", nr); + + return ctrl; +} + +void v5x_le_ctrl_destroy(struct v5x_ctrl_proto *ctrl) +{ + /* get rid of pending messages */ + msgb_queue_free(&ctrl->tx_queue); + if (ctrl->tx_msg) + msgb_free(ctrl->tx_msg); + + if (ctrl->fi) + osmo_fsm_inst_free(ctrl->fi); + + talloc_free(ctrl); +} + +/*********************************************************************** + * V5 Message encoding / sending + ***********************************************************************/ + +/* G.964 Section 14.4.1.1 / Table 48 */ +static struct msgb *v5x_enc_ctrl_port(struct v5x_user_port *v5up, enum v5x_ctrl_func_el cfe, bool is_isdn) +{ + uint8_t cfe_ie = cfe | 0x80; + struct v5x_l3_hdr *l3h; + struct msgb *msg = msgb_alloc_v5x(); + if (!msg) + return NULL; + + l3h = (struct v5x_l3_hdr *) msgb_put(msg, sizeof(*l3h)); + l3h->pdisc = V5X_CTRL_PDISC; + l3h->l3_addr = ntohs(v5x_l3_addr_enc(v5up->nr, is_isdn)); + l3h->msg_type = V5X_CTRL_MSGT_PORT_CTRL; + + msgb_tlv_put(msg, V5X_CTRL_IEI_CTRL_F_ELEMENT, 1, &cfe_ie); + + return msg; +} + +/* G.964 Section 14.4.1.2 / Table 49 */ +static struct msgb *v5x_enc_ctrl_port_ack(struct v5x_user_port *v5up, enum v5x_ctrl_func_el cfe, bool is_isdn) +{ + uint8_t cfe_ie = cfe | 0x80; + struct v5x_l3_hdr *l3h; + struct msgb *msg = msgb_alloc_v5x(); + if (!msg) + return NULL; + + l3h = (struct v5x_l3_hdr *) msgb_put(msg, sizeof(*l3h)); + l3h->pdisc = V5X_CTRL_PDISC; + l3h->l3_addr = ntohs(v5x_l3_addr_enc(v5up->nr, is_isdn)); + l3h->msg_type = V5X_CTRL_MSGT_PORT_CTRL_ACK; + + msgb_tlv_put(msg, V5X_CTRL_IEI_CTRL_F_ELEMENT, 1, &cfe_ie); + + return msg; +} + +/* G.964 Section 14.4.1.3 / Table 50 */ +static struct msgb *v5x_enc_ctrl_common(struct v5x_interface *v5if, enum v5x_ctrl_func_id cfi, + uint8_t *variant, uint8_t *rej_cause, uint32_t *interface_id) +{ + uint8_t cfi_ie = cfi | 0x80; + struct v5x_l3_hdr *l3h; + struct msgb *msg = msgb_alloc_v5x(); + if (!msg) + return NULL; + + l3h = (struct v5x_l3_hdr *) msgb_put(msg, sizeof(*l3h)); + l3h->pdisc = V5X_CTRL_PDISC; + l3h->l3_addr = ntohs(v5x_l3_addr_enc(V5X_DLADDR_CTRL, true)); + l3h->msg_type = V5X_CTRL_MSGT_COMMON_CTRL; + + msgb_tlv_put(msg, V5X_CTRL_IEI_CTRL_F_ID, 1, &cfi_ie); + if (variant) { + /* Conditional: Variant */ + uint8_t variant_ie = *variant | 0x80; + msgb_tlv_put(msg, V5X_CTRL_IEI_VARIANT, 1, &variant_ie); + } + if (rej_cause) { + /* Conditional: Rejection Cause */ + msgb_put_u8(msg, 0xF0 | (*rej_cause & 0x0F)); + } + if (interface_id) { + /* Conditional: Interface Id */ + uint8_t iid_ie[3]; + osmo_store32be_ext(*interface_id, iid_ie, 3); + msgb_tlv_put(msg, V5X_CTRL_IEI_INTERFACE_ID, sizeof(iid_ie), iid_ie); + } + + return msg; +} + +/* G.964 Section 14.4.1.4 / Table 51 */ +static struct msgb *v5x_enc_ctrl_common_ack(struct v5x_interface *v5if, enum v5x_ctrl_func_id cfi) +{ + uint8_t cfi_ie = cfi | 0x80; + struct v5x_l3_hdr *l3h; + struct msgb *msg = msgb_alloc_v5x(); + if (!msg) + return NULL; + + l3h = (struct v5x_l3_hdr *) msgb_put(msg, sizeof(*l3h)); + l3h->pdisc = V5X_CTRL_PDISC; + l3h->l3_addr = htons(v5x_l3_addr_enc(V5X_DLADDR_CTRL, true)); + l3h->msg_type = V5X_CTRL_MSGT_COMMON_CTRL_ACK; + + msgb_tlv_put(msg, V5X_CTRL_IEI_CTRL_F_ID, 1, &cfi_ie); + + return msg; +} + +/*********************************************************************** + * V5 Message receiving / decoding + ***********************************************************************/ + +static int v5x_rcv_ctrl_port(struct v5x_user_port *v5up, uint8_t msg_type, const struct tlv_parsed *tp) +{ + enum v5x_ctrl_func_el cfe = *TLVP_VAL(tp, V5X_CTRL_IEI_CTRL_F_ELEMENT) & 0x7f; + + switch (msg_type) { + case V5X_CTRL_MSGT_PORT_CTRL: + /* send ACK to AN */ + v5x_snd(v5up->inst, V5X_DLADDR_CTRL, v5x_enc_ctrl_port_ack(v5up, cfe, v5up->type == V5X_USER_TYPE_ISDN)); + /* FIXME: send event to FSM */ + osmo_fsm_inst_dispatch(v5up->ctrl->fi, V5X_CTRL_EV_RX_CONTROL, (void *)tp); + return 0; + case V5X_CTRL_MSGT_PORT_CTRL_ACK: + osmo_fsm_inst_dispatch(v5up->ctrl->fi, V5X_CTRL_EV_RX_CONTROL_ACK, (void *)tp); + default: + return -EINVAL; + } +} + +static int v5x_rcv_ctrl_common(struct v5x_interface *v5if, uint8_t msg_type, const struct tlv_parsed *tp) +{ + enum v5x_ctrl_func_id cfi = *TLVP_VAL(tp, V5X_CTRL_IEI_CTRL_F_ID) & 0x7f; + + switch (msg_type) { + case V5X_CTRL_MSGT_COMMON_CTRL: + v5x_snd(v5if, V5X_DLADDR_CTRL, v5x_enc_ctrl_common_ack(v5if, cfi)); + /* send event to FSM */ + osmo_fsm_inst_dispatch(v5if->control.ctrl->fi, V5X_CTRL_EV_RX_CONTROL, (void *)tp); + /* send ACK to AN */ + return 0; + case V5X_CTRL_MSGT_COMMON_CTRL_ACK: + /* send event to FSM */ + osmo_fsm_inst_dispatch(v5if->control.ctrl->fi, V5X_CTRL_EV_RX_CONTROL_ACK, (void *)tp); + return 0; + default: + return -EINVAL; + } +} + +/* receive message from lower (DL) layer */ +int v5x_le_ctrl_rcv(struct v5x_interface *v5if, uint16_t l3_addr, bool is_isdn, uint8_t msg_type, const struct tlv_parsed *tp) +{ + struct v5x_user_port *v5up; + + switch (msg_type) { + case V5X_CTRL_MSGT_PORT_CTRL: + case V5X_CTRL_MSGT_PORT_CTRL_ACK: + v5up = v5x_user_port_find(v5if, l3_addr, is_isdn); + if (!v5up) { + LOGP(DV5CTRL, LOGL_ERROR, "Received port control message from unknown layer 3 address %d. Please check provisioning!\n", l3_addr); + return -ENODEV; + } + if (v5up->type == V5X_USER_TYPE_ISDN && !is_isdn) { + LOGP(DV5CTRL, LOGL_ERROR, "Received port control message from layer 3 address %d. AN is set to PSTN, LE to ISDN, please check provisioning!", l3_addr); + return -EIO; + } + if (v5up->type == V5X_USER_TYPE_PSTN && is_isdn) { + LOGP(DV5CTRL, LOGL_ERROR, "Received port control message from layer 3 address %d. AN is set to ISDN, LE to PSTN, please check provisioning!", l3_addr); + return -EIO; + } + return v5x_rcv_ctrl_port(v5up, msg_type, tp); + case V5X_CTRL_MSGT_COMMON_CTRL: + case V5X_CTRL_MSGT_COMMON_CTRL_ACK: + if (l3_addr != V5X_DLADDR_CTRL) + return -EINVAL; + return v5x_rcv_ctrl_common(v5if, msg_type, tp); + } + + return -EINVAL; +} + +/*********************************************************************** + * V5 Message sending / encoding + ***********************************************************************/ + +/* send common message from upper layer */ +int v5x_le_ctrl_common_snd(struct v5x_interface *v5if, enum v5x_ctrl_func_id cfi, + uint8_t *rej_cause, uint8_t *variant, uint32_t *interface_id) +{ + LOGP(DV5CTRL, LOGL_DEBUG, "Sending MDU-CTRL.\n"); + + osmo_fsm_inst_dispatch(v5if->control.ctrl->fi, V5X_CTRL_EV_MDU_CTRL, + v5x_enc_ctrl_common(v5if, cfi, variant, rej_cause, interface_id)); + return 0; +} + +/* send port message from upper layer */ +int v5x_le_ctrl_port_snd(struct v5x_user_port *v5up, enum v5x_ctrl_func_el cfe) +{ + LOGP(DV5CTRL, LOGL_DEBUG, "Sending FE.\n"); + + osmo_fsm_inst_dispatch(v5up->ctrl->fi, V5X_CTRL_EV_MDU_CTRL, + v5x_enc_ctrl_port(v5up, cfe, v5up->type == V5X_USER_TYPE_ISDN)); + return 0; +} + +void v5x_le_ctrl_start(struct v5x_ctrl_proto *ctrl) +{ + osmo_fsm_inst_dispatch(ctrl->fi, V5X_CTRL_EV_MDU_START_TRAFFIC, NULL); +} +void v5x_le_ctrl_stop(struct v5x_ctrl_proto *ctrl) +{ + osmo_fsm_inst_dispatch(ctrl->fi, V5X_CTRL_EV_MDU_STOP_TRAFFIC, NULL); +} diff --git a/src/v5x_le_ctrl_fsm.h b/src/v5x_le_ctrl_fsm.h new file mode 100644 index 0000000..f56d991 --- /dev/null +++ b/src/v5x_le_ctrl_fsm.h @@ -0,0 +1,10 @@ + +void v5x_le_ctrl_init(void); +struct v5x_ctrl_proto *v5x_le_ctrl_create(struct v5x_interface *v5if, struct v5x_user_port *v5up, uint16_t nr); +void v5x_le_ctrl_destroy(struct v5x_ctrl_proto *ctrl); +int v5x_le_ctrl_rcv(struct v5x_interface *v5if, uint16_t l3_addr, bool is_isdn, uint8_t msg_type, const struct tlv_parsed *tp); +int v5x_le_ctrl_common_snd(struct v5x_interface *v5if, enum v5x_ctrl_func_id cfi, + uint8_t *rej_cause, uint8_t *variant, uint32_t *interface_id); +int v5x_le_ctrl_port_snd(struct v5x_user_port *v5up, enum v5x_ctrl_func_el cfe); +void v5x_le_ctrl_start(struct v5x_ctrl_proto *ctrl); +void v5x_le_ctrl_stop(struct v5x_ctrl_proto *ctrl); diff --git a/v51_le_ctrl.c b/v51_le_ctrl.c deleted file mode 100644 index 6322e99..0000000 --- a/v51_le_ctrl.c +++ /dev/null @@ -1,582 +0,0 @@ -/* (C) 2021 by Harald Welte - * - * 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. - * - */ - -#include -#include -#include -#include - -#include -#include -#include - -#include "v5x_internal.h" -#include "v5x_protocol.h" -#include "v51_le_ctrl.h" -#include "v51_le_provisioning.h" -#include "v52_le_user_port_fsm.h" -#include "logging.h" - -#define S(x) (1 << (x)) - -#define TIMEOUT 1 - -/* uncomment to test lost TX messages at the first transmission */ -//#define TEST_TX_FAILURE - -/* PSTN Protocol FSM */ -enum v51_le_ctrl_fsm_state { - V51_PSTN_PROT_S_OUT_OF_SERVICE, /* LE0 */ - V51_PSTN_PROT_S_NULL, /* LE1 */ - V51_PSTN_PROT_S_PATH_INIT_BY_LE, /* LE2 */ - V51_PSTN_PROT_S_PATH_INIT_BY_AN, /* LE3 */ - V51_PSTN_PROT_S_PATH_ACTIVE, /* LE4 */ - V51_PSTN_PROT_S_PATH_DISC_REQ, /* LE5 */ - V51_PSTN_PROT_S_PORT_BLOCKED, /* LE6 */ -}; - -/*********************************************************************** - * port/common CTRL protocol FSM / G.964 Section 14 - ***********************************************************************/ - -enum v51_ctrl_fsm_state { - V51_CTRL_ST_OUT_OF_SERVICE = 0, - V51_CTRL_ST_IN_SERVICE, - V51_CTRL_ST_AWAIT_ACK, -}; - -enum v51_ctrl_fsm_event { - V51_CTRL_EV_MDU_START_TRAFFIC, - V51_CTRL_EV_MDU_STOP_TRAFFIC, - V51_CTRL_EV_MDU_CTRL, - V51_CTRL_EV_RX_CONTROL, - V51_CTRL_EV_RX_CONTROL_ACK, -}; - -static const struct value_string v51_ctrl_fsm_event_names[] = { - { V51_CTRL_EV_MDU_START_TRAFFIC, "MDU-start_traffic" }, - { V51_CTRL_EV_MDU_STOP_TRAFFIC, "MDU-stop_traffic" }, - { V51_CTRL_EV_MDU_CTRL, "MDU-CTRL" }, - { V51_CTRL_EV_RX_CONTROL, "CONTROL" }, - { V51_CTRL_EV_RX_CONTROL_ACK, "CONTROL_ACK" }, - { 0, NULL } -}; - -static void v51_mdu_ctrl(struct osmo_fsm_inst *fi, const struct tlv_parsed *tp) -{ - struct v5x_ctrl_proto *ctrl = fi->priv; - - if (ctrl->v5if) { - enum v51_ctrl_func_id cfi = *TLVP_VAL(tp, V51_CTRL_IEI_CTRL_F_ID) & 0x7f; - uint8_t variant = 0; - uint8_t rej_cause = 0; - uint32_t interface_id = 0; - - LOGP(DV5CTRL, LOGL_DEBUG, "MDU-CTRL received.\n"); - - if (TLVP_PRESENT(tp, V51_CTRL_IEI_VARIANT)) - variant = *TLVP_VAL(tp, V51_CTRL_IEI_VARIANT) & 0x7f; - if (TLVP_PRESENT(tp, V51_CTRL_IEI_REJECTION_CAUSE)) - rej_cause = *TLVP_VAL(tp, V51_CTRL_IEI_REJECTION_CAUSE) & 0x0f; - if (TLVP_PRESENT(tp, V51_CTRL_IEI_INTERFACE_ID)) - interface_id = osmo_load32be_ext_2(TLVP_VAL(tp, V51_CTRL_IEI_INTERFACE_ID), 3); - v51_rcv_provisioning(ctrl->v5if, cfi, variant, rej_cause, interface_id); - } - if (ctrl->v5up) { - enum v51_ctrl_func_id cfe = *TLVP_VAL(tp, V51_CTRL_IEI_CTRL_F_ELEMENT) & 0x7f; - uint8_t perf_grading = 0; - - LOGP(DV5CTRL, LOGL_DEBUG, "FE received for address %d.\n", ctrl->v5up->nr); - - if (TLVP_PRESENT(tp, V51_CTRL_IEI_PERFORMANCE_GRADING)) - perf_grading = *TLVP_VAL(tp, V51_CTRL_IEI_PERFORMANCE_GRADING) & 0x0f; - switch (ctrl->v5up->type) { - case V5X_USER_TYPE_ISDN: - v51_ctrl_le_i_port_rcv(ctrl->v5up, cfe, perf_grading); - break; - case V5X_USER_TYPE_PSTN: - v51_ctrl_le_p_port_rcv(ctrl->v5up, cfe); - break; - } - } -} - -/* display MDU-error_indication */ -static void v51_mdu_error(struct osmo_fsm_inst *fi, const char *error) -{ - LOGP(DV5CTRL, LOGL_NOTICE, "MDU-error_indication: %s\n", error); -} - -/* send control towards lower (DL) layer */ -static void v51_ctrl_send(struct v5x_ctrl_proto *ctrl, struct msgb *msg) -{ - struct osmo_fsm_inst *fi = ctrl->fi; - - switch (fi->state) { - case V51_CTRL_ST_IN_SERVICE: - LOGP(DV5CTRL, LOGL_DEBUG, "We are in service, so we send our message now.\n"); - /* no message, clone current message and store to be repeated */ - ctrl->tx_msg = msgb_copy(msg, NULL); - /* go to AWAIT_PENDING_ACK */ - osmo_fsm_inst_state_chg(fi, V51_CTRL_ST_AWAIT_ACK, TIMEOUT, (ctrl->v5up) ? 1 : 2); - /* send message towards DL */ -#ifdef TEST_TX_FAILURE - msgb_free(msg); -#else - v5x_snd((ctrl->v5if) ? : ctrl->v5up->inst, V51_DLADDR_CTRL, msg); -#endif - break; - case V51_CTRL_ST_AWAIT_ACK: - LOGP(DV5CTRL, LOGL_DEBUG, "We are waiting for ack, so we queue our message.\n"); - /* pending message, save message in queue */ - msgb_enqueue(&ctrl->tx_queue, msg); - break; - } -} - -/* receive acknowledge */ -static void v51_ctrl_ack(struct osmo_fsm_inst *fi, const struct tlv_parsed *tp) -{ - struct v5x_ctrl_proto *ctrl = fi->priv; - struct msgb *msg; - - LOGP(DV5CTRL, LOGL_DEBUG, "Received acknowledge, removing pending message, if any.\n"); - /* free pending copy of message, if acked before retry */ - if (ctrl->tx_msg) { - msgb_free(ctrl->tx_msg); - ctrl->tx_msg = NULL; - } - /* go to IN_SERVICE */ - osmo_fsm_inst_state_chg(fi, V51_CTRL_ST_IN_SERVICE, 0, 0); - /* sending next pending message in queue */ - msg = msgb_dequeue(&ctrl->tx_queue); - if (msg) { - LOGP(DV5CTRL, LOGL_DEBUG, "Found pending message in queue.\n"); - v51_ctrl_send(ctrl, msg); - } -} - -/* stop traffic */ -static void v51_ctrl_stop(struct osmo_fsm_inst *fi) -{ - struct v5x_ctrl_proto *ctrl = fi->priv; - struct msgb *msg; - - /* flush pending messages */ - if (ctrl->tx_msg) { - msgb_free(ctrl->tx_msg); - ctrl->tx_msg = NULL; - } - while ((msg = msgb_dequeue(&ctrl->tx_queue))) - msgb_free(msg); - /* go to OUT_OF_SERVICE */ - osmo_fsm_inst_state_chg(fi, V51_CTRL_ST_OUT_OF_SERVICE, 0, 0); -} - -/* T01 / T02 */ -static int v51_ctrl_fsm_timer_cb(struct osmo_fsm_inst *fi) -{ - struct v5x_ctrl_proto *ctrl = fi->priv; - struct msgb *msg; - - if (ctrl->tx_msg) { - LOGP(DV5CTRL, LOGL_DEBUG, "Timer fired the first time, resending message.\n"); - /* first expiry: repeat CONTROL; re-start T01 */ - /* send message towards DL */ - osmo_timer_schedule(&fi->timer, TIMEOUT, 0); - msg = ctrl->tx_msg; - ctrl->tx_msg = NULL; - v5x_snd((ctrl->v5if) ? : ctrl->v5up->inst, V51_DLADDR_CTRL, msg); - } else { - LOGP(DV5CTRL, LOGL_DEBUG, "Timer fired the second time, indicate an error.\n"); - /* second expiry: send MDU-error_ind; go to IN_SERVICE */ - v51_mdu_error(fi, "Second timeout while waiting for CONTROL ACK."); - osmo_fsm_inst_state_chg(fi, V51_CTRL_ST_IN_SERVICE, 0, 0); - /* sending next pending message in queue */ - msg = msgb_dequeue(&ctrl->tx_queue); - if (msg) { - LOGP(DV5CTRL, LOGL_DEBUG, "Found pending message in queue.\n"); - v51_ctrl_send(ctrl, msg); - } - } - - return 0; -} - -/* events in state OUT OF SERVICE */ -static void v51_ctrl_fsm_oos(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - switch (event) { - case V51_CTRL_EV_MDU_START_TRAFFIC: - /* go to IN_SERVICE */ - osmo_fsm_inst_state_chg(fi, V51_CTRL_ST_IN_SERVICE, 0, 0); - break; - case V51_CTRL_EV_MDU_STOP_TRAFFIC: - v51_mdu_error(fi, "Got MDU-stop, but traffic not started."); - break; - case V51_CTRL_EV_MDU_CTRL: - msgb_free(data); - v51_mdu_error(fi, "Got MDU-CTRL, but traffic not started."); - break; - case V51_CTRL_EV_RX_CONTROL: - v51_mdu_error(fi, "Received CONTROL, but traffic not started."); - break; - case V51_CTRL_EV_RX_CONTROL_ACK: - v51_mdu_error(fi, "Received CONTROL ACK, but traffic not started."); - break; - } -} - -/* events in state IN SERVICE */ -static void v51_ctrl_fsm_ins(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - switch (event) { - case V51_CTRL_EV_MDU_START_TRAFFIC: - break; - case V51_CTRL_EV_MDU_STOP_TRAFFIC: - v51_ctrl_stop(fi); - break; - case V51_CTRL_EV_MDU_CTRL: - v51_ctrl_send(fi->priv, data); - break; - case V51_CTRL_EV_RX_CONTROL: - v51_mdu_ctrl(fi, data); - break; - } -} - -/* events in state AWAIT PORT/COMMON ACK */ -static void v51_ctrl_fsm_aa(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - switch (event) { - case V51_CTRL_EV_MDU_START_TRAFFIC: - break; - case V51_CTRL_EV_MDU_STOP_TRAFFIC: - v51_ctrl_stop(fi); - break; - case V51_CTRL_EV_MDU_CTRL: - v51_ctrl_send(fi->priv, data); - break; - case V51_CTRL_EV_RX_CONTROL: - v51_mdu_ctrl(fi, data); - break; - case V51_CTRL_EV_RX_CONTROL_ACK: - v51_ctrl_ack(fi, data); - break; - } -} - -static const struct osmo_fsm_state v51_ctrl_fsm_states[] = { - [V51_CTRL_ST_OUT_OF_SERVICE] = { - .name = "OUT_OF_SERVICE", - .in_event_mask = S(V51_CTRL_EV_MDU_START_TRAFFIC) | - S(V51_CTRL_EV_MDU_STOP_TRAFFIC) | - S(V51_CTRL_EV_MDU_CTRL) | - S(V51_CTRL_EV_RX_CONTROL) | - S(V51_CTRL_EV_RX_CONTROL_ACK), - .out_state_mask = S(V51_CTRL_ST_IN_SERVICE), - .action = v51_ctrl_fsm_oos, - }, - [V51_CTRL_ST_IN_SERVICE] = { - .name = "IN_SERVICE", - .in_event_mask = S(V51_CTRL_EV_MDU_START_TRAFFIC) | - S(V51_CTRL_EV_MDU_STOP_TRAFFIC) | - S(V51_CTRL_EV_MDU_CTRL) | - S(V51_CTRL_EV_RX_CONTROL), - - .out_state_mask = S(V51_CTRL_ST_OUT_OF_SERVICE) | - S(V51_CTRL_ST_AWAIT_ACK), - .action = v51_ctrl_fsm_ins, - }, - [V51_CTRL_ST_AWAIT_ACK] = { - .name = "AWAIT_ACK", - .in_event_mask = S(V51_CTRL_EV_MDU_START_TRAFFIC) | - S(V51_CTRL_EV_MDU_STOP_TRAFFIC) | - S(V51_CTRL_EV_MDU_CTRL) | - S(V51_CTRL_EV_RX_CONTROL) | - S(V51_CTRL_EV_RX_CONTROL_ACK), - .out_state_mask = S(V51_CTRL_ST_OUT_OF_SERVICE) | - S(V51_CTRL_ST_IN_SERVICE), - .action = v51_ctrl_fsm_aa, - }, -}; - -struct osmo_fsm v51_ctrl_fsm = { - .name = "V51_CTRL", - .states = v51_ctrl_fsm_states, - .num_states = ARRAY_SIZE(v51_ctrl_fsm_states), - .allstate_event_mask = 0, - .allstate_action = NULL, - .cleanup = NULL, - .timer_cb = v51_ctrl_fsm_timer_cb, - .log_subsys = DV5CTRL, - .event_names = v51_ctrl_fsm_event_names, -}; - -void v51_ctrl_init(void) -{ - int rc; - - rc = osmo_fsm_register(&v51_ctrl_fsm); - OSMO_ASSERT(!rc); - LOGP(DV5CTRL, LOGL_NOTICE, "Using V5x control protocol\n"); -} - -struct v5x_ctrl_proto *v51_ctrl_create(struct v5x_interface *v5if, struct v5x_user_port *v5up, uint16_t nr) -{ - struct v5x_ctrl_proto *ctrl; - - OSMO_ASSERT((v5if && !v5up) || (!v5if && v5up)); - - ctrl = talloc_zero((v5if) ? : v5up->inst, struct v5x_ctrl_proto); - if (!ctrl) - return NULL; - ctrl->v5if = v5if; - ctrl->v5up = v5up; - INIT_LLIST_HEAD(&ctrl->tx_queue); - - ctrl->fi = osmo_fsm_inst_alloc(&v51_ctrl_fsm, ctrl, ctrl, LOGL_DEBUG, NULL); - if (!ctrl->fi) { - v51_ctrl_destroy(ctrl); - return NULL; - } - osmo_fsm_inst_update_id_f(ctrl->fi, "%d", nr); - - return ctrl; -} - -void v51_ctrl_destroy(struct v5x_ctrl_proto *ctrl) -{ - /* get rid of pending messages */ - msgb_queue_free(&ctrl->tx_queue); - if (ctrl->tx_msg) - msgb_free(ctrl->tx_msg); - - if (ctrl->fi) - osmo_fsm_inst_free(ctrl->fi); - - free(ctrl); -} - -/*********************************************************************** - * V5 Message encoding / sending - ***********************************************************************/ - -/* G.964 Section 14.4.1.1 / Table 48 */ -struct msgb *v51_enc_ctrl_port(struct v5x_user_port *v5up, enum v51_ctrl_func_el cfe, bool is_isdn) -{ - uint8_t cfe_ie = cfe | 0x80; - struct v51_l3_hdr *l3h; - struct msgb *msg = msgb_alloc_v5x(); - if (!msg) - return NULL; - - l3h = (struct v51_l3_hdr *) msgb_put(msg, sizeof(*l3h)); - l3h->pdisc = V51_CTRL_PDISC; - l3h->l3_addr = ntohs(v51_l3_addr_enc(v5up->nr, is_isdn)); - l3h->msg_type = V51_CTRL_MSGT_PORT_CTRL; - - msgb_tlv_put(msg, V51_CTRL_IEI_CTRL_F_ELEMENT, 1, &cfe_ie); - - return msg; -} - -/* G.964 Section 14.4.1.2 / Table 49 */ -static struct msgb *v51_enc_ctrl_port_ack(struct v5x_user_port *v5up, enum v51_ctrl_func_el cfe, bool is_isdn) -{ - uint8_t cfe_ie = cfe | 0x80; - struct v51_l3_hdr *l3h; - struct msgb *msg = msgb_alloc_v5x(); - if (!msg) - return NULL; - - l3h = (struct v51_l3_hdr *) msgb_put(msg, sizeof(*l3h)); - l3h->pdisc = V51_CTRL_PDISC; - l3h->l3_addr = ntohs(v51_l3_addr_enc(v5up->nr, is_isdn)); - l3h->msg_type = V51_CTRL_MSGT_PORT_CTRL_ACK; - - msgb_tlv_put(msg, V51_CTRL_IEI_CTRL_F_ELEMENT, 1, &cfe_ie); - - return msg; -} - -/* G.964 Section 14.4.1.3 / Table 50 */ -static struct msgb *v51_enc_ctrl_common(struct v5x_interface *v5if, enum v51_ctrl_func_id cfi, - uint8_t *variant, uint8_t *rej_cause, uint32_t *interface_id) -{ - uint8_t cfi_ie = cfi | 0x80; - struct v51_l3_hdr *l3h; - struct msgb *msg = msgb_alloc_v5x(); - if (!msg) - return NULL; - - l3h = (struct v51_l3_hdr *) msgb_put(msg, sizeof(*l3h)); - l3h->pdisc = V51_CTRL_PDISC; - l3h->l3_addr = ntohs(v51_l3_addr_enc(V51_DLADDR_CTRL, true)); - l3h->msg_type = V51_CTRL_MSGT_COMMON_CTRL; - - msgb_tlv_put(msg, V51_CTRL_IEI_CTRL_F_ID, 1, &cfi_ie); - if (variant) { - /* Conditional: Variant */ - uint8_t variant_ie = *variant | 0x80; - msgb_tlv_put(msg, V51_CTRL_IEI_VARIANT, 1, &variant_ie); - } - if (rej_cause) { - /* Conditional: Rejection Cause */ - msgb_put_u8(msg, 0xF0 | (*rej_cause & 0x0F)); - } - if (interface_id) { - /* Conditional: Interface Id */ - uint8_t iid_ie[3]; - osmo_store32be_ext(*interface_id, iid_ie, 3); - msgb_tlv_put(msg, V51_CTRL_IEI_INTERFACE_ID, sizeof(iid_ie), iid_ie); - } - - return msg; -} - -/* G.964 Section 14.4.1.4 / Table 51 */ -static struct msgb *v51_enc_ctrl_common_ack(struct v5x_interface *v5if, enum v51_ctrl_func_id cfi) -{ - uint8_t cfi_ie = cfi | 0x80; - struct v51_l3_hdr *l3h; - struct msgb *msg = msgb_alloc_v5x(); - if (!msg) - return NULL; - - l3h = (struct v51_l3_hdr *) msgb_put(msg, sizeof(*l3h)); - l3h->pdisc = V51_CTRL_PDISC; - l3h->l3_addr = htons(v51_l3_addr_enc(V51_DLADDR_CTRL, true)); - l3h->msg_type = V51_CTRL_MSGT_COMMON_CTRL_ACK; - - msgb_tlv_put(msg, V51_CTRL_IEI_CTRL_F_ID, 1, &cfi_ie); - - return msg; -} - -/*********************************************************************** - * V5 Message receiving / decoding - ***********************************************************************/ - -static int v51_rcv_ctrl_port(struct v5x_user_port *v5up, uint8_t msg_type, const struct tlv_parsed *tp) -{ - enum v51_ctrl_func_el cfe = *TLVP_VAL(tp, V51_CTRL_IEI_CTRL_F_ELEMENT) & 0x7f; - - switch (msg_type) { - case V51_CTRL_MSGT_PORT_CTRL: - /* send ACK to AN */ - v5x_snd(v5up->inst, V51_DLADDR_CTRL, v51_enc_ctrl_port_ack(v5up, cfe, v5up->type == V5X_USER_TYPE_ISDN)); - /* FIXME: send event to FSM */ - osmo_fsm_inst_dispatch(v5up->ctrl->fi, V51_CTRL_EV_RX_CONTROL, (void *)tp); - return 0; - case V51_CTRL_MSGT_PORT_CTRL_ACK: - osmo_fsm_inst_dispatch(v5up->ctrl->fi, V51_CTRL_EV_RX_CONTROL_ACK, (void *)tp); - default: - return -EINVAL; - } -} - -static int v51_rcv_ctrl_common(struct v5x_interface *v5if, uint8_t msg_type, const struct tlv_parsed *tp) -{ - enum v51_ctrl_func_id cfi = *TLVP_VAL(tp, V51_CTRL_IEI_CTRL_F_ID) & 0x7f; - - switch (msg_type) { - case V51_CTRL_MSGT_COMMON_CTRL: - v5x_snd(v5if, V51_DLADDR_CTRL, v51_enc_ctrl_common_ack(v5if, cfi)); - /* send event to FSM */ - osmo_fsm_inst_dispatch(v5if->control.ctrl->fi, V51_CTRL_EV_RX_CONTROL, (void *)tp); - /* send ACK to AN */ - return 0; - case V51_CTRL_MSGT_COMMON_CTRL_ACK: - /* send event to FSM */ - osmo_fsm_inst_dispatch(v5if->control.ctrl->fi, V51_CTRL_EV_RX_CONTROL_ACK, (void *)tp); - return 0; - default: - return -EINVAL; - } -} - -/* receive message from lower (DL) layer */ -int v51_rcv_ctrl(struct v5x_interface *v5if, uint16_t l3_addr, bool is_isdn, uint8_t msg_type, const struct tlv_parsed *tp) -{ - struct v5x_user_port *v5up; - - switch (msg_type) { - case V51_CTRL_MSGT_PORT_CTRL: - case V51_CTRL_MSGT_PORT_CTRL_ACK: - v5up = v5x_user_port_find(v5if, l3_addr, is_isdn); - if (!v5up) { - LOGP(DV5CTRL, LOGL_ERROR, "Received port control message from unknown layer 3 address %d. Please check provisioning!\n", l3_addr); - return -ENODEV; - } - if (v5up->type == V5X_USER_TYPE_ISDN && !is_isdn) { - LOGP(DV5CTRL, LOGL_ERROR, "Received port control message from layer 3 address %d. AN is set to PSTN, LE to ISDN, please check provisioning!", l3_addr); - return -EIO; - } - if (v5up->type == V5X_USER_TYPE_PSTN && is_isdn) { - LOGP(DV5CTRL, LOGL_ERROR, "Received port control message from layer 3 address %d. AN is set to ISDN, LE to PSTN, please check provisioning!", l3_addr); - return -EIO; - } - return v51_rcv_ctrl_port(v5up, msg_type, tp); - case V51_CTRL_MSGT_COMMON_CTRL: - case V51_CTRL_MSGT_COMMON_CTRL_ACK: - if (l3_addr != V51_DLADDR_CTRL) - return -EINVAL; - return v51_rcv_ctrl_common(v5if, msg_type, tp); - } - - return -EINVAL; -} - -/*********************************************************************** - * V5 Message sending / encoding - ***********************************************************************/ - -/* send common message from upper layer */ -int v51_snd_ctrl_common(struct v5x_interface *v5if, enum v51_ctrl_func_id cfi, - uint8_t *rej_cause, uint8_t *variant, uint32_t *interface_id) -{ - LOGP(DV5CTRL, LOGL_DEBUG, "Sending MDU-CTRL.\n"); - - osmo_fsm_inst_dispatch(v5if->control.ctrl->fi, V51_CTRL_EV_MDU_CTRL, - v51_enc_ctrl_common(v5if, cfi, variant, rej_cause, interface_id)); - return 0; -} - -/* send port message from upper layer */ -int v51_snd_ctrl_port(struct v5x_user_port *v5up, enum v51_ctrl_func_el cfe) -{ - LOGP(DV5CTRL, LOGL_DEBUG, "Sending FE.\n"); - - osmo_fsm_inst_dispatch(v5up->ctrl->fi, V51_CTRL_EV_MDU_CTRL, - v51_enc_ctrl_port(v5up, cfe, v5up->type == V5X_USER_TYPE_ISDN)); - return 0; -} - -void v51_start_ctrl(struct v5x_ctrl_proto *ctrl) -{ - osmo_fsm_inst_dispatch(ctrl->fi, V51_CTRL_EV_MDU_START_TRAFFIC, NULL); -} -void v51_stop_ctrl(struct v5x_ctrl_proto *ctrl) -{ - osmo_fsm_inst_dispatch(ctrl->fi, V51_CTRL_EV_MDU_STOP_TRAFFIC, NULL); -} diff --git a/v51_le_ctrl.h b/v51_le_ctrl.h deleted file mode 100644 index 63176d7..0000000 --- a/v51_le_ctrl.h +++ /dev/null @@ -1,10 +0,0 @@ - -void v51_ctrl_init(void); -struct v5x_ctrl_proto *v51_ctrl_create(struct v5x_interface *v5if, struct v5x_user_port *v5up, uint16_t nr); -void v51_ctrl_destroy(struct v5x_ctrl_proto *ctrl); -int v51_rcv_ctrl(struct v5x_interface *v5if, uint16_t l3_addr, bool is_isdn, uint8_t msg_type, const struct tlv_parsed *tp); -int v51_snd_ctrl_common(struct v5x_interface *v5if, enum v51_ctrl_func_id cfi, - uint8_t *rej_cause, uint8_t *variant, uint32_t *interface_id); -int v51_snd_ctrl_port(struct v5x_user_port *v5up, enum v51_ctrl_func_el cfe); -void v51_start_ctrl(struct v5x_ctrl_proto *ctrl); -void v51_stop_ctrl(struct v5x_ctrl_proto *ctrl);