laforge has submitted this change. ( 
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 multiframe. This multiframe
carries 30 individual CAS signaling channels.

Whenever a CAS frames changes, an event (E1DP_EVT_CAS) is forwarded to
the e1d client. The event includes one octet with the CAS information.

The e1d client requests to transmit a new CAS frame by sending a command
(E1DP_CMD_CAS). The command includes one octet with the CAS information.

The e1d client requests to receive current CAS frame of a given time
slot by setting a query flag in the E1DP_CMD_CAS command. This is useful
if the CAS signals changed before the application was started.

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: 'xxxxABCD'

To enable cas support on an interface, set line attribute "cas" via VTY.

Change-Id: Ib4f5e6ef02c9b0d1eec2a86d9c48376112805972
---
M TODO-RELEASE
M include/osmocom/e1d/proto.h
M include/osmocom/e1d/proto_clnt.h
M src/ctl.c
M src/e1d.h
M src/intf_line.c
M src/mux_demux.c
M src/proto.c
M src/proto_clnt.c
M src/vty.c
10 files changed, 270 insertions(+), 0 deletions(-)

Approvals:
  tnt: Looks good to me, but someone else must approve
  laforge: Looks good to me, approved
  Jenkins Builder: Verified




diff --git a/TODO-RELEASE b/TODO-RELEASE
index 0ed7189..586a7ff 100644
--- a/TODO-RELEASE
+++ b/TODO-RELEASE
@@ -7,3 +7,5 @@
 # If any interfaces have been added since the last public release: c:r:a + 1.
 # If any interfaces have been removed or changed since the last public 
release: c:r:0.
 #library       what                    description / commit summary line
+libosmo-e1d    added                   New API function: 
osmo_e1dp_client_set_cas()
+libosmo-e1d    added                   New API event: E1DP_EVT_CAS,  struct 
osmo_e1dp_cas_bits
diff --git a/include/osmocom/e1d/proto.h b/include/osmocom/e1d/proto.h
index 6481b62..ea1a9ed 100644
--- a/include/osmocom/e1d/proto.h
+++ b/include/osmocom/e1d/proto.h
@@ -60,6 +60,10 @@
         * filter: intf (required), line (required), ts n/a; in: uint8_t; */
        E1DP_CMD_SABITS         = 0x05,

+       /*! Send CAS bits to line.
+        * filter: intf (required), line (required), ts (required), in: 
uint8_t; */
+       E1DP_CMD_CAS            = 0x06,
+
        /*! Received signal loss from interface. */
        E1DP_EVT_LOS_ON         = 0x40,

@@ -84,6 +88,10 @@
        /*! Ceased frame loss from interface. */
        E1DP_EVT_LOF_OFF        = 0x47,

+       /*! Received CAS bits from interface.
+        * out: uint8_t; */
+       E1DP_EVT_CAS            = 0x7e,
+
        /*! Received Sa bits from interface.
         * out: uint8_t; */
        E1DP_EVT_SABITS         = 0x7f,
@@ -182,6 +190,12 @@
        uint8_t status;                         /*< TBD */
 } __attribute__((packed));

+/*! Information about CAS bits */
+struct osmo_e1dp_cas_bits {
+       uint8_t bits;                           /*< CAS value */
+       uint8_t query_rx;                       /*< set to 1, to query RX CAS 
information */
+} __attribute__((packed));
+

 struct msgb *osmo_e1dp_recv(struct osmo_fd *ofd, int *fd);
 int osmo_e1dp_send(struct osmo_fd *ofd, struct msgb *msgb, int fd);
diff --git a/include/osmocom/e1d/proto_clnt.h b/include/osmocom/e1d/proto_clnt.h
index b5be2ce..e23e92b 100644
--- a/include/osmocom/e1d/proto_clnt.h
+++ b/include/osmocom/e1d/proto_clnt.h
@@ -44,6 +44,8 @@
 int osmo_e1dp_client_line_config(struct osmo_e1dp_client *clnt,
        uint8_t intf, uint8_t line, enum osmo_e1dp_line_mode mode);
 int osmo_e1dp_client_set_sa_bits(struct osmo_e1dp_client *clnt, uint8_t intf, 
uint8_t line, uint8_t sa_bits);
+int osmo_e1dp_client_set_cas(struct osmo_e1dp_client *clnt, uint8_t intf, 
uint8_t line, uint8_t ts,
+                            struct osmo_e1dp_cas_bits *cas);
 int osmo_e1dp_client_ts_open(struct osmo_e1dp_client *clnt,
        uint8_t intf, uint8_t line, uint8_t ts,
        enum osmo_e1dp_ts_mode mode, uint16_t read_bufsize);
