WIP initial import of far incomplete ETSI V5 implementation

This commit is contained in:
Harald Welte 2021-12-28 11:32:53 +01:00
commit e1070616f1
8 changed files with 3014 additions and 0 deletions

735
lapv5.c Normal file
View File

@ -0,0 +1,735 @@
/* LAPV5-DL as per Section 10.1 of ITU-T G.964 */
/* (C) 2021 by Harald Welte <laforge@gnumonks.org>
*
* 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 "internal.h"
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/timer.h>
#include <osmocom/abis/lapd.h>
#include <osmocom/abis/lapd_pcap.h>
#define LAPD_ADDR2(sapi, cr) ((((sapi) & 0x3f) << 2) | (((cr) & 0x1) << 1))
#define LAPD_ADDR3(tei) ((((tei) & 0x7f) << 1) | 0x1)
#define LAPD_ADDR_SAPI(addr) ((addr) >> 2)
#define LAPD_ADDR_CR(addr) (((addr) >> 1) & 0x1)
#define LAPD_ADDR_EA(addr) ((addr) & 0x1)
#define LAPD_ADDR_TEI(addr) ((addr) >> 1)
#define LAPD_CTRL_I4(ns) (((ns) & 0x7f) << 1)
#define LAPD_CTRL_I5(nr, p) ((((nr) & 0x7f) << 1) | ((p) & 0x1))
#define LAPD_CTRL_S4(s) ((((s) & 0x3) << 2) | 0x1)
#define LAPD_CTRL_S5(nr, p) ((((nr) & 0x7f) << 1) | ((p) & 0x1))
#define LAPD_CTRL_U4(u, p) ((((u) & 0x1c) << (5-2)) | (((p) & 0x1) << 4) | (((u) & 0x3) << 2) | 0x3)
#define LAPD_CTRL_is_I(ctrl) (((ctrl) & 0x1) == 0)
#define LAPD_CTRL_is_S(ctrl) (((ctrl) & 0x3) == 1)
#define LAPD_CTRL_is_U(ctrl) (((ctrl) & 0x3) == 3)
#define LAPD_CTRL_U_BITS(ctrl) ((((ctrl) & 0xC) >> 2) | ((ctrl) & 0xE0) >> 3)
#define LAPD_CTRL_U_PF(ctrl) (((ctrl) >> 4) & 0x1)
#define LAPD_CTRL_S_BITS(ctrl) (((ctrl) & 0xC) >> 2)
#define LAPD_CTRL_S_PF(ctrl) (ctrl & 0x1)
#define LAPD_CTRL_I_Ns(ctrl) (((ctrl) & 0xFE) >> 1)
#define LAPD_CTRL_I_P(ctrl) (ctrl & 0x1)
#define LAPD_CTRL_Nr(ctrl) (((ctrl) & 0xFE) >> 1)
#define LAPD_LEN(len) ((len << 2) | 0x1)
#define LAPD_EL 0x1
#define LAPD_SET_K(n, o) {n,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}
#define LOGLI(li, level, fmt, args ...) \
LOGP(DLLAPD, level, "(%s): " fmt, (li)->name, ## args)
#define LOGTEI(teip, level, fmt, args ...) \
LOGP(DLLAPD, level, "(%s-T%u): " fmt, (teip)->li->name, (teip)->tei, ## args)
#define LOGSAP(sap, level, fmt, args ...) \
LOGP(DLLAPD, level, "(%s): " fmt, (sap)->dl.name, ## args)
#define DLSAP_MSGB_SIZE 128
#define DLSAP_MSGB_HEADROOM 56
const struct lapd_profile lapd_profile_lapv5dl = {
.k = LAPD_SET_K(7,7),
.n200 = 3,
.n201 = 260,
.n202 = 3,
.t200_sec = 1, .t200_usec = 0,
.t203_sec = 10, .t203_usec = 0,
.short_address = 0
};
typedef enum {
LAPD_TEI_NONE = 0,
LAPD_TEI_ASSIGNED,
LAPD_TEI_ACTIVE,
} lapd_tei_state;
const char *lapd_tei_states[] = {
"NONE",
"ASSIGNED",
"ACTIVE",
};
/* Structure representing an allocated TEI within a LAPD instance. */
struct lapd_tei {
struct llist_head list;
struct lapd_instance *li;
uint8_t tei;
lapd_tei_state state;
struct llist_head sap_list;
};
/* Structure representing a SAP within a TEI. It includes exactly one datalink
* instance. */
struct lapd_sap {
struct llist_head list;
struct lapd_tei *tei;
uint8_t sapi;
struct lapd_datalink dl;
};
/* Resolve TEI structure from given numeric TEI */
static struct lapd_tei *teip_from_tei(struct lapd_instance *li, uint8_t tei)
{
struct lapd_tei *lt;
llist_for_each_entry(lt, &li->tei_list, list) {
if (lt->tei == tei)
return lt;
}
return NULL;
};
/* Change state of TEI */
static void lapd_tei_set_state(struct lapd_tei *teip, int newstate)
{
LOGTEI(teip, LOGL_INFO, "LAPD state change on TEI %d: %s -> %s\n",
teip->tei, lapd_tei_states[teip->state],
lapd_tei_states[newstate]);
teip->state = newstate;
};
/* Allocate a new TEI */
struct lapd_tei *lapd_tei_alloc(struct lapd_instance *li, uint8_t tei)
{
struct lapd_tei *teip;
teip = talloc_zero(li, struct lapd_tei);
if (!teip)
return NULL;
teip->li = li;
teip->tei = tei;
llist_add(&teip->list, &li->tei_list);
INIT_LLIST_HEAD(&teip->sap_list);
lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED);
return teip;
}
/* Find a SAP within a given TEI */
static struct lapd_sap *lapd_sap_find(struct lapd_tei *teip, uint8_t sapi)
{
struct lapd_sap *sap;
llist_for_each_entry(sap, &teip->sap_list, list) {
if (sap->sapi == sapi)
return sap;
}
return NULL;
}
static int send_ph_data_req(struct lapd_msg_ctx *lctx, struct msgb *msg);
static int send_dlsap(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx);
/* Allocate a new SAP within a given TEI */
static struct lapd_sap *lapd_sap_alloc(struct lapd_tei *teip, uint8_t sapi)
{
struct lapd_sap *sap;
struct lapd_datalink *dl;
struct lapd_instance *li = teip->li;
struct lapd_profile *profile;
char name[256];
int k;
snprintf(name, sizeof(name), "%s-T%u-S%u", li->name, teip->tei, sapi);
sap = talloc_zero(teip, struct lapd_sap);
if (!sap)
return NULL;
LOGP(DLLAPD, LOGL_NOTICE,
"(%s): LAPD Allocating SAP for SAPI=%u / TEI=%u (dl=%p, sap=%p)\n",
name, sapi, teip->tei, &sap->dl, sap);
sap->sapi = sapi;
sap->tei = teip;
dl = &sap->dl;
profile = &li->profile;
k = profile->k[sapi & 0x3f];
LOGP(DLLAPD, LOGL_NOTICE, "(%s): k=%d N200=%d N201=%d T200=%d.%d T203=%d.%d\n",
name, k, profile->n200, profile->n201, profile->t200_sec,
profile->t200_usec, profile->t203_sec, profile->t203_usec);
lapd_dl_init2(dl, k, 128, profile->n201, name);
dl->use_sabme = 1; /* use SABME instead of SABM (GSM) */
dl->send_ph_data_req = send_ph_data_req;
dl->send_dlsap = send_dlsap;
dl->n200 = profile->n200;
dl->n200_est_rel = profile->n200;
dl->t200_sec = profile->t200_sec; dl->t200_usec = profile->t200_usec;
dl->t203_sec = profile->t203_sec; dl->t203_usec = profile->t203_usec;
dl->lctx.dl = &sap->dl;
dl->lctx.sapi = sapi;
dl->lctx.tei = teip->tei;
dl->lctx.n201 = profile->n201;
lapd_set_mode(&sap->dl, (teip->li->network_side) ? LAPD_MODE_NETWORK
: LAPD_MODE_USER);
llist_add(&sap->list, &teip->sap_list);
return sap;
}
/* Free SAP instance, including the datalink */
static void lapd_sap_free(struct lapd_sap *sap)
{
LOGSAP(sap, LOGL_NOTICE,
"LAPD Freeing SAP for SAPI=%u / TEI=%u (dl=%p, sap=%p)\n",
sap->sapi, sap->tei->tei, &sap->dl, sap);
/* free datalink structures and timers */
lapd_dl_exit(&sap->dl);
llist_del(&sap->list);
talloc_free(sap);
}
/* Free TEI instance */
static void lapd_tei_free(struct lapd_tei *teip)
{
struct lapd_sap *sap, *sap2;
llist_for_each_entry_safe(sap, sap2, &teip->sap_list, list) {
lapd_sap_free(sap);
}
llist_del(&teip->list);
talloc_free(teip);
}
/* Input function into TEI manager */
static int lapd_tei_receive(struct lapd_instance *li, uint8_t *data, int len)
{
uint8_t entity;
uint8_t ref;
uint8_t mt;
uint8_t action;
uint8_t e;
uint8_t resp[8];
struct lapd_tei *teip;
struct msgb *msg;
if (len < 5) {
LOGLI(li, LOGL_ERROR, "LAPD TEIMGR frame receive len %d < 5"
", ignoring\n", len);
return -EINVAL;
};
entity = data[0];
ref = data[1];
mt = data[3];
action = data[4] >> 1;
e = data[4] & 1;
DEBUGP(DLLAPD, "LAPD TEIMGR: entity %x, ref %x, mt %x, action %x, "
"e %x\n", entity, ref, mt, action, e);
switch (mt) {
case 0x01: /* IDENTITY REQUEST */
DEBUGP(DLLAPD, "LAPD TEIMGR: identity request for TEI %u\n",
action);
teip = teip_from_tei(li, action);
if (!teip) {
LOGLI(li, LOGL_INFO, "TEI MGR: New TEI %u\n",
action);
teip = lapd_tei_alloc(li, action);
if (!teip)
return -ENOMEM;
}
/* Send ACCEPT */
memmove(resp, "\xfe\xff\x03\x0f\x00\x00\x02\x00", 8);
resp[7] = (action << 1) | 1;
msg = msgb_alloc_headroom(DLSAP_MSGB_SIZE, DLSAP_MSGB_HEADROOM, "DL EST");
msg->l2h = msgb_push(msg, 8);
memcpy(msg->l2h, resp, 8);
/* write to PCAP file, if enabled. */
osmo_pcap_lapd_write(li->pcap_fd, OSMO_LAPD_PCAP_OUTPUT, msg);
LOGTEI(teip, LOGL_DEBUG, "TX: %s\n", osmo_hexdump(msg->data, msg->len));
li->transmit_cb(msg, li->transmit_cbdata);
if (teip->state == LAPD_TEI_NONE)
lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED);
break;
default:
LOGLI(li, LOGL_NOTICE, "LAPD TEIMGR: unknown mt %x action %x\n", mt, action);
break;
};
return 0;
}
/* General input function for any data received for this LAPD instance */
int lapd_receive(struct lapd_instance *li, struct msgb *msg, int *error)
{
int i;
struct lapd_msg_ctx lctx;
int rc;
struct lapd_sap *sap;
struct lapd_tei *teip;
/* write to PCAP file, if enabled. */
osmo_pcap_lapd_write(li->pcap_fd, OSMO_LAPD_PCAP_INPUT, msg);
LOGLI(li, LOGL_DEBUG, "RX: %s\n", osmo_hexdump(msg->data, msg->len));
if (msg->len < 2) {
LOGLI(li, LOGL_ERROR, "LAPD frame receive len %d < 2, ignoring\n", msg->len);
*error = LAPD_ERR_BAD_LEN;
return -EINVAL;
};
msg->l2h = msg->data;
memset(&lctx, 0, sizeof(lctx));
i = 0;
/* adress field */
lctx.sapi = LAPD_ADDR_SAPI(msg->l2h[i]);
lctx.cr = LAPD_ADDR_CR(msg->l2h[i]);
lctx.lpd = 0;
if (!LAPD_ADDR_EA(msg->l2h[i])) {
if (msg->len < 3) {
LOGLI(li, LOGL_ERROR, "LAPD frame with TEI receive "
"len %d < 3, ignoring\n", msg->len);
*error = LAPD_ERR_BAD_LEN;
return -EINVAL;
};
i++;
lctx.tei = LAPD_ADDR_TEI(msg->l2h[i]);
}
i++;
/* control field */
if (LAPD_CTRL_is_I(msg->l2h[i])) {
lctx.format = LAPD_FORM_I;
lctx.n_send = LAPD_CTRL_I_Ns(msg->l2h[i]);
i++;
if (msg->len < 3 && i == 2) {
LOGLI(li, LOGL_ERROR, "LAPD I frame without TEI "
"receive len %d < 3, ignoring\n", msg->len);
*error = LAPD_ERR_BAD_LEN;
return -EINVAL;
};
if (msg->len < 4 && i == 3) {
LOGLI(li, LOGL_ERROR, "LAPD I frame with TEI "
"receive len %d < 4, ignoring\n", msg->len);
*error = LAPD_ERR_BAD_LEN;
return -EINVAL;
};
lctx.n_recv = LAPD_CTRL_Nr(msg->l2h[i]);
lctx.p_f = LAPD_CTRL_I_P(msg->l2h[i]);
} else if (LAPD_CTRL_is_S(msg->l2h[i])) {
lctx.format = LAPD_FORM_S;
lctx.s_u = LAPD_CTRL_S_BITS(msg->l2h[i]);
i++;
if (msg->len < 3 && i == 2) {
LOGLI(li, LOGL_ERROR, "LAPD S frame without TEI "
"receive len %d < 3, ignoring\n", msg->len);
*error = LAPD_ERR_BAD_LEN;
return -EINVAL;
};
if (msg->len < 4 && i == 3) {
LOGLI(li, LOGL_ERROR, "LAPD S frame with TEI "
"receive len %d < 4, ignoring\n", msg->len);
*error = LAPD_ERR_BAD_LEN;
return -EINVAL;
};
lctx.n_recv = LAPD_CTRL_Nr(msg->l2h[i]);
lctx.p_f = LAPD_CTRL_S_PF(msg->l2h[i]);
} else if (LAPD_CTRL_is_U(msg->l2h[i])) {
lctx.format = LAPD_FORM_U;
lctx.s_u = LAPD_CTRL_U_BITS(msg->l2h[i]);
lctx.p_f = LAPD_CTRL_U_PF(msg->l2h[i]);
} else
lctx.format = LAPD_FORM_UKN;
i++;
/* length */
msg->l3h = msg->l2h + i;
msgb_pull(msg, i);
lctx.length = msg->len;
/* perform TEI assignment, if received */
if (lctx.tei == 127) {
rc = lapd_tei_receive(li, msg->data, msg->len);
msgb_free(msg);
return rc;
}
/* resolve TEI and SAPI */
teip = teip_from_tei(li, lctx.tei);
if (!teip) {
LOGLI(li, LOGL_NOTICE, "LAPD Unknown TEI %u\n", lctx.tei);
*error = LAPD_ERR_UNKNOWN_TEI;
msgb_free(msg);
return -EINVAL;
}
sap = lapd_sap_find(teip, lctx.sapi);
if (!sap) {
LOGTEI(teip, LOGL_INFO, "LAPD No SAP for TEI=%u / SAPI=%u, "
"allocating\n", lctx.tei, lctx.sapi);
sap = lapd_sap_alloc(teip, lctx.sapi);
if (!sap) {
*error = LAPD_ERR_NO_MEM;
msgb_free(msg);
return -ENOMEM;
}
}
lctx.dl = &sap->dl;
lctx.n201 = lctx.dl->maxf;
if (msg->len > lctx.n201) {
LOGSAP(sap, LOGL_ERROR, "message len %d > N201(%d) "
"(discarding)\n", msg->len, lctx.n201);
msgb_free(msg);
*error = LAPD_ERR_BAD_LEN;
return -EINVAL;
}
/* send to LAPD */
return lapd_ph_data_ind(msg, &lctx);
}
/* Start a (user-side) SAP for the specified TEI/SAPI on the LAPD instance */
int lapd_sap_start(struct lapd_instance *li, uint8_t tei, uint8_t sapi)
{
struct lapd_sap *sap;
struct lapd_tei *teip;
struct osmo_dlsap_prim dp;
struct msgb *msg;
teip = teip_from_tei(li, tei);
if (!teip)
teip = lapd_tei_alloc(li, tei);
sap = lapd_sap_find(teip, sapi);
if (sap)
return -EEXIST;
sap = lapd_sap_alloc(teip, sapi);
if (!sap)
return -ENOMEM;
LOGSAP(sap, LOGL_NOTICE, "LAPD DL-ESTABLISH request TEI=%d SAPI=%d\n", tei, sapi);
/* prepare prim */
msg = msgb_alloc_headroom(DLSAP_MSGB_SIZE, DLSAP_MSGB_HEADROOM, "DL EST");
msg->l3h = msg->data;
osmo_prim_init(&dp.oph, 0, PRIM_DL_EST, PRIM_OP_REQUEST, msg);
/* send to L2 */
return lapd_recv_dlsap(&dp, &sap->dl.lctx);
}
/* Stop a (user-side) SAP for the specified TEI/SAPI on the LAPD instance */
int lapd_sap_stop(struct lapd_instance *li, uint8_t tei, uint8_t sapi)
{
struct lapd_tei *teip;
struct lapd_sap *sap;
struct osmo_dlsap_prim dp;
struct msgb *msg;
teip = teip_from_tei(li, tei);
if (!teip)
return -ENODEV;
sap = lapd_sap_find(teip, sapi);
if (!sap)
return -ENODEV;
LOGSAP(sap, LOGL_NOTICE, "LAPD DL-RELEASE request TEI=%d SAPI=%d\n", tei, sapi);
/* prepare prim */
msg = msgb_alloc_headroom(DLSAP_MSGB_SIZE, DLSAP_MSGB_HEADROOM, "DL REL");
msg->l3h = msg->data;
osmo_prim_init(&dp.oph, 0, PRIM_DL_REL, PRIM_OP_REQUEST, msg);
/* send to L2 */
return lapd_recv_dlsap(&dp, &sap->dl.lctx);
}
/* Transmit Data (DL-DATA request) on the given LAPD Instance / TEI / SAPI */
void lapd_transmit(struct lapd_instance *li, uint8_t tei, uint8_t sapi,
struct msgb *msg)
{
struct lapd_tei *teip = teip_from_tei(li, tei);
struct lapd_sap *sap;
struct osmo_dlsap_prim dp;
if (!teip) {
LOGLI(li, LOGL_ERROR, "LAPD Cannot transmit on non-existing TEI %u\n", tei);
msgb_free(msg);
return;
}
sap = lapd_sap_find(teip, sapi);
if (!sap) {
LOGTEI(teip, LOGL_INFO, "LAPD Tx on unknown SAPI=%u in TEI=%u\n", sapi, tei);
msgb_free(msg);
return;
}
/* prepare prim */
msg->l3h = msg->data;
osmo_prim_init(&dp.oph, 0, PRIM_DL_DATA, PRIM_OP_REQUEST, msg);
/* send to L2 */
lapd_recv_dlsap(&dp, &sap->dl.lctx);
};
static int send_ph_data_req(struct lapd_msg_ctx *lctx, struct msgb *msg)
{
struct lapd_datalink *dl = lctx->dl;
struct lapd_sap *sap =
container_of(dl, struct lapd_sap, dl);
struct lapd_instance *li = sap->tei->li;
int format = lctx->format;
int addr_len;
/* control field */
switch (format) {
case LAPD_FORM_I:
msg->l2h = msgb_push(msg, 2);
msg->l2h[0] = LAPD_CTRL_I4(lctx->n_send);
msg->l2h[1] = LAPD_CTRL_I5(lctx->n_recv, lctx->p_f);
break;
case LAPD_FORM_S:
msg->l2h = msgb_push(msg, 2);
msg->l2h[0] = LAPD_CTRL_S4(lctx->s_u);
msg->l2h[1] = LAPD_CTRL_S5(lctx->n_recv, lctx->p_f);
break;
case LAPD_FORM_U:
msg->l2h = msgb_push(msg, 1);
msg->l2h[0] = LAPD_CTRL_U4(lctx->s_u, lctx->p_f);
break;
default:
msgb_free(msg);
return -EINVAL;
}
/* address field */
if (li->profile.short_address && lctx->tei == 0)
addr_len = 1;
else
addr_len = 2;
msg->l2h = msgb_push(msg, addr_len);
msg->l2h[0] = LAPD_ADDR2(lctx->sapi, lctx->cr);
if (addr_len == 1)
msg->l2h[0] |= 0x1;
else
msg->l2h[1] = LAPD_ADDR3(lctx->tei);
/* write to PCAP file, if enabled. */
osmo_pcap_lapd_write(li->pcap_fd, OSMO_LAPD_PCAP_OUTPUT, msg);
/* forward frame to L1 */
LOGDL(dl, LOGL_DEBUG, "TX: %s\n", osmo_hexdump(msg->data, msg->len));
li->transmit_cb(msg, li->transmit_cbdata);
return 0;
}
/* A DL-SAP message is received from datalink instance and forwarded to L3 */
static int send_dlsap(struct osmo_dlsap_prim *dp, struct lapd_msg_ctx *lctx)
{
struct lapd_datalink *dl = lctx->dl;
struct lapd_sap *sap =
container_of(dl, struct lapd_sap, dl);
struct lapd_instance *li;
uint8_t tei, sapi;
char *op = (dp->oph.operation == PRIM_OP_INDICATION) ? "indication"
: "confirm";
li = sap->tei->li;
tei = lctx->tei;
sapi = lctx->sapi;
switch (dp->oph.primitive) {
case PRIM_DL_EST:
LOGDL(dl, LOGL_NOTICE, "LAPD DL-ESTABLISH %s TEI=%d "
"SAPI=%d\n", op, lctx->tei, lctx->sapi);
break;
case PRIM_DL_REL:
LOGDL(dl, LOGL_NOTICE, "LAPD DL-RELEASE %s TEI=%d "
"SAPI=%d\n", op, lctx->tei, lctx->sapi);
lapd_sap_free(sap);
/* note: sap and dl is now gone, don't use it anymore */
break;
default:
;
}
li->receive_cb(dp, tei, sapi, li->receive_cbdata);
return 0;
}
/* Allocate a new LAPD instance */
struct lapd_instance *lapd_instance_alloc2(int network_side,
void (*tx_cb)(struct msgb *msg, void *cbdata), void *tx_cbdata,
void (*rx_cb)(struct osmo_dlsap_prim *odp, uint8_t tei, uint8_t sapi,
void *rx_cbdata), void *rx_cbdata,
const struct lapd_profile *profile, const char *name)
{
struct lapd_instance *li;
li = talloc_zero(NULL, struct lapd_instance);
if (!li)
return NULL;
li->network_side = network_side;
li->transmit_cb = tx_cb;
li->transmit_cbdata = tx_cbdata;
li->receive_cb = rx_cb;
li->receive_cbdata = rx_cbdata;
li->pcap_fd = -1;
li->name = talloc_strdup(li, name);
memcpy(&li->profile, profile, sizeof(li->profile));
INIT_LLIST_HEAD(&li->tei_list);
return li;
}
struct lapd_instance *lapd_instance_alloc(int network_side,
void (*tx_cb)(struct msgb *msg, void *cbdata), void *tx_cbdata,
void (*rx_cb)(struct osmo_dlsap_prim *odp, uint8_t tei, uint8_t sapi,
void *rx_cbdata), void *rx_cbdata,
const struct lapd_profile *profile)
{
return lapd_instance_alloc2(network_side, tx_cbdata, tx_cb, rx_cb, rx_cbdata, profile, NULL);
}
/* Change lapd-profile on the fly (use with caution!) */
void lapd_instance_set_profile(struct lapd_instance *li,
const struct lapd_profile *profile)
{
memcpy(&li->profile, profile, sizeof(li->profile));
}
void lapd_instance_free(struct lapd_instance *li)
{
struct lapd_tei *teip, *teip2;
/* Free all TEI instances */
llist_for_each_entry_safe(teip, teip2, &li->tei_list, list) {
lapd_tei_free(teip);
}
talloc_free(li);
}
/***********************************************************************
* LAPV5-EF (Encapsulation Function)
***********************************************************************/
/* The LAPV5-EF adds another layer around of the LAPD-style LAPV5-DL. It consists of
* a two-byte header in front of the normal data link header. Examples see Figure E.1/G.964 */
/* main entry point for receiving signaling frames from the AN. The assumption is that
* the flag octets are removed and the msg->data points to the first octet of the EFaddr,
* while the last octet (before msg->tail) points to the last FCS octet. */
int lapv5ef_rx(struct v5x_link *link, struct msgb *msg)
{
uint16_t efaddr, efaddr_enc;
bool is_isdn;
msg->l1h = msg->data;
if (msgb_length(msg) < 2) {
msgb_free(msg);
return -EINVAL;
}
msg->l2h = msg->l1h + 2;
efaddr_enc = msg->l1h[0] << 8 | msg->l1h[1];
efaddr = v51_l3_addr_dec(efaddr_enc, &is_isdn);
if (!is_isdn) {
/* EFaddr are structured like isdn-type L3 Address */
msgb_free(msg);
return -EINVAL;
}
switch (efaddr) {
case V51_DLADDR_PSTN:
/* hand-over to LAPD-DL instance for PSTN */
break;
case V51_DLADDR_CTRL:
/* hand-over to LAPD-DL instance for CTRL */
break;
case V52_DLADDR_BCC:
case V52_DLADDR_PROTECTION:
case V52_DLADDR_LCP:
/* TOOD: implement V5.2 */
msgb_free(msg);
break;
default:
if (efaddr >= 8176) {
/* reserved as per Section 9.2.2.2 of G.964 */
msgb_free(msg);
return -EINVAL;
}
/* relay function for LAPD of user ports */
}
}

