aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/e1_input_vty.c5
-rw-r--r--src/input/misdn.c182
2 files changed, 152 insertions, 35 deletions
diff --git a/src/e1_input_vty.c b/src/e1_input_vty.c
index aa70805..bcc0251 100644
--- a/src/e1_input_vty.c
+++ b/src/e1_input_vty.c
@@ -38,8 +38,9 @@
/* CONFIG */
-#define E1_DRIVER_NAMES "(misdn|dahdi|ipa|hsl)"
-#define E1_DRIVER_HELP "mISDN supported E1 Card\n" \
+#define E1_DRIVER_NAMES "(misdn|misdn_lapd|dahdi|ipa|hsl)"
+#define E1_DRIVER_HELP "mISDN supported E1 Card (kernel LAPD)\n" \
+ "mISDN supported E1 Card (userspace LAPD)\n" \
"DAHDI supported E1/T1/J1 Card\n" \
"IPA TCP/IP input" \
"HSL TCP/IP input"
diff --git a/src/input/misdn.c b/src/input/misdn.c
index 2af81f2..716c1e7 100644
--- a/src/input/misdn.c
+++ b/src/input/misdn.c
@@ -1,6 +1,6 @@
/* OpenBSC Abis input driver for mISDNuser */
-/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
+/* (C) 2008-2011 by Harald Welte <laforge@gnumonks.org>
* (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
*
* All Rights Reserved
@@ -20,6 +20,19 @@
*
*/
+/*! \file misdn.c
+ * \brief Osmocom A-bis input driver for mISDN
+ *
+ * This driver has two modes of operations, exported via two different
+ * \ref e1_input_driver structures:
+ * "misdn" is the classic version and it uses the in-kernel LAPD
+ * implementation. This is somewhat limited in e.g. the fact that
+ * you can only have one E1 timeslot in signaling mode.
+ * "misdn_lapd" is a newer version which uses userspace LAPD code
+ * contained in libosmo-abis. It offers the same flexibilty as the
+ * DAHDI driver, i.e. any number of signaling slots.
+ */
+
#include "internal.h"
#include <stdio.h>
@@ -46,10 +59,16 @@
#include <osmocom/core/msgb.h>
#include <osmocom/core/logging.h>
#include <osmocom/abis/e1_input.h>
+#include <osmocom/abis/lapd.h>
#include <osmocom/core/talloc.h>
#define TS1_ALLOC_SIZE 300
+/*! \brief driver-specific data for \ref e1inp_line::driver_data */
+struct misdn_line {
+ int use_userspace_lapd;
+};
+
const struct value_string prim_names[] = {
{ PH_CONTROL_IND, "PH_CONTROL_IND" },
{ PH_DATA_IND, "PH_DATA_IND" },
@@ -70,6 +89,7 @@ const struct value_string prim_names[] = {
static int handle_ts1_read(struct osmo_fd *bfd)
{
struct e1inp_line *line = bfd->data;
+ struct misdn_line *mline = line->driver_data;
unsigned int ts_nr = bfd->priv_nr;
struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
struct e1inp_sign_link *link;
@@ -145,6 +165,10 @@ static int handle_ts1_read(struct osmo_fd *bfd)
case DL_UNITDATA_IND:
msg->l2h = msg->data + MISDN_HEADER_LEN;
DEBUGP(DLMI, "RX: %s\n", osmo_hexdump(msgb_l2(msg), ret - MISDN_HEADER_LEN));
+ if (mline->use_userspace_lapd) {
+ LOGP(DLMI, LOGL_ERROR, "DL_DATA_IND but userspace LAPD ?!?\n");
+ return -EIO;
+ }
ret = e1inp_rx_ts(e1i_ts, msg, l2addr.tei, l2addr.sapi);
break;
case PH_ACTIVATE_IND:
@@ -155,6 +179,17 @@ static int handle_ts1_read(struct osmo_fd *bfd)
DEBUGP(DLMI, "PH_DEACTIVATE_IND: channel(%d) sapi(%d) tei(%d)\n",
l2addr.channel, l2addr.sapi, l2addr.tei);
break;
+ case PH_DATA_IND:
+ if (!mline->use_userspace_lapd) {
+ LOGP(DLMI, LOGL_ERROR, "PH_DATA_IND but kernel LAPD ?!?\n");
+ return -EIO;
+ }
+ /* remove the Misdn Header */
+ msgb_pull(msg, MISDN_HEADER_LEN);
+ /* hand into the LAPD code */
+ DEBUGP(DLMI, "RX: %s\n", osmo_hexdump(msg->data, msg->len));
+ ret = e1inp_rx_ts_lapd(e1i_ts, msg);
+ break;
default:
break;
}
@@ -185,6 +220,7 @@ static void timeout_ts1_write(void *data)
static int handle_ts1_write(struct osmo_fd *bfd)
{
struct e1inp_line *line = bfd->data;
+ struct misdn_line *mline = line->driver_data;
unsigned int ts_nr = bfd->priv_nr;
struct e1inp_ts *e1i_ts = &line->ts[ts_nr-1];
struct e1inp_sign_link *sign_link;
@@ -203,26 +239,35 @@ static int handle_ts1_write(struct osmo_fd *bfd)
return 0;
}
- l2_data = msg->data;
-
- /* prepend the mISDNhead */
- hh = (struct mISDNhead *) msgb_push(msg, sizeof(*hh));
- hh->prim = DL_DATA_REQ;
-
- DEBUGP(DLMI, "TX channel(%d) TEI(%d) SAPI(%d): %s\n",
- sign_link->driver.misdn.channel, sign_link->tei,
- sign_link->sapi, osmo_hexdump(l2_data, msg->len - MISDN_HEADER_LEN));
-
- /* construct the sockaddr */
- sa.family = AF_ISDN;
- sa.sapi = sign_link->sapi;
- sa.dev = sign_link->tei;
- sa.channel = sign_link->driver.misdn.channel;
+ if (mline->use_userspace_lapd) {
+ DEBUGP(DLMI, "TX %u/%u/%u: %s\n",
+ line->num, sign_link->tei, sign_link->sapi,
+ osmo_hexdump(msg->data, msg->len));
+ lapd_transmit(e1i_ts->lapd, sign_link->tei,
+ sign_link->sapi, msg->data, msg->len);
+ } else {
+ l2_data = msg->data;
+
+ /* prepend the mISDNhead */
+ hh = (struct mISDNhead *) msgb_push(msg, sizeof(*hh));
+ hh->prim = DL_DATA_REQ;
+
+ DEBUGP(DLMI, "TX channel(%d) TEI(%d) SAPI(%d): %s\n",
+ sign_link->driver.misdn.channel, sign_link->tei,
+ sign_link->sapi, osmo_hexdump(l2_data, msg->len - MISDN_HEADER_LEN));
+
+ /* construct the sockaddr */
+ sa.family = AF_ISDN;
+ sa.sapi = sign_link->sapi;
+ sa.dev = sign_link->tei;
+ sa.channel = sign_link->driver.misdn.channel;
+
+ ret = sendto(bfd->fd, msg->data, msg->len, 0,
+ (struct sockaddr *)&sa, sizeof(sa));
+ if (ret < 0)
+ fprintf(stderr, "%s sendto failed %d\n", __func__, ret);
+ }
- ret = sendto(bfd->fd, msg->data, msg->len, 0,
- (struct sockaddr *)&sa, sizeof(sa));
- if (ret < 0)
- fprintf(stderr, "%s sendto failed %d\n", __func__, ret);
msgb_free(msg);
/* set tx delay timer for next event */
@@ -233,6 +278,29 @@ static int handle_ts1_write(struct osmo_fd *bfd)
return ret;
}
+/*! \brief call-back from LAPD code, called when it wants to Tx data */
+static void misdn_write_msg(uint8_t *data, int len, void *cbdata)
+{
+ struct osmo_fd *bfd = cbdata;
+ 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(1024, "mISDN PH_DATA_REQ");
+ struct mISDNhead *hh;
+ int ret;
+
+ hh = (struct mISDNhead *) msgb_put(msg, MISDN_HEADER_LEN);
+ hh->prim = PH_DATA_REQ;
+
+ memcpy(msgb_put(msg, len), data, len);
+
+ ret = write(bfd->fd, msg->data, msg->len);
+ if (ret < 0)
+ LOGP(DLMI, LOGL_NOTICE, "write failed %d\n", ret);
+
+ msgb_free(msg);
+}
+
#define BCHAN_TX_GRAN 160
/* write to a B channel TS */
static int handle_tsX_write(struct osmo_fd *bfd)
@@ -367,6 +435,7 @@ static int activate_bchan(struct e1inp_line *line, int ts, int act)
}
static int mi_e1_line_update(struct e1inp_line *line);
+static int mi_e1_line_update_lapd(struct e1inp_line *line);
struct e1inp_driver misdn_driver = {
.name = "misdn",
@@ -375,8 +444,16 @@ struct e1inp_driver misdn_driver = {
.line_update = &mi_e1_line_update,
};
+struct e1inp_driver misdn_lapd_driver = {
+ .name = "misdn_lapd",
+ .want_write = ts_want_write,
+ .default_delay = 50000,
+ .line_update = &mi_e1_line_update_lapd,
+};
+
static int mi_e1_setup(struct e1inp_line *line, int release_l2)
{
+ struct misdn_line *mline = line->driver_data;
int ts, ret;
/* TS0 is CRC4, don't need any fd for it */
@@ -395,7 +472,10 @@ static int mi_e1_setup(struct e1inp_line *line, int release_l2)
continue;
break;
case E1INP_TS_TYPE_SIGN:
- bfd->fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_LAPD_NT);
+ if (mline->use_userspace_lapd)
+ bfd->fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_B_HDLC);
+ else
+ bfd->fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_LAPD_NT);
bfd->when = BSC_FD_READ;
break;
case E1INP_TS_TYPE_TRAU:
@@ -418,11 +498,16 @@ static int mi_e1_setup(struct e1inp_line *line, int release_l2)
addr.dev = line->num;
switch (e1i_ts->type) {
case E1INP_TS_TYPE_SIGN:
- addr.channel = 0;
- /* SAPI not supported yet in kernel */
- //addr.sapi = e1inp_ts->sign.sapi;
- addr.sapi = 0;
- addr.tei = GROUP_TEI;
+ if (mline->use_userspace_lapd) {
+ addr.channel = ts;
+ e1i_ts->lapd = lapd_instance_alloc(1, misdn_write_msg, bfd);
+ } else {
+ addr.channel = 0;
+ /* SAPI not supported yet in kernel */
+ //addr.sapi = e1inp_ts->sign.sapi;
+ addr.sapi = 0;
+ addr.tei = GROUP_TEI;
+ }
break;
case E1INP_TS_TYPE_TRAU:
addr.channel = ts;
@@ -441,11 +526,14 @@ static int mi_e1_setup(struct e1inp_line *line, int release_l2)
}
if (e1i_ts->type == E1INP_TS_TYPE_SIGN) {
- ret = ioctl(bfd->fd, IMCLEAR_L2, &release_l2);
- if (ret < 0) {
- fprintf(stderr, "could not send IOCTL IMCLEAN_L2 %s\n", strerror(errno));
- return -EIO;
- }
+ if (!mline->use_userspace_lapd) {
+ ret = ioctl(bfd->fd, IMCLEAR_L2, &release_l2);
+ if (ret < 0) {
+ fprintf(stderr, "could not send IOCTL IMCLEAN_L2 %s\n", strerror(errno));
+ return -EIO;
+ }
+ } else
+ activate_bchan(line, ts, 1);
}
/* FIXME: only activate B-Channels once we start to
@@ -464,12 +552,13 @@ static int mi_e1_setup(struct e1inp_line *line, int release_l2)
return 0;
}
-static int mi_e1_line_update(struct e1inp_line *line)
+static int _mi_e1_line_update(struct e1inp_line *line)
{
struct mISDN_devinfo devinfo;
int sk, ret, cnt;
- if (line->driver != &misdn_driver)
+ if (line->driver != &misdn_driver &&
+ line->driver != &misdn_lapd_driver)
return -EINVAL;
/* open the ISDN card device */
@@ -517,8 +606,35 @@ static int mi_e1_line_update(struct e1inp_line *line)
return 0;
}
+static int mi_e1_line_update(struct e1inp_line *line)
+{
+ struct misdn_line *ml;
+
+ if (!line->driver_data)
+ line->driver_data = talloc_zero(line, struct misdn_line);
+
+ ml = line->driver_data;
+ ml->use_userspace_lapd = 0;
+
+ return _mi_e1_line_update(line);
+}
+
+static int mi_e1_line_update_lapd(struct e1inp_line *line)
+{
+ struct misdn_line *ml;
+
+ if (!line->driver_data)
+ line->driver_data = talloc_zero(line, struct misdn_line);
+
+ ml = line->driver_data;
+ ml->use_userspace_lapd = 1;
+
+ return _mi_e1_line_update(line);
+}
+
void e1inp_misdn_init(void)
{
/* register the driver with the core */
e1inp_driver_register(&misdn_driver);
+ e1inp_driver_register(&misdn_lapd_driver);
}