diff --git a/src/ctl.c b/src/ctl.c
index a9818b3..52ac56c 100644
--- a/src/ctl.c
+++ b/src/ctl.c
@@ -379,6 +379,11 @@
                return 0;
        }

+       if (hdr->ts == 16 && line->ts[16].mode == E1_TS_MODE_CAS) {
+               LOGPLI(line, DE1D, LOGL_NOTICE, "Client request for timeslot %u 
in CAS mode\n", hdr->ts);
+               return 0;
+       }
+
        /* Select mode */
        switch (cfg->mode) {
        case E1DP_TSMODE_RAW:
@@ -432,6 +437,46 @@
 }

 static int
+_e1d_ctl_cas(void *data, struct msgb *msgb, struct msgb *rmsgb, int *rfd)
+{
+       struct e1_daemon *e1d = (struct e1_daemon *)data;
+       struct osmo_e1dp_msg_hdr *hdr = msgb_l1(msgb);
+       struct osmo_e1dp_cas_bits *cas = msgb_l2(msgb);
+       struct e1_intf *intf = NULL;
+       struct e1_line *line = NULL;
+
+       /* Process query and find timeslot */
+       intf = e1d_find_intf(e1d, hdr->intf);
+       if (!intf) {
+               LOGP(DE1D, LOGL_NOTICE, "Client request for non-existant 
Interface %u\n", hdr->intf);
+               return 0;
+       }
+
+       line = e1_intf_find_line(intf, hdr->line);
+       if (!line) {
+               LOGPIF(intf, DE1D, LOGL_NOTICE, "Client request for 
non-existant line %u\n", hdr->line);
+               return 0;
+       }
+
+       if (hdr->ts < 1 || hdr->ts == 16 || hdr->ts > 31) {
+               LOGPIF(intf, DE1D, LOGL_NOTICE, "Client request for invalid ts 
%u of line %u\n", hdr->ts, hdr->line);
+               return 0;
+       }
+
+       if (line->ts[16].mode != E1_TS_MODE_CAS) {
+               LOGPIF(intf, DE1D, LOGL_NOTICE, "Client request for CAS control 
on non CAS line %u\n", hdr->line);
+               return 0;
+       }
+
+       line->cas.tx.buf[hdr->ts - 1 - (hdr->ts >= 17)] = cas->bits;
+       /* Trigger CAS update. */
+       if (cas->query_rx)
+               line->cas.rx.buf_valid[hdr->ts - 1 - (hdr->ts >= 17)] = false;
+
+       return 0;
+}
+
+static int
 _e1d_ctl_sabits(void *data, struct msgb *msgb, struct msgb *rmsgb, int *rfd)
 {
        struct e1_daemon *e1d = (struct e1_daemon *)data;
@@ -498,6 +543,12 @@
                .fn = _e1d_ctl_ts_open,
        },
        {
+               .type = E1DP_CMD_CAS,
+               .flags = E1DP_SF_INTF_REQ | E1DP_SF_LINE_REQ | E1DP_SF_TS_REQ,
+               .payload_len = sizeof(struct osmo_e1dp_cas_bits),
+               .fn = _e1d_ctl_cas,
+       },
+       {
                .type = E1DP_CMD_SABITS,
                .flags = E1DP_SF_INTF_REQ | E1DP_SF_LINE_REQ,
                .payload_len = sizeof(uint8_t),
diff --git a/src/e1d.h b/src/e1d.h
index 55e5415..5015761 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,11 @@
        E1_FRAMING_MODE_NO_CRC4,
 };

+enum e1_cas_state {
+       E1_CAS_STATE_UNSYNC = 0,
+       E1_CAS_STATE_SYNC,
+};
+
 struct e1_ts {
        struct e1_line *line;
        uint8_t id;
@@ -126,6 +132,21 @@
 #define E1L_TS0_RX_CRC4_ERR    0x01
 #define E1L_TS0_RX_ALARM       0x02

+#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    buf_valid[30];
+};
+
 struct e1_line {
        struct llist_head list;

@@ -173,6 +194,12 @@
                } framing;
        } usb;

+       /* CAS handling */
+       struct {
+               struct e1_cas_tx tx;
+               struct e1_cas_rx rx;
+       } cas;
+
        void *e1gen_priv;
 };

diff --git a/src/intf_line.c b/src/intf_line.c
index 8eac84d..445fe5b 100644
--- a/src/intf_line.c
+++ b/src/intf_line.c
@@ -279,6 +279,8 @@
                _ts_init(&line->ts[i], line, i);
        _ts_init(&line->superchan, line, E1DP_TS_SUPERCHAN);

+       memset(line->cas.tx.buf, 0xff, sizeof(line->cas.tx.buf));
+
        INIT_LLIST_HEAD(&line->list);

        if (line_id == -1) {
diff --git a/src/mux_demux.c b/src/mux_demux.c
index 72379fe..0765079 100644
--- a/src/mux_demux.c
+++ b/src/mux_demux.c
@@ -118,6 +118,24 @@
        return len;
 }

+static int
+_e1_tx_cas(struct e1_line *line, uint8_t *buf, int len)
+{
+       int i;
+
+       for (i = 0; i < len; i++) {
+               if ((line->cas.tx.frame_count) == 0) {
+                       buf[i] = 0x0b;
+               } else {
+                       buf[i] = line->cas.tx.buf[(line->cas.tx.frame_count) - 
1] << 4;
+                       buf[i] |= line->cas.tx.buf[(line->cas.tx.frame_count) + 
14];
+               }
+               line->cas.tx.frame_count = (line->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 +149,9 @@
        case E1_TS_MODE_HDLCFCS:
                l = _e1_tx_hdlcfs(ts, buf, len);
                break;
+       case E1_TS_MODE_CAS:
+               l = _e1_tx_cas(ts->line, buf, len);
+               break;
        default:
                OSMO_ASSERT(0);
                break;
@@ -306,6 +327,89 @@
        return len;
 }

+static int
+_e1_rx_cas(struct e1_line *line, const uint8_t *buf, int len)
+{
+       int rv = 1, i;
+
+       for (i = 0; i < len; i++) {
+               switch (line->cas.rx.state) {
+               case E1_CAS_STATE_UNSYNC:
+                       /* Find sync mark '0000xxxx'. */
+                       if (!line->cas.rx.sync_count) {
+                               /* If we found our first sync mark, align with 
it. */
+                               if ((buf[i] & 0xf0) == 0x00) {
+                                       line->cas.rx.sync_count = 1;
+                                       line->cas.rx.frame_count = 0;
+                               }
+                               break;
+                       }
+                       /* This is not a sync mark. */
+                       if ((buf[i] & 0xf0) != 0x00) {
+                               if (line->cas.rx.frame_count != 0)
+                                       break;
+                               /* If we expect a sync mark, reset sync 
counter. */
+                               line->cas.rx.sync_count = 0;
+                               break;
+                       }
+                       /* This is a sync mark. */
+                       if (line->cas.rx.frame_count != 0) {
+                               /* We got a sync mark at different frame count, 
align again. */
+                               line->cas.rx.sync_count = 1;
+                               line->cas.rx.frame_count = 0;
+                               break;
+                       }
+                       /* Count until the sync is valid. */
+                       if (++line->cas.rx.sync_count < E1_CAS_SYNC_VALID)
+                               break;
+                       line->cas.rx.state = E1_CAS_STATE_SYNC;
+                       LOGPLI(line, DE1D, LOGL_INFO, "CAS frame now in 
sync.\n");
+                       /* FALLTHRU */
+               case E1_CAS_STATE_SYNC:
+                       /* Check if we are still in sync. */
+                       if (line->cas.rx.frame_count == 0) {
+                               if ((buf[i] & 0xf0) == 0x00) {
+                                       /* Set valid-counter to upper limit. */
+                                       line->cas.rx.sync_count = 
E1_CAS_SYNC_VALID;
+                               } else {
+                                       /* Count down until sync expires. */
+                                       if (--line->cas.rx.sync_count == 0) {
+                                               LOGPLI(line, DE1D, LOGL_INFO, 
"CAS frame sync lost.\n");
+                                               line->cas.rx.sync_count = 0;
+                                               line->cas.rx.state = 
E1_CAS_STATE_UNSYNC;
+                                               break;
+                                       }
+                               }
+                               break;
+                       }
+                       /* Skip frame, if sync was not found for this frame. */
+                       if (line->cas.rx.sync_count < E1_CAS_SYNC_VALID)
+                               break;
+                       /* Store received subframe. */
+                       if (!line->cas.rx.buf_valid[line->cas.rx.frame_count - 
1] ||
+                           line->cas.rx.buf[line->cas.rx.frame_count - 1] != 
(buf[i] >> 4)) {
+                               struct osmo_e1dp_cas_bits cas;
+                               cas.bits = 
line->cas.rx.buf[line->cas.rx.frame_count - 1] = (buf[i] >> 4);
+                               line->cas.rx.buf_valid[line->cas.rx.frame_count 
- 1] = true;
+                               osmo_e1dp_server_event(line->intf->e1d->srv, 
E1DP_EVT_CAS, line->intf->id, line->id,
+                                                      line->cas.rx.frame_count 
- 1 + 1, (uint8_t *)&cas, sizeof(cas));
+                       }
+                       if (!line->cas.rx.buf_valid[line->cas.rx.frame_count + 
14] ||
+                           line->cas.rx.buf[line->cas.rx.frame_count + 14] != 
(buf[i] & 0x0f)) {
+                               struct osmo_e1dp_cas_bits cas;
+                               cas.bits = 
line->cas.rx.buf[line->cas.rx.frame_count + 14] = (buf[i] & 0x0f);
+                               line->cas.rx.buf_valid[line->cas.rx.frame_count 
+ 14] = true;
+                               osmo_e1dp_server_event(line->intf->e1d->srv, 
E1DP_EVT_CAS, line->intf->id, line->id,
+                                                      line->cas.rx.frame_count 
- 1 + 17, (uint8_t *)&cas, sizeof(cas));
+                       }
+                       break;
+               }
+               line->cas.rx.frame_count = (line->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 +423,9 @@
        case E1_TS_MODE_HDLCFCS:
                rv = _e1_rx_hdlcfs(ts, buf, len);
                break;
+       case E1_TS_MODE_CAS:
+               rv = _e1_rx_cas(ts->line, buf, len);
+               break;
        default:
                OSMO_ASSERT(0);
                break;
diff --git a/src/proto.c b/src/proto.c
index b7e50bc..f284aaa 100644
--- a/src/proto.c
+++ b/src/proto.c
@@ -42,6 +42,7 @@
        { E1DP_CMD_TS_QUERY,    "CMD_TS_QUERY" },
        { E1DP_CMD_TS_OPEN,     "CMD_TS_OPEN" },
        { E1DP_CMD_SABITS,      "CMD_SABITS" },
+       { E1DP_CMD_CAS,         "CMD_CAS" },
        { E1DP_EVT_LOS_ON,      "EVT_LOS_ON" },
        { E1DP_EVT_LOS_OFF,     "EVT_LOS_OFF" },
        { E1DP_EVT_AIS_ON,      "EVT_AIS_ON" },
@@ -50,6 +51,7 @@
        { E1DP_EVT_RAI_OFF,     "EVT_RAI_OFF" },
        { E1DP_EVT_LOF_ON,      "EVT_LOF_ON" },
        { E1DP_EVT_LOF_OFF,     "EVT_LOF_OFF" },
+       { E1DP_EVT_CAS,         "EVT_CAS" },
        { E1DP_EVT_SABITS,      "EVT_SABITS" },
        { E1DP_RESP_TYPE,       "RESP_TYPE" },
        { E1DP_ERR_TYPE,        "ERR_TYPE" },
diff --git a/src/proto_clnt.c b/src/proto_clnt.c
index cfa9ca2..6ec6a8a 100644
--- a/src/proto_clnt.c
+++ b/src/proto_clnt.c
@@ -432,6 +432,41 @@
        return 0;
 }

+/*! Set CAS bits of a specific time-slot on E1 line in osmo-e1d.
+ *  \param[in] clnt Client previously returned from osmo_e1dp_client_create().
+ *  \param[in] intf E1 interface number to configure.
+ *  \param[in] line E1 line number (within interface) to configure.
+ *  \param[in] time-slot number (within line) to configure.
+ *  \param[in] CAS bits associated to this time-slot.
+ *  \returns zero in case of success; negative in case of error. */
+int
+osmo_e1dp_client_set_cas(struct osmo_e1dp_client *clnt, uint8_t intf, uint8_t 
line, uint8_t ts,
+                        struct osmo_e1dp_cas_bits *cas)
+{
+       struct msgb *msgb;
+       struct osmo_e1dp_msg_hdr hdr;
+       int rc;
+
+       memset(&hdr, 0x00, sizeof(struct osmo_e1dp_msg_hdr));
+       hdr.type = E1DP_CMD_CAS;
+       hdr.intf = intf;
+       hdr.line = line;
+       hdr.ts = ts;
+
+       rc = _e1dp_client_query_base(clnt, &hdr, cas, sizeof(*cas), &msgb, 
NULL);
+       if (rc)
+               return rc;
+
+       if (msgb_l2len(msgb) != 0) {
+               msgb_free(msgb);
+               return -EPIPE;
+       }
+
+       msgb_free(msgb);
+
+       return 0;
+}
+
 static int
 _client_ts_open(struct osmo_e1dp_client *clnt,
        uint8_t intf, uint8_t line, uint8_t ts,
diff --git a/src/vty.c b/src/vty.c
index 3e80204..c955a3f 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 }
 };

@@ -430,6 +431,29 @@
        return CMD_SUCCESS;
 }

+DEFUN(cfg_e1d_if_cas, cfg_e1d_if_line_cas_cmd,
+       "cas",
+       "Enable CAS signaling\n")
+{
+       struct e1_line *line = vty->index;
+       if (line->ts[16].mode != E1_TS_MODE_OFF && line->ts[16].mode != 
E1_TS_MODE_CAS) {
+               vty_out(vty, "%% Cannot set CAS signaling, timeslot 16 is 
already in use%s", VTY_NEWLINE);
+               return CMD_WARNING;
+       }
+       line->ts[16].mode = E1_TS_MODE_CAS;
+       return CMD_SUCCESS;
+}
+
+DEFUN(cfg_e1d_if_no_cas, cfg_e1d_if_line_no_cas_cmd,
+       "no cas",
+       NO_STR "Disable CAS signaling\n")
+{
+       struct e1_line *line = vty->index;
+       if (line->ts[16].mode == E1_TS_MODE_CAS)
+               line->ts[16].mode = E1_TS_MODE_OFF;
+       return CMD_SUCCESS;
+}
+
 DEFUN(cfg_e1d_if_line_framing, cfg_e1d_if_line_framing_cmd,
        "framing (no-crc4|crc4)",
        NO_STR "Sets the E1 framing mode for both TX and RX\n")
@@ -461,6 +485,8 @@
 {
        vty_out(vty, "  line %u%s", line->id, VTY_NEWLINE);
        vty_out(vty, "   mode %s%s", get_value_string(e1_line_mode_names, 
line->mode), VTY_NEWLINE);
+       if (line->ts[16].mode == E1_TS_MODE_CAS)
+               vty_out(vty, "   cas%s", VTY_NEWLINE);

        if (line->intf->drv == E1_DRIVER_USB) {
                if (line->usb.framing.tx !=  line->usb.framing.rx) {
@@ -562,6 +588,8 @@

        install_node(&line_node, NULL);
        install_element(LINE_NODE, &cfg_e1d_if_line_mode_cmd);
+       install_element(LINE_NODE, &cfg_e1d_if_line_cas_cmd);
+       install_element(LINE_NODE, &cfg_e1d_if_line_no_cas_cmd);
        install_element(LINE_NODE, &cfg_e1d_if_line_framing_cmd);
        install_element(LINE_NODE, &cfg_e1d_if_line_framing_txrx_cmd);
 }

--
To view, visit https://gerrit.osmocom.org/c/osmo-e1d/+/41139?usp=email
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings?usp=email

Gerrit-MessageType: merged
Gerrit-Project: osmo-e1d
Gerrit-Branch: master
Gerrit-Change-Id: Ib4f5e6ef02c9b0d1eec2a86d9c48376112805972
Gerrit-Change-Number: 41139
Gerrit-PatchSet: 9
Gerrit-Owner: jolly <[email protected]>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: jolly <[email protected]>
Gerrit-Reviewer: laforge <[email protected]>
Gerrit-Reviewer: pespin <[email protected]>
Gerrit-Reviewer: tnt <[email protected]>
Gerrit-CC: fixeria <[email protected]>

Reply via email to