35
v51_l1_fsm.c Normal file
View File

@ -0,0 +1,35 @@
/* ITU-T G.964 Section 14.3.3 V5.1-intterface Layer 1 FSM - AN and LE */
/* (C) 2021 by Harald Welte <laforge@gnumonks.org>
*
* 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 "v5x_internal.h"
enum v51_l1_fsm_state {
V51_L1FSM_S_LE1_NORMAL,
V51_L1FSM_S_LE2_LOCALLY_DET_FAIL,
V51_L1FSM_S_LE3_REMOTELY_DET_FAIL,
V51_L1FSM_S_LE4_INTERNAL_FAIL,
};
/* TODO */

365
v51_le_ctrl.c Normal file
View File

@ -0,0 +1,365 @@
/* (C) 2021 by Harald Welte <laforge@gnumonks.org>
*
* 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 <unitstd.h>
#include <stdint.h>
#include <errno.h>
#include <osmocom/core/util.h>
#include <osmocom/core/msgb.h>
#include <osmocom/gsm/tlv.h>
#include "v5x_internal.h"
/* PSTN Protocol FSM */
enum v51_le_ctrl_port_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 */
};
/***********************************************************************
* user port CTRL protocol FSM / G.964 Section 14
***********************************************************************/
enum v51_ctrl_port_fsm_state {
V51_CTRL_PORT_ST_OUT_OF_SERVICE,
V51_CTRL_PORT_ST_IN_SERVICE,
V51_CTRL_PORT_AWAIT_PORT_ACK,
};
enum v51_ctrl_port_fsm_event {
V51_CTRL_PE_MDU_START_TRAFFIC,
V51_CTRL_PE_MDU_STOP_TRAFFIC,
V51_CTRL_PE_MDU_CTRL,
V51_CTRL_PE_RX_PORT_CONTROL,
V51_CTRL_PE_RX_PORT_CONTROL_ACK,
};
static const struct value_string v51_ctrl_port_fsm_event_names[] = {
{ V51_CTRL_PE_MDU_START_TRAFFIC, "MDU-start_traffic" },
{ V51_CTRL_PE_MDU_STOP_TRAFFIC, "MDU-stop_traffic" },
{ V51_CTRL_PE_MDU_CTRL, "MDU-CTRL" },
{ V51_CTRL_PE_RX_PORT_CONTROL, "PORT_CONTROL" },
{ V51_CTRL_PE_RX_PORT_CONTROL_ACK, "PORT_CONTROL_ACK" },
{ 0, NULL }
};
static int v51_ctrl_port_fsm_timer_cb(struct osmo_fsm_inst *fi)
{
if () {
/* first expiry: repeat PORT_CONTROL; re-start T01 */
} else {
/* second expiry: send MDU-error_ind; go to IN_SERVICE */
osmo_fsm_inst_state_chg(fi, V51_CTRL_PORT_ST_IN_SERVICE);
}
}
static const struct osmo_fsm_state v51_ctrl_port_fsm_states[] = {
[V51_CTRL_PORT_ST_OUT_OF_SERVICE] = {
.name = "OUT_OF_SERVICE",
.in_event_mask = S(V51_CTRL_PE_MDU_START_TRAFFIC) |
S(V51_CTRL_PE_MDU_STOP_TRAFFIC) |
S(V51_CTRL_PE_MDU_STOP_TRAFFIC) |
S(V51_CTRL_PE_RX_PORT_CONTROL) |
S(V51_CTRL_PE_RX_PORT_CONTROL_ACK),
.out_state_mask = S(V51_CTRL_PORT_ST_IN_SERVICE),
},
[V51_CTRL_PORT_ST_IN_SERVICE] = {
.name = "IN_SERVICE",
.in_event_mask = S(V51_CTRL_PE_MDU_START_TRAFFIC) |
S(V51_CTRL_PE_MDU_STOP_TRAFFIC) |
S(V51_CTRL_PE_MDU_STOP_TRAFFIC) |
S(V51_CTRL_PE_RX_PORT_CONTROL),
.out_state_mask = S(V51_CTRL_PORT_ST_OUT_OF_SERVICE) |
S(V51_CTRL_PORT_AWAIT_PORT_ACK),
},
[V51_CTRL_PORT_AWAIT_PORT_ACK] = {
.name = "AWAIT_PORT_ACK",
.in_event_mask = S(V51_CTRL_PE_MDU_START_TRAFFIC) |
S(V51_CTRL_PE_MDU_STOP_TRAFFIC) |
S(V51_CTRL_PE_MDU_STOP_TRAFFIC) |
S(V51_CTRL_PE_RX_PORT_CONTROL) |
S(V51_CTRL_PE_RX_PORT_CONTROL_ACK),
.out_state_mask = S(V51_CTRL_PORT_ST_OUT_OF_SERVICE) |
S(V51_CTRL_PORT_ST_IN_SERVICE),
},
};
static struct osmo_fsm v51_ctrl_port_fsm = {
.name = "V51_CTRL_PORT",
.states = v51_ctrl_port_fsm_states,
.num_states = ARRAY_SIZE(v51_ctrl_port_fsm_states),
.allstate_event_mask = 0,
.allstate_action = NULL,
.cleanup = NULL,
.timer_cb = v51_ctrl_port_fsm_timer_cb,
.log_subsys = DV5CTRL,
.event_names = v51_ctrl_port_fsm_event_names,
};
/***********************************************************************
* common CTRL protocol FSM / G.964 Section 14
***********************************************************************/
enum v51_ctrl_common_fsm_state {
V51_CTRL_COM_ST_OUT_OF_SERVICE,
V51_CTRL_COM_ST_IN_SERVICE,
V51_CTRL_COM_AWAIT_COMMON_ACK,
};
enum v51_ctrl_common_fsm_event {
V51_CTRL_CE_MDU_START_TRAFFIC,
V51_CTRL_CE_MDU_STOP_TRAFFIC,
V51_CTRL_CE_MDU_CTRL,
V51_CTRL_CE_RX_COMMON_CONTROL,
V51_CTRL_CE_RX_COMMON_CONTROL_ACK,
};
static const struct value_string v51_ctrl_common_fsm_event_names[] = {
{ V51_CTRL_CE_MDU_START_TRAFFIC, "MDU-start_traffic" },
{ V51_CTRL_CE_MDU_STOP_TRAFFIC, "MDU-stop_traffic" },
{ V51_CTRL_CE_MDU_CTRL, "MDU-CTRL" },
{ V51_CTRL_CE_RX_COMMON_CONTROL, "COMMON_CONTROL" },
{ V51_CTRL_CE_RX_COMMON_CONTROL_ACK, "COMMON_CONTROL_ACK" },
{ 0, NULL }
};
static int v51_ctrl_common_fsm_timer_cb(struct osmo_fsm_inst *fi)
{
if () {
/* first expiry: repeat COMMON_CONTROL; re-start T02 */
} else {
/* second expiry: send MDU-error_ind; go to IN_SERVICE */
osmo_fsm_inst_state_chg(fi, V51_CTRL_COM_ST_IN_SERVICE);
}
}
static const struct osmo_fsm_state v51_ctrl_common_fsm_states[] = {
[V51_CTRL_COM_ST_OUT_OF_SERVICE] = {
.name = "OUT_OF_SERVICE",
.in_event_mask = S(V51_CTRL_CE_MDU_START_TRAFFIC) |
S(V51_CTRL_CE_MDU_STOP_TRAFFIC) |
S(V51_CTRL_CE_MDU_CTRL) |
S(V51_CTRL_CE_RX_COMMON_CONTROL) |
S(V51_CTRL_CE_RX_COMMON_CONTROL_ACK),
.out_state_mask = S(V51_CTRL_COM_ST_IN_SERVICE),
},
[V51_CTRL_COM_ST_IN_SERVICE] = {
.name = "IN_SERVICE",
.in_event_mask = S(V51_CTRL_CE_MDU_START_TRAFFIC) |
S(V51_CTRL_CE_MDU_STOP_TRAFFIC) |
S(V51_CTRL_CE_MDU_CTRL) |
S(V51_CTRL_CE_RX_COMMON_CONTROL),
.out_state_mask = S(V51_CTRL_COM_ST_OUT_OF_SERVICE) |
S(V51_CTRL_COM_AWAIT_COMMON_ACK),
},
[V51_CTRL_COM_AWAIT_COMMON_ACK] = {
.name = "AWAIT_COMMON_ACK",
.in_event_mask = S(V51_CTRL_CE_MDU_START_TRAFFIC) |
S(V51_CTRL_CE_MDU_STOP_TRAFFIC) |
S(V51_CTRL_CE_MDU_CTRL) |
S(V51_CTRL_CE_RX_COMMON_CONTROL) |
S(V51_CTRL_CE_RX_COMMON_CONTROL_ACK),
.out_state_mask = S(V51_CTRL_COM_ST_OUT_OF_SERVICE) |
S(V51_CTRL_COM_ST_IN_SERVICE),
},
};
static struct osmo_fsm v51_ctrl_common_fsm = {
.name = "V51_CTRL_COMMON",
.states = v51_ctrl_common_fsm_states,
.num_states = ARRAY_SIZE(v51_ctrl_common_fsm_states),
.allstate_event_mask = 0,
.allstate_action = NULL,
.cleanup = NULL,
.timer_cb = v51_ctrl_common_fsm_timer_cb,
.log_subsys = DV5CTRL,
.event_names = v51_ctrl_common_fsm_event_names,
};
/***********************************************************************
* V5 Message encoding / sending
***********************************************************************/
/* G.964 Section 14.4.1.1 / Table 48 */
struct mgsb *v51_enc_ctrl_port(struct v5x_user_port *v5up, enum v51_ctrl_func_el cfe)
{
uint8_t cfe_ie = cfe;
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 = v51_l3_addr_enc(v5up->nr, true);
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)
{
uint8_t cfe_ie = cfe;
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 = v51_l3_addr_enc(v5up->nr, true);
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_instance *v5i, enum v51_ctrl_func_id cfi,
uint8_t *rej_cause, uint8_t *variant, uint32_t *interface_id)
{
uint8_t cfi_ie = cfi;
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 = 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 */
msgb_tlv_put(msg, V51_CTRL_IEI_VARIANT, 1, variant);
}
if (rej_cause) {
/* Conditional: Rejection Cause */
msgb_put_u8(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_instance *v5i, enum v51_ctrl_func_id cfi)
{
uint8_t cfi_ie = cfi;
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 = 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, struct msgb *msg, const struct tlv_parsed *tp)
{
uint16_t l3_addr;
enum v51_ctrl_func_el cfe = *TLVP_VAL(tp, V51_CTRL_IEI_CTRL_F_ELEMENT);
switch (l3h->msg_type) {
case V51_CTRL_MSGT_PORT_CTRL:
/* FIXME: send event to FSM */
/* send ACK to AN */
return v51_tx(v5up->inst, v51_enc_ctrl_port_ack(v5up, cfe));
case V51_CTRL_MSGT_PORT_CTRL_ACK:
default:
return -EINVAL;
}
}
static int v51_rcv_ctrl_common(struct v5x_instance *v5i, struct msgb *msg, const struct tlv_parsed *tp)
{
uint16_t l3_addr;
enum v51_ctrl_func_id cfi = *TLVP_VAL(tp, V51_CTRL_IEI_CTRL_F_ID);
switch (l3h->msg_type) {
case V51_CTRL_MSGT_COMMON_CTRL:
/* FIXME: send event to FSM */
/* send ACK to AN */
return v51_tx(v5up->inst, v51_enc_ctrl_common_ack(v5i, cfi));
case V51_CTRL_MSGT_COMMON_CTRL_ACK:
default:
return -EINVAL;
}
}
static int v51_rcv_ctrl(struct v5x_instance *v5i, struct msgb *msg, const struct tlv_parsed *tp)
{
struct v51_l3_hdr *l3h = msgb_l3(msg);
uint16_t l3_addr;
bool is_isdn;
int rc;
l3_addr = v51_l3_addr_dec(l3h->l3_addr, &is_isdn);
if (!is_isdn)
return -EINVAL;
switch (l3h->msg_type) {
case V51_CTRL_MSGT_PORT_CTRL:
case V51_CTRL_MSGT_PORT_CTRL_ACK:
v5up = v5x_user_port_find(v5i, l4_addr);
if (!v5up)
return -ENODEV;
return v5x_rcv_ctrl_port(v5up, msg, tp);
case V51_CTRL_MSGT_COMMON_CTRL:
case V51_CTRL_MSGT_COMMON_CTRL_ACK:
if (l3_addr != V51_DLADDR_CTRL)
return -EINVAL;
/* TODO */
return v5x_rcv_ctrl_common(v5i, msg, tp);
}
return rc;
}

612
v52_lcp_fsm.c Normal file
View File

@ -0,0 +1,612 @@
/* ITu-T G.965 Section 16.2 V5.2-interface Link control FSM - LE side */
#include "v5x_internal.h"
/* 16.2.4.2.2 */
enum v52_lcp_fsm_state {
V52_LCPFSM_S_LE01_NOP_LINK_FAILURE,
V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED,
V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED,
V52_LCPFSM_S_LE11_NOP_LOCAL_LINK_UNBLOCK,
V52_LCPFSM_S_LE12_NOP_REMOTE_LINK_UNBLOCK,
V52_LCPFSM_S_LE20_OP_OPERATIONAL,
V52_LCPFSM_S_LE21_OP_REMOTE_LINK_ID,
V52_LCPFSM_S_LE22_OP_LOCAL_LINK_ID,
};
enum v52_lcp_event {
V52_LCPFSM_E_MPH_AI, /* Activate Indication (L1 link operational) */
V52_LCPFSM_E_MPH_DI, /* Deactivate Indication (L1 link not operational) */
V52_LCPFSM_E_MDU_IDReq, /* Identification Request */
V52_LCPFSM_E_FE_IDAck,
V52_LCPFSM_E_MPH_IDI, /* Identification Indication */
V52_LCPFSM_E_MPH_EIg, /* Identification Failure */
V52_LCPFSM_E_FE_IDReq,
V52_LCPFSM_E_MDU_IDAck, /* Send Link ID ACK */
V52_LCPFSM_E_FE_IDRel,
V52_LCPFSM_E_MDU_IDRej, /* Link ID Reject */
V52_LCPFSM_E_FE_IDRej,
V52_LCPFSM_E_MDU_LUBR, /* Link Unblock Request */
V52_LCPFSM_E_MDU_LBI, /* Link Block Indication */
V52_LCPFSM_E_FE302, /* AN Initiated unblocking */
V52_LCPFSM_E_FE304, /* AN initiated link block */
V52_LCPFSM_E_FE305, /* deferred link block request */
V52_LCPFSM_E_FE306, /* non-deferred link block request */
};
static struct msgb *v51_enc_link_control(uint8_t link_id, enum v52_link_ctrl_func lcf)
{
struct v51_l3_hdr *l3h;
uint8_t lcf_enc = lcf;
struct msgb *msg = msgb_alloc_v5x();
if (!msg)
return NULL;
l3h = (struct v51_l3_hdr *) msgb_put(msg, sizeof(*l3h));
l3h->pdisc = V51_LCP_PDISC;
l3h->l3_addr = link_id
l3h->msg_type = V52_CTRL_MSGT_LCP_LINK_CTRL;
msgb_tlv_put(msg, V52_CTRL_IEI_LCP_LINK_CTRL_FUNCTION, 1, &lcf_enc);
return msg;
}
static struct msgb *v51_enc_link_control_ack(uint8_t link_id, enum v52_link_ctrl_func lcf)
{
struct v51_l3_hdr *l3h;
uint8_t lcf_enc = lcf;
struct msgb *msg = msgb_alloc_v5x();
if (!msg)
return NULL;
l3h = (struct v51_l3_hdr *) msgb_put(msg, sizeof(*l3h));
l3h->pdisc = V51_LCP_PDISC;
l3h->l3_addr = link_id;
l3h->msg_type = V52_CTRL_MSGT_LCP_LINK_CTRL_ACK;
msgb_tlv_put(msg, V52_CTRL_IEI_LCP_LINK_CTRL_FUNCTION, 1, &lcf_enc);
return msg;
}
static void lcp_fsm_le01_link_failure(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
switch (event) {
case V52_LCPFSM_E_MPH_AI:
/* TODO: Send MDU-LAI */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
break;
case V52_LCPFSM_E_MPH_DI:
/* ignore */
break;
case V52_LCPFSM_E_MDU_IDReq:
/* TODO: Send MDU-DI */
break;
case V52_LCPFSM_E_FE_IDReq:
/* TODO: Send FE303 */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED, 0, 0);
break;
case V52_LCPFSM_E_FE_IDRel:
case V52_LCPFSM_E_FE_IDRej:
/* ignore */
break;
case V52_LCPFSM_E_MDU_LUBR:
/* TODO: Send MDU-DI */
/* TODO: Send FE303 */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED, 0, 0);
break;
case V52_LCPFSM_E_MDU_LBI:
case V52_LCPFSM_E_FE302:
case V52_LCPFSM_E_FE305:
case V52_LCPFSM_E_FE306:
/* TODO: Send FE303 */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED, 0, 0);
break;
case V52_LCPFSM_E_FE304:
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED, 0, 0);
break;
default:
OSMO_ASSERT(0);
}
}
static void lcp_fsm_le02_link_failure_blocked(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
switch (event) {
case V52_LCPFSM_E_MPH_AI:
/* TODO: Send MDU-LAI */
/* TODO: Send MDU-LBl */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
break;
case V52_LCPFSM_E_MPH_DI:
/* ignore */
break;
case V52_LCPFSM_E_MDU_IDReq:
/* TODO: Send MDU-DI */
break;
case V52_LCPFSM_E_FE_IDReq:
/* TODO: Send FE303 */
break;
case V52_LCPFSM_E_MDU_LUBR:
/* TODO: Send MDU-DI */
/* TODO: Send FE303 */
break;
case V52_LCPFSM_E_MDU_LBI:
case V52_LCPFSM_E_FE302:
case V52_LCPFSM_E_FE305:
case V52_LCPFSM_E_FE306:
/* TODO: Send FE303 */
break;
case V52_LCPFSM_E_FE304:
/* ignore */
break;
default:
OSMO_ASSERT(0);
}
}
static void lcp_fsm_le10_link_blocked(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
switch (event) {
case V52_LCPFSM_E_MPH_AI:
/* TODO: Send MDU-LAI */
break;
case V52_LCPFSM_E_MPH_DI:
/* TODO: Send MDU-DI */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED, 0, 0);
break;
case V52_LCPFSM_E_MDU_IDReq:
/* TODO: Send MDU-LBl */
break;
case V52_LCPFSM_E_FE_IDReq:
/* TODO: Send FE303 */
break;
case V52_LCPFSM_E_FE_IDRel:
case V52_LCPFSM_E_FE_IDRej:
/* ignore */
break;
case V52_LCPFSM_E_MDU_LUBR:
/* TODO: Send FE303 */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE11_NOP_LOCAL_LINK_UNBLOCK, 0, 0);
break;
case V52_LCPFSM_E_FE302:
/* TODO: Send MDL-LUBR */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE12_NOP_REMOTE_LINK_UNBLOCK, 0, 0);
break;
case V52_LCPFSM_E_MDU_LBI:
case V52_LCPFSM_E_FE305:
case V52_LCPFSM_E_FE306:
/* TODO: Send FE303 */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED, 0, 0);
break;
case V52_LCPFSM_E_FE304:
break;
default:
OSMO_ASSERT(0);
}
}
static void lcp_fsm_le11_local_link_unblock(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
switch (event) {
case V52_LCPFSM_E_MPH_AI:
/* ignore */
break;
case V52_LCPFSM_E_MPH_DI:
/* TODO: Send MDU-DI */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED, 0, 0);
break;
case V52_LCPFSM_E_MDU_IDReq:
/* TODO: Send MDU-LBl */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
break;
case V52_LCPFSM_E_FE_IDReq:
/* TODO: Send FE-IDRej */
break;
case V52_LCPFSM_E_FE_IDRel:
case V52_LCPFSM_E_FE_IDRej:
/* ignore */
break;
case V52_LCPFSM_E_MDU_LUBR:
/* TODO: Send FE301 */
break;
case V52_LCPFSM_E_MDU_LBI:
/* TODO: Send FE303 */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
break;
case V52_LCPFSM_E_FE302:
/* TODO: Send MDU-LUBl */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
break;
case V52_LCPFSM_E_FE304:
/* TODO: Send MDU-LBl */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
break;
case V52_LCPFSM_E_FE305:
case V52_LCPFSM_E_FE306:
/* TODO: Send FE303 */
/* TODO: Send MDU-LBl */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
break;
default:
OSMO_ASSERT(0);
}
}
static void lcp_fsm_le12_remote_link_unblock(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
switch (event) {
case V52_LCPFSM_E_MPH_AI:
/* ignore */
break;
case V52_LCPFSM_E_MPH_DI:
/* TODO: Send MDU-Dl */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED, 0, 0);
break;
case V52_LCPFSM_E_MDU_IDReq:
/* TODO: Send MDU-LUBR */
/* TODO: Send MDU-IDRej */
break;
case V52_LCPFSM_E_FE_IDReq:
/* TODO: Send FE-IDRej */
break;
case V52_LCPFSM_E_MDU_LUBR:
/* TODO: Send FE301 */
/* TODO: Send MDU-LUBl */
break;
case V52_LCPFSM_E_MDU_LBI:
/* TODO: Send FE303 */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
break;
case V52_LCPFSM_E_FE302:
/* TODO: Send MDU-LUBR */
break;
case V52_LCPFSM_E_FE304:
/* TODO: Send MDU-LBl */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
break;
case V52_LCPFSM_E_FE305:
case V52_LCPFSM_E_FE306:
/* TODO: Send FE303 */
/* TODO: Send MDU-LBl */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
break;
default:
OSMO_ASSERT(0);
}
}
static void lcp_fsm_le20_operational(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
switch (event) {
case V52_LCPFSM_E_MPH_AI:
/* ignore */
break;
case V52_LCPFSM_E_MPH_DI:
/* TODO: Send MDU-DI */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE01_NOP_LINK_FAILURE, 0, 0);
break;
case V52_LCPFSM_E_MDU_IDReq:
/* TODO: Send FE-IDReq */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE22_OP_LOCAL_LINK_ID, 0, 0);
break;
case V52_LCPFSM_E_FE_IDReq:
/* TODO: Send MDU-IDReq */
break;
case V52_LCPFSM_E_MDU_IDAck:
/* TODO: Send MPH-ID */
/* TODO: Send FE-IDAck */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE21_OP_REMOTE_LINK_ID, 0, 0);
break;
case V52_LCPFSM_E_MDU_IDRej:
/* TODO: Send FE-IDRej */
break;
case V52_LCPFSM_E_MDU_LUBR:
/* TODO: Send FE301 */
break;
case V52_LCPFSM_E_MDU_LBI:
/* TODO: Send FE303 */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
break;
case V52_LCPFSM_E_FE302:
/* TODO: Send MDU-LUBI */
break;
case V52_LCPFSM_E_FE304:
/* TODO: Send MDU-LBI */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
break;
case V52_LCPFSM_E_FE305:
/* TODO: Send MDU-LBR */
break;
case V52_LCPFSM_E_FE306:
/* TODO: Send MDU-LBRN */
break;
default:
OSMO_ASSERT(0);
}
}
static void lcp_fsm_le21_op_remote_link_id(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
switch (event) {
case V52_LCPFSM_E_MPH_AI:
/* ignore */
break;
case V52_LCPFSM_E_MPH_DI:
/* TODO: Send MDU-DI */
/* TODO: Send MPH-NOR */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE01_NOP_LINK_FAILURE, 0, 0);
break;
case V52_LCPFSM_E_MDU_IDReq:
/* TODO: Send MDU-IDRej */
break;
case V52_LCPFSM_E_MDU_IDAck:
/* ignore */
break;
case V52_LCPFSM_E_FE_IDRel:
/* TODO: Send MDU-IDRel */
/* TODO: Sen MPH-NOR */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
break;
case V52_LCPFSM_E_MDU_IDRej:
/* TODO: Send FE-IDRej */
/* TODO: Sen MPH-NOR */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
break;
case V52_LCPFSM_E_MDU_LUBR:
/* TODO: Send FE301 */
/* TODO: Sen MPH-NOR */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
break;
case V52_LCPFSM_E_MDU_LBI:
/* TODO: Send FE303 */
/* TODO: Sen MPH-NOR */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
break;
case V52_LCPFSM_E_FE302:
/* TODO: Send MDU-IDRel */
/* TODO: Send MDU-LUBI */
/* TODO: Sen MPH-NOR */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
break;
case V52_LCPFSM_E_FE304:
/* TODO: Send MDU-LBI */
/* TODO: Sen MPH-NOR */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
break;
case V52_LCPFSM_E_FE305:
/* TODO: Send MDU-LBR */
break;
case V52_LCPFSM_E_FE306:
/* TODO: Send MDU-LBRN */
break;
default:
OSMO_ASSERT(0);
}
}
static void lcp_fsm_le22_op_local_link_id(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
switch (event) {
case V52_LCPFSM_E_MPH_AI:
/* ignore */
break;
case V52_LCPFSM_E_MPH_DI:
/* TODO: Send MDU-DI */
/* TODO: Send FE-IdRel */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE01_NOP_LINK_FAILURE, 0, 0);
break;
case V52_LCPFSM_E_MDU_IDReq:
/* ignore */
break;
case V52_LCPFSM_E_MDU_IDAck:
/* TODO: Send MPH-IDR */
break;
case V52_LCPFSM_E_MPH_IDI:
/* TODO: Send MDU-AI */
/* TODO: Send FE-IDRel */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
break;
case V52_LCPFSM_E_MPH_EIg:
/* TODO: Send FE-IDRel */
/* TODO: Send MDU-ELg */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
break;
case V52_LCPFSM_E_FE_IDRej:
/* TODO: Send MDU-IDRej */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
break;
case V52_LCPFSM_E_MDU_LUBR:
/* TODO: Send FE301 */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
break;
case V52_LCPFSM_E_MDU_LBI:
/* TODO: Send FE303 */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
break;
case V52_LCPFSM_E_FE302:
/* TODO: Send MDU-IDRej */
/* TODO: Send MDU-LUBI */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE20_OP_OPERATIONAL, 0, 0);
break;
case V52_LCPFSM_E_FE304:
/* TODO: Send MDU-LBI */
osmo_fsm_inst_state_chg(fi, V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED, 0, 0);
break;
case V52_LCPFSM_E_FE305:
/* TODO: Send MDU-LBR */
break;
case V52_LCPFSM_E_FE306:
/* TODO: Send MDU-LBRN */
break;
default:
OSMO_ASSERT(0);
}
}
/* Table 17G.965 */
static const struct osmo_fsm_state v52_lcp_le_fsm_states[] = {
[V52_LCPFSM_S_LE01_NOP_LINK_FAILURE] = {
.name = "LE0.1_LINK_FAILURE",
.in_event_mask = S(V52_LCPFSM_E_MPH_AI) |
S(V52_LCPFSM_E_MPH_DI) |
S(V52_LCPFSM_E_MDU_IDReq) |
S(V52_LCPFSM_E_FE_IDReq) |
S(V52_LCPFSM_E_FE_IDRel) |
S(V52_LCPFSM_E_FE_IDRej) |
S(V52_LCPFSM_E_MDU_LUBR) |
S(V52_LCPFSM_E_MDU_LBI) |
S(V52_LCPFSM_E_FE302) |
S(V52_LCPFSM_E_FE304) |
S(V52_LCPFSM_E_FE305) |
S(V52_LCPFSM_E_FE306),
.out_state_mask = S(V52_LCPFSM_S_LE20_OP_OPERATIONAL) |
S(V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED),
.action = lcp_fsm_le01_link_failure,
},
[V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED] = {
.name = "LE0.2_LINK_FAILURE_AND_BLOCKED",
.in_event_mask = S(V52_LCPFSM_E_MPH_AI) |
S(V52_LCPFSM_E_MPH_DI) |
S(V52_LCPFSM_E_MDU_IDReq) |
S(V52_LCPFSM_E_FE_IDReq) |
S(V52_LCPFSM_E_MDU_LUBR) |
S(V52_LCPFSM_E_MDU_LBI) |
S(V52_LCPFSM_E_FE302) |
S(V52_LCPFSM_E_FE304) |
S(V52_LCPFSM_E_FE305) |
S(V52_LCPFSM_E_FE306),
.out_state_mask = S(V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED),
.action = lcp_fsm_le02_link_failure_blocked,
},
[V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED] = {
.name = "LE1.0_NOP_LINK_BLOCKED",
.in_event_mask = S(V52_LCPFSM_E_MPH_AI) |
S(V52_LCPFSM_E_MPH_DI) |
S(V52_LCPFSM_E_MDU_IDReq) |
S(V52_LCPFSM_E_FE_IDReq) |
S(V52_LCPFSM_E_FE_IDRel) |
S(V52_LCPFSM_E_FE_IDRej) |
S(V52_LCPFSM_E_MDU_LUBR) |
S(V52_LCPFSM_E_MDU_LBI) |
S(V52_LCPFSM_E_FE302) |
S(V52_LCPFSM_E_FE304) |
S(V52_LCPFSM_E_FE305) |
S(V52_LCPFSM_E_FE306),
.out_state_mask = S(V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED) |
S(V52_LCPFSM_S_LE11_NOP_LOCAL_LINK_UNBLOCK) |
S(V52_LCPFSM_S_LE21_OP_REMOTE_LINK_ID),
.action = lcp_fsm_le10_link_blocked,
},
[V52_LCPFSM_S_LE11_NOP_LOCAL_LINK_UNBLOCK] = {
.name = "LE1.1_NOP_LOCAL_LINK_UNBLOCK",
.in_event_mask = S(V52_LCPFSM_E_MPH_AI) |
S(V52_LCPFSM_E_MPH_DI) |
S(V52_LCPFSM_E_MDU_IDReq) |
S(V52_LCPFSM_E_FE_IDReq) |
S(V52_LCPFSM_E_FE_IDRel) |
S(V52_LCPFSM_E_FE_IDRej) |
S(V52_LCPFSM_E_MDU_LUBR) |
S(V52_LCPFSM_E_MDU_LBI) |
S(V52_LCPFSM_E_FE302) |
S(V52_LCPFSM_E_FE304) |
S(V52_LCPFSM_E_FE305) |
S(V52_LCPFSM_E_FE306),
.out_state_mask = S(V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED) |
S(V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED) |
S(V52_LCPFSM_S_LE20_OP_OPERATIONAL),
.action = lcp_fsm_le11_local_link_unblock,
},
[V52_LCPFSM_S_LE12_NOP_REMOTE_LINK_UNBLOCK] = {
.name = "LE1.2_NOP_REMOTE_LINK_UNBLOCK",
.in_event_mask = S(V52_LCPFSM_E_MPH_AI) |
S(V52_LCPFSM_E_MPH_DI) |
S(V52_LCPFSM_E_MDU_IDReq) |
S(V52_LCPFSM_E_FE_IDReq) |
S(V52_LCPFSM_E_MDU_LUBR) |
S(V52_LCPFSM_E_MDU_LBI) |
S(V52_LCPFSM_E_FE302) |
S(V52_LCPFSM_E_FE304) |
S(V52_LCPFSM_E_FE305) |
S(V52_LCPFSM_E_FE306),
.out_state_mask = S(V52_LCPFSM_S_LE02_NOP_LINK_FAILURE_AND_BLOCKED) |
S(V52_LCPFSM_S_LE20_OP_OPERATIONAL) |
S(V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED),
.action = lcp_fsm_le12_remote_link_unblock,
},
[V52_LCPFSM_S_LE20_OP_OPERATIONAL] = {
.name = "LE2.0_OP_OPERATIONAL",
.in_event_mask = S(V52_LCPFSM_E_MPH_AI) |
S(V52_LCPFSM_E_MPH_DI) |
S(V52_LCPFSM_E_MDU_IDReq) |
S(V52_LCPFSM_E_FE_IDReq) |
S(V52_LCPFSM_E_MDU_IDAck) |
S(V52_LCPFSM_E_MDU_IDRej) |
S(V52_LCPFSM_E_MDU_LUBR) |
S(V52_LCPFSM_E_MDU_LBI) |
S(V52_LCPFSM_E_FE302) |
S(V52_LCPFSM_E_FE304) |
S(V52_LCPFSM_E_FE305) |
S(V52_LCPFSM_E_FE306),
.out_state_mask = S(V52_LCPFSM_S_LE01_NOP_LINK_FAILURE) |
S(V52_LCPFSM_S_LE22_OP_LOCAL_LINK_ID) |
S(V52_LCPFSM_S_LE21_OP_REMOTE_LINK_ID) |
S(V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED),
.action = lcp_fsm_le20_operational,
},
[V52_LCPFSM_S_LE21_OP_REMOTE_LINK_ID] = {
.name = "LE2.1_OP_REMOTE_LINK_ID",
.in_event_mask = S(V52_LCPFSM_E_MPH_AI) |
S(V52_LCPFSM_E_MPH_DI) |
S(V52_LCPFSM_E_MDU_IDReq) |
S(V52_LCPFSM_E_FE_IDReq) |
S(V52_LCPFSM_E_MDU_IDAck) |
S(V52_LCPFSM_E_MDU_IDRel) |
S(V52_LCPFSM_E_MDU_IDRej) |
S(V52_LCPFSM_E_MDU_LUBR) |
S(V52_LCPFSM_E_MDU_LBI) |
S(V52_LCPFSM_E_FE302) |
S(V52_LCPFSM_E_FE304) |
S(V52_LCPFSM_E_FE305) |
S(V52_LCPFSM_E_FE306),
.out_state_mask = S(V52_LCPFSM_S_LE01_NOP_LINK_FAILURE) |
S(V52_LCPFSM_S_LE20_OP_OPERATIONAL) |
S(V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED),
.action = lcp_fsm_le21_op_remote_link_id,
},
[V52_LCPFSM_S_LE22_OP_LOCAL_LINK_ID] = {
.name = "LE2.2_OP_LOCAL_LINK_ID",
.in_event_mask = S(V52_LCPFSM_E_MPH_AI) |
S(V52_LCPFSM_E_MPH_DI) |
S(V52_LCPFSM_E_MDU_IDReq) |
S(V52_LCPFSM_E_MDU_IDAck) |
S(V52_LCPFSM_E_MPH_IDI) |
S(V52_LCPFSM_E_MPH_EIg) |
S(V52_LCPFSM_E_FE_IDReq) |
S(V52_LCPFSM_E_MDU_LUBR) |
S(V52_LCPFSM_E_MDU_LBI) |
S(V52_LCPFSM_E_FE302) |
S(V52_LCPFSM_E_FE304) |
S(V52_LCPFSM_E_FE305) |
S(V52_LCPFSM_E_FE306),
.out_state_mask = S(V52_LCPFSM_S_LE01_NOP_LINK_FAILURE) |
S(V52_LCPFSM_S_LE20_OP_OPERATIONAL) |
S(V52_LCPFSM_S_LE10_NOP_LINK_BLOCKED),
.action =lcp_fsm_le22_op_local_link_id,
},
};
struct osmo_fsm v52_lcp_le_fsm = {
.name = "V52_LCP_LE",
.states = v52_lcp_le_fsm_states,
.num_states = ARRAY_SIZE(v52_lcp_le_fsm_states),
.timer_cb = NULL,
.log_subsys = 0,
.event_names = v52_lcp_le_fsm_event_names,
};

