Introduce layer1.c to process layer1 data flow and signals

This commit is contained in:
Andreas Eversberg 2022-12-28 20:36:31 +01:00
parent 2f47ca42fc
commit 6cbb9eb346
5 changed files with 424 additions and 9 deletions

View File

@ -10,6 +10,7 @@ osmo_v5_le_SOURCES = logging.c \
v5x_data.c \
v5le_vty.c \
lapv5.c \
layer1.c \
v5x_protocol.c \
v5x_l1_fsm.c \
v5x_le_ctrl_fsm.c \

386
src/layer1.c Normal file
View File

@ -0,0 +1,386 @@
#include <osmocom/abis/e1_input.h>
#include <osmocom/core/gsmtap.h>
#include <osmocom/core/gsmtap_util.h>
#include <osmocom/core/signal.h>
#include "v5x_internal.h"
#include "v5x_protocol.h"
#include "lapv5.h"
#include "layer1.h"
#include "v5x_l1_fsm.h"
#include "v5x_le_management.h"
extern struct v5x_instance *v5i;
static struct gsmtap_inst *g_gti = NULL;
/* only temporarily until this is in libosmocore gsmtap.h */
#ifndef GSMTAP_E1T1_V5EF
#define GSMTAP_E1T1_V5EF 0x06
#endif
static inline struct e1inp_line *e1_line_from_link(struct v5x_link *v5l)
{
struct e1inp_line *e1_line;
if (v5l->e1_line < 0)
return NULL;
e1_line = e1inp_line_find(v5l->e1_line);
if (!e1_line)
return NULL;
return e1_line;
}
/* Callback function to receive L1 signal from E1 port */
static int inp_sig_cb(unsigned int subsys, unsigned int signal, void __attribute__((unused)) *handler_data,
void *signal_data)
{
struct input_signal_data *isd = signal_data;
struct v5x_interface *v5if;
struct v5x_link *v5l;
if (subsys != SS_L_INPUT)
return 0;
/* no interface */
if (llist_empty(&v5i->interfaces))
return 0;
v5if = llist_first_entry(&v5i->interfaces, struct v5x_interface, list);
/* not used by any link */
v5l = v5x_link_find_e1_line(v5if, isd->line->num);
if (!v5l)
return 0;
switch (signal) {
case S_L_INP_LINE_LOS:
v5x_l1_signal_rcv(v5l, L1_SIGNAL_LOS);
break;
case S_L_INP_LINE_NOLOS:
v5x_l1_signal_rcv(v5l, L1_SIGNAL_NO_LOS);
break;
case S_L_INP_LINE_RAI:
v5x_l1_signal_rcv(v5l, L1_SIGNAL_RAI);
break;
case S_L_INP_LINE_NORAI:
v5x_l1_signal_rcv(v5l, L1_SIGNAL_NO_RAI);
break;
case S_L_INP_LINE_AIS:
v5x_l1_signal_rcv(v5l, L1_SIGNAL_AIS);
break;
case S_L_INP_LINE_NOAIS:
v5x_l1_signal_rcv(v5l, L1_SIGNAL_NO_AIS);
break;
case S_L_INP_LINE_SA_BITS:
if ((isd->sa_bits & 0x40))
v5x_l1_signal_rcv(v5l, L1_SIGNAL_SA7_1);
else
v5x_l1_signal_rcv(v5l, L1_SIGNAL_SA7_0);
break;
case S_L_INP_LINE_SLIP_RX:
printf("RX slip detected on link %d.\n", v5l->id);
break;
case S_L_INP_LINE_SLIP_TX:
printf("TX slip detected on link %d.\n", v5l->id);
break;
default:
;
}
return 0;
}
/* Send L1 signal to E1 port */
int v5x_l1_signal_snd(struct v5x_link *v5l, enum l1_signal_prim prim)
{
struct e1inp_line *e1_line;
/* no line assigned */
if (v5l->e1_line < 0)
return 0;
e1_line = e1inp_line_find(v5l->e1_line);
if (!e1_line)
return 0;
switch (prim) {
case L1_SIGNAL_SA7_0:
e1inp_ts_set_sa_bits(e1_line, 0xbf);
break;
case L1_SIGNAL_SA7_1:
e1inp_ts_set_sa_bits(e1_line, 0xff);
break;
default:
;
}
return 0;
}
/* data L1 -> L2 from E1 interface */
static void hdlc_rx_cb(struct e1inp_ts *ts, struct msgb *msg)
{
struct v5x_interface *v5if;
struct v5x_link *v5l;
/* no interface */
if (llist_empty(&v5i->interfaces)) {
msgb_free(msg);
return;
}
v5if = llist_first_entry(&v5i->interfaces, struct v5x_interface, list);
/* not used by any link */
v5l = v5x_link_find_e1_line(v5if, ts->line->num);
if (!v5l) {
msgb_free(msg);
return;
}
LOGP(DLINP, LOGL_DEBUG, "Link %d L1->L2: %s\n", v5l->id, msgb_hexdump(msg));
/* send V5 data via gsmtap so wireshark can receive + decode it */
gsmtap_send_ex(g_gti, GSMTAP_TYPE_E1T1, v5l->id, ts->num, GSMTAP_E1T1_V5EF,
0, 0, 0, 0, msgb_data(msg), msgb_length(msg));
lapv5ef_rx(v5l, msg);
}
/* data B-channel data from E1 interface */
static void raw_rx_cb(struct e1inp_ts *ts, struct msgb *msg)
{
struct v5x_interface *v5if;
struct v5x_link *v5l;
struct v5x_user_port *v5up;
/* no interface */
if (llist_empty(&v5i->interfaces)) {
msgb_free(msg);
return;
}
v5if = llist_first_entry(&v5i->interfaces, struct v5x_interface, list);
/* not used by any link */
v5l = v5x_link_find_e1_line(v5if, ts->line->num);
if (!v5l) {
msgb_free(msg);
return;
}
v5up = v5l->ts[ts->num].v5up;
/* not used by any user port */
if (!v5up) {
msgb_free(msg);
return;
}
/* we want B-channel data flipped */
osmo_revbytebits_buf(msg->data, msg->len);
/* if assigned and active, send B-channel data to socket interface */
if (v5up->ts[0] && v5up->ts[0]->nr == ts->num && v5up->ts_activated[0])
ph_socket_tx_msg(&v5up->ph_socket, 1, PH_PRIM_DATA_IND, msg->data, msg->len);
if (v5up->ts[1] && v5up->ts[1]->nr == ts->num && v5up->ts_activated[1])
ph_socket_tx_msg(&v5up->ph_socket, 2, PH_PRIM_DATA_IND, msg->data, msg->len);
msgb_free(msg);
}
/* send HDLC frame to signaling channel (from ISDN) */
int ph_data_req_hdlc(struct msgb *msg, struct v5x_interface *v5if)
{
struct e1inp_line *e1_line;
e1_line = e1_line_from_link(v5if->cc_link);
if (!e1_line) {
msgb_free(msg);
return 0;
}
LOGP(DLINP, LOGL_DEBUG, "Link %d L2->L1: %s\n", v5if->cc_link->id, msgb_hexdump(msg));
struct e1inp_ts *ts = &e1_line->ts[v5if->cc_link->c_channel[0].ts->nr - 1];
/* send V5 data via gsmtap so wireshark can receive + decode it */
gsmtap_send_ex(g_gti, GSMTAP_TYPE_E1T1, v5if->cc_link->id | GSMTAP_ARFCN_F_UPLINK, ts->num, GSMTAP_E1T1_V5EF,
0, 0, 0, 0, msgb_data(msg), msgb_length(msg));
return e1inp_ts_send_hdlc(ts, msg);
}
/* send HDLC frame to signaling channel (from DL) */
int ph_data_req_dl_cc(struct msgb *msg, void *cbdata)
{
struct v5x_interface *v5if = (struct v5x_interface *)cbdata;
struct e1inp_line *e1_line;
e1_line = e1_line_from_link(v5if->cc_link);
if (!e1_line) {
msgb_free(msg);
return 0;
}
struct e1inp_ts *ts = &e1_line->ts[v5if->cc_link->c_channel[0].ts->nr - 1];
/* add frame relay header */
msg->l2h = msgb_push(msg, 2);
msg->l2h[0] = msg->l2h[2] & 0xfd;
msg->l2h[1] = msg->l2h[3];
LOGP(DLINP, LOGL_DEBUG, "Link %d L2->L1: %s\n", v5if->cc_link->id, msgb_hexdump(msg));
/* send V5 data via gsmtap so wireshark can receive + decode it */
gsmtap_send_ex(g_gti, GSMTAP_TYPE_E1T1, v5if->cc_link->id | GSMTAP_ARFCN_F_UPLINK, ts->num, GSMTAP_E1T1_V5EF,
0, 0, 0, 0, msgb_data(msg), msgb_length(msg));
return e1inp_ts_send_hdlc(ts, msg);
}
/* send HDLC frame to protection link (from DL) */
int ph_data_req_dl_prot(struct msgb *msg, void *cbdata)
{
struct v5x_link *v5l = (struct v5x_link *)cbdata;
struct e1inp_line *e1_line;
e1_line = e1_line_from_link(v5l);
if (!e1_line) {
msgb_free(msg);
return 0;
}
struct e1inp_ts *ts = &e1_line->ts[v5l->c_channel[0].ts->nr - 1];
/* add frame relay header */
msg->l2h = msgb_push(msg, 2);
msg->l2h[0] = msg->l2h[2] & 0xfd;
msg->l2h[1] = msg->l2h[3];
LOGP(DLINP, LOGL_DEBUG, "Link %d L2->L1: %s\n", v5l->id, msgb_hexdump(msg));
/* send V5 data via gsmtap so wireshark can receive + decode it */
gsmtap_send_ex(g_gti, GSMTAP_TYPE_E1T1, v5l->id | GSMTAP_ARFCN_F_UPLINK, ts->num, GSMTAP_E1T1_V5EF,
0, 0, 0, 0, msgb_data(msg), msgb_length(msg));
return e1inp_ts_send_hdlc(ts, msg);
}
/* send raw (B-channel) data to E1 interface */
static int ph_data_req_raw(struct v5x_link *v5l, struct msgb *msg, int ts_nr)
{
struct e1inp_line *e1_line;
/* no line assigned */
e1_line = e1_line_from_link(v5l);
if (!e1_line) {
msgb_free(msg);
return 0;
}
/* we want B-channel data flipped */
osmo_revbytebits_buf(msg->data, msg->len);
struct e1inp_ts *ts = &e1_line->ts[ts_nr-1];
return e1inp_ts_send_raw(ts, msg);
}
/* receive message from PH-socket */
void ph_socket_rx_cb(ph_socket_t *s, int channel, uint8_t prim, uint8_t *data, int length)
{
struct v5x_user_port *v5up = s->priv;
switch (prim) {
case PH_PRIM_CTRL_REQ:
/* deactivate channels, if active */
if ((channel == 0 || channel == 3) && length && *data == PH_CTRL_BLOCK) {
v5x_le_channel_unassign(v5up, 1);
v5x_le_channel_unassign(v5up, 2);
}
v5x_le_nat_ph_rcv(v5up, prim, data, length);
break;
case PH_PRIM_ACT_REQ:
if (channel == 1 || channel == 2) {
v5x_le_channel_assign(v5up, channel);
ph_socket_tx_msg(s, channel, PH_PRIM_ACT_IND, NULL, 0);
break;
}
v5x_le_nat_ph_rcv(v5up, prim, data, length);
break;
case PH_PRIM_DACT_REQ:
if (channel == 1 || channel == 2) {
v5x_le_channel_unassign(v5up, channel);
ph_socket_tx_msg(s, channel, PH_PRIM_DACT_IND, NULL, 0);
break;
}
v5x_le_nat_ph_rcv(v5up, prim, data, length);
break;
case PH_PRIM_DATA_REQ:
if (v5up->type == V5X_USER_TYPE_PSTN && channel == 0) {
struct msgb *msg = msgb_alloc_headroom(length + 32, 32, "V5 PSTN MSG");
memcpy(msgb_put(msg, length), data, length);
v5x_le_nat_fe_rcv(v5up, msg);
} else if (v5up->type == V5X_USER_TYPE_ISDN && channel == 3) {
struct msgb *msg = msgb_alloc_headroom(length + 32, 32, "V5 EF MSG");
memcpy(msgb_put(msg, length), data, length);
lapv5ef_tx(v5up, msg);
} else if ((channel == 1 || channel == 2) && v5up->ts[channel - 1]) {
struct msgb *msg = msgb_alloc_headroom(length + 32, 32, "B MSG");
memcpy(msgb_put(msg, length), data, length);
ph_data_req_raw(v5up->ts[channel - 1]->link, msg, v5up->ts[channel - 1]->nr);
}
/* always confirm */
ph_socket_tx_msg(s, channel, PH_PRIM_DATA_CNF, NULL, 0);
break;
}
}
//FIXME: we use HDLC
static int v5le_rx_sign(struct msgb *msg)
{
LOGP(DLMI, LOGL_NOTICE, "Rx: %s\n", msgb_hexdump(msg));
msgb_free(msg);
return 0;
}
static const struct e1inp_line_ops v5le_e1_line_ops = {
.sign_link_up = NULL,
.sign_link_down = NULL,
.sign_link = v5le_rx_sign,
};
/* init all E1 lines and register input signals */
int e1_init(void)
{
struct e1inp_line *e1_line;
int i, ts;
g_gti = gsmtap_source_init("224.0.0.1", GSMTAP_UDP_PORT, 0);
OSMO_ASSERT(g_gti);
gsmtap_source_add_sink(g_gti);
for (i = 0; i < 256; i++) {
e1_line = e1inp_line_find(i);
if (!e1_line)
continue;
e1inp_line_bind_ops(e1_line, &v5le_e1_line_ops);
for (ts = 1; ts <= 31; ts++) {
struct e1inp_ts *e1_ts = &e1_line->ts[ts-1];
if (ts == 16) { // FIXME: make this depending on c_channel
//e1inp_ts_config_sign(e1_ts, e1_line);
//e1inp_sign_link_create(e1_ts, E1INP_SIGN_NONE, NULL, 115/*TEI*/, 0/*SAPI*/);
e1inp_ts_config_hdlc(e1_ts, e1_line, hdlc_rx_cb);
} else
e1inp_ts_config_raw(e1_ts, e1_line, raw_rx_cb);
}
e1inp_line_update(e1_line);
}
osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL);
return 0;
}

