Add HDLC timeslot mode
This is useful for protocols that use HDLC framing for signalling on E1
timeslots, but which don't use LAPD inside (our E1INP_TS_TYPE_SIGN).
Examples are particularly a MTP2/MTP3 SS7 stack, like it is used on the
A interfaces or on the core network interfaces of classic
circuit-switched networks.
Change-Id: I2d75801df4d7cbb8dad325f4d6689841f0196fa6
diff --git a/include/osmocom/abis/e1_input.h b/include/osmocom/abis/e1_input.h
index 7568f19..c99b133 100644
--- a/include/osmocom/abis/e1_input.h
+++ b/include/osmocom/abis/e1_input.h
@@ -65,9 +65,10 @@
E1INP_TS_TYPE_SIGN,
E1INP_TS_TYPE_TRAU,
E1INP_TS_TYPE_RAW,
+ E1INP_TS_TYPE_HDLC,
};
const char *e1inp_tstype_name(enum e1inp_ts_type tp);
-const struct value_string e1inp_ts_type_names[5];
+const struct value_string e1inp_ts_type_names[6];
/* A timeslot in the E1 interface */
struct e1inp_ts {
@@ -101,6 +102,12 @@
/* queue of pending to-be-transmitted msgbs */
struct llist_head tx_queue;
} raw;
+ struct {
+ /* call-back for every received frame */
+ void (*recv_cb)(struct e1inp_ts *ts, struct msgb *msg);
+ /* queue of pending to-be-transmitted msgbs */
+ struct llist_head tx_queue;
+ } hdlc;
};
union {
struct {
@@ -252,6 +259,11 @@
void (*raw_recv_cb)(struct e1inp_ts *ts,
struct msgb *msg));
+/* configure and initialize one timeslot dedicated to HDLC frames */
+int e1inp_ts_config_hdlc(struct e1inp_ts *ts, struct e1inp_line *line,
+ void (*hdlc_recv_cb)(struct e1inp_ts *ts,
+ struct msgb *msg));
+
/* Receive a packet from the E1 driver */
int e1inp_rx_ts(struct e1inp_ts *ts, struct msgb *msg,
uint8_t tei, uint8_t sapi);
diff --git a/src/e1_input.c b/src/e1_input.c
index 75ae36e..970bdb9 100644
--- a/src/e1_input.c
+++ b/src/e1_input.c
@@ -213,11 +213,12 @@
return get_value_string(e1inp_sign_type_names, tp);
}
-const struct value_string e1inp_ts_type_names[5] = {
+const struct value_string e1inp_ts_type_names[6] = {
{ E1INP_TS_TYPE_NONE, "None" },
{ E1INP_TS_TYPE_SIGN, "Signalling" },
{ E1INP_TS_TYPE_TRAU, "TRAU" },
{ E1INP_TS_TYPE_RAW, "RAW" },
+ { E1INP_TS_TYPE_HDLC, "HDLC" },
{ 0, NULL }
};
@@ -315,6 +316,21 @@
return 0;
}
+int e1inp_ts_config_hdlc(struct e1inp_ts *ts, struct e1inp_line *line,
+ void (*hdlc_recv_cb)(struct e1inp_ts *ts,
+ struct msgb *msg))
+{
+ if (ts->type == E1INP_TS_TYPE_HDLC && ts->line && line)
+ return 0;
+
+ ts->type = E1INP_TS_TYPE_HDLC;
+ ts->line = line;
+ ts->hdlc.recv_cb = hdlc_recv_cb;
+ INIT_LLIST_HEAD(&ts->hdlc.tx_queue);
+
+ return 0;
+}
+
struct e1inp_line *e1inp_line_find(uint8_t e1_nr)
{
struct e1inp_line *e1i_line;
@@ -550,6 +566,9 @@
case E1INP_TS_TYPE_RAW:
ts->raw.recv_cb(ts, msg);
break;
+ case E1INP_TS_TYPE_HDLC:
+ ts->hdlc.recv_cb(ts, msg);
+ break;
default:
ret = -EINVAL;
LOGP(DLMI, LOGL_ERROR, "unknown TS type %u\n", ts->type);
@@ -677,6 +696,10 @@
/* Get msgb from tx_queue */
msg = msgb_dequeue(&e1i_ts->raw.tx_queue);
break;
+ case E1INP_TS_TYPE_HDLC:
+ /* Get msgb from tx_queue */
+ msg = msgb_dequeue(&e1i_ts->hdlc.tx_queue);
+ break;
default:
LOGP(DLMI, LOGL_ERROR, "unsupported E1 TS type %u\n", e1i_ts->type);
return NULL;
diff --git a/src/input/dahdi.c b/src/input/dahdi.c
index db00f5f..0945daa 100644
--- a/src/input/dahdi.c
+++ b/src/input/dahdi.c
@@ -259,6 +259,51 @@
return 0;
}
+static void handle_hdlc_write(struct osmo_fd *bfd)
+{
+ struct e1inp_line *line = bfd->data;
+ unsigned int ts_nr = bfd->priv_nr;
+ struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
+ struct msgb *msg;
+ int ret;
+
+ /* get the next msg for this timeslot */
+ msg = e1inp_tx_ts(e1i_ts, NULL);
+ if (!msg)
+ return;
+
+ ret = write(bfd->fd, msg->data, msg->len + 2);
+ msgb_free(msg);
+ if (ret == -1)
+ handle_dahdi_exception(e1i_ts);
+ else if (ret < 0)
+ LOGP(DLMI, LOGL_NOTICE, "%s write failed %d\n", __func__, ret);
+}
+
+static int handle_hdlc_read(struct osmo_fd *bfd)
+{
+ struct e1inp_line *line = bfd->data;
+ unsigned int ts_nr = bfd->priv_nr;
+ struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
+ struct msgb *msg = msgb_alloc(TS1_ALLOC_SIZE, "DAHDI HDLC Rx");
+ int ret;
+
+ if (!msg)
+ return -ENOMEM;
+
+ ret = read(bfd->fd, msg->data, TS1_ALLOC_SIZE - 16);
+ if (ret == -1)
+ handle_dahdi_exception(e1i_ts);
+ else if (ret < 0) {
+ perror("read ");
+ }
+ msgb_put(msg, ret - 2);
+ if (ret <= 3) {
+ perror("read ");
+ }
+
+ return e1inp_rx_ts(e1i_ts, msg, 0, 0);
+}
static int invertbits = 1;
@@ -451,6 +496,14 @@
if (what & BSC_FD_WRITE)
rc = handle_ts1_write(bfd);
break;
+ case E1INP_TS_TYPE_HDLC:
+ if (what & BSC_FD_EXCEPT)
+ handle_dahdi_exception(e1i_ts);
+ if (what & BSC_FD_READ)
+ rc = handle_hdlc_read(bfd);
+ if (what & BSC_FD_WRITE)
+ handle_hdlc_write(bfd);
+ break;
case E1INP_TS_TYPE_TRAU:
if (what & BSC_FD_EXCEPT)
handle_dahdi_exception(e1i_ts);
@@ -620,6 +673,20 @@
dahdi_write_msg, bfd, e1inp_dlsap_up,
e1i_ts, &lapd_profile_abis);
break;
+ case E1INP_TS_TYPE_HDLC:
+ if (!bfd->fd)
+ bfd->fd = open(openstr, O_RDWR | O_NONBLOCK);
+ if (bfd->fd == -1) {
+ LOGP(DLINP, LOGL_ERROR,
+ "%s could not open %s %s\n",
+ __func__, openstr, strerror(errno));
+ return -EIO;
+ }
+ bfd->when = BSC_FD_READ | BSC_FD_EXCEPT;
+ ret = dahdi_set_bufinfo(bfd->fd, 1);
+ if (ret < 0)
+ return ret;
+ break;
case E1INP_TS_TYPE_TRAU:
case E1INP_TS_TYPE_RAW:
/* close/release LAPD instance, if any */
diff --git a/src/input/misdn.c b/src/input/misdn.c
index 391cd18..f72b496 100644
--- a/src/input/misdn.c
+++ b/src/input/misdn.c
@@ -607,6 +607,11 @@
case E1INP_TS_TYPE_NONE:
continue;
break;
+ case E1INP_TS_TYPE_HDLC:
+ bfd->fd = socket(PF_ISDN, SOCK_DGRAM,
+ ISDN_P_B_HDLC);
+ bfd->when = BSC_FD_READ;
+ break;
case E1INP_TS_TYPE_SIGN:
if (mline->use_userspace_lapd)
bfd->fd = socket(PF_ISDN, SOCK_DGRAM,
@@ -650,6 +655,7 @@
addr.tei = GROUP_TEI;
}
break;
+ case E1INP_TS_TYPE_HDLC:
case E1INP_TS_TYPE_TRAU:
addr.channel = ts;
break;