diff --git a/src/Makefile.am b/src/Makefile.am index b515058..c07e3b4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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 \ diff --git a/src/layer1.c b/src/layer1.c new file mode 100644 index 0000000..21bc862 --- /dev/null +++ b/src/layer1.c @@ -0,0 +1,386 @@ + +#include +#include +#include +#include + +#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; +} + diff --git a/src/layer1.h b/src/layer1.h new file mode 100644 index 0000000..8829c71 --- /dev/null +++ b/src/layer1.h @@ -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); diff --git a/src/v5x_internal.h b/src/v5x_internal.h index 706fb2c..a3a5752 100644 --- a/src/v5x_internal.h +++ b/src/v5x_internal.h @@ -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 { diff --git a/src/v5x_l1_fsm.c b/src/v5x_l1_fsm.c index 95aea2b..ceb65b8 100644 --- a/src/v5x_l1_fsm.c +++ b/src/v5x_l1_fsm.c @@ -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);