From 1e30f0dab7f1afb61666030aab75206aa71ded65 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Fri, 18 Dec 2020 17:13:35 +0100 Subject: Add new "osmo-e1gen" program. osmo-e1gen is a program that re-uses large parts of osmo-e1d, but whose main purpose is to generate a variety of error conditions in order to test a remote E1 implementation. Instead of using the automatisms of the icE1usb transmit IP core, it switches it to transparent mode and uses a host-software based E1 framer "osmo_e1f", over which we have more control than the firmware. Change-Id: I53a86d6730eb76a9cff9eb3f4786139015c91230 --- src/Makefile.am | 20 ++ src/e1d.h | 2 + src/e1gen/crc4itu.c | 56 ++++ src/e1gen/crc4itu.h | 106 ++++++++ src/e1gen/osmo_e1f.c | 704 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/e1gen/osmo_e1f.h | 123 +++++++++ src/osmo-e1gen.c | 438 ++++++++++++++++++++++++++++++++ 7 files changed, 1449 insertions(+) create mode 100644 src/e1gen/crc4itu.c create mode 100644 src/e1gen/crc4itu.h create mode 100644 src/e1gen/osmo_e1f.c create mode 100644 src/e1gen/osmo_e1f.h create mode 100644 src/osmo-e1gen.c diff --git a/src/Makefile.am b/src/Makefile.am index fd61f5a..d245bc6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -24,6 +24,8 @@ noinst_HEADERS = \ ice1usb_proto.h \ log.h \ usb.h \ + e1gen/crc4itu.h \ + e1gen/osmo_e1f.h \ $(NULL) @@ -32,6 +34,10 @@ bin_PROGRAMS = \ osmo-e1d-pipe \ $(NULL) +noinst_PROGRAMS = \ + osmo-e1gen \ + $(NULL) + osmo_e1d_SOURCES = \ ctl.c \ intf_line.c \ @@ -51,3 +57,17 @@ osmo_e1d_pipe_SOURCES = \ $(NULL) osmo_e1d_pipe_LDADD = $(LIBOSMOCORE_LIBS) libosmo-e1d.la + + +osmo_e1gen_LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOVTY_LIBS) \ + $(LIBOSMOUSB_LIBS) $(LIBUSB_LIBS) + +osmo_e1gen_SOURCES = \ + intf_line.c \ + log.c \ + e1gen/crc4itu.c \ + e1gen/osmo_e1f.c \ + osmo-e1gen.c \ + usb.c \ + vty.c \ + $(NULL) diff --git a/src/e1d.h b/src/e1d.h index 7cb5335..70bca38 100644 --- a/src/e1d.h +++ b/src/e1d.h @@ -120,6 +120,8 @@ struct e1_line { /*! timer to re-set the rx_crc4_err and rx_alarm above */ struct osmo_timer_list timer; } ts0; + + void *e1gen_priv; }; enum e1_driver { diff --git a/src/e1gen/crc4itu.c b/src/e1gen/crc4itu.c new file mode 100644 index 0000000..f13895a --- /dev/null +++ b/src/e1gen/crc4itu.c @@ -0,0 +1,56 @@ +/** + * \file + * Functions and types for CRC checks. + * + * Generated on Sat May 12 09:39:22 2018 + * by pycrc v0.9.1, https://pycrc.org + * using the configuration: + * - Width = 4 + * - Poly = 0x3 + * - XorIn = 0x0 + * - ReflectIn = False + * - XorOut = 0x0 + * - ReflectOut = False + * - Algorithm = table-driven + */ +#include "crc4itu.h" /* include the header file generated with pycrc */ +#include +#include + + + +/** + * Static table used for the table_driven implementation. + */ +static const crc_t crc_table[256] = { + 0x00, 0x03, 0x06, 0x05, 0x0c, 0x0f, 0x0a, 0x09, 0x0b, 0x08, 0x0d, 0x0e, 0x07, 0x04, 0x01, 0x02, + 0x05, 0x06, 0x03, 0x00, 0x09, 0x0a, 0x0f, 0x0c, 0x0e, 0x0d, 0x08, 0x0b, 0x02, 0x01, 0x04, 0x07, + 0x0a, 0x09, 0x0c, 0x0f, 0x06, 0x05, 0x00, 0x03, 0x01, 0x02, 0x07, 0x04, 0x0d, 0x0e, 0x0b, 0x08, + 0x0f, 0x0c, 0x09, 0x0a, 0x03, 0x00, 0x05, 0x06, 0x04, 0x07, 0x02, 0x01, 0x08, 0x0b, 0x0e, 0x0d, + 0x07, 0x04, 0x01, 0x02, 0x0b, 0x08, 0x0d, 0x0e, 0x0c, 0x0f, 0x0a, 0x09, 0x00, 0x03, 0x06, 0x05, + 0x02, 0x01, 0x04, 0x07, 0x0e, 0x0d, 0x08, 0x0b, 0x09, 0x0a, 0x0f, 0x0c, 0x05, 0x06, 0x03, 0x00, + 0x0d, 0x0e, 0x0b, 0x08, 0x01, 0x02, 0x07, 0x04, 0x06, 0x05, 0x00, 0x03, 0x0a, 0x09, 0x0c, 0x0f, + 0x08, 0x0b, 0x0e, 0x0d, 0x04, 0x07, 0x02, 0x01, 0x03, 0x00, 0x05, 0x06, 0x0f, 0x0c, 0x09, 0x0a, + 0x0e, 0x0d, 0x08, 0x0b, 0x02, 0x01, 0x04, 0x07, 0x05, 0x06, 0x03, 0x00, 0x09, 0x0a, 0x0f, 0x0c, + 0x0b, 0x08, 0x0d, 0x0e, 0x07, 0x04, 0x01, 0x02, 0x00, 0x03, 0x06, 0x05, 0x0c, 0x0f, 0x0a, 0x09, + 0x04, 0x07, 0x02, 0x01, 0x08, 0x0b, 0x0e, 0x0d, 0x0f, 0x0c, 0x09, 0x0a, 0x03, 0x00, 0x05, 0x06, + 0x01, 0x02, 0x07, 0x04, 0x0d, 0x0e, 0x0b, 0x08, 0x0a, 0x09, 0x0c, 0x0f, 0x06, 0x05, 0x00, 0x03, + 0x09, 0x0a, 0x0f, 0x0c, 0x05, 0x06, 0x03, 0x00, 0x02, 0x01, 0x04, 0x07, 0x0e, 0x0d, 0x08, 0x0b, + 0x0c, 0x0f, 0x0a, 0x09, 0x00, 0x03, 0x06, 0x05, 0x07, 0x04, 0x01, 0x02, 0x0b, 0x08, 0x0d, 0x0e, + 0x03, 0x00, 0x05, 0x06, 0x0f, 0x0c, 0x09, 0x0a, 0x08, 0x0b, 0x0e, 0x0d, 0x04, 0x07, 0x02, 0x01, + 0x06, 0x05, 0x00, 0x03, 0x0a, 0x09, 0x0c, 0x0f, 0x0d, 0x0e, 0x0b, 0x08, 0x01, 0x02, 0x07, 0x04 +}; + + +crc_t crc4itu_update(crc_t crc, const void *data, size_t data_len) +{ + const unsigned char *d = (const unsigned char *)data; + unsigned int tbl_idx; + + while (data_len--) { + tbl_idx = (crc << 4) ^ *d; + crc = crc_table[tbl_idx] & 0xf; + d++; + } + return crc & 0xf; +} diff --git a/src/e1gen/crc4itu.h b/src/e1gen/crc4itu.h new file mode 100644 index 0000000..220b50f --- /dev/null +++ b/src/e1gen/crc4itu.h @@ -0,0 +1,106 @@ +/** + * \file + * Functions and types for CRC checks. + * + * Generated on Sat May 12 09:41:12 2018 + * by pycrc v0.9.1, https://pycrc.org + * using the configuration: + * - Width = 4 + * - Poly = 0x3 + * - XorIn = 0x0 + * - ReflectIn = False + * - XorOut = 0x0 + * - ReflectOut = False + * - Algorithm = table-driven + * + * This file defines the functions crc4itu_init(), crc4itu_update() and crc_finalize(). + * + * The crc4itu_init() function returns the inital \c crc value and must be called + * before the first call to crc4itu_update(). + * Similarly, the crc_finalize() function must be called after the last call + * to crc4itu_update(), before the \c crc is being used. + * is being used. + * + * The crc4itu_update() function can be called any number of times (including zero + * times) in between the crc4itu_init() and crc_finalize() calls. + * + * This pseudo-code shows an example usage of the API: + * \code{.c} + * crc_t crc; + * unsigned char data[MAX_DATA_LEN]; + * size_t data_len; + * + * crc = crc4itu_init(); + * while ((data_len = read_data(data, MAX_DATA_LEN)) > 0) { + * crc = crc4itu_update(crc, data, data_len); + * } + * crc = crc_finalize(crc); + * \endcode + */ +#ifndef CRC4ITU_H +#define CRC4ITU_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * The definition of the used algorithm. + * + * This is not used anywhere in the generated code, but it may be used by the + * application code to call algorithm-specific code, if desired. + */ +#define CRC_ALGO_TABLE_DRIVEN 1 + + +/** + * The type of the CRC values. + * + * This type must be big enough to contain at least 4 bits. + */ +typedef uint_fast8_t crc_t; + + +/** + * Calculate the initial crc value. + * + * \return The initial crc value. + */ +static inline crc_t crc4itu_init(void) +{ + return 0x0; +} + + +/** + * Update the crc value with new data. + * + * \param[in] crc The current crc value. + * \param[in] data Pointer to a buffer of \a data_len bytes. + * \param[in] data_len Number of bytes in the \a data buffer. + * \return The updated crc value. + */ +crc_t crc4itu_update(crc_t crc, const void *data, size_t data_len); + + +/** + * Calculate the final crc value. + * + * \param[in] crc The current crc value. + * \return The final crc value. + */ +static inline crc_t crc_finalize(crc_t crc) +{ + return crc; +} + + +#ifdef __cplusplus +} /* closing brace for extern "C" */ +#endif + +#endif /* CRC4ITU_H */ diff --git a/src/e1gen/osmo_e1f.c b/src/e1gen/osmo_e1f.c new file mode 100644 index 0000000..fa64e17 --- /dev/null +++ b/src/e1gen/osmo_e1f.c @@ -0,0 +1,704 @@ +/* Osmocom Software Defined E1 + * + * (C) 2018 by Harald Welte + * + * Implements ITU-T Rec. G.704 Section 2.3 + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "crc4itu.h" +#include "osmo_e1f.h" + +#define S(x) (1 << (x)) + +/* Frame Alignment Signal (BIT1 may be overwritten with CRC-4) */ +#define G704_E1_FAS 0x1B + +static inline bool is_correct_fas(uint8_t bt) { + if ((bt & 0x7F) == G704_E1_FAS) + return true; + else + return false; +} + +/* are we in SMF II (true) or I (false) */ +static inline bool is_smf_II(const struct osmo_e1f_tx_state *tx) { + if (tx->frame_nr >= 8) + return true; + return false; +} + +static struct osmo_fsm e1_align_fsm; +static void align_fsm_reset(struct osmo_e1f_instance *e1i); + +static void notify_user(struct osmo_e1f_instance *e1i, enum osmo_e1f_notify_event evt, + bool present, void *priv) +{ + if (!e1i->notify_cb) + return; + e1i->notify_cb(e1i, evt, present, priv); +} + +/*! Initialize a (caller-allocated) Osmocom E1 Instance + * \param[inout] e1i E1 Instance to be initialized + * \returns 0 on success, negative on error */ +int osmo_e1f_instance_init(struct osmo_e1f_instance *e1i, const char *name, e1_notify_cb cb, + bool crc4_enabled, void *priv) +{ + int i; + + e1i->crc4_enabled = crc4_enabled; + e1i->notify_cb = cb; + e1i->tx.sa4_sa8 = 0x1f; + + e1i->priv = priv; + + for (i = 1; i < ARRAY_SIZE(e1i->ts); i++) { + struct osmo_e1f_instance_ts *e1t = &e1i->ts[i]; + e1t->ts_nr = i; + e1t->inst = e1i; + INIT_LLIST_HEAD(&e1t->tx.queue); + + e1t->rx.granularity = 256; + } + + e1i->rx.fi = osmo_fsm_inst_alloc(&e1_align_fsm, NULL, e1i, LOGL_DEBUG, name); + if (!e1i->rx.fi) + return -1; + + osmo_e1f_instance_reset(e1i); + + return 0; +} + +/*! stop E1 timeslot; release any pending rx/tx buffers + * \param[in] e1t Timeslot which we are to stop, disable and release buffers */ +void osmo_e1f_ts_reset(struct osmo_e1f_instance_ts *e1t) +{ + e1t->tx.underruns = 0; + msgb_queue_free(&e1t->tx.queue); + + e1t->rx.enabled = false; + msgb_free(e1t->rx.msg); + e1t->rx.msg = NULL; + + osmo_isdnhdlc_rcv_init(&e1t->rx.hdlc, OSMO_HDLC_F_BITREVERSE); + //osmo_isdnhdlc_rcv_init(&e1t->rx.hdlc, 0); + osmo_isdnhdlc_out_init(&e1t->tx.hdlc, 0); +} + +/*! stop E1 instance; stops all timeslots and releases any pending rx/tx buffers + * \param[in] e1t E1 instance which we are to stop */ +void osmo_e1f_instance_reset(struct osmo_e1f_instance *e1i) +{ + int i; + + align_fsm_reset(e1i); + + e1i->tx.remote_alarm = false; + e1i->tx.crc4_error = false; + e1i->tx.frame_nr = 0; + e1i->tx.crc4_last_smf = 0; + e1i->tx.crc4 = crc4itu_init(); + + e1i->rx.frame_nr = 0; + memset(&e1i->rx.ts0_history, 0, sizeof(e1i->rx.ts0_history)); + e1i->rx.ts0_hist_len = 0; + e1i->rx.remote_alarm = false; + e1i->rx.remote_crc4_error = false; + e1i->rx.num_ts0_in_mframe_search = 0; + + for (i = 1; i < ARRAY_SIZE(e1i->ts); i++) { + struct osmo_e1f_instance_ts *e1t = &e1i->ts[i]; + osmo_e1f_ts_reset(e1t); + } +} + +/*! obtain pointer to TS given by instance + timeslot number + * \param[in] e1i E1 intance on which we work + * \param[in] ts_nr E1 timeslot number (1..31) + * \returns pointer to timeslot; NULL on error */ +struct osmo_e1f_instance_ts *osmo_e1f_instance_ts(struct osmo_e1f_instance *e1i, uint8_t ts_nr) +{ + if (ts_nr == 0 || ts_nr >= ARRAY_SIZE(e1i->ts)) + return NULL; + + return &e1i->ts[ts_nr]; +} + +/*! configure an E1 timeslot + * \param[in] e1t Timeslot which we are to configure + * \param[in] granularity granularity (buffer size) to use on Rx + * \param[in] enable enable (true) or disalble (false) receiving on this TS + * \param[in] mode the mode for this timeslot (raw or hdlc) + * \return 0 on success; negative on error */ +int osmo_e1f_ts_config(struct osmo_e1f_instance_ts *e1t, e1_data_cb cb, unsigned int granularity, + bool enable, enum osmo_e1f_ts_mode mode) +{ + e1t->rx.data_cb = cb; + e1t->rx.enabled = enable; + e1t->rx.granularity = granularity; + e1t->mode = mode; + + return 0; +} + +const struct value_string osmo_e1f_notifv_evt_names[] = { + { E1_NTFY_EVT_ALIGN_FRAME, "Aligned to Frame" }, + { E1_NTFY_EVT_ALIGN_CRC_MFRAME, "Aligned to CRC4-Multiframe" }, + { E1_NTFY_EVT_CRC_ERROR, "CRC Error detected (local)" }, + { E1_NTFY_EVT_REMOTE_CRC_ERROR, "CRC Error reported (remote)" }, + { E1_NTFY_EVT_REMOTE_ALARM, "Remote Alarm condition repoorted" }, + { 0, NULL } +}; + +/*********************************************************************** + * Transmit Side + ***********************************************************************/ + +/*! Enqueue a message buffer of to-be-transmitted data for a timeslot + * \param[in] e1i E1 instance for which to enqueue + * \param[in] ts_nr Timeslot number on which data is to be transmitted + * \param[in] msg Message buffer storing the to-be-transmitted data + * \returns 0 on success; negative in case of error. + * + * Ownership of \a msg is transferred from caller into this function, but only + * in case of successful execution (return 0)! + */ +void osmo_e1f_ts_enqueue(struct osmo_e1f_instance_ts *e1t, struct msgb *msg) +{ + msgb_enqueue(&e1t->tx.queue, msg); +} + +/* obtain a CRC4 bit for the current frame number */ +static uint8_t e1_pull_crc4_bit(struct osmo_e1f_instance *e1i) +{ + /* If CRC-4 is disabled, all CRC bits shall be '1' */ + if (e1i->crc4_enabled == 0) { + return 0x01; + } else { + /* CRC is transmitted MSB first */ + switch (e1i->tx.frame_nr % 8) { + case 0: + return (e1i->tx.crc4_last_smf >> 3) & 1; + case 2: + return (e1i->tx.crc4_last_smf >> 2) & 1; + case 4: + return (e1i->tx.crc4_last_smf >> 1) & 1; + case 6: + return (e1i->tx.crc4_last_smf >> 0) & 1; + default: + OSMO_ASSERT(0); + } + } +} + +/* pull a single to-be-transmitted byte for TS0 */ +static uint8_t e1_pull_ts0(struct osmo_e1f_instance *e1i) +{ + uint8_t ret = 0; + + /* re-set CRC4 at start of sub-multiframe */ + if (e1i->tx.frame_nr == 0 || e1i->tx.frame_nr == 8) { + e1i->tx.crc4_last_smf = e1i->tx.crc4; + e1i->tx.crc4 = 0; + } + + /* according to Table 5B/G.704 - CRC-4 multiframe structure */ + if ((e1i->tx.frame_nr % 2) == 0) { + /* FAS */ + ret = G704_E1_FAS | (e1_pull_crc4_bit(e1i) << 7); + } else { + switch (e1i->tx.frame_nr) { + case 1: + case 3: + case 7: + ret = 0x40; + break; + case 5: + case 9: + case 11: + ret = 0xC0; + break; + case 13: + case 15: + ret = 0x40; + if (!e1i->tx.crc4_error) + ret |= 0x80; + break; + } + ret |= e1i->tx.sa4_sa8; + if (e1i->tx.remote_alarm) + ret |= 0x20; + } + + /* increment frame number modulo 16 */ + e1i->tx.frame_nr = (e1i->tx.frame_nr + 1) % 16; + + return ret; +} + +/* pull a single to-be-transmitted byte for TS1..31 */ +static uint8_t e1_pull_tsN(struct osmo_e1f_instance_ts *e1t) +{ + struct msgb *msg = llist_first_entry_or_null(&e1t->tx.queue, struct msgb, list); + uint8_t *cur; + +retry: + /* if there's no message to transmit */ + if (!msg) { + e1t->tx.underruns++; + return 0xFF; + } + if (msgb_length(msg) <= 0) { + llist_del(&msg->list); + msgb_free(msg); + msg = llist_first_entry_or_null(&e1t->tx.queue, struct msgb, list); + goto retry; + } + cur = msgb_pull(msg, 1); + return *cur; +} + +/* update the current in-progress CRC4 value with data from \a out_frame */ +static void e1_tx_update_crc4(struct osmo_e1f_instance *e1i, const uint8_t *out_frame) +{ + uint8_t ts0; + + ts0 = out_frame[0]; + /* mask off the C bits */ + if (is_correct_fas(ts0)) + ts0 &= 0x7F; + e1i->tx.crc4 = crc4itu_update(e1i->tx.crc4, &ts0, 1); + /* add the remaining bytes/bits */ + e1i->tx.crc4 = crc4itu_update(e1i->tx.crc4, out_frame+1, ARRAY_SIZE(e1i->ts)-1); +} + +/*! Pull one to-be-transmitted E1 frame (256bits) from the E1 instance + * \param e1i E1 instance for which the frame shall be generated + * \param[out] out_frame callee-allocated buffer to which function stores 32 bytes + * \returns 0 on success, negative on error */ +int osmo_e1f_pull_tx_frame(struct osmo_e1f_instance *e1i, uint8_t *out_frame) +{ + int i; + + if (e1i->tx.ais) { + memset(out_frame, 0xff, 32); + return 0; + } + + /* generate TS0 */ + out_frame[0] = e1_pull_ts0(e1i); + + /* generate TS1..31 */ + for (i = 1; i < ARRAY_SIZE(e1i->ts); i++) { + struct osmo_e1f_instance_ts *e1t = &e1i->ts[i]; + /* get next to-be-transmitted byte from the TS */ + out_frame[i] = e1_pull_tsN(e1t); + } + /* update our CRC4 computation */ + e1_tx_update_crc4(e1i, out_frame); + + return 0; +} + +/*********************************************************************** + * Receiver Side + ***********************************************************************/ + +/* According to Figure 2 / ITU-T G.706 */ +enum e1_align_state { + /* Frame Alignment Search */ + E1_AS_SEARCH_FRAME, + /* CRC multiframe alignment search */ + E1_AS_SEARCH_CRC_MFRAME, + /* monitoring for incorrect frame alignment and error performance using CRC */ + E1_AS_ALIGNED_CRC_MFRAME, + /* no CRC: just frame alignment loss check */ + E1_AS_ALIGNED_BASIC, +}; + +enum e1_align_event { + /* received a TS0 octet */ + E1_AE_RX_TS0, + E1_AE_RESET +}; + +static const struct value_string e1_align_evt_names[] = { + { E1_AE_RX_TS0, "E1_AE_RX_TS0" }, + { E1_AE_RESET, "E1_AE_RESET" }, + { 0, NULL } +}; + +/* get a TS0 byte from the history. delta 0 == current, delte 1 == previous, ... */ +static uint8_t get_ts0_hist(struct osmo_e1f_instance *e1i, uint8_t delta) +{ + return e1i->rx.ts0_history[((e1i->rx.frame_nr + 16)-delta) % 16]; +} + +/* ITU-T G.706 Section 4.1.1 */ +static bool frame_alignment_lost(struct osmo_e1f_instance *e1i) +{ + if (e1i->rx.frame_nr % 2) + return false; + + /* Frame alignment will be assumed to have been lost when three consecutive incorrect + * frame alignment signals have been received. */ + if (!is_correct_fas(get_ts0_hist(e1i, 0)) && + !is_correct_fas(get_ts0_hist(e1i, 2)) && + !is_correct_fas(get_ts0_hist(e1i, 4))) + return true; + else + return false; +} + +/* ITU-T G.706 Section 4.1.2 */ +static bool frame_alignment_recovered(struct osmo_e1f_instance *e1i) +{ + /* two consecutive FAS with one non-FAS interspersed */ + if (is_correct_fas(get_ts0_hist(e1i, 0)) && + !is_correct_fas(get_ts0_hist(e1i, 1)) && + is_correct_fas(get_ts0_hist(e1i, 2))) + return true; + else + return false; +} + +/* ITU-T G.706 Section 4.2 */ +static bool crc_mframe_alignment_achieved(struct osmo_e1f_instance *e1i) +{ + /* if current TS0 byte is FAS, we cannot detect alignment */ + if (is_correct_fas(get_ts0_hist(e1i, 0))) + return false; + if ((get_ts0_hist(e1i, 0) >> 7) == 1 && + (get_ts0_hist(e1i, 2) >> 7) == 1 && + (get_ts0_hist(e1i, 4) >> 7) == 0 && + (get_ts0_hist(e1i, 6) >> 7) == 1 && + (get_ts0_hist(e1i, 8) >> 7) == 0 && + (get_ts0_hist(e1i, 10) >> 7) == 0) + return true; + else + return false; +} + +/* Get the CRC4 that was received from our Rx TS0 history */ +static uint8_t crc4_from_ts0_hist(struct osmo_e1f_instance *e1i, bool smf2) +{ + uint8_t crc = 0; + uint8_t offset = 0; + + if (smf2) + offset = 8; + + crc |= (e1i->rx.ts0_history[0+offset] >> 7) << 3; + crc |= (e1i->rx.ts0_history[2+offset] >> 7) << 2; + crc |= (e1i->rx.ts0_history[4+offset] >> 7) << 1; + crc |= (e1i->rx.ts0_history[6+offset] >> 7) << 0; + + return crc; +} + +/* update the current in-progress CRC4 value with data from \a rx_frame */ +static void e1_rx_update_crc4(struct osmo_e1f_instance *e1i, const uint8_t *rx_frame) +{ + uint8_t ts0; + + ts0 = rx_frame[0]; + /* mask off the C bits */ + if (is_correct_fas(ts0)) + ts0 &= 0x7F; + e1i->rx.crc4 = crc4itu_update(e1i->rx.crc4, &ts0, 1); + /* add the remaining bytes/bits */ + e1i->rx.crc4 = crc4itu_update(e1i->rx.crc4, rx_frame+1, ARRAY_SIZE(e1i->ts)-1); +} + +/* FSM State handler */ +static void e1_align_search_frame(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct osmo_e1f_instance *e1i = (struct osmo_e1f_instance *) fi->priv; + + if (frame_alignment_recovered(e1i)) { + /* if we detected the 2nd FAS, we must be in FN 2 (or at least FN%2=0 */ + e1i->rx.frame_nr = 2; + notify_user(e1i, E1_NTFY_EVT_ALIGN_FRAME, true, NULL); + osmo_fsm_inst_state_chg(fi, E1_AS_SEARCH_CRC_MFRAME, 0, 0); + } +} + +/* FSM State handler */ +static void e1_align_search_crc_mframe(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct osmo_e1f_instance *e1i = (struct osmo_e1f_instance *) fi->priv; + + if (crc_mframe_alignment_achieved(e1i)) { + /* if we detected the 6-bit CRC multiframe signal, we must be in FN 11 */ + e1i->rx.frame_nr = 11; + /* FIXME: "at least two valid CRC multiframe alignment signals can be located within + * 8 ms, the time separating two CRC multiframe alignment signals being 2 ms or a + * multiple of 2 ms" */ + notify_user(e1i, E1_NTFY_EVT_ALIGN_CRC_MFRAME, true, NULL); + osmo_fsm_inst_state_chg(fi, E1_AS_ALIGNED_CRC_MFRAME, 0, 0); + } else { + /* if no mframe alignment is established within 8ms (64 frames), fall back */ + if (e1i->rx.num_ts0_in_mframe_search >= 64) { + e1i->rx.num_ts0_in_mframe_search = 0; + osmo_fsm_inst_state_chg(fi, E1_AS_SEARCH_FRAME, 0, 0); + } + e1i->rx.num_ts0_in_mframe_search++; + } +} + +static void e1_aligned_common(struct osmo_e1f_instance *e1i) +{ + uint8_t inb = get_ts0_hist(e1i, 0); + + /* All non-FAS frames contain "A" bit in TS0 */ + if (!is_correct_fas(inb & 0x7F)) { + bool old_alarm = e1i->rx.remote_alarm; + /* frame not containing the frame alignment signal */ + if (inb & 0x20) + e1i->rx.remote_alarm = true; + else + e1i->rx.remote_alarm = false; + if (old_alarm != e1i->rx.remote_alarm) + notify_user(e1i, E1_NTFY_EVT_REMOTE_ALARM, e1i->rx.remote_alarm, NULL); + } +} + +/* FSM State handler */ +static void e1_aligned_crc_mframe(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct osmo_e1f_instance *e1i = (struct osmo_e1f_instance *) fi->priv; + + if (frame_alignment_lost(e1i)) { + osmo_fsm_inst_state_chg(fi, E1_AS_SEARCH_FRAME, 0, 0); + return; + } + + if (e1i->crc4_enabled) { + uint8_t crc_rx; + bool crc4_error; + + /* check if we just received a complete CRC4 */ + switch (e1i->rx.frame_nr) { + case 7: + case 15: + crc_rx = crc4_from_ts0_hist(e1i, e1i->rx.frame_nr == 15 ? true : false); + if (crc_rx != e1i->rx.crc4_last_smf) + crc4_error = true; + else + crc4_error = false; + if (crc4_error != e1i->tx.crc4_error) { + notify_user(e1i, E1_NTFY_EVT_CRC_ERROR, crc4_error, NULL); + e1i->tx.crc4_error = crc4_error; + } + /* rotate computed CRC4 one further */ + e1i->rx.crc4_last_smf = e1i->rx.crc4; + e1i->rx.crc4 = crc4itu_init(); + break; + default: + break; + } + + /* check if the remote side reports any CRC errors */ + switch (e1i->rx.frame_nr) { + case 13: + case 15: + crc4_error = false; + if ((get_ts0_hist(e1i, 0) >> 7) == 0) + crc4_error = true; + if (crc4_error != e1i->rx.remote_crc4_error) { + notify_user(e1i, E1_NTFY_EVT_REMOTE_CRC_ERROR, crc4_error, NULL); + e1i->rx.remote_crc4_error = crc4_error; + } + break; + } + } + + e1_aligned_common(e1i); +} + +/* FSM State handler */ +static void e1_aligned_basic(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct osmo_e1f_instance *e1i = (struct osmo_e1f_instance *) fi->priv; + + if (frame_alignment_lost(e1i)) { + osmo_fsm_inst_state_chg(fi, E1_AS_SEARCH_FRAME, 0, 0); + return; + } + + e1_aligned_common(e1i); +} + +static const struct osmo_fsm_state e1_align_states[] = { + [E1_AS_SEARCH_FRAME] = { + .name = "SEARCH_FRAME", + .in_event_mask = S(E1_AE_RX_TS0), + .out_state_mask = S(E1_AS_SEARCH_FRAME) | + S(E1_AS_SEARCH_CRC_MFRAME) | + S(E1_AS_ALIGNED_BASIC), + .action = e1_align_search_frame, + }, + [E1_AS_SEARCH_CRC_MFRAME] = { + .name = "SEARCH_CRC_MFRAME", + .in_event_mask = S(E1_AE_RX_TS0), + .out_state_mask = S(E1_AS_SEARCH_FRAME) | + S(E1_AS_SEARCH_CRC_MFRAME) | + S(E1_AS_ALIGNED_CRC_MFRAME), + .action = e1_align_search_crc_mframe, + }, + [E1_AS_ALIGNED_CRC_MFRAME] = { + .name = "ALIGNED_CRC_MFRAME", + .in_event_mask = S(E1_AE_RX_TS0), + .out_state_mask = S(E1_AS_SEARCH_FRAME) | + S(E1_AS_SEARCH_CRC_MFRAME) | + S(E1_AS_ALIGNED_CRC_MFRAME), + .action = e1_aligned_crc_mframe, + }, + [E1_AS_ALIGNED_BASIC] = { + .name = "ALIGNED_BASIC", + .in_event_mask = S(E1_AE_RX_TS0), + .out_state_mask = S(E1_AS_SEARCH_FRAME), + .action = e1_aligned_basic, + }, +}; + +static void e1_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct osmo_e1f_instance *e1i = (struct osmo_e1f_instance *) fi->priv; + + switch (event) { + case E1_AE_RESET: + e1i->rx.num_ts0_in_mframe_search = 0; + osmo_fsm_inst_state_chg(fi, E1_AS_SEARCH_FRAME, 0, 0); + break; + } +} + +static struct osmo_fsm e1_align_fsm = { + .name = "e1-align", + .states = e1_align_states, + .num_states = ARRAY_SIZE(e1_align_states), + .allstate_event_mask = S(E1_AE_RESET), + .allstate_action = e1_allstate, + .log_subsys = DLGLOBAL, + .event_names = e1_align_evt_names, +}; + +static void align_fsm_reset(struct osmo_e1f_instance *e1i) +{ + osmo_fsm_inst_dispatch(e1i->rx.fi, E1_AE_RESET, NULL); +} + +static void e1_rx_hist_add(struct osmo_e1f_instance *e1i, uint8_t inb) +{ + e1i->rx.ts0_history[e1i->rx.frame_nr] = inb; + if (e1i->rx.ts0_hist_len < 16) + e1i->rx.ts0_hist_len++; +} + +static void e1_rx_ts0(struct osmo_e1f_instance *e1i, uint8_t inb) +{ + /* append just-received byte to the TS0 receive history buffer */ + e1_rx_hist_add(e1i, inb); + + /* notify the FSM that a new TS0 byte was received */ + osmo_fsm_inst_dispatch(e1i->rx.fi, E1_AE_RX_TS0, NULL); + + e1i->rx.frame_nr = (e1i->rx.frame_nr + 1) % 16; +} + +static void e1_rx_tsN(struct osmo_e1f_instance_ts *e1t, uint8_t inb) +{ + struct msgb *msg; + int count, rc; + + if (!e1t->rx.enabled) + return; + + if (!e1t->rx.msg) + e1t->rx.msg = msgb_alloc(e1t->rx.granularity, "E1 Rx"); + msg = e1t->rx.msg; + OSMO_ASSERT(msg); + + switch (e1t->mode) { + case OSMO_E1F_TS_RAW: + /* append byte at end of msgb */ + msgb_put_u8(msg, inb); + /* flush msgb, if full */ + if (msgb_tailroom(msg) <= 0) { + goto flush; + } + break; + case OSMO_E1F_TS_HDLC_CRC: + rc = osmo_isdnhdlc_decode(&e1t->rx.hdlc, &inb, 1, &count, + msgb_data(msg), msgb_tailroom(msg)); + switch (rc) { + case -OSMO_HDLC_FRAMING_ERROR: + fprintf(stdout, "Framing Error\n"); + break; + case -OSMO_HDLC_CRC_ERROR: + fprintf(stdout, "CRC Error\n"); + break; + case -OSMO_HDLC_LENGTH_ERROR: + fprintf(stdout, "Length Error\n"); + break; + case 0: + /* no output yet */ + break; + default: + msgb_put(msg, rc); + goto flush; + } + break; + } + + return; +flush: + + if (!e1t->rx.data_cb) + msgb_free(msg); + else + e1t->rx.data_cb(e1t, msg); + e1t->rx.msg = NULL; +} + +/*! Receive a single E1 frame of 32x8 (=256) bits + * \param e1i E1 instance for which the frame was received + * \param[in] in_frame caller-provided buffer of 32 octets + * + * The idea is that whoever calls us will already have done the bit-alignment, + * i.e. the first bit of TS0 of the frame will be octet-aligned and hence the + * entire 256bit buffer is provided as octet-aligned 32bytes in \a in_frame. + */ +int osmo_e1f_rx_frame(struct osmo_e1f_instance *e1i, const uint8_t *in_frame) +{ + int i; + + e1_rx_update_crc4(e1i, in_frame); + + e1_rx_ts0(e1i, in_frame[0]); + + for (i = 1; i < ARRAY_SIZE(e1i->ts); i++) { + struct osmo_e1f_instance_ts *e1t = &e1i->ts[i]; + e1_rx_tsN(e1t, in_frame[i]); + } + + return 0; +} + +int osmo_e1f_init(void) +{ + return osmo_fsm_register(&e1_align_fsm); +} diff --git a/src/e1gen/osmo_e1f.h b/src/e1gen/osmo_e1f.h new file mode 100644 index 0000000..0b423d9 --- /dev/null +++ b/src/e1gen/osmo_e1f.h @@ -0,0 +1,123 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +struct osmo_e1f_tx_state { + bool remote_alarm; + bool crc4_error; + bool ais; + /* lower 5 bits: Sa4..Sa8 */ + uint8_t sa4_sa8; + /* frame number 0..15 */ + uint8_t frame_nr; + uint8_t crc4_last_smf; + uint8_t crc4; +}; + +struct osmo_e1f_rx_state { + uint8_t frame_nr; + /* history of rceived TS0 octets */ + uint8_t ts0_history[16]; + uint8_t ts0_hist_len; + /* was a remote alarm received? */ + bool remote_alarm; + bool remote_crc4_error; + /* number of TS0 bytes received since entering CRC mframe search */ + uint8_t num_ts0_in_mframe_search; + struct osmo_fsm_inst *fi; + /* computed CRC4 */ + uint8_t crc4_last_smf; + uint8_t crc4; +}; + +enum osmo_e1f_notify_event { + E1_NTFY_EVT_ALIGN_FRAME, + E1_NTFY_EVT_ALIGN_CRC_MFRAME, + E1_NTFY_EVT_CRC_ERROR, + E1_NTFY_EVT_REMOTE_CRC_ERROR, + E1_NTFY_EVT_REMOTE_ALARM, +}; + +enum osmo_e1f_ts_mode { + OSMO_E1F_TS_RAW, + OSMO_E1F_TS_HDLC_CRC, +}; + +struct osmo_e1f_instance_ts; +struct osmo_e1f_instance; +typedef void (*e1_data_cb)(struct osmo_e1f_instance_ts *ts, struct msgb *msg); +typedef void (*e1_notify_cb)(struct osmo_e1f_instance *e1i, enum osmo_e1f_notify_event evt, + bool present, void *data); + +struct osmo_e1f_instance_ts { + /* timeslot number */ + uint8_t ts_nr; + /* mode in which we operate (RAW/HDLC) */ + enum osmo_e1f_ts_mode mode; + /* back-pointer to e1 instance */ + struct osmo_e1f_instance *inst; + struct { + /* optional HDLC encoder state */ + struct osmo_isdnhdlc_vars hdlc; + /* queue of pending to-be-transmitted messages */ + struct llist_head queue; + unsigned long underruns; + } tx; + struct { + /* optional HDLC decoder state */ + struct osmo_isdnhdlc_vars hdlc; + bool enabled; + /* how many bytes to buffer before calling call-back */ + unsigned int granularity; + /* current receive buffer */ + struct msgb *msg; + e1_data_cb data_cb; + /* private data, relevant to user */ + void *priv; + } rx; +}; + +struct osmo_e1f_instance { + /* list; currently not used yet */ + struct llist_head list; + + /* is CRC4 generation + parsing enabled? */ + bool crc4_enabled; + /* notification call-back function */ + e1_notify_cb notify_cb; + + /* Rx + Tx related state */ + struct osmo_e1f_tx_state tx; + struct osmo_e1f_rx_state rx; + + /* our 32 timeslots (only 1..32 are used) */ + struct osmo_e1f_instance_ts ts[32]; + + /* private data, relevant to user */ + void *priv; +}; + +extern const struct value_string osmo_e1f_notifv_evt_names[]; + +static inline const char *osmo_e1f_notify_event_name(enum osmo_e1f_notify_event evt) { + return get_value_string(osmo_e1f_notifv_evt_names, evt); +} + +int osmo_e1f_init(void); +struct osmo_e1f_instance_ts *osmo_e1f_instance_ts(struct osmo_e1f_instance *e1i, uint8_t ts_nr); +int osmo_e1f_instance_init(struct osmo_e1f_instance *e1i, const char *name, e1_notify_cb cb, + bool crc4_enabled, void *priv); +void osmo_e1f_instance_reset(struct osmo_e1f_instance *e1i); +int osmo_e1f_ts_config(struct osmo_e1f_instance_ts *e1t, e1_data_cb cb, unsigned int granularity, + bool enable, enum osmo_e1f_ts_mode mode); +void osmo_e1f_ts_reset(struct osmo_e1f_instance_ts *e1t); + + +void osmo_e1f_ts_enqueue(struct osmo_e1f_instance_ts *e1t, struct msgb *msg); +int osmo_e1f_pull_tx_frame(struct osmo_e1f_instance *e1i, uint8_t *out_frame); +int osmo_e1f_rx_frame(struct osmo_e1f_instance *e1i, const uint8_t *in_frame); diff --git a/src/osmo-e1gen.c b/src/osmo-e1gen.c new file mode 100644 index 0000000..15d677a --- /dev/null +++ b/src/osmo-e1gen.c @@ -0,0 +1,438 @@ +/* + * osmo-e1gen.c + * + * (C) 2020 by Harald Welte + * + * 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 +#include +#include +#include +#include + +#define _GNU_SOURCE +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "e1d.h" +#include "usb.h" +#include "log.h" + +#include "e1gen/osmo_e1f.h" + +#ifndef OSMO_VTY_PORT_E1D +#define OSMO_VTY_PORT_E1D 4269 +#endif + +/*********************************************************************** + * global variables + ***********************************************************************/ + +extern struct e1_daemon *vty_e1d; +static const char *g_config_file = "osmo-e1d.cfg"; +static void *g_e1d_ctx = NULL; +static int g_shutdown = 0; + +/*********************************************************************** + * stubs for external linkage of normal osmo-e1d + ***********************************************************************/ + +void e1_ts_stop(struct e1_ts *ts) {} +int e1d_vpair_create(struct e1_daemon *e1d, unsigned int num_lines) { return -1; } +struct e1_intf *e1d_vpair_intf_peer(struct e1_intf *intf) { return NULL; } + + + +struct e1gen_line_data { + struct ice1usb_tx_config tx_config; + struct ice1usb_rx_config rx_config; + struct osmo_e1f_instance e1f; +}; + +static void e1f_notify_cb(struct osmo_e1f_instance *e1f, enum osmo_e1f_notify_event evt, + bool present, void *data) +{ + struct e1_line *line = e1f->priv; + LOGPLI(line, DE1D, LOGL_NOTICE, "NOTIFY: %s %s\n", + osmo_e1f_notify_event_name(evt), present ? "PRESENT" : "ABSENT"); +} + +static const struct e1gen_line_data default_ld = { + .tx_config = { + .mode = ICE1USB_TX_MODE_TRANSP, + .timing = ICE1USB_TX_TIME_SRC_LOCAL, + .ext_loopback = ICE1USB_TX_EXT_LOOPBACK_OFF, + .alarm = 0, + }, + .rx_config = { + .mode = ICE1USB_RX_MODE_MULTIFRAME, + //.mode = ICE1USB_RX_MODE_TRANSP, + }, +}; + +struct e1gen_line_data *ensure_gld(struct e1_line *line) +{ + struct e1gen_line_data *gld = (struct e1gen_line_data *) line->e1gen_priv; + + if (gld) + return gld; + + LOGPLI(line, DE1D, LOGL_INFO, "Creating e1gen structures\n"); + + line->e1gen_priv = gld = talloc_zero(line, struct e1gen_line_data); + gld->tx_config = default_ld.tx_config; + gld->rx_config = default_ld.rx_config; + osmo_e1f_instance_init(&gld->e1f, "LINE", e1f_notify_cb, true, line); + + /* set the default configuration */ + e1_usb_ctrl_set_tx_cfg(line, gld->tx_config.mode, gld->tx_config.timing, + gld->tx_config.ext_loopback, gld->tx_config.alarm); + e1_usb_ctrl_set_rx_cfg(line, gld->rx_config.mode); + + return gld; +} + +/*! process (demultiplex) input data for the specified e1_line. + * \param[in] line E1 line for which to genrate output data + * \param[in] buf input buffer of multiplexed data received on line + * \param[in] size size of buf in octets */ +int e1_line_demux_in(struct e1_line *line, const uint8_t *buf, int size, int frame_base) +{ + struct e1gen_line_data *gld = ensure_gld(line); + int offs; + + for (offs = 0; offs + 32 <= size; offs += 32) { + //printf("Rx "OSMO_BIT_SPEC"\n", OSMO_BIT_PRINT(buf[offs])); + //printf("Rx %s\n", osmo_hexdump(buf + offs, 32)); + osmo_e1f_rx_frame(&gld->e1f, buf + offs); + } + return 0; +} + +/*! generate (multiplex) output data for the specified e1_line + * \param[in] line E1 line for which to genrate output data + * \param[in] buf caller-allocated output buffer for multiplexed data + * \param[in] fts number of E1 frames (32 bytes each) to generate + * \return number of bytes written to buf */ +int e1_line_mux_out(struct e1_line *line, uint8_t *buf, int fts) +{ + struct e1gen_line_data *gld = ensure_gld(line); + int f; + + //printf("FRAME:\n"); + for (f = 0; f < fts; f++) { + osmo_e1f_pull_tx_frame(&gld->e1f, buf + f*32); + //printf("Tx "OSMO_BIT_SPEC"\n", OSMO_BIT_PRINT(buf[f*32])); + //printf("Tx %s\n", osmo_hexdump(buf + f*32, 32)); + } + + return 32*fts; +} + +static struct cmd_node line_node = { + (enum node_type) LINE_NODE, + "%s(line)# ", + 1, +}; + +#define TX_STR "Transmitter\n" +#define OFF_ON_STR "Off\n" "On\n" + +DEFUN(line_tx_alarm, line_tx_alarm_cmd, + "tx report-alarm (0|1)", + TX_STR "Report Alarm (A-Bits)\n" OFF_ON_STR) +{ + struct e1_line *line = vty->index; + struct e1gen_line_data *gld = ensure_gld(line); + int on = atoi(argv[0]); + + gld->e1f.tx.remote_alarm = on ? true : false; + + return CMD_SUCCESS; +} + +DEFUN(line_tx_crc4, line_tx_crc4_cmd, + "tx generate-crc4 (0|1)", + TX_STR "Generate CRC4\n" OFF_ON_STR) +{ + struct e1_line *line = vty->index; + struct e1gen_line_data *gld = ensure_gld(line); + int on = atoi(argv[0]); + + gld->e1f.crc4_enabled = on ? true : false; + + return CMD_SUCCESS; +} + +DEFUN(line_tx_report_crc4, line_tx_report_crc4_cmd, + "tx report-crc4-error (0|1)", + TX_STR "Report a CRC4 Error\n" OFF_ON_STR) +{ + struct e1_line *line = vty->index; + struct e1gen_line_data *gld = ensure_gld(line); + int on = atoi(argv[0]); + + gld->e1f.tx.crc4_error = on ? true : false; + + return CMD_SUCCESS; +} + +DEFUN(line_tx_sa48, line_tx_sa48_cmd, + "tx sa4 <0-31>", + TX_STR "Set the SA4..SA8 bit content\n") +{ + struct e1_line *line = vty->index; + struct e1gen_line_data *gld = ensure_gld(line); + + gld->e1f.tx.sa4_sa8 = atoi(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN(line_tx_ais, line_tx_ais_cmd, + "tx ais (0|1)", + TX_STR "Transmit AIS (All-1)\n" OFF_ON_STR) +{ + struct e1_line *line = vty->index; + struct e1gen_line_data *gld = ensure_gld(line); + int on = atoi(argv[0]); + + gld->e1f.tx.ais = on ? true : false; + + return CMD_SUCCESS; +} + +DEFUN(line_show, line_show_cmd, + "show", "Show Line status\n") +{ + struct e1_line *line = vty->index; + struct e1gen_line_data *gld = ensure_gld(line); + struct osmo_e1f_instance *e1f = &gld->e1f; + + vty_out(vty, "Rx RemoteAlarm=%u, RemoteCrcErr=%u%s", + e1f->rx.remote_alarm, e1f->rx.remote_crc4_error, VTY_NEWLINE); + vty_out(vty, "Tx RemoteAlarm=%u, CrcErr=%u%s", + e1f->tx.remote_alarm, e1f->tx.crc4_error, VTY_NEWLINE); + + return CMD_SUCCESS; +} + +DEFUN(line, line_cmd, + "interface <0-255> line <0-225>", + "E1 Interface\n" "E1 Interface\n" + "E1 Line number\n" "E1 Line number\n") +{ + struct e1_intf *intf; + struct e1_line *line; + + intf = e1d_find_intf(vty_e1d, atoi(argv[0])); + if (!intf) { + vty_out(vty, "No such interface %s%s", argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + line = e1_intf_find_line(intf, atoi(argv[1])); + if (!line) { + vty_out(vty, "No such line %s in interface %s%s", argv[1], argv[0], VTY_NEWLINE); + return CMD_WARNING; + } + + vty->node = LINE_NODE; + vty->index = line; + + return CMD_SUCCESS; +} + +static void e1gen_init(void) +{ + install_element(ENABLE_NODE, &line_cmd); + install_node(&line_node, NULL); + install_element(LINE_NODE, &line_tx_alarm_cmd); + install_element(LINE_NODE, &line_tx_crc4_cmd); + install_element(LINE_NODE, &line_tx_report_crc4_cmd); + install_element(LINE_NODE, &line_tx_sa48_cmd); + install_element(LINE_NODE, &line_tx_ais_cmd); + install_element(LINE_NODE, &line_show_cmd); +} + +/*********************************************************************** + * initialization (mostly shared with osmo-e1d.c + ***********************************************************************/ + +static struct vty_app_info vty_info = { + .name = "osmo-e1gen", + .version = PACKAGE_VERSION, + .copyright = + "(C) 2019-2020 by Sylvain Munaut and Harald Welte\r\n", + "License GPLv2+: GNU GPL version 2 or later \r\n" + "This is free software: you are free to change and redistribute it.\r\n" + "There is NO WARRANTY, to the extent permitted by law.\r\n", +}; + +static void sig_handler(int signo) +{ + fprintf(stdout, "signal %d received\n", signo); + switch (signo) { + case SIGINT: + case SIGTERM: + fprintf(stdout, "shutting down\n"); + g_shutdown = 1; + break; + case SIGABRT: + case SIGUSR1: + talloc_report(g_e1d_ctx, stderr); + talloc_report_full(g_e1d_ctx, stderr); + break; + case SIGUSR2: + talloc_report_full(g_e1d_ctx, stderr); + break; + default: + break; + } +} + +static void print_help(void) +{ + printf(" Some useful help...\n"); + printf(" -h --help This text.\n"); + printf(" -d --debug option --debug=DE1D:DXFR enable debugging.\n"); + printf(" -c --config-file filename The config file to use.\n"); +} + +static void handle_options(int argc, char **argv) +{ + while (1) { + int option_index = 0, c; + static const struct option long_options[] = { + {"help", 0, 0, 'h'}, + {"debug", 1, 0, 'd'}, + {"config-file", 1, 0, 'c'}, + {0, 0, 0, 0} + }; + + c = getopt_long(argc, argv, "hd:c:", long_options, &option_index); + if (c == -1) + break; + + switch (c) { + case 'h': + print_help(); + exit(0); + break; + case 'd': + log_parse_category_mask(osmo_stderr_target, optarg); + break; + case 'c': + g_config_file = optarg; + break; + default: + fprintf(stderr, "Error in command line options. Exiting.\n"); + exit(-1); + } + } + + if (argc > optind) { + fprintf(stderr, "Unsupported positional arguments on command line\n"); + exit(2); + } +} + +int main(int argc, char **argv) +{ + struct e1_daemon *e1d = NULL; + struct sched_param sp; + int rv; + + /* talloc init */ + g_e1d_ctx = talloc_named_const(NULL, 0, "osmo-e1d"); + msgb_talloc_ctx_init(g_e1d_ctx, 0); + + /* logging init */ + osmo_init_logging2(g_e1d_ctx, &log_info); + + /* signals init */ + signal(SIGINT, &sig_handler); + signal(SIGTERM, &sig_handler); + signal(SIGABRT, &sig_handler); + signal(SIGUSR1, &sig_handler); + signal(SIGUSR2, &sig_handler); + osmo_init_ignore_signals(); + + /* rt prio */ + memset(&sp, 0x00, sizeof(sp)); + sp.sched_priority = 50; + rv = sched_setscheduler(0, SCHED_RR, &sp); + if (rv != 0) { + LOGP(DE1D, LOGL_ERROR, "Failed to set Real-Time priority. USB comms might be unstable.\n"); + perror("sched_setscheduler"); + } + + /* main state */ + e1d = talloc_zero(g_e1d_ctx, struct e1_daemon); + OSMO_ASSERT(e1d); + + INIT_LLIST_HEAD(&e1d->interfaces); + vty_init(&vty_info); + logging_vty_add_cmds(); + e1d_vty_init(e1d); + + handle_options(argc, argv); + + rv = vty_read_config_file(g_config_file, NULL); + if (rv < 0) { + LOGP(DE1D, LOGL_FATAL, "Failed to parse the config file '%s'\n", g_config_file); + exit(2); + } + + rv = telnet_init_dynif(g_e1d_ctx, e1d, vty_get_bind_addr(), OSMO_VTY_PORT_E1D); + if (rv != 0) { + LOGP(DE1D, LOGL_FATAL, "Failed to bind VTY interface to %s:%u\n", + vty_get_bind_addr(), OSMO_VTY_PORT_E1D); + exit(1); + } + + osmo_e1f_init(); + e1gen_init(); + + /* probe devices */ + rv = e1_usb_probe(e1d); + if (rv != 0) { + LOGP(DE1D, LOGL_ERROR, "Failed to prove usb devices\n"); + } + + /* main loop */ + while (!g_shutdown) { + osmo_select_main(0); + } + + talloc_free(e1d); + + return 0; +} -- cgit v1.2.3