Work on v5x_data.c and v5x_internal.h

This commit is contained in:
Andreas Eversberg 2022-12-26 14:46:50 +01:00
parent be536b9029
commit 11c87bdea7
2 changed files with 461 additions and 105 deletions

View File

@ -1,3 +1,4 @@
#include <errno.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/linuxlist.h>
@ -6,9 +7,14 @@
#include "v5x_internal.h"
#include "v5x_protocol.h"
#include "lapv5.h"
#include "layer1.h"
#include "v5x_l1_fsm.h"
#include "v5x_le_ctrl_fsm.h"
#include "v5x_le_port_fsm.h"
#include "v5x_le_pstn_fsm.h"
#include "v52_le_lcp_fsm.h"
#include "v52_le_bcc_fsm.h"
#include "v52_le_pp_fsm.h"
#include "v5x_le_management.h"
#include "logging.h"
@ -27,11 +33,25 @@ struct v5x_instance *v5x_instance_alloc(void *ctx)
return v5i;
}
static void v5x_ts_init(struct v5x_timeslot *v5ts, uint8_t nr, struct v5x_link *v5l, uint16_t l3_addr)
void v5x_instance_free(struct v5x_instance *v5i)
{
struct v5x_interface *v5if, *v5if2;
llist_for_each_entry_safe(v5if, v5if2, &v5i->interfaces, list)
v5x_interface_free(v5if);
talloc_free(v5i);
}
static void v5x_ts_init(struct v5x_timeslot *v5ts, uint8_t nr, struct v5x_link *v5l, bool b_channel)
{
/* Never use slot 16 as B-channel, because some drivers (mISDN) will not allow this. */
if (nr == 16)
b_channel = false;
v5ts->nr = nr;
v5ts->link = v5l;
v5ts->l3_address = l3_addr;
v5ts->b_channel = b_channel;
}
static void v5x_cchan_init(struct v5x_c_channel *v5cc, struct v5x_link *v5l, struct v5x_timeslot *v5ts, bool active)
@ -41,80 +61,242 @@ static void v5x_cchan_init(struct v5x_c_channel *v5cc, struct v5x_link *v5l, str
v5cc->active = active;
}
static void v5x_link_init(struct v5x_link *v5l, struct v5x_interface *v5if, uint8_t id)
struct v5x_link *v5x_link_create(struct v5x_interface *v5if, uint8_t id)
{
int count;
struct v5x_link *v5l;
unsigned int i;
int rc;
if (v5x_link_find_id(v5if, id)) {
LOGP(DV5, LOGL_ERROR, "Link %d already exists.\n", id);
return NULL;
}
count = v5x_link_count(v5if);
v5l = talloc_zero(v5if, struct v5x_link);
if (!v5l)
return NULL;
llist_add_tail(&v5l->list, &v5if->links);
count++;
v5l->id = id;
v5l->interface = v5if;
v5l->e1_line = -1;
for (i = 1; i < ARRAY_SIZE(v5l->ts); i++) {
v5x_ts_init(&v5l->ts[i], i, v5l, 0 /*l3_addr*/);
/* primary and secondary link will get TS 16 */
if (count <= 2) {
v5x_cchan_init(&v5l->c_channel[0], v5l, &v5l->ts[16], true);
}
/* primary c-channel must always be present */
v5x_cchan_init(&v5l->c_channel[0], v5l, &v5l->ts[16], true);
for (i = 1; i < ARRAY_SIZE(v5l->ts); i++) {
bool b_channel = true;
if (v5l->c_channel[0].ts && v5l->c_channel[0].ts->nr == i)
b_channel = false;
if (v5l->c_channel[1].ts && v5l->c_channel[1].ts->nr == i)
b_channel = false;
if (v5l->c_channel[2].ts && v5l->c_channel[2].ts->nr == i)
b_channel = false;
v5x_ts_init(&v5l->ts[i], i, v5l, b_channel);
}
v5l->l1 = v5x_l1_fsm_create(v5if, v5l, id);
if (!v5l->l1)
goto error;
if (v5if->dialect == V5X_DIALECT_V52) {
v5l->fi = v52_le_lcp_create(v5if, v5l, id);
if (!v5l->fi)
goto error;
v5l->ctrl = v5x_le_ctrl_create(V5X_CTRL_TYPE_LINK, v5if, v5l, id);
if (!v5l->ctrl)
goto error;
}
if (count == 1) {
v5if->primary_link = v5l;
v5if->cc_link = v5l;
}
if (count == 2) {
v5if->secondary_link = v5l;
/* add PP */
v5if->protection.pp = v52_le_pp_create(v5if);
if (!v5if->protection.pp)
goto error;
v5if->protection.li[0] = lapv5_instance_alloc(1, &ph_data_req_dl_prot, v5if->primary_link, v5x_dl_rcv,
v5if->primary_link, &lapd_profile_lapv5dl, "protection0");
if (!v5if->protection.li[0])
goto error;
v5if->protection.li[1] = lapv5_instance_alloc(1, &ph_data_req_dl_prot, v5if->secondary_link, v5x_dl_rcv,
v5if->secondary_link, &lapd_profile_lapv5dl, "protection1");
if (!v5if->protection.li[1])
goto error;
}
return v5l;
error:
rc = v5x_link_destroy(v5l);
OSMO_ASSERT(rc == 0);
return NULL;
}
struct v5x_interface *v5x_interface_alloc(struct v5x_instance *v5i, enum v5x_dialect dialect,
int (*ph_data_req_cb)(struct msgb *msg, void *cbdata))
int v5x_link_destroy(struct v5x_link *v5l)
{
struct v5x_interface *v5if = talloc_zero(v5i, struct v5x_interface);
//struct v5x_link *v5l;
struct v5x_c_channel *primary_c_chan;
struct v5x_interface *v5if = v5l->interface;
int count;
struct v5x_user_port *v5up;
int i;
count = v5x_link_count(v5l->interface);
if (count > 1 && v5l->interface->primary_link == v5l) {
LOGP(DV5, LOGL_ERROR, "Link %d is primary. Cannot remove it, while other links exist.\n", v5l->id);
return -EINVAL;
}
if (count > 2 && v5l->interface->secondary_link == v5l) {
LOGP(DV5, LOGL_ERROR, "Link %d is secondary (standby). Cannot remove it, while other (not primary) links exist.\n", v5l->id);
return -EINVAL;
}
/* detach user ports */
for (i = 1; i < (int)ARRAY_SIZE(v5l->ts); i++) {
if ((v5up = v5l->ts[i].v5up)) {
if (v5up->ts[0] == &v5l->ts[i]) {
v5up->ts[0] = NULL;
v5up->ts_activated[0] = 0;
}
if (v5up->ts[1] == &v5l->ts[i]) {
v5up->ts[1] = NULL;
v5up->ts_activated[1] = 0;
}
}
}
if (v5l->ctrl)
v5x_le_ctrl_destroy(v5l->ctrl);
if (v5l->fi)
v52_le_lcp_destroy(v5l->fi);
if (v5l->l1)
v5x_l1_fsm_destroy(v5l->l1);
if (v5l->interface->primary_link == v5l)
v5l->interface->primary_link = NULL;
if (v5l->interface->secondary_link == v5l)
v5l->interface->secondary_link = NULL;
if (v5l->interface->cc_link == v5l)
v5l->interface->cc_link = NULL;
llist_del(&v5l->list);
count--;
talloc_free(v5l);
/* if we have only one link left */
if (count == 1) {
/* remove PP */
if (v5if->protection.li[0]) {
lapv5_instance_free(v5if->protection.li[0]);
v5if->protection.li[0] = NULL;
}
if (v5if->protection.li[1]) {
lapv5_instance_free(v5if->protection.li[1]);
v5if->protection.li[1] = NULL;
}
if (v5if->protection.pp) {
v52_le_pp_destroy(v5if->protection.pp);
v5if->protection.pp = NULL;
}
}
return 0;
}
struct v5x_interface *v5x_interface_alloc(struct v5x_instance *v5i, enum v5x_dialect dialect)
{
struct v5x_interface *v5if;
struct v5x_link *v5l;
// struct v5x_c_channel *primary_c_chan;
v5if = talloc_zero(v5i, struct v5x_interface);
if (!v5if)
return NULL;
INIT_LLIST_HEAD(&v5if->links);
INIT_LLIST_HEAD(&v5if->user_ports);
INIT_LLIST_HEAD(&v5if->bcc_procs);
llist_add_tail(&v5if->list, &v5i->interfaces);
v5if->instance = v5i;
v5if->dialect = dialect;
/* primary link must alwasy be present */
v5x_link_init(&v5if->links[0], v5if, 0);
v5if->primary_link = &v5if->links[0];
primary_c_chan = &v5if->primary_link->c_channel[0];
v5if->control.ctrl = v5x_le_ctrl_create(v5if, NULL, 0);
if (!v5if->control.ctrl)
return NULL;
v5if->control.li = lapv5_instance_alloc(1, ph_data_req_cb, v5if, v5x_rcv, v5if,
&lapd_profile_lapv5dl, "control");
v5if->control.c_chan = primary_c_chan;
v5if->pstn.li = lapv5_instance_alloc(1, ph_data_req_cb, v5if, v5x_rcv, v5if,
&lapd_profile_lapv5dl, "pstn");
v5if->pstn.c_chan = primary_c_chan;
if (v5if->dialect == V5X_DIALECT_V52) {
v5if->lcp.li = lapv5_instance_alloc(1, ph_data_req_cb, v5if, v5x_rcv, v5if,
&lapd_profile_lapv5dl, "lcp");
v5if->lcp.c_chan = primary_c_chan;
v5if->bcc.li = lapv5_instance_alloc(1, ph_data_req_cb, v5if, v5x_rcv, v5if,
&lapd_profile_lapv5dl, "bcc");
v5if->bcc.c_chan = primary_c_chan;
v5if->protection[0].li = lapv5_instance_alloc(1, ph_data_req_cb, v5if, v5x_rcv, v5if,
&lapd_profile_lapv5dl, "protection0");
v5if->protection[0].c_chan = primary_c_chan;
//protection[1] ?
if (v5if->dialect == V5X_DIALECT_V51) {
v5l = v5x_link_create(v5if, 0);
if (!v5l)
goto error;
}
INIT_LLIST_HEAD(&v5if->user_ports);
v5if->control.ctrl = v5x_le_ctrl_create(V5X_CTRL_TYPE_COMMON, v5if, v5if, 0);
if (!v5if->control.ctrl)
goto error;
v5if->control.li = lapv5_instance_alloc(1, &ph_data_req_dl_cc, v5if, v5x_dl_rcv, v5if,
&lapd_profile_lapv5dl, "control");
if (!v5if->control.li)
goto error;
llist_add_tail(&v5if->list, &v5i->interfaces);
v5if->pstn.li = lapv5_instance_alloc(1, &ph_data_req_dl_cc, v5if, v5x_dl_rcv, v5if,
&lapd_profile_lapv5dl, "pstn");
if (!v5if->pstn.li)
goto error;
if (v5if->dialect == V5X_DIALECT_V52) {
v5if->lcp.li = lapv5_instance_alloc(1, &ph_data_req_dl_cc, v5if, v5x_dl_rcv, v5if,
&lapd_profile_lapv5dl, "lcp");
if (!v5if->lcp.li)
goto error;
v5if->bcc.li = lapv5_instance_alloc(1, &ph_data_req_dl_cc, v5if, v5x_dl_rcv, v5if,
&lapd_profile_lapv5dl, "bcc");
if (!v5if->bcc.li)
goto error;
}
return v5if;
error:
v5x_interface_free(v5if);
return NULL;
}
void v5x_interface_free(struct v5x_interface *v5if) {
struct v5x_user_port *v5up, *v5up2;
struct v52_bcc_proc *bcc, *bcc2;
struct v5x_link *v5l;
int rc;
if (!v5if)
return;
llist_for_each_entry_safe(v5up, v5up2, &v5if->user_ports, list)
v5x_user_port_destroy(v5up);
llist_for_each_entry_safe(bcc, bcc2, &v5if->bcc_procs, list)
v52_le_bcc_destroy(bcc);
/* delete reversed safely */
while (!llist_empty(&v5if->links)) {
v5l = llist_last_entry(&v5if->links, struct v5x_link, list);
rc = v5x_link_destroy(v5l);
OSMO_ASSERT(rc == 0);
}
if (v5if->control.ctrl)
v5x_le_ctrl_destroy(v5if->control.ctrl);
if (v5if->control.li)
lapv5_instance_free(v5if->control.li);
@ -127,11 +309,6 @@ void v5x_interface_free(struct v5x_interface *v5if) {
if (v5if->bcc.li)
lapv5_instance_free(v5if->bcc.li);
if (v5if->protection[0].li)
lapv5_instance_free(v5if->protection[0].li);
if (v5if->protection[1].li)
lapv5_instance_free(v5if->protection[1].li);
llist_del(&v5if->list);
talloc_free(v5if);
@ -143,15 +320,34 @@ struct v5x_user_port *v5x_user_port_create(struct v5x_interface *v5if, uint16_t
struct v5x_user_port *v5up;
int rc;
if ((v5up = v5if->links[0].ts[ts1].v5up)) {
LOGP(DV5, LOGL_ERROR, "Time slot %d is already assigned to another user port\n", ts1);
if (v5x_user_port_find(v5if, nr, (type == V5X_USER_TYPE_ISDN))) {
LOGP(DV5, LOGL_ERROR, "User port %d already exists.\n", nr);
return NULL;
}
if (type == V5X_USER_TYPE_ISDN) {
if ((v5up = v5if->links[0].ts[ts2].v5up)) {
LOGP(DV5, LOGL_ERROR, "Time slot %d is already assigned to another user port\n", ts2);
if (v5if->dialect == V5X_DIALECT_V51) {
struct v5x_link *v5l;
v5l = llist_first_entry(&v5if->links, struct v5x_link, list);
if (!v5l->ts[ts1].b_channel) {
LOGP(DV5, LOGL_ERROR, "Time slot %d is not a B-channel, select a different one.\n", ts1);
return NULL;
}
if ((v5up = v5l->ts[ts1].v5up)) {
LOGP(DV5, LOGL_ERROR, "Time slot %d is already assigned to another user port.\n", ts1);
return NULL;
}
if (type == V5X_USER_TYPE_ISDN) {
if (!v5l->ts[ts2].b_channel) {
LOGP(DV5, LOGL_ERROR, "Time slot %d is not a B-channel, select a different one.\n",
ts2);
return NULL;
}
if ((v5up = v5l->ts[ts2].v5up)) {
LOGP(DV5, LOGL_ERROR, "Time slot %d is already assigned to another user port.\n", ts2);
return NULL;
}
}
}
v5up = talloc_zero(v5if, struct v5x_user_port);
@ -160,27 +356,29 @@ struct v5x_user_port *v5x_user_port_create(struct v5x_interface *v5if, uint16_t
llist_add_tail(&v5up->list, &v5if->user_ports);
v5up->inst = v5if;
v5up->interface = v5if;
v5up->nr = nr;
v5up->type = type;
/* assign ports */
if (v5if->dialect == V5X_DIALECT_V51) {
struct v5x_link *v5l;
v5l = llist_first_entry(&v5if->links, struct v5x_link, list);
OSMO_ASSERT(ts1 >= 1 && ts1 <= 31);
v5if->links[0].ts[ts1].v5up = v5up;
v5up->ts_nr[0] = ts1;
v5l->ts[ts1].v5up = v5up;
v5up->ts[0] = &v5l->ts[ts1];
if (type == V5X_USER_TYPE_ISDN) {
OSMO_ASSERT(ts2 >= 1 && ts2 <= 31);
v5if->links[0].ts[ts2].v5up = v5up;
v5up->ts_nr[1] = ts2;
v5l->ts[ts2].v5up = v5up;
v5up->ts[1] = &v5l->ts[ts2];
}
}
v5up->ctrl = v5x_le_ctrl_create(NULL, v5up, nr);
v5up->ctrl = v5x_le_ctrl_create(V5X_CTRL_TYPE_PORT, v5up, v5up, nr);
if (!v5up->ctrl) {
LOGP(DV5, LOGL_ERROR, "Failed to create control protocol\n");
v5x_user_port_destroy(v5up);
return NULL;
goto error;
}
switch (type) {
@ -191,8 +389,7 @@ struct v5x_user_port *v5x_user_port_create(struct v5x_interface *v5if, uint16_t
v5up->port_fi = v5x_le_port_isdn_create(v5up, nr);
if (!v5up->port_fi) {
LOGP(DV5, LOGL_ERROR, "Failed to create port control\n");
v5x_user_port_destroy(v5up);
return NULL;
goto error;
}
break;
case V5X_USER_TYPE_PSTN:
@ -202,14 +399,12 @@ struct v5x_user_port *v5x_user_port_create(struct v5x_interface *v5if, uint16_t
v5up->port_fi = v5x_le_port_pstn_create(v5up, nr);
if (!v5up->port_fi) {
LOGP(DV5, LOGL_ERROR, "Failed to create port control\n");
v5x_user_port_destroy(v5up);
return NULL;
goto error;
}
v5up->pstn.proto = v5x_le_pstn_create(v5up, nr);
if (!v5up->pstn.proto) {
LOGP(DV5, LOGL_ERROR, "Failed to create PSTN protocol\n");
v5x_user_port_destroy(v5up);
return NULL;
goto error;
}
/* bring port into service */
v5x_le_pstn_mdu_snd(v5up, MDU_CTRL_port_blocked);
@ -219,27 +414,36 @@ struct v5x_user_port *v5x_user_port_create(struct v5x_interface *v5if, uint16_t
rc = ph_socket_init(&v5up->ph_socket, ph_socket_rx_cb, v5up, v5up->ifname, 1);
if (rc < 0) {
LOGP(DV5, LOGL_ERROR, "Failed to create PH-socket\n");
v5x_user_port_destroy(v5up);
return NULL;
goto error;
}
/* start ctrl FSM for this port */
if (v5if->control.established)
v5x_le_ctrl_start(v5up->ctrl);
return v5up;
error:
v5x_user_port_destroy(v5up);
return NULL;
}
void v5x_user_port_destroy(struct v5x_user_port *v5up)
{
struct v5x_interface *v5if = v5up->inst;
LOGP(DV5, LOGL_NOTICE, "Destroying V5 user port with L3 addr %d\n", v5up->nr);
/* close fitst, because it sends messages */
/* close first, because it sends messages */
ph_socket_exit(&v5up->ph_socket);
/* unassign ports */
if (v5up->ts_nr[0])
v5if->links[v5up->link_nr[0]].ts[v5up->ts_nr[0]].v5up = NULL;
if (v5up->ts_nr[1])
v5if->links[v5up->link_nr[1]].ts[v5up->ts_nr[1]].v5up = NULL;
if (v5up->ts[0]) {
v5up->ts[0]->v5up = NULL;
v5up->ts[0] = NULL;
}
if (v5up->ts[1]) {
v5up->ts[1]->v5up = NULL;
v5up->ts[1] = NULL;
}
if (v5up->ctrl)
v5x_le_ctrl_destroy(v5up->ctrl);
@ -265,3 +469,35 @@ struct v5x_user_port *v5x_user_port_find(struct v5x_interface *v5if, uint16_t nr
}
return NULL;
}
int v5x_link_count(struct v5x_interface *v5if)
{
struct v5x_link *v5l;
int count = 0;
llist_for_each_entry(v5l, &v5if->links, list)
count++;
return count;
}
struct v5x_link *v5x_link_find_id(struct v5x_interface *v5if, uint8_t id)
{
struct v5x_link *v5l;
llist_for_each_entry(v5l, &v5if->links, list) {
if (v5l->id == id)
return v5l;
}
return NULL;
}
struct v5x_link *v5x_link_find_e1_line(struct v5x_interface *v5if, uint8_t e1_line)
{
struct v5x_link *v5l;
llist_for_each_entry(v5l, &v5if->links, list) {
if (v5l->e1_line == e1_line)
return v5l;
}
return NULL;
}

View File

@ -48,6 +48,21 @@ enum v5x_mph_prim {
MPH_GI, /* Grading Information */
MPH_DB, /* Block D-Channel from user port */
MPH_DU, /* Unblock D-Channel from user port */
MPH_ID, /* Identity Sa7=0 signal request (TX) */
MPH_NOR, /* Normal Sa7=1 signal request (TX) */
MPH_IDR, /* Request Sa7 signal (RX) */
MPH_IDI, /* Indicate Sa7=0 signal (RX) */
MPH_EIg, /* Indicate Sa7=1 singal (RX) */
MPH_EIa, /* Indicate LOS */
MPH_EIb, /* RAI */
MPH_EIc, /* AIS */
MPH_EId, /* Internal failure */
MPH_EIe, /* CRC received */
MPH_EIf, /* E-bit received */
MPH_stop, /* See L1 FSM */
MPH_proceed, /* See L1 FSM */
MPH_EIbr, /* See L1 FSM */
MPH_EIdr, /* See L1 FSM */
};
/* Table 3/G.964 */
@ -64,14 +79,56 @@ enum v5x_fe_prim {
FE_disconnect_compl_ind = 0x33,
};
/* Table 3/G.964 */
enum v5x_mgmt_prim {
/* Table 3/G.964 */
MDU_CTRL_port_blocked,
MDU_CTRL_port_unblocked,
MDU_CTRL_port_restart_req,
MDU_CTRL_port_restart_ack,
MDU_CTRL_port_restart_compl,
MDU_error_ind,
/* Table 14/G.965 */
MDU_AI,
MDU_DI,
MDU_LAI,
MDU_IDReq,
MDU_IDAck,
MDU_IDRel,
MDU_IDRej,
MDU_EIg,
MDU_LUBR,
MDU_LUBI,
MDU_LBI,
MDU_LBR,
MDU_LBRN,
/* Table 26/G.965 */
MDU_BCC_allocation_req,
MDU_BCC_allocation_conf,
MDU_BCC_allocation_reject_ind,
MDU_BCC_allocation_error_ind,
MDU_BCC_deallocation_req,
MDU_BCC_deallocation_conf,
MDU_BCC_deallocation_reject_ind,
MDU_BCC_deallocation_error_ind,
MDU_BCC_audit_req,
MDU_BCC_audit_conf,
MDU_BCC_audit_error_ind,
MDU_BCC_AN_fault_ind,
MDU_BCC_protocol_error_ind,
/* Table 50/G.965 */
MDU_Protection_switch_over_com,
MDU_Protection_OS_switch_over_com,
MDU_Protection_switch_over_ack,
MDU_Protection_switch_over_rej,
MDU_Protection_switch_over_req,
MDU_Protection_switch_over_reject_ind,
MDU_Protection_switch_over_error_ind,
MDU_Protection_reset_SN_ind,
MDU_Protection_reset_SN_com,
MDU_Protection_reset_SN_req,
MDU_Protection_reset_SN_ack,
MDU_Protection_reset_SN_error_ind,
MDU_Protection_protocol_error_ind,
};
struct osmo_fsm_inst;
@ -86,11 +143,19 @@ enum v5x_user_type {
V5X_USER_TYPE_PSTN = 2,
};
enum v5x_ctrl_type {
V5X_CTRL_TYPE_COMMON = 1,
V5X_CTRL_TYPE_PORT = 2,
V5X_CTRL_TYPE_LINK = 3,
};
/* forward-declarations */
struct v5x_interface;
struct v5x_instance;
struct v5x_user_port;
struct v5x_link;
struct v5x_l1_proto;
struct v52_pp_proto;
/* A C-channel is a 64k timeslot used for signalling */
struct v5x_c_channel {
@ -104,16 +169,20 @@ struct v5x_timeslot {
uint8_t nr;
struct v5x_link *link; /* back-pointer */
struct v5x_user_port *v5up; /* user port that this TS is assigned to */
uint16_t l3_address;
bool b_channel; /* channel can be used as b-channel */
};
/* one physical E1 interface used within a V5.2 interface */
struct v5x_link {
struct llist_head list; /* interface.links */
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 */
struct v5x_l1_proto *l1; /* Layer 1 Link FSM protocol */
struct v5x_ctrl_proto *ctrl; /* Link control protocol instance */
struct osmo_fsm_inst *fi; /* Link Control FSM instance */
int e1_line; /* E1 line to use or -1 if not */
};
/* one V5.x interface between AN (Access Network) and LE (Local Exchange) */
@ -121,43 +190,54 @@ struct v5x_interface {
struct llist_head list; /* instance.interfaces */
struct v5x_instance *instance; /* back-pointer */
enum v5x_dialect dialect;
uint32_t id; /* interface id */
uint8_t variant; /* provitioning variant */
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];
uint32_t id_local, id_remote; /* interface id */
uint8_t variant_local, variant_remote; /* provitioning variant */
bool id_remote_valid;
bool variant_remote_valid;
bool use_capability; /* use bearer transfer capability */
uint8_t capability; /* bearer transfer capability */
struct {
struct v5x_ctrl_proto *ctrl; /* common control protocol instance */
struct lapv5_instance *li; /* Control data link */
bool established; /* track if link is up or down */
struct v5x_c_channel *c_chan; /* pointer to active C-channel */
} control;
struct {
struct lapv5_instance *li; /* Link control data link */
struct v5x_c_channel *c_chan; /* pointer to active C-channel */
struct osmo_fsm_inst *fi; /* Link Control FSM instance */
bool established; /* track if link is up or down */
} lcp;
struct {
struct lapv5_instance *li; /* PSTN data link */
struct v5x_c_channel *c_chan; /* pointer to active C-channel */
bool established; /* track if link is up or down */
} pstn;
struct {
struct lapv5_instance *li; /* BCC data link */
struct v5x_c_channel *c_chan; /* pointer to active C-channel */
bool established; /* track if link is up or down */
} bcc;
struct {
struct lapv5_instance *li; /* Protection data link 1 + 2 */
struct v5x_c_channel *c_chan; /* pointer to active C-channel */
} protection[2];
uint16_t cc_id; /* CC-ID of protected C-channel */
struct v52_pp_proto *pp; /* protection protocol instance */
struct lapv5_instance *li[2]; /* Protection data link 1 + 2 */
bool established[2]; /* track if link is up or down */
} protection;
struct llist_head links; /* list of v5x_link */
struct v5x_link *primary_link; /* primary link */
struct v5x_link *secondary_link; /* standby link */
struct v5x_link *cc_link; /* currently used link for signaling (primary on startup) */
struct llist_head user_ports; /* list of v5x_user_port */
struct llist_head bcc_procs; /* list of v52_bcc_procs */
};
struct v5x_l1_proto {
struct v5x_link *v5l; /* back pointer */
struct osmo_fsm_inst *fi; /* L1 FSM */
int los, rai, ais, sa7_zero; /* RX signal states */
};
struct v5x_ctrl_proto {
struct v5x_user_port *v5up; /* back pointer, if port control is used */
struct v5x_interface *v5if; /* back pointer, if common control is used */
enum v5x_ctrl_type type; /* type of control protocol: common/port/link */
void *priv; /* back pointer to the user */
struct osmo_fsm_inst *fi; /* control FSM */
struct llist_head tx_queue; /* list of message to be transmitted */
struct msgb *tx_msg; /* copy of unacked message, for second try */
@ -177,7 +257,7 @@ struct v5x_pstn_proto {
/* one user-facing port (subscriber line) */
struct v5x_user_port {
struct llist_head list; /* part of v5x_instance.ports */
struct v5x_interface *inst; /* back-pointer to instance we're part of */
struct v5x_interface *interface; /* back-pointer to instance we're part of */
uint16_t nr; /* port-number in decoded form (0..32767) */
char ifname[64]; /* name of interface, also used for PH-socket */
@ -185,8 +265,7 @@ struct v5x_user_port {
struct v5x_ctrl_proto *ctrl; /* port control protocol instance */
struct osmo_fsm_inst *port_fi; /* port FSM */
uint8_t ts_nr[2]; /* time slots used (one for PSTN, two for ISDN) */
uint8_t link_nr[2]; /* link used */
struct v5x_timeslot *ts[2]; /* two time slots */
uint8_t ts_activated[2]; /* set if data stream is active */
struct {
} isdn;
@ -194,22 +273,63 @@ struct v5x_user_port {
struct v5x_pstn_proto *proto;
} pstn;
ph_socket_t ph_socket; /* unix socket to connect port to */
bool blocking_enabled; /* we may only send blocking updates, if true */
bool le_unblocked; /* if port is not blocked by LE */
bool an_unblocked; /* if port is not blocked by AN */
};
/* BCC process */
struct v52_bcc_proc {
struct llist_head list; /* part of v5x_instance.ports */
struct v5x_interface *interface; /* back-pointer to instance we're part of */
struct osmo_fsm_inst *fi; /* BCC FSM */
uint16_t ref; /* reference of this process */
uint8_t source_id; /* reference source */
int timer; /* timer used */
int expired; /* number of timeouts */
uint16_t user_port_id; /* port ID */
bool is_isdn; /* user port type */
uint8_t link_id; /* link ID */
uint8_t ts; /* TS nr */
uint8_t override; /* override assigned channel */
uint8_t isdn_slot; /* channel on ISDN interface */
uint8_t *isdn_multislot; /* multiple slot assignment */
bool use_capability; /* use bearer transfer capability */
uint8_t capability; /* bearer transfer capability */
};
struct v52_pp_mgmt_info {
uint8_t link_id;
uint8_t ts;
uint8_t cause;
};
/* PP process */
struct v52_pp_proto {
struct v5x_interface *interface; /* back pointer, if port control is used */
struct osmo_fsm_inst *fi; /* PP FSM */
uint8_t vp_s, vp_r; /* sequence numbers */
bool vp_r_set; /* if we ever received a sequence number */
struct v52_pp_mgmt_info info; /* keep management info for resend after timeout */
int timeout_event; /* event when timer times out */
int timeout_count; /* how many times the timer was started/timed out */
};
struct v5x_instance {
struct llist_head list; /* part of global list of instances */
struct llist_head interfaces; /* v5x_interface.list */
};
struct v5x_instance *v5x_instance_alloc(void *ctx);
struct v5x_interface *v5x_interface_alloc(struct v5x_instance *v5i, enum v5x_dialect dialect,
int (*ph_data_req_cb)(struct msgb *msg, void *cbdata));
void v5x_instance_free(struct v5x_instance *v5i);
struct v5x_interface *v5x_interface_alloc(struct v5x_instance *v5i, enum v5x_dialect dialect);
struct v5x_user_port *v5x_user_port_create(struct v5x_interface *v5if, uint16_t nr, enum v5x_user_type,
uint8_t ts1, uint8_t ts2);
void v5x_interface_free(struct v5x_interface *v5if);
struct v5x_user_port *v5x_user_port_find(struct v5x_interface *v5if, uint16_t nr, bool is_isdn);
void v5x_user_port_destroy(struct v5x_user_port *v5up);
//FIXME: move this
void ph_socket_rx_cb(ph_socket_t *s, int channel, uint8_t prim, uint8_t *data, int length);
struct v5x_link *v5x_link_create(struct v5x_interface *v5if, uint8_t id);
int v5x_link_destroy(struct v5x_link *v5l);
int v5x_link_count(struct v5x_interface *v5if);
struct v5x_link *v5x_link_find_id(struct v5x_interface *v5if, uint8_t id);
struct v5x_link *v5x_link_find_e1_line(struct v5x_interface *v5if, uint8_t id);