jolly has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-e1d/+/41139?usp=email )
Change subject: Add Channel-Associated Signalling (CAS) support ......................................................................
Add Channel-Associated Signalling (CAS) support
CAS frames are sent and received repeatedly. They consist of 16 frames (octets) on time slot 16. This is a CAS multi frame. This multiframe carries 30 individual CAS signaling channels.
Whenever a CAS frame is received, the included 30 CAS channels are forwarded to the application with a message of 30 octets, one octet for each CAS channel.
The application requests to transmit a CAS frame by sending a message with 30 octets, one for each CAS channel. Most recent CAS frame will be transmitted repeatedly until the application sends a new message.
The 30 octets in the message correspond to the signaling channels of these 30 time slots: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31
The lower 4 bits of each octet in the message represent the signaling sub-channels: A, B, C and D. They are packed like this: '0000ABCD'
Change-Id: Ib4f5e6ef02c9b0d1eec2a86d9c48376112805972 --- M include/osmocom/e1d/proto.h M src/ctl.c M src/e1d.h M src/mux_demux.c M src/proto.c M src/vty.c 6 files changed, 178 insertions(+), 0 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmo-e1d refs/changes/39/41139/1
diff --git a/include/osmocom/e1d/proto.h b/include/osmocom/e1d/proto.h index 6481b62..e4219df 100644 --- a/include/osmocom/e1d/proto.h +++ b/include/osmocom/e1d/proto.h @@ -118,6 +118,8 @@ E1DP_TSMODE_RAW = 0x10, /*! Timeslot is in HLDC-FCS mode; e1d will run software HDLC processor. */ E1DP_TSMODE_HDLCFCS = 0x11, + /*! Timeslot is in CAS mode; e1d will run software CAS processor. */ + E1DP_TSMODE_CAS = 0x12, };
/*! Flag that can be used as osmo_e1dp_ts_config.flags to force opening a TS. */ diff --git a/src/ctl.c b/src/ctl.c index a9818b3..d8f47ba 100644 --- a/src/ctl.c +++ b/src/ctl.c @@ -92,6 +92,9 @@ case E1_TS_MODE_HDLCFCS: ti->cfg.mode = E1DP_TSMODE_HDLCFCS; break; + case E1_TS_MODE_CAS: + ti->cfg.mode = E1DP_TSMODE_CAS; + break; case E1_TS_MODE_OFF: ti->cfg.mode = E1DP_TSMODE_OFF; break; @@ -141,6 +144,7 @@
switch (mode) { case E1_TS_MODE_HDLCFCS: + case E1_TS_MODE_CAS: sock_type = SOCK_SEQPACKET; break; case E1_TS_MODE_RAW: @@ -163,6 +167,11 @@ osmo_isdnhdlc_rcv_init(&ts->hdlc.rx, OSMO_HDLC_F_BITREVERSE); }
+ if (mode == E1_TS_MODE_CAS) { + memset(&ts->cas.tx, 0, sizeof(ts->cas.tx)); + memset(ts->cas.tx.buf, 0xff, sizeof(ts->cas.tx.buf)); + } + int flags = fcntl(ts->fd, F_GETFL); if (flags < 0) goto out_err; @@ -387,6 +396,14 @@ case E1DP_TSMODE_HDLCFCS: mode = E1_TS_MODE_HDLCFCS; break; + case E1DP_TSMODE_CAS: + if (hdr->ts != 16) { + LOGPTS(ts, DE1D, LOGL_NOTICE, "Client request CAS for timeslot %d, " + "only timeslot 16 is allowed.\n", hdr->ts); + return 0; + } + mode = E1_TS_MODE_CAS; + break; default: LOGPTS(ts, DE1D, LOGL_NOTICE, "Client request for unknown mode %u\n", cfg->mode); return 0; diff --git a/src/e1d.h b/src/e1d.h index 55e5415..0a2b265 100644 --- a/src/e1d.h +++ b/src/e1d.h @@ -75,6 +75,7 @@ E1_TS_MODE_OFF = 0, E1_TS_MODE_RAW, E1_TS_MODE_HDLCFCS, + E1_TS_MODE_CAS, }; extern const struct value_string e1_ts_mode_names[];
@@ -83,6 +84,26 @@ E1_FRAMING_MODE_NO_CRC4, };
+enum e1_cas_state { + E1_CAS_STATE_UNSYNC = 0, + E1_CAS_STATE_SYNC, +}; + +#define E1_CAS_SYNC_VALID 4 + +struct e1_cas_tx { + uint8_t frame_count; + uint8_t buf[30]; +}; + +struct e1_cas_rx { + enum e1_cas_state state; + uint8_t frame_count; + uint8_t sync_count; + uint8_t buf[30]; + bool new_data; +}; + struct e1_ts { struct e1_line *line; uint8_t id; @@ -101,6 +122,12 @@ int tx_len; } hdlc;
+ /* CAS handling */ + struct { + struct e1_cas_tx tx; + struct e1_cas_rx rx; + } cas; + /* RAW handling */ struct { uint8_t *rx_buf; /* actual buffer storage */ diff --git a/src/mux_demux.c b/src/mux_demux.c index 72379fe..f5bea91 100644 --- a/src/mux_demux.c +++ b/src/mux_demux.c @@ -118,6 +118,45 @@ return len; }
+static int +_e1_tx_cas(struct e1_ts *ts, uint8_t *buf, int len) +{ + int rv, i; + bool got_update = false; + + /* Get most recent update of all the signaling bits, if any. */ + while ((rv = recv(ts->fd, ts->cas.tx.buf, sizeof(ts->cas.tx.buf), MSG_TRUNC)) > 0) { + if (rv > (int)sizeof(ts->cas.tx.buf)) { + LOGPTS(ts, DXFR, LOGL_ERROR, "Truncated message: Client tries to " + "send %d bytes but our buffer is limited to %zu\n", + rv, sizeof(ts->cas.tx.buf)); + rv = sizeof(ts->cas.tx.buf); + } + LOGPTS(ts, DXFR, LOGL_DEBUG, "TX CAS Message: %s\n", osmo_hexdump(ts->cas.tx.buf, rv)); + for (i = 0; i < ((rv < 15) ? rv : 15); i++) { + if ((ts->cas.tx.buf[i] & 0xf) == 0x0) { + LOGPTS(ts, DXFR, LOGL_ERROR, + "TX CAS ERROR: Channel %d/30 imitates frame alignment.\n", i + 1); + } + } + got_update = true; + } + if (!got_update && ((rv < 0 && errno != EAGAIN) || rv == 0)) + return rv; + + for (i = 0; i < len; i++) { + if ((ts->cas.tx.frame_count) == 0) { + buf[i] = 0x0b; + } else { + buf[i] = ts->cas.tx.buf[(ts->cas.tx.frame_count) - 1] << 4; + buf[i] |= ts->cas.tx.buf[(ts->cas.tx.frame_count) + 14]; + } + ts->cas.tx.frame_count = (ts->cas.tx.frame_count + 1) & 0xf; + } + + return len; +} + /* read from a timeslot-FD (direction application -> hardware) */ static int _e1_ts_read(struct e1_ts *ts, uint8_t *buf, size_t len) @@ -131,6 +170,9 @@ case E1_TS_MODE_HDLCFCS: l = _e1_tx_hdlcfs(ts, buf, len); break; + case E1_TS_MODE_CAS: + l = _e1_tx_cas(ts, buf, len); + break; default: OSMO_ASSERT(0); break; @@ -306,6 +348,91 @@ return len; }
+static int +_e1_rx_cas(struct e1_ts *ts, const uint8_t *buf, int len) +{ + int rv = 1, i; + + for (i = 0; i < len; i++) { + switch (ts->cas.rx.state) { + case E1_CAS_STATE_UNSYNC: + /* Find sync mark '0000xxxx'. */ + if (!ts->cas.rx.sync_count) { + /* If we found our first sync mark, align with it. */ + if ((buf[i] & 0xf0) == 0x00) { + ts->cas.rx.sync_count = 1; + ts->cas.rx.frame_count = 0; + } + break; + } + /* This is not a sync mark. */ + if ((buf[i] & 0xf0) != 0x00) { + if (ts->cas.rx.frame_count != 0) + break; + /* If we expect a sync mark, reset sync counter. */ + ts->cas.rx.sync_count = 0; + break; + } + /* This is a sync mark. */ + if (ts->cas.rx.frame_count != 0) { + /* We got a sync mark at different frame count, align again. */ + ts->cas.rx.sync_count = 1; + ts->cas.rx.frame_count = 0; + break; + } + /* Count until the sync is valid. */ + if (++ts->cas.rx.sync_count < E1_CAS_SYNC_VALID) + break; + ts->cas.rx.state = E1_CAS_STATE_SYNC; + LOGPTS(ts, DE1D, LOGL_INFO, "CAS frame now in sync.\n"); + /* FALLTHRU */ + case E1_CAS_STATE_SYNC: + /* Check if we are still in sync. */ + if (ts->cas.rx.frame_count == 0) { + if ((buf[i] & 0xf0) == 0x00) { + /* Set valid-counter to upper limit. */ + ts->cas.rx.sync_count = E1_CAS_SYNC_VALID; + } else { + /* Count down until sync expires. */ + if (--ts->cas.rx.sync_count == 0) { + LOGPTS(ts, DE1D, LOGL_INFO, "CAS frame sync lost.\n"); + ts->cas.rx.sync_count = 0; + ts->cas.rx.state = E1_CAS_STATE_UNSYNC; + break; + } + } + break; + } + /* Skip frame, if sync was not found for this frame. */ + if (ts->cas.rx.sync_count < E1_CAS_SYNC_VALID) + break; + /* Store received subframe. */ + if (ts->cas.rx.buf[ts->cas.rx.frame_count - 1] != (buf[i] >> 4)) { + ts->cas.rx.buf[ts->cas.rx.frame_count - 1] = (buf[i] >> 4); + ts->cas.rx.new_data = true; + } + if (ts->cas.rx.buf[ts->cas.rx.frame_count + 14] != (buf[i] & 0x0f)) { + ts->cas.rx.buf[ts->cas.rx.frame_count + 14] = (buf[i] & 0x0f); + ts->cas.rx.new_data = true; + } + /* Forward mulitframe to application. Even if there is no change, the appliction + * may need it to clock a filter (for detecting stable signals). */ + if (ts->cas.rx.frame_count == 15) { + rv = write(ts->fd, ts->cas.rx.buf, sizeof(ts->cas.rx.buf)); + if (ts->cas.rx.new_data) { + LOGPTS(ts, DXFR, LOGL_DEBUG, "RX CAS Message: %s\n", + osmo_hexdump(ts->cas.rx.buf, sizeof(ts->cas.rx.buf))); + ts->cas.rx.new_data = false; + } + } + break; + } + ts->cas.rx.frame_count = (ts->cas.rx.frame_count + 1) % 16; + } + + return (rv <= 0) ? rv : i; +} + /* write data to a timeslot (hardware -> application direction) */ static int _e1_ts_write(struct e1_ts *ts, const uint8_t *buf, size_t len) @@ -319,6 +446,9 @@ case E1_TS_MODE_HDLCFCS: rv = _e1_rx_hdlcfs(ts, buf, len); break; + case E1_TS_MODE_CAS: + rv = _e1_rx_cas(ts, buf, len); + break; default: OSMO_ASSERT(0); break; diff --git a/src/proto.c b/src/proto.c index b7e50bc..acd7e8c 100644 --- a/src/proto.c +++ b/src/proto.c @@ -65,6 +65,7 @@ { E1DP_TSMODE_OFF, "OFF" }, { E1DP_TSMODE_RAW, "RAW" }, { E1DP_TSMODE_HDLCFCS, "HDLC-FCS" }, + { E1DP_TSMODE_CAS, "CAS" }, { 0, NULL } };
diff --git a/src/vty.c b/src/vty.c index 3e80204..77ed708 100644 --- a/src/vty.c +++ b/src/vty.c @@ -158,6 +158,7 @@ { E1_TS_MODE_OFF, "off" }, { E1_TS_MODE_RAW, "raw" }, { E1_TS_MODE_HDLCFCS, "hdlc-fcs" }, + { E1_TS_MODE_CAS, "cas" }, { 0, NULL } };