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 5addae556a
commit 3ebe29e28f
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/talloc.h>
#include <osmocom/core/linuxlist.h> #include <osmocom/core/linuxlist.h>
@ -6,9 +7,14 @@
#include "v5x_internal.h" #include "v5x_internal.h"
#include "v5x_protocol.h" #include "v5x_protocol.h"
#include "lapv5.h" #include "lapv5.h"
#include "layer1.h"
#include "v5x_l1_fsm.h"
#include "v5x_le_ctrl_fsm.h" #include "v5x_le_ctrl_fsm.h"
#include "v5x_le_port_fsm.h" #include "v5x_le_port_fsm.h"
#include "v5x_le_pstn_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 "v5x_le_management.h"
#include "logging.h" #include "logging.h"
@ -27,11 +33,25 @@ struct v5x_instance *v5x_instance_alloc(void *ctx)
return v5i; 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->nr = nr;
v5ts->link = v5l; 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) 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; 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; 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->id = id;
v5l->interface = v5if; v5l->interface = v5if;
v5l->e1_line = -1;
for (i = 1; i < ARRAY_SIZE(v5l->ts); i++) { /* primary and secondary link will get TS 16 */
v5x_ts_init(&v5l->ts[i], i, v5l, 0 /*l3_addr*/); if (count <= 2) {
v5x_cchan_init(&v5l->c_channel[0], v5l, &v5l->ts[16], true);
} }
/* primary c-channel must always be present */ for (i = 1; i < ARRAY_SIZE(v5l->ts); i++) {
v5x_cchan_init(&v5l->c_channel[0], v5l, &v5l->ts[16], true); 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 v5x_link_destroy(struct v5x_link *v5l)
int (*ph_data_req_cb)(struct msgb *msg, void *cbdata))
{ {
struct v5x_interface *v5if = talloc_zero(v5i, struct v5x_interface); struct v5x_interface *v5if = v5l->interface;
//struct v5x_link *v5l; int count;
struct v5x_c_channel *primary_c_chan; 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->instance = v5i;
v5if->dialect = dialect; v5if->dialect = dialect;
/* primary link must alwasy be present */ if (v5if->dialect == V5X_DIALECT_V51) {
v5x_link_init(&v5if->links[0], v5if, 0); v5l = v5x_link_create(v5if, 0);
v5if->primary_link = &v5if->links[0]; if (!v5l)
goto error;
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] ?
} }
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; return v5if;
error:
v5x_interface_free(v5if);
return NULL;
} }
void v5x_interface_free(struct v5x_interface *v5if) { void v5x_interface_free(struct v5x_interface *v5if) {
struct v5x_user_port *v5up, *v5up2; 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) llist_for_each_entry_safe(v5up, v5up2, &v5if->user_ports, list)
v5x_user_port_destroy(v5up); 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) if (v5if->control.ctrl)
v5x_le_ctrl_destroy(v5if->control.ctrl); v5x_le_ctrl_destroy(v5if->control.ctrl);
if (v5if->control.li) if (v5if->control.li)
lapv5_instance_free(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) if (v5if->bcc.li)
lapv5_instance_free(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); llist_del(&v5if->list);
talloc_free(v5if); 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; struct v5x_user_port *v5up;
int rc; int rc;
if ((v5up = v5if->links[0].ts[ts1].v5up)) { if (v5x_user_port_find(v5if, nr, (type == V5X_USER_TYPE_ISDN))) {
LOGP(DV5, LOGL_ERROR, "Time slot %d is already assigned to another user port\n", ts1); LOGP(DV5, LOGL_ERROR, "User port %d already exists.\n", nr);
return NULL; return NULL;
} }
if (type == V5X_USER_TYPE_ISDN) {
if ((v5up = v5if->links[0].ts[ts2].v5up)) { if (v5if->dialect == V5X_DIALECT_V51) {
LOGP(DV5, LOGL_ERROR, "Time slot %d is already assigned to another user port\n", ts2); 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; 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); 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); llist_add_tail(&v5up->list, &v5if->user_ports);
v5up->inst = v5if; v5up->interface = v5if;
v5up->nr = nr; v5up->nr = nr;
v5up->type = type; v5up->type = type;
/* assign ports */ /* assign ports */
if (v5if->dialect == V5X_DIALECT_V51) { 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); OSMO_ASSERT(ts1 >= 1 && ts1 <= 31);
v5if->links[0].ts[ts1].v5up = v5up; v5l->ts[ts1].v5up = v5up;
v5up->ts_nr[0] = ts1; v5up->ts[0] = &v5l->ts[ts1];
if (type == V5X_USER_TYPE_ISDN) { if (type == V5X_USER_TYPE_ISDN) {
OSMO_ASSERT(ts2 >= 1 && ts2 <= 31); OSMO_ASSERT(ts2 >= 1 && ts2 <= 31);
v5if->links[0].ts[ts2].v5up = v5up; v5l->ts[ts2].v5up = v5up;
v5up->ts_nr[1] = ts2; 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) { if (!v5up->ctrl) {
LOGP(DV5, LOGL_ERROR, "Failed to create control protocol\n"); LOGP(DV5, LOGL_ERROR, "Failed to create control protocol\n");
v5x_user_port_destroy(v5up); goto error;
return NULL;
} }
switch (type) { 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); v5up->port_fi = v5x_le_port_isdn_create(v5up, nr);
if (!v5up->port_fi) { if (!v5up->port_fi) {
LOGP(DV5, LOGL_ERROR, "Failed to create port control\n"); LOGP(DV5, LOGL_ERROR, "Failed to create port control\n");
v5x_user_port_destroy(v5up); goto error;
return NULL;
} }
break; break;
case V5X_USER_TYPE_PSTN: 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); v5up->port_fi = v5x_le_port_pstn_create(v5up, nr);
if (!v5up->port_fi) { if (!v5up->port_fi) {
LOGP(DV5, LOGL_ERROR, "Failed to create port control\n"); LOGP(DV5, LOGL_ERROR, "Failed to create port control\n");
v5x_user_port_destroy(v5up); goto error;
return NULL;
} }
v5up->pstn.proto = v5x_le_pstn_create(v5up, nr); v5up->pstn.proto = v5x_le_pstn_create(v5up, nr);
if (!v5up->pstn.proto) { if (!v5up->pstn.proto) {
LOGP(DV5, LOGL_ERROR, "Failed to create PSTN protocol\n"); LOGP(DV5, LOGL_ERROR, "Failed to create PSTN protocol\n");
v5x_user_port_destroy(v5up); goto error;
return NULL;
} }
/* bring port into service */ /* bring port into service */
v5x_le_pstn_mdu_snd(v5up, MDU_CTRL_port_blocked); 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); rc = ph_socket_init(&v5up->ph_socket, ph_socket_rx_cb, v5up, v5up->ifname, 1);
if (rc < 0) { if (rc < 0) {
LOGP(DV5, LOGL_ERROR, "Failed to create PH-socket\n"); LOGP(DV5, LOGL_ERROR, "Failed to create PH-socket\n");
v5x_user_port_destroy(v5up); goto error;
return NULL;
} }
/* start ctrl FSM for this port */
if (v5if->control.established)
v5x_le_ctrl_start(v5up->ctrl);
return v5up; return v5up;
error:
v5x_user_port_destroy(v5up);
return NULL;
} }
void v5x_user_port_destroy(struct v5x_user_port *v5up) 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); 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); ph_socket_exit(&v5up->ph_socket);
/* unassign ports */ /* unassign ports */
if (v5up->ts_nr[0]) if (v5up->ts[0]) {
v5if->links[v5up->link_nr[0]].ts[v5up->ts_nr[0]].v5up = NULL; v5up->ts[0]->v5up = NULL;
if (v5up->ts_nr[1]) v5up->ts[0] = NULL;
v5if->links[v5up->link_nr[1]].ts[v5up->ts_nr[1]].v5up = NULL; }
if (v5up->ts[1]) {
v5up->ts[1]->v5up = NULL;
v5up->ts[1] = NULL;
}
if (v5up->ctrl) if (v5up->ctrl)
v5x_le_ctrl_destroy(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; 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_GI, /* Grading Information */
MPH_DB, /* Block D-Channel from user port */ MPH_DB, /* Block D-Channel from user port */
MPH_DU, /* Unblock 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 */ /* Table 3/G.964 */
@ -64,14 +79,56 @@ enum v5x_fe_prim {
FE_disconnect_compl_ind = 0x33, FE_disconnect_compl_ind = 0x33,
}; };
/* Table 3/G.964 */
enum v5x_mgmt_prim { enum v5x_mgmt_prim {
/* Table 3/G.964 */
MDU_CTRL_port_blocked, MDU_CTRL_port_blocked,
MDU_CTRL_port_unblocked, MDU_CTRL_port_unblocked,
MDU_CTRL_port_restart_req, MDU_CTRL_port_restart_req,
MDU_CTRL_port_restart_ack, MDU_CTRL_port_restart_ack,
MDU_CTRL_port_restart_compl, MDU_CTRL_port_restart_compl,
MDU_error_ind, 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; struct osmo_fsm_inst;
@ -86,11 +143,19 @@ enum v5x_user_type {
V5X_USER_TYPE_PSTN = 2, 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 */ /* forward-declarations */
struct v5x_interface; struct v5x_interface;
struct v5x_instance; struct v5x_instance;
struct v5x_user_port; struct v5x_user_port;
struct v5x_link; struct v5x_link;
struct v5x_l1_proto;
struct v52_pp_proto;
/* A C-channel is a 64k timeslot used for signalling */ /* A C-channel is a 64k timeslot used for signalling */
struct v5x_c_channel { struct v5x_c_channel {
@ -104,16 +169,20 @@ struct v5x_timeslot {
uint8_t nr; uint8_t nr;
struct v5x_link *link; /* back-pointer */ struct v5x_link *link; /* back-pointer */
struct v5x_user_port *v5up; /* user port that this TS is assigned to */ 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 */ /* one physical E1 interface used within a V5.2 interface */
struct v5x_link { struct v5x_link {
struct llist_head list; /* interface.links */
uint8_t id; uint8_t id;
struct v5x_interface *interface; /* back-pointer */ struct v5x_interface *interface; /* back-pointer */
struct v5x_timeslot ts[32]; /* 32 E1 slots; 0 not available */ 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 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) */ /* 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 llist_head list; /* instance.interfaces */
struct v5x_instance *instance; /* back-pointer */ struct v5x_instance *instance; /* back-pointer */
enum v5x_dialect dialect; enum v5x_dialect dialect;
uint32_t id; /* interface id */ uint32_t id_local, id_remote; /* interface id */
uint8_t variant; /* provitioning variant */ uint8_t variant_local, variant_remote; /* provitioning variant */
struct v5x_link *primary_link; /* one of the links below */ bool id_remote_valid;
struct v5x_link *secondary_link; /* one of the links below */ bool variant_remote_valid;
/* 1..16 links in one interface */ bool use_capability; /* use bearer transfer capability */
struct v5x_link links[16]; uint8_t capability; /* bearer transfer capability */
struct { struct {
struct v5x_ctrl_proto *ctrl; /* common control protocol instance */ struct v5x_ctrl_proto *ctrl; /* common control protocol instance */
struct lapv5_instance *li; /* Control data link */ struct lapv5_instance *li; /* Control data link */
bool established; /* track if link is up or down */ bool established; /* track if link is up or down */
struct v5x_c_channel *c_chan; /* pointer to active C-channel */
} control; } control;
struct { struct {
struct lapv5_instance *li; /* Link control data link */ struct lapv5_instance *li; /* Link control data link */
struct v5x_c_channel *c_chan; /* pointer to active C-channel */ bool established; /* track if link is up or down */
struct osmo_fsm_inst *fi; /* Link Control FSM instance */
} lcp; } lcp;
struct { struct {
struct lapv5_instance *li; /* PSTN data link */ 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; } pstn;
struct { struct {
struct lapv5_instance *li; /* BCC data link */ 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; } bcc;
struct { struct {
struct lapv5_instance *li; /* Protection data link 1 + 2 */ uint16_t cc_id; /* CC-ID of protected C-channel */
struct v5x_c_channel *c_chan; /* pointer to active C-channel */ struct v52_pp_proto *pp; /* protection protocol instance */
} protection[2]; 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 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_ctrl_proto {
struct v5x_user_port *v5up; /* back pointer, if port control is used */ enum v5x_ctrl_type type; /* type of control protocol: common/port/link */
struct v5x_interface *v5if; /* back pointer, if common control is used */ void *priv; /* back pointer to the user */
struct osmo_fsm_inst *fi; /* control FSM */ struct osmo_fsm_inst *fi; /* control FSM */
struct llist_head tx_queue; /* list of message to be transmitted */ struct llist_head tx_queue; /* list of message to be transmitted */
struct msgb *tx_msg; /* copy of unacked message, for second try */ 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) */ /* one user-facing port (subscriber line) */
struct v5x_user_port { struct v5x_user_port {
struct llist_head list; /* part of v5x_instance.ports */ 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) */ uint16_t nr; /* port-number in decoded form (0..32767) */
char ifname[64]; /* name of interface, also used for PH-socket */ 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 v5x_ctrl_proto *ctrl; /* port control protocol instance */
struct osmo_fsm_inst *port_fi; /* port FSM */ struct osmo_fsm_inst *port_fi; /* port FSM */
uint8_t ts_nr[2]; /* time slots used (one for PSTN, two for ISDN) */ struct v5x_timeslot *ts[2]; /* two time slots */
uint8_t link_nr[2]; /* link used */
uint8_t ts_activated[2]; /* set if data stream is active */ uint8_t ts_activated[2]; /* set if data stream is active */
struct { struct {
} isdn; } isdn;
@ -194,22 +273,63 @@ struct v5x_user_port {
struct v5x_pstn_proto *proto; struct v5x_pstn_proto *proto;
} pstn; } pstn;
ph_socket_t ph_socket; /* unix socket to connect port to */ 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 le_unblocked; /* if port is not blocked by LE */
bool an_unblocked; /* if port is not blocked by AN */ 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 v5x_instance {
struct llist_head list; /* part of global list of instances */ struct llist_head list; /* part of global list of instances */
struct llist_head interfaces; /* v5x_interface.list */ struct llist_head interfaces; /* v5x_interface.list */
}; };
struct v5x_instance *v5x_instance_alloc(void *ctx); struct v5x_instance *v5x_instance_alloc(void *ctx);
struct v5x_interface *v5x_interface_alloc(struct v5x_instance *v5i, enum v5x_dialect dialect, void v5x_instance_free(struct v5x_instance *v5i);
int (*ph_data_req_cb)(struct msgb *msg, void *cbdata)); 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, 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); uint8_t ts1, uint8_t ts2);
void v5x_interface_free(struct v5x_interface *v5if); 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); 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); void v5x_user_port_destroy(struct v5x_user_port *v5up);
//FIXME: move this struct v5x_link *v5x_link_create(struct v5x_interface *v5if, uint8_t id);
void ph_socket_rx_cb(ph_socket_t *s, int channel, uint8_t prim, uint8_t *data, int length); 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);