From ce307b4420e27b4a2c20aae25336ee5568a61c30 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Sun, 21 Aug 2011 01:18:05 +0200 Subject: mISDN: optionally bypass kernel LAPD code and use userspace LAPD The problem with kernel LAPD mainly is that you can only have one signalling timeslot in each E1 line/interface. However, with many BTS attached to the same line, we need multiple signalling slots per line. This allows the user to have a per-line selection between kernel-LAPD (misdn) and userspace-LAPD (misdn_lapd) drivers in the config file. --- src/e1_input_vty.c | 5 +- src/input/misdn.c | 182 +++++++++++++++++++++++++++++++++++++++++++---------- 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 +/* (C) 2008-2011 by Harald Welte * (C) 2009 by Holger Hans Peter Freyther * * 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 @@ -46,10 +59,16 @@ #include #include #include +#include #include #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); } -- cgit v1.2.3