448
v52_le_user_port_fsm.c Normal file
View File

@ -0,0 +1,448 @@
/* ITU-T G.964 Section 14.1.3.2.2 ISDN user port FSM - LE */
/***********************************************************************/
/* internal data structures */
/***********************************************************************/
#include <osmocom/core/linuxlist.h>
#include "v5x_internal.h"
/***********************************************************************/
/* state names, event names, primitives, ... */
/***********************************************************************/
/* Table 35/G.964 */
enum v51_mph_prim {
V51_LE_MPH_UB, /* Unblock (req, ind)*/
V51_LE_MPH_B, /* Block (req, ind) */
V51_LE_MPH_A, /* Activate (req, ind) */
V51_LE_MPH_AW, /* Access activation by user (ind) */
V51_LE_MPH_DSA, /* DS Activated (ind) */
V51_LE_MPH_D, /* Deactivate (req, ind) */
V51_LE_MPH_G, /* Grading Information */
V51_LE_MPH_DB, /* Block D-Channel from user port */
V51_LE_MPH_DU, /* Unblock D-Channel from user port */
};
/* 14.1.3.2.2 ISDN user port FSM - LE(ISDN port) */
enum v51_le_isdn_port_state {
V51_LE_UP_I_S_LE10_NOP_BLOCKED, /* LE1.0 */
V51_LE_UP_I_S_LE11_NOP_LOCAL_UNBLOCK, /* LE1.1 */
V51_LE_UP_I_S_LE12_NOP_REMOTE_UNBLOCK, /* LE1.2 */
V51_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED, /* LE2.0 */
V51_LE_UP_I_S_LE21_OP_ACTIVATION_INITIATED, /* LE2.1 */
V51_LE_UP_I_S_LE22_OP_ACCESS_ACTIVATED, /* LE2.2 */
};
/* 14.2.3.2.2 PSTN user port FSM - LE(PSTN port) */
enum v51_le_pstn_port_state {
V51_LE_UP_P_S_LE10_NOP_BLOCKED, /* LE1.0 */
V51_LE_UP_P_S_LE11_NOP_LOCAL_UNBLOCK, /* LE1.1 */
V51_LE_UP_P_S_LE12_NOP_REMOTE_UNBLOCK, /* LE1.2 */
V51_LE_UP_P_S_LE22_OPERATIONAL, /* LE2.0 */
};
enum v51_ctrl_le_port_fsm_event {
/* inbound function elements from AN (Table 34/G.964) */
V51_CUP_LE_FE102_ACTIV_INIT_USER_IND,
V51_CUP_LE_FE103_DS_ACTIVATED_IND,
V51_CUP_LE_FE104_ACCESS_ACTIVATED_IND,
V51_CUP_LE_FE106_ACCESS_DEACTIVATED_IND,
V51_CUP_LE_FE202_UNBLOCK_REQ,
V51_CUP_LE_FE202_UNBLOCK_ACK,
V51_CUP_LE_FE204_BLOCK_CMD,
V51_CUP_LE_FE205_BLOCK_REQ,
V51_CUP_LE_FE206_PREFORMANCE_GRADING_IND,
/* inbound primitives from Mgmt (Table 35/G.964) */
V51_CUP_LE_MPH_UBR_UNBLOCK_REQ,
V51_CUP_LE_MPH_BI_BLOCK_CMD,
V51_CUP_LE_MPH_AR_ACTIVATE_ACCESS_REQ,
V51_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ,
V51_CUP_LE_MPH_DB_BLOCK_DCHAN_REQ,
V51_CUP_LE_MPH_DU_BLOCK_DCHAN_REQ,
};
static void isdn_up_le10_blocked(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
switch (event) {
case V51_CUP_LE_FE102_ACTIV_INIT_USER_IND:
case V51_CUP_LE_FE103_DS_ACTIVATED_IND:
case V51_CUP_LE_FE104_ACCESS_ACTIVATED_IND:
case V51_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ:
case V51_CUP_LE_FE106_ACCESS_DEACTIVATED_IND:
/* ignore */
break;
case V51_CUP_LE_MPH_UBR_UNBLOCK_REQ:
/* Send FE201 */
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE201_UNBLOCK));
osmo_fsm_inst_state_change(fi, V51_LE_UP_I_S_LE11_NOP_LOCAL_UNBLOCK, 0, 0);
break;
case V51_CUP_LE_MPH_BI_BLOCK_CMD:
/* Send FE203 */
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE203_BLOCK));
break;
case V51_CUP_LE_FE202_UNBLOCK_ACK:
/* TODO: Send MPU-UBR */
osmo_fsm_inst_state_change(fi, V51_LE_UP_I_S_LE11_NOP_LOCAL_UNBLOCK, 0, 0);
break;
case V51_CUP_LE_FE204_BLOCK_CMD:
case V51_CUP_LE_FE205_BLOCK_REQ:
/* ignore */
break;
default:
OSMO_ASSERT(0);
}
}
static void isdn_up_le11_local_unblock(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
switch (event) {
case V51_CUP_LE_FE102_ACTIV_INIT_USER_IND:
/* TODO: send MPG-AWI */
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE21_OP_ACTIVATION_INITIATED, 0, 0);
break;
case V51_CUP_LE_FE103_DS_ACTIVATED_IND:
/* ignore */
break;
case V51_CUP_LE_FE104_ACCESS_ACTIVATED_IND:
/* TODO: send MPH-AI*/
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE22_OP_ACCESS_ACTIVATED, 0, 0);
break;
case V51_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ:
case V51_CUP_LE_FE106_ACCESS_DEACTIVATED_IND:
/* ignore */
break;
case V51_CUP_LE_MPH_UBR_UNBLOCK_REQ:
/* send FE201 */
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE201_UNBLOCK));
break;
case V51_CUP_LE_MPH_BI_BLOCK_CMD:
/* send FE203 */
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE203_BLOCK));
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE10_NOP_BLOCKED, 0, 0);
break;
case V51_CUP_LE_FE202_UNBLOCK_ACK:
/* TODO: send MPH-UBI */
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED, 0, 0);
break;
case V51_CUP_LE_FE204_BLOCK_CMD:
/* TODO: send MPH-BI */
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE10_NOP_BLOCKED, 0, 0);
break;
case V51_CUP_LE_FE205_BLOCK_REQ:
/* ignore */
break;
default:
OSMO_ASSERT(0);
}
}
static void isdn_up_le12_remote_unblock(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
switch (event) {
case V51_CUP_LE_FE103_DS_ACTIVATED_IND:
/* ignore */
break;
case V51_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ:
case V51_CUP_LE_FE106_ACCESS_DEACTIVATED_IND:
/* ignore */
break;
case V51_CUP_LE_MPH_UBR_UNBLOCK_REQ:
/* TODO: send MPH-UBI */
/* send FE201 */
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE201_UNBLOCK));
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED, 0, 0);
break;
case V51_CUP_LE_MPH_BI_BLOCK_CMD:
/* send FE203 */
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE203_BLOCK));
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE10_NOP_BLOCKED, 0, 0);
break;
case V51_CUP_LE_FE202_UNBLOCK_ACK:
/* TODO: send MPH-UBR */
break;
case V51_CUP_LE_FE204_BLOCK_CMD:
/* TODO: send MPH-BI */
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE10_NOP_BLOCKED, 0, 0);
break;
case V51_CUP_LE_FE205_BLOCK_REQ:
/* ignore */
break;
default:
OSMO_ASSERT(0);
}
}
/* LE 2.0 Operational deactivated */
static void isdn_up_le20_op_deact(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
switch (event) {
case V51_CUP_LE_MPH_AR_ACTIVATE_ACCESS_REQ:
/* Send FE101 */
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE101_ACTIVATE_ACCESS));
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE21_OP_ACTIVATION_INITIATED, 0, 0);
break;
case V51_CUP_LE_FE102_ACTIV_INIT_USER_IND:
/* TODO: Send MPH-AWI */
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE21_OP_ACTIVATION_INITIATED, 0, 0);
break;
case V51_CUP_LE_FE103_DS_ACTIVATED_IND:
/* ignore */
break;
case V51_CUP_LE_FE104_ACCESS_ACTIVATED_IND:
/* TODO: Send MPH-AI */
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE22_OP_ACCESS_ACTIVATED, 0, 0);
break;
case V51_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ:
/* Send FE105 */
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE105_DEACTIVATE_ACCESS));
break;
case V51_CUP_LE_FE106_ACCESS_DEACTIVATED_IND:
/* TODO: Send MPH-DI */
break;
case V51_CUP_LE_MPH_UBR_UNBLOCK_REQ:
/* Send FE201 */
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE201_UNBLOCK));
break;
case V51_CUP_LE_MPH_BI_BLOCK_CMD:
/* Send FE203 */
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE203_BLOCK));
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE10_NOP_BLOCKED, 0, 0);
break;
case V51_CUP_LE_FE202_UNBLOCK_ACK:
/* TODO: Send MPH-UBI */
break;
case V51_CUP_LE_FE204_BLOCK_CMD:
/* TODO: Send MPH-BI */
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE10_NOP_BLOCKED, 0, 0);
break;
case V51_CUP_LE_FE205_BLOCK_REQ:
/* TODO: Send MPH-BR */
break;
case V51_CUP_LE_FE206_PREFORMANCE_GRADING_IND:
/* TODO: Send MPH-GI */
break;
default:
OSMO_ASSERT(0);
}
}
/* LE 2.1 Access initiated */
static void isdn_up_le21_op_act_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
switch (event) {
case V51_CUP_LE_MPH_AR_ACTIVATE_ACCESS_REQ:
case V51_CUP_LE_FE102_ACTIV_INIT_USER_IND:
/* ignore */
break;
case V51_CUP_LE_FE103_DS_ACTIVATED_IND:
/* TODO: Send MPH-DSAI */
break;
case V51_CUP_LE_FE104_ACCESS_ACTIVATED_IND:
/* TODO: Send MPH-AI */
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE22_OP_ACCESS_ACTIVATED, 0, 0);
break;
case V51_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ:
/* Send FE105 */
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE105_DEACTIVATE_ACCESS));
/* TODO: Send MPH-DI */
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED, 0, 0);
break;
case V51_CUP_LE_FE106_ACCESS_DEACTIVATED_IND:
/* TODO: Send MPH-DI */
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED, 0, 0);
break;
case V51_CUP_LE_MPH_UBR_UNBLOCK_REQ:
/* Send FE201 */
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE201_UNBLOCK));
break;
case V51_CUP_LE_MPH_BI_BLOCK_CMD:
/* Send FE203 */
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE203_BLOCK));
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE10_NOP_BLOCKED, 0, 0);
break;
case V51_CUP_LE_FE202_UNBLOCK_ACK:
/* TODO: Send MPH-UBI */
break;
case V51_CUP_LE_FE204_BLOCK_CMD:
/* TODO: Send MPH-BI */
/* TODO: Send MPH-DI */
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE10_NOP_BLOCKED, 0, 0);
break;
case V51_CUP_LE_FE205_BLOCK_REQ:
/* TODO: Send MPH-BR */
break;
case V51_CUP_LE_FE206_PREFORMANCE_GRADING_IND:
/* TODO: Send MPH-GI */
break;
default:
OSMO_ASSERT(0);
}
}
/* LE 2.2 Access activated */
static void isdn_up_le22_op_acc_act(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
switch (event) {
case V51_CUP_LE_FE104_ACCESS_ACTIVATED_IND:
/* ignore */
break;
case V51_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ:
/* Send FE105 */
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE105_DEACTIVATE_ACCESS));
/* TODO: Send MPH-DI */
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED, 0, 0);
break;
case V51_CUP_LE_FE106_ACCESS_DEACTIVATED_IND:
/* TODO: Send MPH-DI */
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED, 0, 0);
break;
case V51_CUP_LE_MPH_UBR_UNBLOCK_REQ:
/* TODO: Send MPH-AI */
/* Send FE201 */
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE201_UNBLOCK));
break;
case V51_CUP_LE_MPH_BI_BLOCK_CMD:
/* Send FE203 */
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE203_BLOCK));
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE10_NOP_BLOCKED, 0, 0);
break;
case V51_CUP_LE_FE202_UNBLOCK_ACK:
/* TODO: Send MPH-UBI */
break;
case V51_CUP_LE_FE204_BLOCK_CMD:
/* TODO: Send MPH-BI */
/* TODO: Send MPH-DI */
osmo_fsm_inst_state_chg(fi, V51_LE_UP_I_S_LE10_NOP_BLOCKED, 0, 0);
break;
case V51_CUP_LE_FE205_BLOCK_REQ:
/* TODO: Send MPH-BR */
break;
case V51_CUP_LE_FE206_PREFORMANCE_GRADING_IND:
/* TODO: Send MPH-GI */
break;
case V51_CUP_LE_MPH_DB_BLOCK_DCHAN_REQ:
/* Send FE207 */
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE207_D_CHANNEL_BLOCK));
break;
case V51_CUP_LE_MPH_DU_BLOCK_DCHAN_REQ:
/* Send FE208 */
v51_ctrl_tx(v5up->inst, v51_enc_ctrl_port(v5up, V51_CTRL_FE208_D_CHANNEL_UNBLOCK));
break;
default:
OSMO_ASSERT(0);
}
}
/* Table 38/G.964 LE (ISDN port) FSM for ISDN basic access user ports */
static const struct osmo_fsm_state v51_ctrl_le_port_fsm_states[] = {
[V51_LE_UP_I_S_LE10_NOP_BLOCKED] = {
.name = "LE1.0_BLOCKED",
.in_event_mask = S(V51_CUP_LE_FE102_ACTIV_INIT_USER_IND) |
S(V51_CUP_LE_FE103_DS_ACTIVATED_IND) |
S(V51_CUP_LE_FE104_ACCESS_ACTIVATED_IND) |
S(V51_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ) |
S(V51_CUP_LE_FE106_ACCESS_DEACTIVATED_IND) |
S(V51_CUP_LE_MPH_UBR_UNBLOCK_REQ) |
S(V51_CUP_LE_MPH_BI_BLOCK_CMD) |
S(V51_CUP_LE_FE202_UNBLOCK_ACK) |
S(V51_CUP_LE_FE204_BLOCK_CMD) |
S(V51_CUP_LE_FE205_BLOCK_REQ),
.out_state_mask = S(V51_LE_UP_I_S_LE10_NOP_BLOCKED) |
S(V51_LE_UP_I_S_LE11_NOP_LOCAL_UNBLOCK) |
S(V51_LE_UP_I_S_LE12_NOP_REMOTE_UNBLOCK),
.action = isdn_up_le10_blocked,
},
[V51_LE_UP_I_S_LE11_NOP_LOCAL_UNBLOCK] = {
.name = "LE1.1_LOCAL_UNBLOCK",
.in_event_mask = S(V51_CUP_LE_FE102_ACTIV_INIT_USER_IND) |
S(V51_CUP_LE_FE103_DS_ACTIVATED_IND) |
S(V51_CUP_LE_FE104_ACCESS_ACTIVATED_IND) |
S(V51_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ) |
S(V51_CUP_LE_FE106_ACCESS_DEACTIVATED_IND) |
S(V51_CUP_LE_MPH_UBR_UNBLOCK_REQ) |
S(V51_CUP_LE_MPH_BI_BLOCK_CMD) |
S(V51_CUP_LE_FE202_UNBLOCK_ACK) |
S(V51_CUP_LE_FE204_BLOCK_CMD) |
S(V51_CUP_LE_FE205_BLOCK_REQ),
.out_state_mask = S(V51_LE_UP_I_S_LE21_OP_ACTIVATION_INITIATED) |
S(V51_LE_UP_I_S_LE22_OP_ACCESS_ACTIVATED) |
S(V51_LE_UP_I_S_LE10_NOP_BLOCKED) |
S(V51_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED),
.action = isdn_up_le11_local_unblock,
},
[V51_LE_UP_I_S_LE12_NOP_REMOTE_UNBLOCK] = {
.name = "LE1.2_REMOTE_UNBLOCK",
.in_event_mask = S(V51_CUP_LE_FE103_DS_ACTIVATED_IND) |
S(V51_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ) |
S(V51_CUP_LE_FE106_ACCESS_DEACTIVATED_IND) |
S(V51_CUP_LE_MPH_UBR_UNBLOCK_REQ) |
S(V51_CUP_LE_MPH_BI_BLOCK_CMD) |
S(V51_CUP_LE_FE202_UNBLOCK_ACK) |
S(V51_CUP_LE_FE204_BLOCK_CMD) |
S(V51_CUP_LE_FE205_BLOCK_REQ),
.out_state_mask = S(V51_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED) |
S(V51_LE_UP_I_S_LE10_NOP_BLOCKED),
.action = isdn_up_le12_remote_unblock,
},
[V51_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED] = {
.name = "LE2.0_OPERATIONAL_DEACTIVATED",
.in_event_mask = S(V51_CUP_LE_MPH_AR_ACTIVATE_ACCESS_REQ) |
S(V51_CUP_LE_FE102_ACTIV_INIT_USER_IND) |
S(V51_CUP_LE_FE103_DS_ACTIVATED_IND) |
S(V51_CUP_LE_FE104_ACCESS_ACTIVATED_IND) |
S(V51_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ) |
S(V51_CUP_LE_FE106_ACCESS_DEACTIVATED_IND) |
S(V51_CUP_LE_MPH_UBR_UNBLOCK_REQ) |
S(V51_CUP_LE_MPH_BI_BLOCK_CMD) |
S(V51_CUP_LE_FE202_UNBLOCK_ACK) |
S(V51_CUP_LE_FE204_BLOCK_CMD) |
S(V51_CUP_LE_FE205_BLOCK_REQ) |
S(V51_CUP_LE_FE206_PREFORMANCE_GRADING_IND),
.out_state_mask = S(V51_LE_UP_I_S_LE21_OP_ACTIVATION_INITIATED) |
S(V51_LE_UP_I_S_LE22_OP_ACCESS_ACTIVATED) |
S(V51_LE_UP_I_S_LE10_NOP_BLOCKED),
.action = isdn_up_le21_op_deact,
},
[V51_LE_UP_I_S_LE21_OP_ACTIVATION_INITIATED] = {
.name = "LE2.1_ACTIVATION_INITIATED",
.in_event_mask = S(V51_CUP_LE_MPH_AR_ACTIVATE_ACCESS_REQ) |
S(V51_CUP_LE_FE102_ACTIV_INIT_USER_IND) |
S(V51_CUP_LE_FE103_DS_ACTIVATED_IND) |
S(V51_CUP_LE_FE104_ACCESS_ACTIVATED_IND) |
S(V51_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ) |
S(V51_CUP_LE_FE106_ACCESS_DEACTIVATED_IND) |
S(V51_CUP_LE_MPH_UBR_UNBLOCK_REQ) |
S(V51_CUP_LE_MPH_BI_BLOCK_CMD) |
S(V51_CUP_LE_FE202_UNBLOCK_ACK) |
S(V51_CUP_LE_FE204_BLOCK_CMD) |
S(V51_CUP_LE_FE205_BLOCK_REQ),
.out_state_mask = S(V51_LE_UP_I_S_LE22_OP_ACCESS_ACTIVATED) |
S(V51_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED) |
S(V51_LE_UP_I_S_LE10_NOP_BLOCKED),
.action = isdn_up_le21_op_act_init,
},
[V51_LE_UP_I_S_LE22_OP_ACCESS_ACTIVATED] = {
.name = "LE2.2_ACCESS_ACTIVATED",
.in_event_mask = S(V51_CUP_LE_FE104_ACCESS_ACTIVATED_IND) |
S(V51_CUP_LE_MPH_DR_DEACTIVATE_ACCESS_REQ) |
S(V51_CUP_LE_FE106_ACCESS_DEACTIVATED_IND) |
S(V51_CUP_LE_MPH_UBR_UNBLOCK_REQ) |
S(V51_CUP_LE_MPH_BI_BLOCK_CMD) |
S(V51_CUP_LE_FE202_UNBLOCK_ACK) |
S(V51_CUP_LE_FE204_BLOCK_CMD) |
S(V51_CUP_LE_FE205_BLOCK_REQ) |
S(V51_CUP_LE_FE206_PREFORMANCE_GRADING_IND) |
S(V51_CUP_LE_MPH_DB_BLOCK_DCHAN_REQ) |
S(V51_CUP_LE_MPH_DU_BLOCK_DCHAN_REQ),
.out_state_mask = S(V51_LE_UP_I_S_LE20_OP_OPERATIONAL_DEACTIVTED) |
S(V51_LE_UP_I_S_LE10_NOP_BLOCKED),
.action = isdn_up_le22_op_acc_act,
},
};

