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); }