18
src/layer1.h Normal file
View File

@ -0,0 +1,18 @@
enum l1_signal_prim {
L1_SIGNAL_LOS,
L1_SIGNAL_NO_LOS,
L1_SIGNAL_RAI,
L1_SIGNAL_NO_RAI,
L1_SIGNAL_AIS,
L1_SIGNAL_NO_AIS,
L1_SIGNAL_SA7_1,
L1_SIGNAL_SA7_0,
};
int v5x_l1_signal_snd(struct v5x_link *v5l, enum l1_signal_prim prim);
int ph_data_req_hdlc(struct msgb *msg, struct v5x_interface *v5if);
int ph_data_req_dl_cc(struct msgb *msg, void *cbdata);
int ph_data_req_dl_prot(struct msgb *msg, void *cbdata);
void ph_socket_rx_cb(ph_socket_t *s, int channel, uint8_t prim, uint8_t *data, int length);
int e1_init(void);

View File

@ -232,7 +232,7 @@ struct v5x_interface {
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 */
int los, rai, ais, sa7; /* RX signal states */
};
struct v5x_ctrl_proto {

View File

@ -320,7 +320,7 @@ static void l1_fsm_le4_internal_fail(struct osmo_fsm_inst *fi, uint32_t event, v
* We must do this, because there is no frame reception to trigger this.
*/
if (!l1->los && !l1->rai && !l1->ais) {
if (l1->sa7_zero)
if (l1->sa7 == 0)
osmo_fsm_inst_dispatch(fi, V5X_L1FSM_E_NORMAL_Sa7, NULL);
else
osmo_fsm_inst_dispatch(fi, V5X_L1FSM_E_NORMAL, NULL);
@ -583,6 +583,7 @@ struct v5x_l1_proto *v5x_l1_fsm_create(void *ctx, struct v5x_link *v5l, uint8_t
if (!l1)
return NULL;
l1->v5l = v5l;
l1->sa7 = -1;
l1->fi = osmo_fsm_inst_alloc(&v5x_l1_fsm, l1, l1, LOGL_DEBUG, NULL);
if (!l1->fi)
@ -612,7 +613,7 @@ void v5x_l1_init(void)
rc = osmo_fsm_register(&v5x_l1_fsm);
OSMO_ASSERT(!rc);
LOGP(DV5PSTN, LOGL_NOTICE, "Using V5x L1 protocol\n");
LOGP(DV5L1, LOGL_NOTICE, "Using V5x L1 protocol\n");
}
@ -629,55 +630,64 @@ int v5x_l1_signal_rcv(struct v5x_link *v5l, enum l1_signal_prim prim)
switch (prim) {
case L1_SIGNAL_LOS:
LOGP(DV5L1, LOGL_DEBUG, "Signal LOS detected on link %d.\n", v5l->id);
if (!l1->los) {
l1->los = 1;
osmo_fsm_inst_dispatch(fi, V5X_L1FSM_E_LOS, NULL);
}
break;
case L1_SIGNAL_NO_LOS:
LOGP(DV5L1, LOGL_DEBUG, "Signal LOS gone on link %d.\n", v5l->id);
if (l1->los) {
l1->los = 0;
state_changed = 1;
}
break;
case L1_SIGNAL_RAI:
LOGP(DV5L1, LOGL_DEBUG, "Signal RAI detected on link %d.\n", v5l->id);
if (!l1->rai) {
l1->rai = 1;
osmo_fsm_inst_dispatch(fi, V5X_L1FSM_E_RAI, NULL);
}
break;
case L1_SIGNAL_NO_RAI:
LOGP(DV5L1, LOGL_DEBUG, "Signal RAI gone on link %d.\n", v5l->id);
if (l1->rai) {
l1->rai = 0;
state_changed = 1;
}
break;
case L1_SIGNAL_AIS:
LOGP(DV5L1, LOGL_DEBUG, "Signal AIS detected on link %d.\n", v5l->id);
if (!l1->ais) {
l1->ais = 1;
osmo_fsm_inst_dispatch(fi, V5X_L1FSM_E_AIS, NULL);
}
break;
case L1_SIGNAL_NO_AIS:
LOGP(DV5L1, LOGL_DEBUG, "Signal AIS gone on link %d.\n", v5l->id);
if (l1->ais) {
l1->ais = 0;
state_changed = 1;
}
break;
case L1_SIGNAL_SA7_0:
if (!l1->sa7_zero) {
l1->sa7_zero = 1;
if (l1->sa7 != 0) {
LOGP(DV5L1, LOGL_DEBUG, "Bit Sa7=0 on link %d.\n", v5l->id);
l1->sa7 = 0;
state_changed = 1;
}
break;
case L1_SIGNAL_SA7_1:
if (l1->sa7_zero) {
l1->sa7_zero = 0;
if (l1->sa7 != 1) {
LOGP(DV5L1, LOGL_DEBUG, "Bit Sa7=1 on link %d.\n", v5l->id);
l1->sa7 = 1;
state_changed = 1;
}
break;
default:
LOGP(DV5PSTN, LOGL_NOTICE, "Invalid L1 primitive %d receied from physical layer.\n", prim);
LOGP(DV5L1, LOGL_NOTICE, "Invalid L1 primitive %d receied from physical layer.\n", prim);
return -EINVAL;
}
@ -685,7 +695,7 @@ int v5x_l1_signal_rcv(struct v5x_link *v5l, enum l1_signal_prim prim)
* We must do this, because there is no frame reception to trigger this.
*/
if (state_changed && !l1->los && !l1->rai && !l1->ais) {
if (l1->sa7_zero)
if (l1->sa7 == 0)
osmo_fsm_inst_dispatch(fi, V5X_L1FSM_E_NORMAL_Sa7, NULL);
else
osmo_fsm_inst_dispatch(fi, V5X_L1FSM_E_NORMAL, NULL);