120
v5x_internal.h Normal file
View File

@ -0,0 +1,120 @@
#pragma once
/* (C) 2021 by Harald Welte <laforge@gnumonks.org>
*
* 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 <stdint.h>
#include <osmocom/core/utils.h>
#include <osmocom/gsm/lapd_core.h>
struct osmo_fsm_inst;
enum v5x_dialect {
V5X_DIALECT_V51 = 1,
V5X_DIALECT_V52 = 2,
};
/* forward-declarations */
struct v5x_interface;
struct v5x_instance;
struct v5x_link;
/* A C-channel is a 64k timeslot used for signalling */
struct v5x_c_channel {
struct v5x_link *link; /* back-pointer */
struct v5x_timeslot *ts; /* E1 link timeslot. NULL = C-channel doesn't exist */
bool active; /* false == standby */
};
/* one physical E1 timeslot used on an E1 interface part of a V5.2 interface */
struct v5x_timeslot {
uint8_t nr;
struct v5x_link *link; /* back-pointer */
uint16_t l3_address;
};
/* one physical E1 interface used within a V5.2 interface */
struct v5x_link {
uint8_t id;
struct v5x_interface *interface; /* back-pointer */
struct v5x_timeslot ts[32]; /* 32 E1 slots; 0 not available */
struct v5x_c_channel c_channel[3]; /* 64k signaling possible on TS16, TS15 and TS31 */
struct osmo_fsm_inst *l1_link_fi; /* Layer 1 Link FSM instance */
};
/* one V5.x interface between AN (Access Network) and LE (Local Exchange) */
struct v5x_interface {
struct llist_head list; /* instance.interfaces */
struct v5x_instance *instance; /* back-pointer */
enum v5x_dialect dialect;
struct v5x_link *primary_link; /* one of the links below */
struct v5x_link *secondary_link; /* one of the links below */
/* 1..16 links in one interface */
struct v5x_link links[16];
struct osmo_fsm_inst *fi; /* Interface FSM instance */
struct {
struct lapd_datalink dl; /* Control data link */
struct v5x_c_channel *c_chan; /* pointer to active C-channel */
} control;
struct {
struct lapd_datalink dl; /* Link control data link */
struct v5x_c_channel *c_chan; /* pointer to active C-channel */
struct osmo_fsm_inst *fi; /* Linkg Control FSM instance */
} lcp;
struct {
struct lapd_datalink dl; /* PSTN data link */
struct v5x_c_channel *c_chan; /* pointer to active C-channel */
} pstn;
struct {
struct lapd_datalink dl; /* BCC data link */
struct v5x_c_channel *c_chan; /* pointer to active C-channel */
} bcc;
struct {
struct lapd_datalink dl; /* Protection data link 1 + 2 */
struct v5x_c_channel *c_chan; /* pointer to active C-channel */
} protection[2];
};
/* one user-facing port (subscriber line) */
struct v5x_user_port {
struct llist_head list; /* part of v5x_instance.ports */
struct v5x_instance *inst; /* back-pointer to instance we're part of */
uint16_t nr; /* port-number in decoded form (0..32767) */
iool is_isdn; /* is this port an ISDN port? */
struct {
struct osmo_fsm_inst *ctrl_fi; /* control protocol FSM instance */
struct osmo_fsm_inst *fi; /* port state FSM instance */
} isdn;
struct {
} pstn;
};
struct v5x_instance {
struct llist_head list; /* part of global list of instances */
struct llist_head interfaces; /* v5x_interface.list */
struct llist_head user_ports; /* list of v5x_user_port */
struct osmo_fsm_inst *ctrl_fi; /* common control FSM */
};

