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]>