forked from retronetworking/osmo-v5
818 lines
27 KiB
C
818 lines
27 KiB
C
/* ITu-T G.965 Section 17 V5.2-interface BCC protocol - LE side */
|
|
|
|
/* (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.
|
|
*
|
|
*/
|
|
|
|
/***********************************************************************/
|
|
/* internal data structures */
|
|
/***********************************************************************/
|
|
|
|
#include <unistd.h>
|
|
#include <stdint.h>
|
|
#include <errno.h>
|
|
#include <arpa/inet.h>
|
|
|
|
#include <osmocom/core/utils.h>
|
|
|
|
#include "v5x_internal.h"
|
|
#include "v5x_protocol.h"
|
|
#include "v5x_le_management.h"
|
|
#include "v52_le_bcc_fsm.h"
|
|
#include "logging.h"
|
|
|
|
#define S(x) (1 << (x))
|
|
|
|
#define TIMEOUT_Tbcc1 2 // 1500 ms
|
|
#define TIMEOUT_Tbcc2 2
|
|
#define TIMEOUT_Tbcc3 2
|
|
#define TIMEOUT_Tbcc4 1
|
|
|
|
/***********************************************************************/
|
|
/* state names, event names, primitives, ... */
|
|
/***********************************************************************/
|
|
|
|
/* 17.2.1.2 */
|
|
enum v52_bcc_fsm_state {
|
|
V52_BCCFSM_S_LEBcc0_NULL,
|
|
V52_BCCFSM_S_LEBcc1_WAITING_ALLOCATION,
|
|
V52_BCCFSM_S_LEBcc2_ALLOCATION_ABORT,
|
|
V52_BCCFSM_S_LEBcc3_WAITING_DE_ALLOCATION,
|
|
V52_BCCFSM_S_LEBcc4_WAITING_AUDIT,
|
|
};
|
|
|
|
enum v52_bcc_fsm_event {
|
|
V52_BCCFSM_E_MDU_BCC_allocation_req,
|
|
V52_BCCFSM_E_MDU_BCC_deallocation_req,
|
|
V52_BCCFSM_E_MDU_BCC_audit_req,
|
|
V52_BCCFSM_E_ALLOCATION_COMPLETE,
|
|
V52_BCCFSM_E_ALLOCATION_REJECT,
|
|
V52_BCCFSM_E_DE_ALLOCATION_COMPLETE,
|
|
V52_BCCFSM_E_DE_ALLOCATION_REJECT,
|
|
V52_BCCFSM_E_AUDIT_COMPLETE,
|
|
V52_BCCFSM_E_AN_FAULT,
|
|
V52_BCCFSM_E_PROTOCOL_ERROR,
|
|
V52_BCCFSM_E_TIMEOUT_Tbcc1,
|
|
V52_BCCFSM_E_TIMEOUT_Tbcc2,
|
|
V52_BCCFSM_E_TIMEOUT_Tbcc3,
|
|
V52_BCCFSM_E_TIMEOUT_Tbcc4,
|
|
};
|
|
|
|
static const struct value_string v52_bcc_fsm_event_names[] = {
|
|
{ V52_BCCFSM_E_MDU_BCC_allocation_req, "MDU-BCC (Allocation request)" },
|
|
{ V52_BCCFSM_E_MDU_BCC_deallocation_req, "MDU-BCC (De-allocation request)" },
|
|
{ V52_BCCFSM_E_MDU_BCC_audit_req, "MDU-BCC (Audit request)" },
|
|
{ V52_BCCFSM_E_ALLOCATION_COMPLETE, "ALLOCATION COMPLETE" },
|
|
{ V52_BCCFSM_E_ALLOCATION_REJECT, "ALLOCATION REJECT" },
|
|
{ V52_BCCFSM_E_DE_ALLOCATION_COMPLETE, "DE-ALLOCATION COMPLETE" },
|
|
{ V52_BCCFSM_E_DE_ALLOCATION_REJECT, "DE-ALLOCATION REJECT" },
|
|
{ V52_BCCFSM_E_AUDIT_COMPLETE, "AUDIT COMPLETE" },
|
|
{ V52_BCCFSM_E_AN_FAULT, "AN FAULT" },
|
|
{ V52_BCCFSM_E_PROTOCOL_ERROR, "PROTOCOL ERROR" },
|
|
{ V52_BCCFSM_E_TIMEOUT_Tbcc1, "Timeout Tbbc1" },
|
|
{ V52_BCCFSM_E_TIMEOUT_Tbcc2, "Timeout Tbbc2" },
|
|
{ V52_BCCFSM_E_TIMEOUT_Tbcc3, "Timeout Tbbc3" },
|
|
{ V52_BCCFSM_E_TIMEOUT_Tbcc4, "Timeout Tbbc4" },
|
|
{ 0, NULL }
|
|
};
|
|
|
|
/***********************************************************************
|
|
* V5 Message encoding / sending
|
|
***********************************************************************/
|
|
|
|
static inline uint16_t v52_bcc_ref_enc(uint16_t ref, uint8_t source_id)
|
|
{
|
|
uint8_t low, high;
|
|
|
|
high = (ref >> 6) | (source_id << 7);
|
|
low = ref & 0x3f;
|
|
|
|
return (high << 8) | low;
|
|
}
|
|
|
|
static inline uint16_t v52_bcc_ref_dec(uint16_t ie, uint8_t *source_id)
|
|
{
|
|
uint8_t low, high;
|
|
|
|
high = (ie >> 8) & 0x7f;
|
|
low = ie & 0x3f;
|
|
|
|
*source_id = ie >> 15;
|
|
return (high << 6) | low;
|
|
}
|
|
|
|
static inline uint16_t v52_bcc_ts_enc(uint8_t link_id, uint8_t ts, uint8_t override)
|
|
{
|
|
OSMO_ASSERT(!(override >> 1));
|
|
return (link_id << 8) | (override << 5) | ts;
|
|
}
|
|
|
|
static inline void v52_bcc_ts_dec(uint16_t ie, uint8_t *link_id, uint8_t *ts, uint8_t *override)
|
|
{
|
|
*link_id = ie >> 8;
|
|
*override = (ie >> 5) & 0x01;
|
|
*ts = ie & 0x1f;
|
|
}
|
|
|
|
static struct v52_bcc_proc *find_bcc_proto_ref(struct v5x_interface *v5if, uint16_t ref, uint8_t source_id)
|
|
{
|
|
struct v52_bcc_proc *bcc;
|
|
|
|
llist_for_each_entry(bcc, &v5if->bcc_procs, list) {
|
|
if (bcc->ref == ref && bcc->source_id == source_id)
|
|
return bcc;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static struct v52_bcc_proc *find_bcc_proto_ts(struct v5x_interface *v5if, uint8_t link_id, uint8_t ts)
|
|
{
|
|
struct v52_bcc_proc *bcc;
|
|
|
|
llist_for_each_entry(bcc, &v5if->bcc_procs, list) {
|
|
if (bcc->isdn_multislot)
|
|
continue;
|
|
if (bcc->link_id == link_id && bcc->ts == ts)
|
|
return bcc;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static struct v52_bcc_proc *find_bcc_proto_multislot(struct v5x_interface *v5if, uint8_t link_id,
|
|
uint8_t *isdn_multislot)
|
|
{
|
|
struct v52_bcc_proc *bcc;
|
|
|
|
llist_for_each_entry(bcc, &v5if->bcc_procs, list) {
|
|
if (!bcc->isdn_multislot)
|
|
continue;
|
|
if (bcc->link_id == link_id && !memcmp(bcc->isdn_multislot, isdn_multislot, 8))
|
|
return bcc;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* G.965 Section 17.3.1 / Table 29 */
|
|
static struct msgb *v52_enc_allocation(uint16_t ref, uint8_t source_id, uint16_t user_port_id, bool is_isdn,
|
|
uint8_t link_id, uint8_t ts, uint8_t override, uint8_t isdn_slot, uint8_t *isdn_multislot,
|
|
uint8_t *capability)
|
|
{
|
|
uint16_t user_port_ie;
|
|
uint8_t isdn_ts_ie, capa_ie;
|
|
uint16_t v5_ts_ie;
|
|
uint8_t multislot_ie[9];
|
|
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(v52_bcc_ref_enc(ref, source_id));
|
|
l3h->msg_type = V52_CTRL_MSGT_ALLOCATION;
|
|
/* user port identification */
|
|
user_port_ie = htons(v5x_l3_addr_enc(user_port_id, is_isdn));
|
|
msgb_tlv_put(msg, V52_CTRL_IEI_BCC_USER_PORT_ID, 2, (uint8_t *)&user_port_ie);
|
|
if (is_isdn) {
|
|
/* ISDN port time slot */
|
|
isdn_ts_ie = 0x80 | isdn_slot;
|
|
msgb_tlv_put(msg, V52_CTRL_IEI_BCC_ISDN_PORT_TS_ID, 1, &isdn_ts_ie);
|
|
}
|
|
if (!isdn_multislot) {
|
|
/* V5 time slot */
|
|
v5_ts_ie = htons(v52_bcc_ts_enc(link_id, ts, override));
|
|
msgb_tlv_put(msg, V52_CTRL_IEI_BCC_V5_TS_ID, 2, (uint8_t *)&v5_ts_ie);
|
|
} else {
|
|
/* V5 multi slot */
|
|
multislot_ie[0] = link_id;
|
|
memcpy(multislot_ie + 1, isdn_multislot, 8);
|
|
msgb_tlv_put(msg, V52_CTRL_IEI_BCC_MULTI_TS_MAP, 9, multislot_ie);
|
|
}
|
|
if (is_isdn && capability) {
|
|
/* information transfer capability */
|
|
capa_ie = 0x80 | *capability;
|
|
msgb_tlv_put(msg, V52_CTRL_IEI_BCC_INFO_TRANSFER_CAPABILITY, 1, &capa_ie);
|
|
}
|
|
|
|
return msg;
|
|
}
|
|
|
|
/* G.965 Section 17.3.4 / Table 32 */
|
|
static struct msgb *v52_enc_de_allocation(uint16_t ref, uint8_t source_id, uint16_t user_port_id, bool is_isdn,
|
|
uint8_t link_id, uint8_t ts, uint8_t override, uint8_t isdn_slot, uint8_t *isdn_multislot)
|
|
{
|
|
uint16_t user_port_ie;
|
|
uint8_t isdn_ts_ie;
|
|
uint16_t v5_ts_ie;
|
|
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(v52_bcc_ref_enc(ref, source_id));
|
|
l3h->msg_type = V52_CTRL_MSGT_DE_ALLOCATION;
|
|
/* user port identification */
|
|
user_port_ie = htons(v5x_l3_addr_enc(user_port_id, is_isdn));
|
|
msgb_tlv_put(msg, V52_CTRL_IEI_BCC_USER_PORT_ID, 2, (uint8_t *)&user_port_ie);
|
|
if (is_isdn) {
|
|
/* ISDN port time slot */
|
|
isdn_ts_ie = 0x80 | isdn_slot;
|
|
msgb_tlv_put(msg, V52_CTRL_IEI_BCC_ISDN_PORT_TS_ID, 1, &isdn_ts_ie);
|
|
}
|
|
if (!isdn_multislot) {
|
|
/* V5 time slot */
|
|
v5_ts_ie = htons(v52_bcc_ts_enc(link_id, ts, override));
|
|
msgb_tlv_put(msg, V52_CTRL_IEI_BCC_V5_TS_ID, 2, (uint8_t *)&v5_ts_ie);
|
|
} else {
|
|
/* V5 multi slot */
|
|
msgb_tlv_put(msg, V52_CTRL_IEI_BCC_MULTI_TS_MAP, 9, isdn_multislot);
|
|
}
|
|
|
|
return msg;
|
|
}
|
|
|
|
/* G.965 Section 17.3.7 / Table 35 */
|
|
static struct msgb *v52_enc_audit(uint16_t ref, uint8_t source_id, uint8_t link_id, uint8_t ts, uint8_t override)
|
|
{
|
|
uint16_t v5_ts_ie;
|
|
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(v52_bcc_ref_enc(ref, source_id));
|
|
l3h->msg_type = V52_CTRL_MSGT_AUDIT;
|
|
/* V5 time slot */
|
|
v5_ts_ie = htons(v52_bcc_ts_enc(link_id, ts, override));
|
|
msgb_tlv_put(msg, V52_CTRL_IEI_BCC_V5_TS_ID, 2, (uint8_t *)&v5_ts_ie);
|
|
|
|
return msg;
|
|
}
|
|
|
|
/* G.965 Section 17.3.10 / Table 38 */
|
|
static struct msgb *v52_enc_an_fault_ack(uint16_t ref, uint8_t source_id)
|
|
{
|
|
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(v52_bcc_ref_enc(ref, source_id));
|
|
l3h->msg_type = V52_CTRL_MSGT_AN_FAULT_ACK;
|
|
|
|
return msg;
|
|
}
|
|
|
|
/***********************************************************************/
|
|
/* Messages to other layers */
|
|
/***********************************************************************/
|
|
|
|
/* send message to upper (management) layer */
|
|
static void rcv_mdu(struct osmo_fsm_inst *fi, enum v5x_mgmt_prim prim, const uint8_t *cause, uint8_t cause_len)
|
|
{
|
|
struct v52_bcc_proc *bcc = fi->priv;
|
|
|
|
v52_le_bcc_mdu_rcv(bcc->interface, bcc->link_id, bcc->ts, bcc->isdn_multislot, prim, cause, cause_len);
|
|
}
|
|
|
|
/* message to lower (DL) layer */
|
|
static void snd_msg(struct osmo_fsm_inst *fi, struct msgb *msg)
|
|
{
|
|
struct v52_bcc_proc *bcc = fi->priv;
|
|
|
|
if (!msg)
|
|
return;
|
|
v5x_dl_snd(bcc->interface, V52_DLADDR_BCC, msg);
|
|
}
|
|
|
|
/***********************************************************************/
|
|
/* LCP state FSM */
|
|
/***********************************************************************/
|
|
|
|
static int v52_le_bcc_fsm_timer_cb(struct osmo_fsm_inst *fi)
|
|
{
|
|
struct v52_bcc_proc *bcc = fi->priv;
|
|
|
|
bcc->expired++;
|
|
osmo_fsm_inst_dispatch(fi, bcc->timer, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void bcc_fsm_lebcc0_null(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
|
{
|
|
struct v52_bcc_proc *bcc = fi->priv;
|
|
const struct tlv_parsed *tp = data;
|
|
uint8_t const *cause;
|
|
int cause_len;
|
|
|
|
switch (event) {
|
|
case V52_BCCFSM_E_MDU_BCC_allocation_req:
|
|
/* Start Tbcc1 */
|
|
bcc->timer = V52_BCCFSM_E_TIMEOUT_Tbcc1;
|
|
bcc->expired = 0;
|
|
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc1_WAITING_ALLOCATION, TIMEOUT_Tbcc1, 1);
|
|
/* Send ALLOCATION */
|
|
snd_msg(fi, v52_enc_allocation(bcc->ref, bcc->source_id, bcc->user_port_id, bcc->is_isdn, bcc->link_id,
|
|
bcc->ts, bcc->override, bcc->isdn_slot, bcc->isdn_multislot,
|
|
(bcc->use_capability) ? &bcc->capability : NULL));
|
|
break;
|
|
case V52_BCCFSM_E_ALLOCATION_COMPLETE:
|
|
/* Send MDU-BCC (Allocation confirmation) */
|
|
rcv_mdu(fi, MDU_BCC_allocation_conf, 0, 0);
|
|
break;
|
|
case V52_BCCFSM_E_ALLOCATION_REJECT:
|
|
/* Send MDU-BCC (Allocation rejedct indication) */
|
|
cause = TLVP_VAL(tp, V52_CTRL_IEI_BCC_REJECT_CAUSE);
|
|
cause_len = TLVP_LEN(tp, V52_CTRL_IEI_BCC_REJECT_CAUSE);
|
|
rcv_mdu(fi, MDU_BCC_allocation_reject_ind, cause, cause_len);
|
|
break;
|
|
case V52_BCCFSM_E_MDU_BCC_deallocation_req:
|
|
/* Start Tbcc3 */
|
|
bcc->timer = V52_BCCFSM_E_TIMEOUT_Tbcc3;
|
|
bcc->expired = 0;
|
|
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc3_WAITING_DE_ALLOCATION, TIMEOUT_Tbcc3, 3);
|
|
/* Send DE-ALLOCATION */
|
|
snd_msg(fi, v52_enc_de_allocation(bcc->ref, bcc->source_id, bcc->user_port_id, bcc->is_isdn,
|
|
bcc->link_id, bcc->ts, bcc->override, bcc->isdn_slot, bcc->isdn_multislot));
|
|
break;
|
|
case V52_BCCFSM_E_DE_ALLOCATION_COMPLETE:
|
|
/* Send MDU-BCC (De-allocation confirmation) */
|
|
rcv_mdu(fi, MDU_BCC_deallocation_conf, 0, 0);
|
|
break;
|
|
case V52_BCCFSM_E_DE_ALLOCATION_REJECT:
|
|
/* Send MDU-BCC (De-allocation reject indication) */
|
|
cause = TLVP_VAL(tp, V52_CTRL_IEI_BCC_REJECT_CAUSE);
|
|
cause_len = TLVP_LEN(tp, V52_CTRL_IEI_BCC_REJECT_CAUSE);
|
|
rcv_mdu(fi, MDU_BCC_deallocation_reject_ind, cause, cause_len);
|
|
break;
|
|
case V52_BCCFSM_E_MDU_BCC_audit_req:
|
|
/* Start Tbcc4 */
|
|
bcc->timer = V52_BCCFSM_E_TIMEOUT_Tbcc4;
|
|
bcc->expired = 0;
|
|
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc4_WAITING_AUDIT, TIMEOUT_Tbcc4, 4);
|
|
/* Send ADUIT */
|
|
snd_msg(fi, v52_enc_audit(bcc->ref, bcc->source_id, bcc->link_id, bcc->ts, bcc->override));
|
|
break;
|
|
case V52_BCCFSM_E_AN_FAULT:
|
|
/* Send AN FAULT ACK */
|
|
snd_msg(fi, v52_enc_an_fault_ack(bcc->ref, bcc->source_id));
|
|
/* Send MDU-BCC (AN Fault indication) */
|
|
rcv_mdu(fi, MDU_BCC_AN_fault_ind, 0, 0);
|
|
break;
|
|
default:
|
|
OSMO_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
static void bcc_fsm_lebcc1_waiting_alloc(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
|
{
|
|
struct v52_bcc_proc *bcc = fi->priv;
|
|
const struct tlv_parsed *tp = data;
|
|
uint8_t const *cause;
|
|
int cause_len;
|
|
|
|
switch (event) {
|
|
case V52_BCCFSM_E_ALLOCATION_COMPLETE:
|
|
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc0_NULL, 0, 0);
|
|
/* Send MDU-BCC (Allocation confirmation) */
|
|
rcv_mdu(fi, MDU_BCC_allocation_conf, 0, 0);
|
|
break;
|
|
case V52_BCCFSM_E_ALLOCATION_REJECT:
|
|
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc0_NULL, 0, 0);
|
|
/* Send MDU-BCC (Allocation reject indication) */
|
|
cause = TLVP_VAL(tp, V52_CTRL_IEI_BCC_REJECT_CAUSE);
|
|
cause_len = TLVP_LEN(tp, V52_CTRL_IEI_BCC_REJECT_CAUSE);
|
|
rcv_mdu(fi, MDU_BCC_allocation_reject_ind, cause, cause_len);
|
|
break;
|
|
case V52_BCCFSM_E_MDU_BCC_deallocation_req:
|
|
/* Start Tbcc2 */
|
|
bcc->timer = V52_BCCFSM_E_TIMEOUT_Tbcc2;
|
|
bcc->expired = 0;
|
|
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc2_ALLOCATION_ABORT, TIMEOUT_Tbcc2, 2);
|
|
/* Send DE-ALLOCATION */
|
|
snd_msg(fi, v52_enc_de_allocation(bcc->ref, bcc->source_id, bcc->user_port_id, bcc->is_isdn,
|
|
bcc->link_id, bcc->ts, bcc->override, bcc->isdn_slot, bcc->isdn_multislot));
|
|
break;
|
|
case V52_BCCFSM_E_TIMEOUT_Tbcc1:
|
|
if (bcc->expired == 1) {
|
|
/* Restart Tbcc1 */
|
|
osmo_timer_schedule(&fi->timer, TIMEOUT_Tbcc1, 0);
|
|
/* Send ALLOCATION */
|
|
snd_msg(fi, v52_enc_allocation(bcc->ref, bcc->source_id, bcc->user_port_id, bcc->is_isdn,
|
|
bcc->link_id, bcc->ts, bcc->override, bcc->isdn_slot, bcc->isdn_multislot,
|
|
(bcc->use_capability) ? &bcc->capability : NULL));
|
|
break;
|
|
}
|
|
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc0_NULL, 0, 0);
|
|
/* Send MDU-BCC (Allocation error indication) */
|
|
rcv_mdu(fi, MDU_BCC_allocation_error_ind, 0, 0);
|
|
break;
|
|
case V52_BCCFSM_E_PROTOCOL_ERROR:
|
|
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc0_NULL, 0, 0);
|
|
/* Send MDU-BCC (Protocol error indication) */
|
|
cause = TLVP_VAL(tp, V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE);
|
|
cause_len = TLVP_LEN(tp, V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE);
|
|
rcv_mdu(fi, MDU_BCC_protocol_error_ind, cause, cause_len);
|
|
break;
|
|
default:
|
|
OSMO_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
static void bcc_fsm_lebcc2_alloc_abort(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
|
{
|
|
struct v52_bcc_proc *bcc = fi->priv;
|
|
const struct tlv_parsed *tp = data;
|
|
uint8_t const *cause;
|
|
int cause_len;
|
|
|
|
switch (event) {
|
|
case V52_BCCFSM_E_ALLOCATION_COMPLETE:
|
|
/* ignore */
|
|
break;
|
|
case V52_BCCFSM_E_ALLOCATION_REJECT:
|
|
/* ignore */
|
|
break;
|
|
case V52_BCCFSM_E_DE_ALLOCATION_COMPLETE:
|
|
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc0_NULL, 0, 0);
|
|
/* Send MDU-BCC (De-allocation confirmation) */
|
|
rcv_mdu(fi, MDU_BCC_deallocation_conf, 0, 0);
|
|
break;
|
|
case V52_BCCFSM_E_DE_ALLOCATION_REJECT:
|
|
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc0_NULL, 0, 0);
|
|
/* Send MDU-BCC (De-allocation reject indication) */
|
|
cause = TLVP_VAL(tp, V52_CTRL_IEI_BCC_REJECT_CAUSE);
|
|
cause_len = TLVP_LEN(tp, V52_CTRL_IEI_BCC_REJECT_CAUSE);
|
|
rcv_mdu(fi, MDU_BCC_deallocation_reject_ind, cause, cause_len);
|
|
break;
|
|
case V52_BCCFSM_E_TIMEOUT_Tbcc2:
|
|
if (bcc->expired == 1) {
|
|
/* Restart Tbcc2 */
|
|
osmo_timer_schedule(&fi->timer, TIMEOUT_Tbcc2, 0);
|
|
/* Send DE-ALLOCATION */
|
|
snd_msg(fi, v52_enc_de_allocation(bcc->ref, bcc->source_id, bcc->user_port_id, bcc->is_isdn,
|
|
bcc->link_id, bcc->ts, bcc->override, bcc->isdn_slot, bcc->isdn_multislot));
|
|
break;
|
|
}
|
|
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc0_NULL, 0, 0);
|
|
/* Send MDU-BCC (De-allocation error indication) */
|
|
rcv_mdu(fi, MDU_BCC_deallocation_error_ind, 0, 0);
|
|
break;
|
|
case V52_BCCFSM_E_PROTOCOL_ERROR:
|
|
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc0_NULL, 0, 0);
|
|
/* Send MDU-BCC (Protocol error indication) */
|
|
cause = TLVP_VAL(tp, V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE);
|
|
cause_len = TLVP_LEN(tp, V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE);
|
|
rcv_mdu(fi, MDU_BCC_protocol_error_ind, cause, cause_len);
|
|
break;
|
|
default:
|
|
OSMO_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
static void bcc_fsm_lebcc3_waiting_dealloc(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
|
{
|
|
struct v52_bcc_proc *bcc = fi->priv;
|
|
const struct tlv_parsed *tp = data;
|
|
uint8_t const *cause;
|
|
int cause_len;
|
|
|
|
switch (event) {
|
|
case V52_BCCFSM_E_DE_ALLOCATION_COMPLETE:
|
|
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc0_NULL, 0, 0);
|
|
/* Send MDU-BCC (De-allocation confirmation) */
|
|
rcv_mdu(fi, MDU_BCC_deallocation_conf, 0, 0);
|
|
break;
|
|
case V52_BCCFSM_E_DE_ALLOCATION_REJECT:
|
|
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc0_NULL, 0, 0);
|
|
/* Send MDU-BCC (De-allocation reject indication) */
|
|
cause = TLVP_VAL(tp, V52_CTRL_IEI_BCC_REJECT_CAUSE);
|
|
cause_len = TLVP_LEN(tp, V52_CTRL_IEI_BCC_REJECT_CAUSE);
|
|
rcv_mdu(fi, MDU_BCC_deallocation_reject_ind, cause, cause_len);
|
|
break;
|
|
case V52_BCCFSM_E_TIMEOUT_Tbcc3:
|
|
if (bcc->expired == 1) {
|
|
/* Restart Tbcc3 */
|
|
osmo_timer_schedule(&fi->timer, TIMEOUT_Tbcc3, 0);
|
|
/* Send DE-ALLOCATION */
|
|
snd_msg(fi, v52_enc_de_allocation(bcc->ref, bcc->source_id, bcc->user_port_id, bcc->is_isdn,
|
|
bcc->link_id, bcc->ts, bcc->override, bcc->isdn_slot, bcc->isdn_multislot));
|
|
break;
|
|
}
|
|
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc0_NULL, 0, 0);
|
|
/* Send MDU-BCC (De-allocation error indication) */
|
|
rcv_mdu(fi, MDU_BCC_deallocation_error_ind, 0, 0);
|
|
break;
|
|
case V52_BCCFSM_E_PROTOCOL_ERROR:
|
|
/* Send MDU-BCC (Protocol error indication) */
|
|
cause = TLVP_VAL(tp, V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE);
|
|
cause_len = TLVP_LEN(tp, V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE);
|
|
rcv_mdu(fi, MDU_BCC_protocol_error_ind, cause, cause_len);
|
|
break;
|
|
default:
|
|
OSMO_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
static void bcc_fsm_lebcc4_waiting_audit(struct osmo_fsm_inst *fi, uint32_t event, void *data)
|
|
{
|
|
struct v52_bcc_proc *bcc = fi->priv;
|
|
const struct tlv_parsed *tp = data;
|
|
uint8_t const *cause;
|
|
int cause_len;
|
|
|
|
switch (event) {
|
|
case V52_BCCFSM_E_AUDIT_COMPLETE:
|
|
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc0_NULL, 0, 0);
|
|
/* Send MDU-BCC (Audit confirmation) */
|
|
rcv_mdu(fi, MDU_BCC_audit_conf, 0, 0);
|
|
break;
|
|
case V52_BCCFSM_E_TIMEOUT_Tbcc4:
|
|
if (bcc->expired == 1) {
|
|
/* Restart Tbcc4 */
|
|
osmo_timer_schedule(&fi->timer, TIMEOUT_Tbcc4, 0);
|
|
/* Send AUDIT */
|
|
snd_msg(fi, v52_enc_audit(bcc->ref, bcc->source_id, bcc->user_port_id, bcc->ts, bcc->override));
|
|
break;
|
|
}
|
|
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc0_NULL, 0, 0);
|
|
/* Send MDU-BCC (Audit error indication) */
|
|
rcv_mdu(fi, MDU_BCC_audit_error_ind, 0, 0);
|
|
break;
|
|
case V52_BCCFSM_E_PROTOCOL_ERROR:
|
|
osmo_fsm_inst_state_chg(fi, V52_BCCFSM_S_LEBcc0_NULL, 0, 0);
|
|
/* Send MDU-BCC (Protocol error indication) */
|
|
cause = TLVP_VAL(tp, V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE);
|
|
cause_len = TLVP_LEN(tp, V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE);
|
|
rcv_mdu(fi, MDU_BCC_protocol_error_ind, cause, cause_len);
|
|
break;
|
|
default:
|
|
OSMO_ASSERT(0);
|
|
}
|
|
}
|
|
|
|
/* Table 47/G.965 */
|
|
static const struct osmo_fsm_state v52_le_bcc_fsm_states[] = {
|
|
[V52_BCCFSM_S_LEBcc0_NULL] = {
|
|
.name = "LEBcc0 BCC BULL",
|
|
.in_event_mask = S(V52_BCCFSM_E_MDU_BCC_allocation_req) |
|
|
S(V52_BCCFSM_E_ALLOCATION_COMPLETE) |
|
|
S(V52_BCCFSM_E_ALLOCATION_REJECT) |
|
|
S(V52_BCCFSM_E_MDU_BCC_deallocation_req) |
|
|
S(V52_BCCFSM_E_DE_ALLOCATION_COMPLETE) |
|
|
S(V52_BCCFSM_E_DE_ALLOCATION_REJECT) |
|
|
S(V52_BCCFSM_E_MDU_BCC_audit_req) |
|
|
S(V52_BCCFSM_E_AN_FAULT) ,
|
|
.out_state_mask = S(V52_BCCFSM_S_LEBcc1_WAITING_ALLOCATION) |
|
|
S(V52_BCCFSM_S_LEBcc3_WAITING_DE_ALLOCATION) |
|
|
S(V52_BCCFSM_S_LEBcc4_WAITING_AUDIT),
|
|
.action = bcc_fsm_lebcc0_null,
|
|
},
|
|
[V52_BCCFSM_S_LEBcc1_WAITING_ALLOCATION] = {
|
|
.name = "LEBcc1 BCC WAITING ALLOCATION",
|
|
.in_event_mask = S(V52_BCCFSM_E_ALLOCATION_COMPLETE) |
|
|
S(V52_BCCFSM_E_ALLOCATION_REJECT) |
|
|
S(V52_BCCFSM_E_MDU_BCC_deallocation_req) |
|
|
S(V52_BCCFSM_E_TIMEOUT_Tbcc1) |
|
|
S(V52_BCCFSM_E_PROTOCOL_ERROR),
|
|
.out_state_mask = S(V52_BCCFSM_S_LEBcc0_NULL) |
|
|
S(V52_BCCFSM_S_LEBcc2_ALLOCATION_ABORT),
|
|
.action = bcc_fsm_lebcc1_waiting_alloc,
|
|
},
|
|
[V52_BCCFSM_S_LEBcc2_ALLOCATION_ABORT] = {
|
|
.name = "LEBcc2 BCC ALLOCATION ABORT",
|
|
.in_event_mask = S(V52_BCCFSM_E_ALLOCATION_COMPLETE) |
|
|
S(V52_BCCFSM_E_ALLOCATION_REJECT) |
|
|
S(V52_BCCFSM_E_DE_ALLOCATION_COMPLETE) |
|
|
S(V52_BCCFSM_E_DE_ALLOCATION_REJECT) |
|
|
S(V52_BCCFSM_E_TIMEOUT_Tbcc2) |
|
|
S(V52_BCCFSM_E_PROTOCOL_ERROR),
|
|
.out_state_mask = S(V52_BCCFSM_S_LEBcc0_NULL),
|
|
.action = bcc_fsm_lebcc2_alloc_abort,
|
|
},
|
|
[V52_BCCFSM_S_LEBcc3_WAITING_DE_ALLOCATION] = {
|
|
.name = "LEBcc3 BCC WAITING DE-ALLOCATION",
|
|
.in_event_mask = S(V52_BCCFSM_E_DE_ALLOCATION_COMPLETE) |
|
|
S(V52_BCCFSM_E_DE_ALLOCATION_REJECT) |
|
|
S(V52_BCCFSM_E_TIMEOUT_Tbcc3) |
|
|
S(V52_BCCFSM_E_PROTOCOL_ERROR),
|
|
.out_state_mask = S(V52_BCCFSM_S_LEBcc0_NULL),
|
|
.action = bcc_fsm_lebcc3_waiting_dealloc,
|
|
},
|
|
[V52_BCCFSM_S_LEBcc4_WAITING_AUDIT] = {
|
|
.name = "LEBcc4 BCC wAITING AUDIT",
|
|
.in_event_mask = S(V52_BCCFSM_E_MDU_BCC_audit_req) |
|
|
S(V52_BCCFSM_E_TIMEOUT_Tbcc1) |
|
|
S(V52_BCCFSM_E_PROTOCOL_ERROR),
|
|
.out_state_mask = S(V52_BCCFSM_S_LEBcc0_NULL),
|
|
.action = bcc_fsm_lebcc4_waiting_audit,
|
|
},
|
|
};
|
|
|
|
struct osmo_fsm v52_le_bcc_fsm = {
|
|
.name = "V52_LE_BCC",
|
|
.states = v52_le_bcc_fsm_states,
|
|
.num_states = ARRAY_SIZE(v52_le_bcc_fsm_states),
|
|
.timer_cb = v52_le_bcc_fsm_timer_cb,
|
|
.log_subsys = DV5BCC,
|
|
.event_names = v52_bcc_fsm_event_names,
|
|
};
|
|
|
|
static uint16_t bcc_new_ref = 0;
|
|
|
|
static struct v52_bcc_proc *v52_le_bcc_create(struct v5x_interface *v5if, uint16_t user_port_id, bool is_isdn,
|
|
uint8_t link_id, uint8_t ts, uint8_t override, uint8_t isdn_slot, uint8_t *isdn_multislot, uint8_t *capability,
|
|
int source_id)
|
|
{
|
|
struct v52_bcc_proc *bcc;
|
|
|
|
OSMO_ASSERT(v5if);
|
|
|
|
LOGP(DV5BCC, LOGL_DEBUG, "Creating BCC instance, initiated by %s.\n", (source_id) ? "AN" : "LE");
|
|
|
|
bcc = talloc_zero(v5if, struct v52_bcc_proc);
|
|
if (!bcc)
|
|
return NULL;
|
|
bcc->interface = v5if;
|
|
|
|
bcc->fi = osmo_fsm_inst_alloc(&v52_le_bcc_fsm, bcc, bcc, LOGL_DEBUG, NULL);
|
|
if (!bcc->fi) {
|
|
talloc_free(bcc);
|
|
return NULL;
|
|
}
|
|
|
|
bcc->ref = (bcc_new_ref++) & 0x1fff;
|
|
bcc->source_id = source_id;
|
|
bcc->user_port_id = user_port_id;
|
|
bcc->is_isdn = is_isdn;
|
|
bcc->link_id = link_id;
|
|
bcc->ts = ts;
|
|
bcc->override = override;
|
|
bcc->isdn_slot = isdn_slot;
|
|
if (isdn_multislot) {
|
|
/* copy multislot IE content */
|
|
bcc->isdn_multislot = talloc_zero_size(bcc, 9);
|
|
if (bcc->isdn_multislot) {
|
|
bcc->isdn_multislot[0] = link_id;
|
|
memcpy(bcc->isdn_multislot + 1, isdn_multislot, 8);
|
|
}
|
|
}
|
|
if (capability) {
|
|
bcc->use_capability = true;
|
|
bcc->capability = *capability;
|
|
}
|
|
|
|
llist_add_tail(&bcc->list, &v5if->bcc_procs);
|
|
|
|
return bcc;
|
|
}
|
|
|
|
void v52_le_bcc_destroy(struct v52_bcc_proc *bcc)
|
|
{
|
|
LOGP(DV5BCC, LOGL_DEBUG, "Destroying BCC instance.\n");
|
|
|
|
if (bcc->fi)
|
|
osmo_fsm_inst_free(bcc->fi);
|
|
|
|
llist_del(&bcc->list);
|
|
|
|
talloc_free(bcc);
|
|
}
|
|
|
|
void v52_le_bcc_init(void)
|
|
{
|
|
int rc;
|
|
|
|
rc = osmo_fsm_register(&v52_le_bcc_fsm);
|
|
OSMO_ASSERT(!rc);
|
|
LOGP(DV5BCC, LOGL_NOTICE, "Using V52 BCC protocol\n");
|
|
}
|
|
|
|
/***********************************************************************
|
|
* Message receiving / decoding
|
|
***********************************************************************/
|
|
|
|
/* receive message from lower (DL) layer */
|
|
int v52_le_bcc_dl_rcv(struct v5x_interface *v5if, uint16_t l3_addr, uint8_t msg_type, const struct tlv_parsed *tp)
|
|
{
|
|
struct v52_bcc_proc *bcc;
|
|
uint16_t ref;
|
|
uint8_t source_id;
|
|
|
|
/* search for ref */
|
|
ref = v52_bcc_ref_dec(l3_addr, &source_id);
|
|
bcc = find_bcc_proto_ref(v5if, ref, source_id);
|
|
|
|
/* handle AN fault here, so there is no need to create a process */
|
|
if (msg_type == V52_CTRL_MSGT_AN_FAULT) {
|
|
LOGP(DV5BCC, LOGL_NOTICE, "Replying to AN FAULT message. No action is taken here.\n");
|
|
/* Send AN FAULT ACK */
|
|
v5x_dl_snd(v5if, V52_DLADDR_BCC, v52_enc_an_fault_ack(ref, source_id));
|
|
return 0;
|
|
}
|
|
|
|
/* ignoring remote process (may happen due to message repetition in state 0) */
|
|
if (!bcc)
|
|
return 0;
|
|
|
|
switch (msg_type) {
|
|
case V52_CTRL_MSGT_ALLOCATION_COMPLETE:
|
|
osmo_fsm_inst_dispatch(bcc->fi, V52_BCCFSM_E_ALLOCATION_COMPLETE, (void *)tp);
|
|
break;
|
|
case V52_CTRL_MSGT_ALLOCATION_REJECT:
|
|
osmo_fsm_inst_dispatch(bcc->fi, V52_BCCFSM_E_ALLOCATION_REJECT, (void *)tp);
|
|
break;
|
|
case V52_CTRL_MSGT_DE_ALLOCATION_COMPLETE:
|
|
osmo_fsm_inst_dispatch(bcc->fi, V52_BCCFSM_E_DE_ALLOCATION_COMPLETE, (void *)tp);
|
|
break;
|
|
case V52_CTRL_MSGT_DE_ALLOCATION_REJECT:
|
|
osmo_fsm_inst_dispatch(bcc->fi, V52_BCCFSM_E_DE_ALLOCATION_REJECT, (void *)tp);
|
|
break;
|
|
case V52_CTRL_MSGT_AUDIT_COMPLETE:
|
|
osmo_fsm_inst_dispatch(bcc->fi, V52_BCCFSM_E_AUDIT_COMPLETE, (void *)tp);
|
|
break;
|
|
case V52_CTRL_MSGT_AN_FAULT:
|
|
osmo_fsm_inst_dispatch(bcc->fi, V52_BCCFSM_E_AN_FAULT, (void *)tp);
|
|
break;
|
|
case V52_CTRL_MSGT_PROTOCOL_ERROR:
|
|
osmo_fsm_inst_dispatch(bcc->fi, V52_BCCFSM_E_PROTOCOL_ERROR, (void *)tp);
|
|
break;
|
|
default:
|
|
LOGP(DV5BCC, LOGL_NOTICE, "Invalid BCC protocol message %d receied from AN.\n", msg_type);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (bcc->fi->state == V52_BCCFSM_S_LEBcc0_NULL) {
|
|
/* destroy, if NULL state is reached */
|
|
v52_le_bcc_destroy(bcc);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* receive message from upper (management) layer */
|
|
int v52_le_bcc_mdu_snd(struct v5x_interface *v5if, uint16_t user_port_id, bool is_isdn, uint8_t link_id, uint8_t ts,
|
|
uint8_t override, uint8_t isdn_slot, uint8_t *isdn_multislot, uint8_t *capability, enum v5x_mgmt_prim prim)
|
|
{
|
|
struct v52_bcc_proc *bcc;
|
|
|
|
/* search TS */
|
|
if (isdn_multislot)
|
|
bcc = find_bcc_proto_multislot(v5if, link_id, isdn_multislot);
|
|
else
|
|
bcc = find_bcc_proto_ts(v5if, link_id, ts);
|
|
if (!bcc) {
|
|
bcc = v52_le_bcc_create(v5if, user_port_id, is_isdn, link_id, ts, override, isdn_slot, isdn_multislot,
|
|
capability, 0 /* LE */);
|
|
if (!bcc)
|
|
return -ENOMEM;
|
|
}
|
|
|
|
switch (prim) {
|
|
case MDU_BCC_allocation_req:
|
|
osmo_fsm_inst_dispatch(bcc->fi, V52_BCCFSM_E_MDU_BCC_allocation_req, NULL);
|
|
break;
|
|
case MDU_BCC_deallocation_req:
|
|
osmo_fsm_inst_dispatch(bcc->fi, V52_BCCFSM_E_MDU_BCC_deallocation_req, NULL);
|
|
break;
|
|
case MDU_BCC_audit_req:
|
|
osmo_fsm_inst_dispatch(bcc->fi, V52_BCCFSM_E_MDU_BCC_audit_req, NULL);
|
|
break;
|
|
default:
|
|
LOGP(DV5BCC, LOGL_NOTICE, "Invalid BCC protocol message %d receied from AN.\n", prim);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (bcc->fi->state == V52_BCCFSM_S_LEBcc0_NULL) {
|
|
/* destroy, if NULL state is reached */
|
|
v52_le_bcc_destroy(bcc);
|
|
}
|
|
|
|
return 0;
|
|
}
|