399
v5x_protocol.c Normal file
View File

@ -0,0 +1,399 @@
/* (C) 2021 by Harald Welte <laforge@gnumonks.org>
*
* 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 <unitstd.h>
#include <stdint.h>
#include <errno.h>
#include <osmocom/core/util.h>
#include <osmocom/core/msgb.h>
#include <osmocom/gsm/tlv.h>
#include "v5x_protocol.h"
const struct value_string v51_ctrl_msg_typ_str[] = {
{ V51_CTRL_MSGT_ESTABLISH, "ESTABLISH" },
{ V51_CTRL_MSGT_ESTABLISH_ACK, "ESTABLISH_ACK" },
{ V51_CTRL_MSGT_SIGNAL, "SIGNAL" },
{ V51_CTRL_MSGT_SIGNAL_ACK, "SIGNAL_ACK" },
{ V51_CTRL_MSGT_DISCONNECT, "DISCONNECT" },
{ V51_CTRL_MSGT_DISCONNECT_ACK, "DISCONNECT_ACK" },
{ V51_CTRL_MSGT_STATUS_ENQUIRY, "STATUS_ENQUIRY" },
{ V51_CTRL_MSGT_STATUS, "STATUS" },
{ V51_CTRL_MSGT_PROTOCOL_PARAMETER, "PROTOCOL_PARAMETER" },
{ V51_CTRL_MSGT_PORT_CTRL, "PORT_CTRL" },
{ V51_CTRL_MSGT_PORT_CTRL_ACK, "PORT_CTRL_ACK" },
{ V51_CTRL_MSGT_COMMON_CTRL, "COMMON_CTRL" },
{ V51_CTRL_MSGT_COMMON_CTRL_ACK, "COMMON_CTRL_ACK" },
{ V52_CTRL_MSGT_SWITCH_OVER_REQ, "SWITCH_OVER_REQ" },
{ V52_CTRL_MSGT_SWITCH_OVER_COM, "SWITCH_OVER_COM" },
{ V52_CTRL_MSGT_OS_SWITCH_OVER_COM, "OS_SWITCH_OVER_COM" },
{ V52_CTRL_MSGT_SWITCH_OVER_ACK, "SWITCH_OVER_ACK" },
{ V52_CTRL_MSGT_SWITCH_OVER_REJECT, "SWITCH_OVER_REJECT" },
{ V52_CTRL_MSGT_RESET_SN_COM, "RESET_SN_COM" },
{ V52_CTRL_MSGT_RESET_SN_ACK, "RESET_SN_ACK" },
{ V52_CTRL_MSGT_ALLOCATION, "ALLOCATION" },
{ V52_CTRL_MSGT_ALLOCATION_COMPLETE, "ALLOCATION_COMPLETE" },
{ V52_CTRL_MSGT_ALLOCATION_REJECT, "ALLOCATION_REJECT" },
{ V52_CTRL_MSGT_DE_ALLOCATION, "DE_ALLOCATION" },
{ V52_CTRL_MSGT_DE_ALLOCATION_COMPLETE, "DE_ALLOCATION_COMPLETE" },
{ V52_CTRL_MSGT_DE_ALLOCATION_REJECT, "DE_ALLOCATION_REJECT" },
{ V52_CTRL_MSGT_AUDIT, "AUDIT" },
{ V52_CTRL_MSGT_AUDIT_COMPLETE, "AUDIT_COMPLETE" },
{ V52_CTRL_MSGT_AN_FAULT, "AN_FAULT" },
{ V52_CTRL_MSGT_AN_FAULT_ACKNOWLEDGE "AN_FAULT_ACKNOWLEDGE" },
{ V52_CTRL_MSGT_PROTOCOL_ERROR "PROTOCOL_ERROR" },
{ V52_CTRL_MSGT_LINK_CTRL, "LINK_CTRL" },
{ V52_CTRL_MSGT_LINK_CTRL_ACK, "LINK_CTRL_ACK" },
{ 0, NULL }
};
const struct value_string v51_ctrl_iei_str[] = {
{ V51_CTRL_IEI_PULSE_NOTIFICATION, "PULSE_NOTIFICATION" },
{ V51_CTLR_IEI_LINE_NOTIFICATION, "LINE_NOTIFICATION" },
{ V51_CTLR_IEI_STATE, "STATE" },
{ V51_CTLR_IEI_AUTONOMOUS_SIG_SEQ, "AUTONOMOUS_SIG_SEQ" },
{ V51_CTLR_IEI_SEQUENCE_RESPONSE, "SEQUENCE_RESPONSE" },
{ V51_CTRL_IEI_PERFORMANCE_GRADING, "PERFORMANCE_GRADING" },
{ V51_CTRL_IEI_REJECTION_CAUSE, "REJECTION_CAUSE" },
{ V51_CTRL_IEI_SEQUENCE_NR, "SEQUENCE_NR" },
{ V51_CTRL_IEI_CADENCED_RINGING, "CADENCED_RINGING" },
{ V51_CTRL_IEI_PULSED_SIGNAL, "PULSED_SIGNAL" },
{ V51_CTRL_IEI_STEADY_SIGNAL, "STEADY_SIGNAL" },
{ V51_CTRL_IEI_DIGIT_SIGNAL, "DIGIT_SIGNAL" },
{ V51_CTRL_IEI_RECOGNITION_TIME, "RECOGNITION_TIME" },
{ V51_CTRL_IEI_ENABLE_AUTONOMOUS_ACK, "ENABLE_AUTONOMOUS_ACK" },
{ V51_CTRL_IEI_DISABLE_AUTONOMOUS_ACK, "DISABLE_AUTONOMOUS_ACK" },
{ V51_CTRL_IEI_CAUSE, "CAUSE" },
{ V51_CTRL_IEI_RESOURCE_UNAVAILABLE, "RESOURCE_UNAVAILABLE" },
{ V51_CTRL_IEI_ENABLE_METERING, "ENABLE_METERING" },
{ V51_CTRL_IEI_METERING_REPORT, "METERING_REPORT" },
{ V51_CTRL_IEI_ATTENUATION, "ATTENUATION" },
{ V51_CTRL_IEI_CTRL_F_ELEMENT, "CTRL_F_ELEMENT" },
{ V51_CTRL_IEI_CTRL_F_ID, "CTRL_F_ID" },
{ V51_CTRL_IEI_VARIANT, "VARIANT" },
{ V51_CTRL_IEI_INTERFACE_ID, "INTERFACE_ID" },
{ V52_CTRL_IEI_LCP_LINK_CTRL_FUNCTION, "LCP_LINK_CTRL_FUNCTION" },
{ V52_CTRL_IEI_BCC_USER_PORT_ID "BCC_USER_PORT_ID" },
{ V52_CTRL_IEI_BCC_ISDN_PORT_TS_ID, "BCC_ISDN_PORT_TS_ID" },
{ V52_CTRL_IEI_BCC_V5_TS_ID, "BCC_V5_TS_ID" },
{ V52_CTRL_IEI_BCC_MULTI_TS_MAP, "BCC_MULTI_TS_MAP" },
{ V52_CTRL_IEI_BCC_REJECT_CAUSE, "BCC_REJECT_CAUSE" },
{ V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE, "BCC_PROTOCOL_ERROR_CAUSE" },
{ V52_CTRL_IEI_BCC_CONNECTION_INCOMPLETE, "BCC_CONNECTION_INCOMPLETE" },
{ V52_CTRL_IEI_BCC_INFO_TRANSFER_CAPABILITY, "BCC_INFO_TRANSFER_CAPABILITY" },
{ V52_CTRL_IEI_PP_SEQUENCE_NR, "PP_SEQUENCE_NR" },
{ V52_CTRL_IEI_PP_PHYSICAL_C_CHAN_ID, "PP_PHYSICAL_C_CHAN_ID" },
{ V52_CTRL_IEI_PP_REJECTION_CAUSE, "PP_REJECTION_CAUSE" },
{ V52_CTRL_IEI_PP_PROTOCOL_ERROR_CAUSE, "PP_PROTOCOL_ERROR_CAUSE" },
{ 0, NULL }
};
const struct value_string v51_ctrl_func_el_str[] = {
{ V51_CTRL_FE101_ACTIVATE_ACCESS, "FE101_ACTIVATE_ACCESS" },
{ V51_CTRL_FE102_ACT_INIT_BY_USER, "FE102_ACT_INIT_BY_USER" },
{ V51_CTRL_FE103_DS_ACTIVATED, "FE103_DS_ACTIVATED" },
{ V51_CTRL_FE104_ACCESS_ACTIVATED, "FE104_ACCESS_ACTIVATED" },
{ V51_CTRL_FE105_DEACTIVATE_ACCESS, "FE105_DEACTIVATE_ACCESS" },
{ V51_CTRL_FE106_ACCESS_DEACTIVATED, "FE106_ACCESS_DEACTIVATED" },
{ V51_CTRL_FE201_UNBLOCK, "FE201_UNBLOCK" },
{ V51_CTRL_FE203_BLOCK, "FE203_BLOCK" },
{ V51_CTRL_FE205_BLOCK_REQ, "FE205_BLOCK_REQ" },
{ V51_CTRL_FE206_PERFORMANCE_GRADING, "FE206_PERFORMANCE_GRADING" },
{ V51_CTRL_FE206_D_CHANNEL_BLOCK, "FE206_D_CHANNEL_BLOCK" },
{ V51_CTRL_FE206_D_CHANNEL_UNBLOCK, "FE206_D_CHANNEL_UNBLOCK" }
{ 0, NULL }
};
const struct value_string v51_ctrl_func_id_str[] = {
{ V51_CTRL_ID_VERIFY_RE_PROVISIONING, "VERIFY_RE_PROVISIONING" },
{ V51_CTRL_ID_READY_FOR_RE_PROVISIONING, "READY_FOR_RE_PROVISIONING" },
{ V51_CTRL_ID_NOT_READY_FOR_RE_PROVISIONING, "NOT_READY_FOR_RE_PROVISIONING" },
{ V51_CTRL_ID_SWITCH_OVER_TO_NEW_VARIANT, "SWITCH_OVER_TO_NEW_VARIANT" },
{ V51_CTRL_ID_RE_PROVISIONING_STARTED, "RE_PROVISIONING_STARTED" },
{ V51_CTRL_ID_CANNOT_RE_PROVISION, "CANNOT_RE_PROVISION" },
{ V51_CTRL_ID_REQUEST_VARIANT_AND_INTERFACE_ID, "REQUEST_VARIANT_AND_INTERFACE_ID" },
{ V51_CTRL_ID_VARIANT_AND_INTERFACE_ID, "VARIANT_AND_INTERFACE_ID" },
{ V51_CTRL_ID_BLOCKING_STARTED, "BLOCKING_STARTED" },
{ V51_CTRL_ID_RESTART_REQUEST, "RESTART_REQUEST" },
{ V51_CTRL_ID_RESTART_COMPLETE, "RESTART_COMPLETE" },
{ 0, NULL }
};
const struct value_string v51_cause_type_str[] = {
{ V51_CTRL_CAUSE_T_RESP_TO_STATUS_ENQ, "RESP_TO_STATUS_ENQ" },
{ V51_CTRL_CAUSE_T_L3_ADDRESS_ERROR, "L3_ADDRESS_ERROR" },
{ V51_CTRL_CAUSE_T_MSG_TYPE_UNRECOGNIZED, "MSG_TYPE_UNRECOGNIZED" },
{ V51_CTRL_CAUSE_T_OUT_OF_SEQUENCE_IE, "OUT_OF_SEQUENCE_IE" },
{ V51_CTRL_CAUSE_T_REPEATED_OPT_IE, "REPEATED_OPT_IE" },
{ V51_CTRL_CAUSE_T_MAND_IE_MISSING, "MAND_IE_MISSING" },
{ V51_CTRL_CAUSE_T_UNRECOGNIZED_IE, "UNRECOGNIZED_IE" },
{ V51_CTRL_CAUSE_T_MAND_IE_CONTENT_ERROR, "MAND_IE_CONTENT_ERROR" },
{ V51_CTRL_CAUSE_T_OPT_IE_CONTENT_ERROR, "OPT_IE_CONTENT_ERROR" },
{ V51_CTRL_CAUSE_T_MSG_INCOMP_PATH_STATE, "MSG_INCOMP_PATH_STATE" },
{ V51_CTRL_CAUSE_T_REPEATED_MAND_IE, "REPEATED_MAND_IE" },
{ V51_CTRL_CAUSE_T_TOO_MANY_IE, "TOO_MANY_IE" },
{ 0, NULL }
};
static const uint8_t signal_mand_ies[] = { V51_CTRL_IEI_SEQUENCE_NR };
static const uint8_t signal_ack_mand_ies[] = { V51_CTRL_IEI_SEQUENCE_NR };
static const uint8_t status_mand_ies[] = { V51_CTRL_IEI_STATE, V51_CTRL_IEI_CAUSE };
static const uint8_t prot_par_mand_ies[] = { V51_CTRL_IEI_SEQUENCE_NR };
static const uint8_t port_ctrl_mand_ies[] = { V51_CTRL_IEI_CTRL_F_ELEMENT };
static const uint8_t lcp_mand_ies[] = { V52_CTRL_IEI_LCP_LINK_CTRL_FUNCTION };
static const uint8_t alloc_mand_ies[] = { V52_CTRL_IEI_BCC_USER_PORT_ID };
static const uint8_t alloc_rej_mand_ies[] = { V52_CTRL_IEI_BCC_REJECT_CAUSE };
static const uint8_t prot_err_mand_ies[] = { V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE };
static const uint8_t pp_swo_mand_ies[] = {
V52_CTRL_IEI_PP_SEQUENCE_NR,
V52_CTRL_IEI_PP_PHYSICAL_C_CHAN_ID
};
static const uint8_t pp_swo_rej_mand_ies[] = {
V52_CTRL_IEI_PP_SEQUENCE_NR,
V52_CTRL_IEI_PP_PHYSICAL_C_CHAN_ID,
V52_CTRL_IEI_PP_REJECTION_CAUSE,
};
static const uint8_t pp_perr_mand_ies[] = {
V52_CTRL_IEI_PP_SEQUENCE_NR,
V52_CTRL_IEI_PP_PROTOCOL_ERROR_CAUSE,
};
const struct osmo_tlv_prot_def v51_ctrl_msg_tlv = {
.name = "V51_CTRL",
.tlv_def = v51_ctrl_tlv_def,
.msg_def = {
/* G.964 Section 13.3 */
[V51_CTRL_MSGT_ESTABLISH] = MSG_DEF("ESTABLISH", NULL, 0),
[V51_CTRL_MSGT_ESTABLISH_ACK] = MSG_DEF("ESTABLISH", NULL, 0),
[V51_CTRL_MSGT_SIGNAL] = MSG_DEF("SIGNAL", signal_mand_ies, 0),
[V51_CTRL_MSGT_SIGNAL_ACK] = MSG_DEF("SIGNAL_ACK", signal_ack_mand_ies, 0),
[V51_CTRL_MSGT_STATUS] = MSG_DEF("STATUS", status_mand_ies, 0),
[V51_CTRL_MSGT_STATUS_ENQUIRY] = MSG_DEF("STATUS_ENQUIRY", NULL, 0),
[V51_CTRL_MSGT_DISCONNECT] = MSG_DEF("DISCONNECT", NULL, 0),
[V51_CTRL_MSGT_DISCONNECT_ACK] = MSG_DEF("DISCONNECT_ACK", NULL, 0),
[V51_CTRL_MSGT_PROTOCOL_PARAMETER] = MSG_DEF("PROTOCOL_PARAMETER", prot_par_mand_ies, 0),
/* G.964 Section 14.4 */
[V51_CTRL_MSGT_PORT_CTRL] = MSG_DEF("PORT_CTRL", port_ctrl_mand_ies, 0),
[V51_CTRL_MSGT_PORT_CTRL_ACK] = MSG_DEF("PORT_CTRL_ACK", port_ctrl_mand_ies, 0),
[V51_CTRL_MSGT_COMMON_CTRL] = MSG_DEF("COMMON_CTRL", port_ctrl_mand_ies, 0),
[V51_CTRL_MSGT_COMMON_CTRL_ACK] = MSG_DEF("COMMON_CTRL_ACK", port_ctrl_mand_ies, 0),
/* G.965 Section 16.3 LCP */
[V52_CTRL_MSGT_LCP_LINK_CTRL] = MSG_DEF("LINK_CONTROL", lcp_mand_ies, 0),
[V52_CTRL_MSGT_LCP_LINK_CTRL_ACK] = MSG_DEF("LINK_CONTROL_ACK", lcp_mand_ies, 0),
/* G.965 Section 17.3 BCC */
[V52_CTRL_MSGT_ALLOCATION] = MSG_DEF("ALLOCATION", alloc_mand_ies, 0),
[V52_CTRL_MSGT_ALLOCATION_COMPLETE] = MSG_DEF("ALLOCATION_COMPLETE", NULL, 0),
[V52_CTRL_MSGT_ALLOCATION_REJECT] = MSG_DEF("ALLOCATION_REJECT", alloc_rej_mand_ies, 0),
[V52_CTRL_MSGT_DE_ALLOCATION] = MSG_DEF("DE_ALLOCATION", alloc_mand_ies, 0),
[V52_CTRL_MSGT_DE_ALLOCATION_COMPLETE] = MSG_DEF("DE_ALLOCATION_COMPLETE", NULL, 0),
[V52_CTRL_MSGT_DE_ALLOCATION_REJECT] = MSG_DEF("DE_ALLOCATION_REJECT", alloc_rej_mand_ies, 0),
[V52_CTRL_MSGT_AUDIT] = MSG_DEF("AUDIT", NULL, 0),
[V52_CTRL_MSGT_AUDIT_COMPLETE] = MSG_DEF("AUDIT_COMPLETE", NULL, 0),
[V52_CTRL_MSGT_AN_FAULT] = MSG_DEF("AN_FAULT", NULL, 0),
[V52_CTRL_MSGT_AN_FAULT_ACK] = MSG_DEF("AN_FAULT_ACK", NULL, 0),
[V52_CTRL_MSGT_PROTOCOL_ERROR] = MSG_DEF("PROTOCOL_ERROR", prot_err_mand_ies, 0),
/* G.965 Section 18.3 PP */
[V52_CTRL_MSGT_PP_SWITCH_OVER_REQ] = MSG_DEF("SWITCH_OVER_REQ", pp_swo_mand_ies, 0),
[V52_CTRL_MSGT_PP_SWITCH_OVER_COM] = MSG_DEF("SWITCH_OVER_COM", pp_swo_mand_ies, 0),
[V52_CTRL_MSGT_PP_OS_SWITCH_OVER_COM] = MSG_DEF("OS_SWITCH_OVER_COM", pp_swo_mand_ies, 0),
[V52_CTRL_MSGT_PP_SWITCH_OVER_ACK] = MSG_DEF("SWITCH_OVER_ACK", pp_swo_mand_ies, 0),
[V52_CTRL_MSGT_PP_SWITCH_OVER_REJECT] = MSG_DEF("SWITCH_OVER_REJECT", pp_swo_rej_mand_ies, 0),
[V52_CTRL_MSGT_PP_PROTOCOL_ERROR] = MSG_DEF("PROTOCOL_ERROR", pp_perr_mand_ies, 0),
[V52_CTRL_MSGT_PP_RESET_SN_COM] = MSG_DEF("RESET_SN_COM", NULL, 0),
[V52_CTRL_MSGT_PP_RESET_SN_ACK] = MSG_DEF("RESET_SN_ACK", NULL, 0),
},
.ie_def = {
/* single byte, only upper nibble matches IEI */
[V51_CTRL_IEI_PULSE_NOTIFICATION] = { 0, "PULSE_NOTIFICATION" },
[V51_CTLR_IEI_LINE_NOTIFICATION] = { 0, "LINE_NOTIFICATION" },
[V51_CTLR_IEI_STATE] = { 0, "STATE" },
[V51_CTLR_IEI_AUTONOMOUS_SIG_SEQ] = { 0, "AUTONOMOUS_SIG_SEQ" },
[V51_CTLR_IEI_SEQUENCE_RESPONSE] = { 0, "SEQUENCE_RESPONSE" },
/* single byte: ISDN */
[V51_CTRL_IEI_PERFORMANCE_GRADING] = { 0, "PERFORMANCE_GRADING" },
[V51_CTRL_IEI_REJECTION_CAUSE] = { 0, "REJECTION_CAUSE" },
/* variable length: PSTN */
[V51_CTRL_IEI_SEQUENCE_NR] = { 1, "SEQUENCE_NR" },
[V51_CTRL_IEI_CADENCED_RINGING] = { 1, "CADENCED_RINGING" },
[V51_CTRL_IEI_PULSED_SIGNAL] = { 2, "PULSED_SIGNAL" },
[V51_CTRL_IEI_STEADY_SIGNAL] = { 1, "STEADY_SIGNAL" },
[V51_CTRL_IEI_DIGIT_SIGNAL] = { 1, "DIGIT_SIGNAL" },
[V51_CTRL_IEI_RECOGNITION_TIME] = { 2, "RECOGNITION_TIME" },
[V51_CTRL_IEI_ENABLE_AUTONOMOUS_ACK] = { 2, "ENABLE_AUTONOMOUS_ACK" },
[V51_CTRL_IEI_DISABLE_AUTONOMOUS_ACK] = { 1, "DISABLE_AUTONOMOUS_ACK" },
[V51_CTRL_IEI_CAUSE] = { 2, "CAUSE" },
[V51_CTRL_IEI_RESOURCE_UNAVAILABLE] = { 1, "RESOURCE_UNAVAILABLE" },
[V51_CTRL_IEI_ENABLE_METERING] = { 1, "ENABLE_METERING" },
[V51_CTRL_IEI_METERING_REPORT] = { 2, "METERING_REPORT" },
[V51_CTRL_IEI_ATTENUATION] = { 1, "ATTENUATION" },
/* variable length: ISDN */
[V51_CTRL_IEI_CTRL_F_ELEMENT] = { 1, "CTRL_F_ELEMENT" },
[V51_CTRL_IEI_CTRL_F_ID] = { 1, "CTRL_F_ID" },
[V51_CTRL_IEI_VARIANT] = { 1, "VARIANT" },
[V51_CTRL_IEI_INTERFACE_ID] = { 3, "INTERFACE_ID" },
/* variable length: LCP */
[V52_CTRL_IEI_LCP_LINK_CTRL_FUNCTION] = { 1, "LINK_CTRL_FUNCTION" },
/* variable length: BCC */
[V52_CTRL_IEI_BCC_USER_PORT_ID] = { 2, "USER_PORT_ID" },
[V52_CTRL_IEI_BCC_ISDN_PORT_TS_ID] = { 1, "ISDN_PORT_TS_ID" },
[V52_CTRL_IEI_BCC_V5_TS_ID] = { 2, "V5_TS_ID" },
[V52_CTRL_IEI_BCC_MULTI_TS_MAP] = { 9, "MULTI_TS_MAP" },
[V52_CTRL_IEI_BCC_REJECT_CAUSE] = { 1, "REJECT_CAUSE" },
[V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE] = { 1, "PROTOCOL_ERR_CAUSE" },
[V52_CTRL_IEI_BCC_CONNECTION_INCOMPLETE]= { 1, "CONNECTION_INCOMPLETE" },
[V52_CTRL_IEI_BCC_INFO_TRANSFER_CAPABILITY] = { 1, "INFO_TRANSFER_CAPABILITY" },
/* variable length: PP */
[V52_CTRL_IEI_PP_SEQUENCE_NR] = { 1, "SEQUENCE_NR" },
[V52_CTRL_IEI_PP_PHYSICAL_C_CHAN_ID] = { 2, "PHYSICAL_C_CHAN_ID" },
[V52_CTRL_IEI_PP_REJECTION_CAUSE] = { 1, "REJECTION_CAUSE" },
[V52_CTRL_IEI_PP_PROTOCOL_ERROR_CAUSE] = { 1, "PROTOCOL_ERR_CAUSE" },
},
.msgt_names = v51_ctrl_msg_typ_str,
};
struct v5x_user_port *v5x_user_port_find(struct v5x_instance *v5i, uint16_t nr)
{
struct v5x_user_port *v5up;
llist_for_each_entry(v5up, &v5i->user_ports, list) {
if (v5up->nr == l3_addr)
return v5up;
}
}
static int v51_rcv_pstn(struct v5x_user_port *v5i, struct msgb *msg, const struct tlv_parsed *tp)
{
struct v51_l3_hdr *l3h = msgb_l3(msg);
switch (l3h->msg_type) {
case V51_CTRL_MSGT_ESTABLISH:
case V51_CTRL_MSGT_ESTABLISH_ACK:
case V51_CTRL_MSGT_SIGNAL:
case V51_CTRL_MSGT_SIGNAL_ACK:
case V51_CTRL_MSGT_DISCONNECT:
case V51_CTRL_MSGT_DISCONNECT_ACK:
case V51_CTRL_MSGT_STATUS_ENQUIRY:
case V51_CTRL_MSGT_STATUS:
case V51_CTRL_MSGT_PROTOCOL_PARAMETER:
return -EINVAL;
}
}
static int v52_rcv_pp(struct v5x_instance *v5i, struct msgb *msg, const struct tlv_parsed *tp)
{
struct v51_l3_hdr *l3h = msgb_l3(msg);
}
static int v52_rcv_bcc(struct v5x_instance *v5i, struct msgb *msg, const struct tlv_parsed *tp)
{
struct v51_l3_hdr *l3h = msgb_l3(msg);
}
static int v52_rcv_lcp(struct v5x_instance *v5i, struct msgb *msg, const struct tlv_parsed *tp)
{
struct v51_l3_hdr *l3h = msgb_l3(msg);
}
/* main entry point for received V5 messages */
int v5x_rcv(struct v5x_instance *v5i, struct msgb *msg)
{
struct v51_l3_hdr *l3h = msgb_l3(msg);
struct tlv_parsed tp;
uint16_t l3_addr;
if (msgb_l3len(msg) < sizeof(*l3h)) {
LOGP(DV5, LOGL_ERROR, "Received short message (%u bytes < %u)\n", msgb_l3len(msg), sizeof(*l3h));
return -EINVAL;
}
if (l3h->pdisc != V51_CTRL_PDISC) {
LOGP(DV5, LOGL_ERROR, "Received unsupported protocol discriminator 0x%02x\n", l3h->pdisc);
return -EINVAL;
}
rc = osmo_tlv_prot_parse(v51_ctrl_msg_tlv, &tp, 1, l3h->msg_type, msgb_l3(msg) + sizeof(*l3h),
msgb_l3len(msg) - sizeof(*l3h), 0, 0, DV5, __func__);
if (rc < 0)
return -EINVAL;
l3_addr = v51_l3_addr_dec(l3h->l3_addr);
/* look-up user port based on L3 addr? */
v5up = v5x_user_port_find(v5i, l3_addr);
switch (l3h->msg_type) {
case V51_CTRL_MSGT_ESTABLISH:
case V51_CTRL_MSGT_ESTABLISH_ACK:
case V51_CTRL_MSGT_SIGNAL:
case V51_CTRL_MSGT_SIGNAL_ACK:
case V51_CTRL_MSGT_DISCONNECT:
case V51_CTRL_MSGT_DISCONNECT_ACK:
case V51_CTRL_MSGT_STATUS_ENQUIRY:
case V51_CTRL_MSGT_STATUS:
case V51_CTRL_MSGT_PROTOCOL_PARAMETER:
rc = v51_rcv_pstn(v5up, msg, &tp);
break;
case V51_CTRL_MSGT_PORT_CTRL:
case V51_CTRL_MSGT_PORT_CTRL_ACK:
case V51_CTRL_MSGT_COMMON_CTRL:
case V51_CTRL_MSGT_COMMON_CTRL_ACK:
rc = v51_rcv_ctrl(v5i, msg, &tp);
break;
case V52_CTRL_MSGT_PP_SWITCH_OVER_REQ:
case V52_CTRL_MSGT_PP_SWITCH_OVER_COM:
case V52_CTRL_MSGT_PP_OS_SWITCH_OVER_COM:
case V52_CTRL_MSGT_PP_SWITCH_OVER_ACK:
case V52_CTRL_MSGT_PP_SWITCH_OVER_REJECT:
case V52_CTRL_MSGT_PP_PROTOCOL_ERROR:
case V52_CTRL_MSGT_PP_RESET_SN_COM:
case V52_CTRL_MSGT_PP_RESET_SN_ACK:
rc = v52_rcv_pp(v5i, msg, &tp);
break;
case V52_CTRL_MSGT_ALLOCATION:
case V52_CTRL_MSGT_ALLOCATION_COMPLETE:
case V52_CTRL_MSGT_ALLOCATION_REJECT:
case V52_CTRL_MSGT_DE_ALLOCATION:
case V52_CTRL_MSGT_DE_ALLOCATION_COMPLETE:
case V52_CTRL_MSGT_DE_ALLOCATION_REJECT:
case V52_CTRL_MSGT_AUDIT:
case V52_CTRL_MSGT_AUDIT_COMPLETE:
case V52_CTRL_MSGT_AN_FAULT:
case V52_CTRL_MSGT_AN_FAULT_ACK:
case V52_CTRL_MSGT_PROTOCOL_ERROR:
rc = v52_rcv_bcc(v5i, msg, &tp);
break;
case V52_CTRL_MSGT_LCP_LINK_CTRL:
case V52_CTRL_MSGT_LCP_LINK_CTRL_ACK:
rc = v52_rcv_lcp(v5i, msg, &tp);
break;
default:
LOGP(DV5, LOGL_ERROR, "Received unsupported message type %s\n",
osmo_tlv_prot_msg_name(v51_ctrl_msg_tlv, l3h->msg_type);
}
}

300
v5x_protocol.h Normal file
View File

@ -0,0 +1,300 @@
#pragma once
#include <stdint.h>
#include <osmocom/core/utils.h>
#include <osmocom/gsm/tlv.h>
/* Definitions related to the V5.1 and V5.2 interface between AN (access
* network) and LE (local exchange) as per ITU-T G.964 + G.965.
*
* (C) 2021 by Harald Welte <laforge@gnumonks.org>
*/
/***********************************************************************/
/* protocol wire format */
/***********************************************************************/
/* Table 14/G.964 */
#define V51_CTRL_PDISC 0x48
/* Table 1/G.965 + Table 1/G.964 */
#define V51_DLADDR_PSTN 8176
#define V51_DLADDR_CTRL 8177
#define V52_DLADDR_BCC 8178
#define V52_DLADDR_PROTECTION 8179
#define V52_DLADDR_LCP 8180
/* G.965 Section 13.1 */
struct v51_l3_hdr {
uint8_t pdisc;
uint16_t l3_addr; /* with C/R and EA bits! */
uint8_t msg_type;
uint8_t payload[0];
} __attribute__ ((packed));
/* G.964 Section 14.4.2.3 Figure 33 + 34 */
static inline bool v51_l3_addr_is_isdn(uint16_t in)
{
/* extract two bytes */
uint8_t b1 = in >> 8;
uint8_t b2 = in & 0xff;
if ((b1 & 0x03) == 0x00) && (b2 & 0x01)
return true;
return false;
}
static inline uint16_t v51_l3_addr_dec(uint16_t in, bool *is_isdn)
{
/* extract two bytes */
uint8_t b1 = in >> 8;
uint8_t b2 = in & 0xff;
if (v51_l3_addr_is_isdn(in)) {
/* remove EA/C/R bits */
uint8_t upper = b1 >> 2;
uint8_t lower = b2 >> 1;
if (is_isdn)
*is_isdn = true;
/* shift them together */
return lower | (upper << 7);
} else{
uint8_t upper = b1 >> 1;
uint8_t lower = b2;
if (is_isdn)
*is_isdn = false;
return lower | (upper << 8);
}
}
static inline uint16_t v51_l3_addr_enc(uint16_t in, bool is_isdn)
{
uint8_t b1, b2;
if (is_isdn) {
uint8_t upper = in >> 7;
uint8_t lower = in & 0x7f;
b1 = upper << 2;
b2 = (lower << 1) | 0x01;
} else {
uint8_t upper = in >> 8;
uint8_t lower = in & 0xff;
b1 = (upper << 1) | 0x01;
b2 = lower;
}
return (b1 << 8) | b2;
}
/* Table 15 + 52/G.964 */
enum v51_ctrl_msg_type {
/* 000xxxx: PSTN protocol message types */
V51_CTRL_MSGT_ESTABLISH = 0x00,
V51_CTRL_MSGT_ESTABLISH_ACK = 0x01,
V51_CTRL_MSGT_SIGNAL = 0x02,
V51_CTRL_MSGT_SIGNAL_ACK = 0x03,
V51_CTRL_MSGT_DISCONNECT = 0x08,
V51_CTRL_MSGT_DISCONNECT_ACK = 0x09,
V51_CTRL_MSGT_STATUS_ENQUIRY = 0x0c,
V51_CTRL_MSGT_STATUS = 0x0d,
V51_CTRL_MSGT_PROTOCOL_PARAMETER = 0x0e,
/* 0010xxx: Control protocol message types */
V51_CTRL_MSGT_PORT_CTRL = 0x10,
V51_CTRL_MSGT_PORT_CTRL_ACK = 0x11,
V51_CTRL_MSGT_COMMON_CTRL = 0x12,
V51_CTRL_MSGT_COMMON_CTRL_ACK = 0x13,
/* 0011xxx: Protection protocol message types */
V52_CTRL_MSGT_PP_SWITCH_OVER_REQ = 0x18,
V52_CTRL_MSGT_PP_SWITCH_OVER_COM = 0x19,
V52_CTRL_MSGT_PP_OS_SWITCH_OVER_COM = 0x1a,
V52_CTRL_MSGT_PP_SWITCH_OVER_ACK = 0x1b,
V52_CTRL_MSGT_PP_SWITCH_OVER_REJECT = 0x1c,
V52_CTRL_MSGT_PP_PROTOCOL_ERROR = 0x1d,
V52_CTRL_MSGT_PP_RESET_SN_COM = 0x1e,
V52_CTRL_MSGT_PP_RESET_SN_ACK = 0x1f,
/* 010xxxx: BCC protocol message types */
V52_CTRL_MSGT_ALLOCATION = 0x20,
V52_CTRL_MSGT_ALLOCATION_COMPLETE = 0x21,
V52_CTRL_MSGT_ALLOCATION_REJECT = 0x22,
V52_CTRL_MSGT_DE_ALLOCATION = 0x23,
V52_CTRL_MSGT_DE_ALLOCATION_COMPLETE = 0x24,
V52_CTRL_MSGT_DE_ALLOCATION_REJECT = 0x25,
V52_CTRL_MSGT_AUDIT = 0x26,
V52_CTRL_MSGT_AUDIT_COMPLETE = 0x27,
V52_CTRL_MSGT_AN_FAULT = 0x28,
V52_CTRL_MSGT_AN_FAULT_ACK = 0x29,
V52_CTRL_MSGT_PROTOCOL_ERROR = 0x2a,
/* 0110xxx: Link control protocol message types */
V52_CTRL_MSGT_LCP_LINK_CTRL = 0x30,
V52_CTRL_MSGT_LCP_LINK_CTRL_ACK = 0x31,
};
extern const struct value_string v51_ctrl_msg_typ_str[];
/* Table 17 + 53/G.964 */
enum v51_ctrl_iei {
/* single byte, only upper nibble matches IEI */
V51_CTRL_IEI_PULSE_NOTIFICATION = 0xc0,
V51_CTLR_IEI_LINE_NOTIFICATION = 0x80,
V51_CTLR_IEI_STATE = 0x90,
V51_CTLR_IEI_AUTONOMOUS_SIG_SEQ = 0xa0,
V51_CTLR_IEI_SEQUENCE_RESPONSE = 0xb0,
/* single byte: ISDN */
V51_CTRL_IEI_PERFORMANCE_GRADING = 0xe0,
V51_CTRL_IEI_REJECTION_CAUSE = 0xf0,
/* variable length: PSTN */
V51_CTRL_IEI_SEQUENCE_NR = 0x00,
V51_CTRL_IEI_CADENCED_RINGING = 0x01,
V51_CTRL_IEI_PULSED_SIGNAL = 0x02,
V51_CTRL_IEI_STEADY_SIGNAL = 0x03,
V51_CTRL_IEI_DIGIT_SIGNAL = 0x04,
V51_CTRL_IEI_RECOGNITION_TIME = 0x10,
V51_CTRL_IEI_ENABLE_AUTONOMOUS_ACK = 0x11,
V51_CTRL_IEI_DISABLE_AUTONOMOUS_ACK = 0x12,
V51_CTRL_IEI_CAUSE = 0x13,
V51_CTRL_IEI_RESOURCE_UNAVAILABLE = 0x14,
V51_CTRL_IEI_ENABLE_METERING = 0x32,
V51_CTRL_IEI_METERING_REPORT = 0x33,
V51_CTRL_IEI_ATTENUATION = 0x34,
/* variable length: ISDN */
V51_CTRL_IEI_CTRL_F_ELEMENT = 0x20,
V51_CTRL_IEI_CTRL_F_ID = 0x21,
V51_CTRL_IEI_VARIANT = 0x22,
V51_CTRL_IEI_INTERFACE_ID = 0x23,
/* variable length: LCP */
V52_CTRL_IEI_LCP_LINK_CTRL_FUNCTION = 0x30,
/* variable length: BCC */
V52_CTRL_IEI_BCC_USER_PORT_ID = 0x40,
V52_CTRL_IEI_BCC_ISDN_PORT_TS_ID = 0x41,
V52_CTRL_IEI_BCC_V5_TS_ID = 0x42,
V52_CTRL_IEI_BCC_MULTI_TS_MAP = 0x43,
V52_CTRL_IEI_BCC_REJECT_CAUSE = 0x44,
V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE = 0x45,
V52_CTRL_IEI_BCC_CONNECTION_INCOMPLETE = 0x46,
V52_CTRL_IEI_BCC_INFO_TRANSFER_CAPABILITY = 0x47,
/* variable-length: Protection */
V52_CTRL_IEI_PP_SEQUENCE_NR = 0x50,
V52_CTRL_IEI_PP_PHYSICAL_C_CHAN_ID = 0x51,
V52_CTRL_IEI_PP_REJECTION_CAUSE = 0x52,
V52_CTRL_IEI_PP_PROTOCOL_ERROR_CAUSE = 0x53,
};
extern const struct value_string v51_ctrl_iei_str[];
extern const struct tlv_definition v51_ctrl_tlv_def[] = {
/* single byte: PSTN / G.964 Table 17 */
[V51_CTRL_IEI_PULSE_NOTIFICATION] = TLV_TYPE_SINGLE_TV,
[V51_CTLR_IEI_LINE_NOTIFICATION] = TLV_TYPE_SINGLE_TV,
[V51_CTLR_IEI_STATE] = TLV_TYPE_SINGLE_TV,
[V51_CTLR_IEI_AUTONOMOUS_SIG_SEQ] = TLV_TYPE_SINGLE_TV,
[V51_CTLR_IEI_SEQUENCE_RESPONSE] = TLV_TYPE_SIGNLE_TV,
/* single byte: ISDN / G.964 Table 53 */
[V51_CTRL_IEI_PERFORMANCE_GRADING] = TLV_TYPE_SINGLE_TV,
[V51_CTRL_IEI_REJECTION_CAUSE] = TLV_TYPE_SINGLE_TV,
/* variable length: PSTN / G.964 Table 17 */
[V51_CTRL_IEI_SEQUENCE_NR] = TLV_TYPE_TLV,
[V51_CTRL_IEI_CADENCED_RINGING] = TLV_TYPE_TLV,
[V51_CTRL_IEI_PULSED_SIGNAL] = TLV_TYPE_TLV,
[V51_CTRL_IEI_STEADY_SIGNAL] = TLV_TYPE_TLV,
[V51_CTRL_IEI_DIGIT_SIGNAL] = TLV_TYPE_TLV,
[V51_CTRL_IEI_RECOGNITION_TIME] = TLV_TYPE_TLV,
[V51_CTRL_IEI_ENABLE_AUTONOMOUS_ACK] = TLV_TYPE_TLV,
[V51_CTRL_IEI_DISABLE_AUTONOMOUS_ACK] = TLV_TYPE_TLV,
[V51_CTRL_IEI_CAUSE] = TLV_TYPE_TLV,
[V51_CTRL_IEI_RESOURCE_UNAVAILABLE] = TLV_TYPE_TLV,
[V51_CTRL_IEI_ENABLE_METERING] = TLV_TYPE_TLV,
[V51_CTRL_IEI_METERING_REPORT] = TLV_TYPE_TLV,
[V51_CTRL_IEI_ATTENUATION] = TLV_TYPE_TLV,
/* variable length: ISDN / G.964 Table 53 */
[V51_CTRL_IEI_CTRL_F_ELEMENT] = TLV_TYPE_TLV,
[V51_CTRL_IEI_CTRL_F_ID] = TLV_TYPE_TLV,
[V51_CTRL_IEI_VARIANT] = TLV_TYPE_TLV,
[V51_CTRL_IEI_INTERFACE_ID] = TLV_TYPE_TLV,
/* variable length: LCP / G.965 Table FIXME */
[V52_CTRL_IEI_LCP_LINK_CTRL_FUNCTION] = TLV_TYPE_TLV,
/* variable length: BCC */
[V52_CTRL_IEI_BCC_USER_PORT_ID] = TLV_TYPE_TLV,
[V52_CTRL_IEI_BCC_ISDN_PORT_TS_ID] = TLV_TYPE_TLV,
[V52_CTRL_IEI_BCC_V5_TS_ID] = TLV_TYPE_TLV,
[V52_CTRL_IEI_BCC_MULTI_TS_MAP] = TLV_TYPE_TLV,
[V52_CTRL_IEI_BCC_REJECT_CAUSE] = TLV_TYPE_TLV,
[V52_CTRL_IEI_BCC_PROTOCOL_ERROR_CAUSE] = TLV_TYPE_TLV,
[V52_CTRL_IEI_BCC_CONNECTION_INCOMPLETE] = TLV_TYPE_TLV,
[V52_CTRL_IEI_BCC_INFO_TRANSFER_CAPABILITY] = TLV_TYPE_TLV,
/* variable-length: Protection */
[V52_CTRL_IEI_PP_SEQUENCE_NR] = TLV_TYPE_TLV,
[V52_CTRL_IEI_PP_PHYSICAL_C_CHAN_ID] = TLV_TYPE_TLV,
[V52_CTRL_IEI_PP_REJECTION_CAUSE] = TLV_TYPE_TLV,
[V52_CTRL_IEI_PP_PROTOCOL_ERROR_CAUSE] = TLV_TYPE_TLV,
};
extern const struct osmo_tlv_prot_def v51_ctrl_msg_tlv;
/* 14.4.2.5.4 Table 56/G.964 - Coding of Control Function Element */
enum v51_ctrl_func_el {
V51_CTRL_FE101_ACTIVATE_ACCESS = 0x01,
V51_CTRL_FE102_ACT_INIT_BY_USER = 0x02,
V51_CTRL_FE103_DS_ACTIVATED = 0x03,
V51_CTRL_FE104_ACCESS_ACTIVATED = 0x04,
V51_CTRL_FE105_DEACTIVATE_ACCESS = 0x05,
V51_CTRL_FE106_ACCESS_DEACTIVATED = 0x06,
V51_CTRL_FE201_UNBLOCK = 0x11,
V51_CTRL_FE203_BLOCK = 0x13,
V51_CTRL_FE205_BLOCK_REQ = 0x15,
V51_CTRL_FE206_PERFORMANCE_GRADING = 0x16,
V51_CTRL_FE207_D_CHANNEL_BLOCK = 0x17,
V51_CTRL_FE208_D_CHANNEL_UNBLOCK = 0x18,
};
extern const struct value_string v51_ctrl_func_el_str[];
/* 14.4.2.5.5 Table 57/G.964 - Coding of Control Function ID */
enum v51_ctrl_func_id {
V51_CTRL_ID_VERIFY_RE_PROVISIONING = 0x00,
V51_CTRL_ID_READY_FOR_RE_PROVISIONING = 0x01,
V51_CTRL_ID_NOT_READY_FOR_RE_PROVISIONING = 0x02,
V51_CTRL_ID_SWITCH_OVER_TO_NEW_VARIANT = 0x03,
V51_CTRL_ID_RE_PROVISIONING_STARTED = 0x04,
V51_CTRL_ID_CANNOT_RE_PROVISION = 0x05,
V51_CTRL_ID_REQUEST_VARIANT_AND_INTERFACE_ID = 0x06,
V51_CTRL_ID_VARIANT_AND_INTERFACE_ID = 0x07,
V51_CTRL_ID_BLOCKING_STARTED = 0x08,
V51_CTRL_ID_RESTART_REQUEST = 0x10,
V51_CTRL_ID_RESTART_COMPLETE = 0x11,
};
extern const struct value_string v51_ctrl_func_id_str[];
/* 13.4.7.9 Cause */
enum v51_cause_type {
V51_CTRL_CAUSE_T_RESP_TO_STATUS_ENQ = 0x00,
V51_CTRL_CAUSE_T_L3_ADDRESS_ERROR = 0x03,
V51_CTRL_CAUSE_T_MSG_TYPE_UNRECOGNIZED = 0x04,
V51_CTRL_CAUSE_T_OUT_OF_SEQUENCE_IE = 0x05,
V51_CTRL_CAUSE_T_REPEATED_OPT_IE = 0x06,
V51_CTRL_CAUSE_T_MAND_IE_MISSING = 0x07,
V51_CTRL_CAUSE_T_UNRECOGNIZED_IE = 0x08,
V51_CTRL_CAUSE_T_MAND_IE_CONTENT_ERROR = 0x09,
V51_CTRL_CAUSE_T_OPT_IE_CONTENT_ERROR = 0x0a,
V51_CTRL_CAUSE_T_MSG_INCOMP_PATH_STATE = 0x0b,
V51_CTRL_CAUSE_T_REPEATED_MAND_IE = 0x0c,
V51_CTRL_CAUSE_T_TOO_MANY_IE = 0x0d,
};
extern const struct value_string v51_cause_type_str[];
const struct osmo_tlv_prot_def v51_ctrl_msg_tlv;
/* 16.3.2.2 Table 22/G.965 */
enum v52_link_ctrl_func {
V52_LCP_FE_IDReq = 0,
V52_LCP_FE_IDAck = 1,
V52_LCP_FE_IDRel = 2,
V52_LCP_FE_IDRej = 3,
V52_LCP_FE_301_302_LINK_UNBLOCK = 4,
V52_LCP_FE_303_304_LINK_BLOCK = 5,
V52_LCP_FE_305_DEF_LINK_BLOCK_REQ = 6,
V52_LCP_FE_306_NON_DEF_LINK_BLOCK_REQ = 7,
};