aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src/input
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2011-03-03 23:29:05 +0100
committerHarald Welte <laforge@gnumonks.org>2011-03-03 23:29:05 +0100
commit31c00f7d6fa63937f2c973157d196a427f6eef95 (patch)
tree6b7c81d92b6a8b83d0588b2b59d47fd0cca7a052 /openbsc/src/input
parent9349d7ff7c5866110a1f2421ccc68a487e4030be (diff)
re-structure the OpenBSC directory layout
The new structure divides the code into a number of libraries for the BSC core functionality, MSC core functionality, Abis transport, TRAU and other bits. This doesn't introduce any functional code change but simply moves around files and alters Makefile.am accordingly. Next step would be to disentangle a lot of the inter-library dependencies and make the individual bits of code more independent.
Diffstat (limited to 'openbsc/src/input')
-rw-r--r--openbsc/src/input/dahdi.c494
-rw-r--r--openbsc/src/input/ipaccess.c797
-rw-r--r--openbsc/src/input/lapd.c710
-rw-r--r--openbsc/src/input/lapd.h46
-rw-r--r--openbsc/src/input/misdn.c542
5 files changed, 0 insertions, 2589 deletions
diff --git a/openbsc/src/input/dahdi.c b/openbsc/src/input/dahdi.c
deleted file mode 100644
index 2fc99f58e..000000000
--- a/openbsc/src/input/dahdi.c
+++ /dev/null
@@ -1,494 +0,0 @@
-/* OpenBSC Abis input driver for DAHDI */
-
-/* (C) 2008-2011 by Harald Welte <laforge@gnumonks.org>
- * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2010 by Digium and Matthew Fredrickson <creslin@digium.com>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- */
-
-#include "../../bscconfig.h"
-
-#ifdef HAVE_DAHDI_USER_H
-
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <string.h>
-#include <time.h>
-#include <sys/fcntl.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <arpa/inet.h>
-#include <dahdi/user.h>
-
-#include <osmocore/select.h>
-#include <osmocore/msgb.h>
-#include <openbsc/debug.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/abis_nm.h>
-#include <openbsc/abis_rsl.h>
-#include <openbsc/subchan_demux.h>
-#include <openbsc/e1_input.h>
-#include <openbsc/signal.h>
-#include <osmocore/talloc.h>
-
-#include "lapd.h"
-
-#define TS1_ALLOC_SIZE 300
-
-/* Corresponds to dahdi/user.h, only PRI related events */
-static const struct value_string dahdi_evt_names[] = {
- { DAHDI_EVENT_NONE, "NONE" },
- { DAHDI_EVENT_ALARM, "ALARM" },
- { DAHDI_EVENT_NOALARM, "NOALARM" },
- { DAHDI_EVENT_ABORT, "HDLC ABORT" },
- { DAHDI_EVENT_OVERRUN, "HDLC OVERRUN" },
- { DAHDI_EVENT_BADFCS, "HDLC BAD FCS" },
- { DAHDI_EVENT_REMOVED, "REMOVED" },
- { 0, NULL }
-};
-
-static void handle_dahdi_exception(struct e1inp_ts *ts)
-{
- int rc, evt;
- struct input_signal_data isd;
-
- rc = ioctl(ts->driver.dahdi.fd.fd, DAHDI_GETEVENT, &evt);
- if (rc < 0)
- return;
-
- LOGP(DMI, LOGL_NOTICE, "Line %u(%s) / TS %u DAHDI EVENT %s\n",
- ts->line->num, ts->line->name, ts->num,
- get_value_string(dahdi_evt_names, evt));
-
- isd.line = ts->line;
-
- switch (evt) {
- case DAHDI_EVENT_ALARM:
- /* we should notify the code that the line is gone */
- dispatch_signal(SS_INPUT, S_INP_LINE_ALARM, &isd);
- break;
- case DAHDI_EVENT_NOALARM:
- /* alarm has gone, we should re-start the SABM requests */
- dispatch_signal(SS_INPUT, S_INP_LINE_NOALARM, &isd);
- break;
- }
-}
-
-static int handle_ts1_read(struct bsc_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 TS1");
- lapd_mph_type prim;
- unsigned int sapi, tei;
- int ilen, ret;
- uint8_t *idata;
-
- 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 ");
- }
-
- sapi = msg->data[0] >> 2;
- tei = msg->data[1] >> 1;
-
- DEBUGP(DMI, "<= len = %d, sapi(%d) tei(%d)", ret, sapi, tei);
-
- idata = lapd_receive(e1i_ts->driver.dahdi.lapd, msg->data, msg->len, &ilen, &prim);
- if (!idata && prim == 0)
- return -EIO;
-
- msgb_pull(msg, 2);
-
- DEBUGP(DMI, "prim %08x\n", prim);
-
- switch (prim) {
- case 0:
- break;
- case LAPD_MPH_ACTIVATE_IND:
- DEBUGP(DMI, "MPH_ACTIVATE_IND: sapi(%d) tei(%d)\n", sapi, tei);
- ret = e1inp_event(e1i_ts, S_INP_TEI_UP, tei, sapi);
- break;
- case LAPD_MPH_DEACTIVATE_IND:
- DEBUGP(DMI, "MPH_DEACTIVATE_IND: sapi(%d) tei(%d)\n", sapi, tei);
- ret = e1inp_event(e1i_ts, S_INP_TEI_DN, tei, sapi);
- break;
- case LAPD_DL_DATA_IND:
- case LAPD_DL_UNITDATA_IND:
- if (prim == LAPD_DL_DATA_IND)
- msg->l2h = msg->data + 2;
- else
- msg->l2h = msg->data + 1;
- DEBUGP(DMI, "RX: %s\n", hexdump(msgb_l2(msg), ret));
- ret = e1inp_rx_ts(e1i_ts, msg, tei, sapi);
- break;
- default:
- printf("ERROR: unknown prim\n");
- break;
- }
-
- DEBUGP(DMI, "Returned ok\n");
- return ret;
-}
-
-static int ts_want_write(struct e1inp_ts *e1i_ts)
-{
- /* We never include the DAHDI B-Channel FD into the
- * writeset, since it doesn't support poll() based
- * write flow control */
- if (e1i_ts->type == E1INP_TS_TYPE_TRAU) {
- fprintf(stderr, "Trying to write TRAU ts\n");
- return 0;
- }
-
- e1i_ts->driver.dahdi.fd.when |= BSC_FD_WRITE;
-
- return 0;
-}
-
-static void timeout_ts1_write(void *data)
-{
- struct e1inp_ts *e1i_ts = (struct e1inp_ts *)data;
-
- /* trigger write of ts1, due to tx delay timer */
- ts_want_write(e1i_ts);
-}
-
-static void dahdi_write_msg(uint8_t *data, int len, void *cbdata)
-{
- struct bsc_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];
- int ret;
-
- ret = write(bfd->fd, data, len + 2);
- if (ret == -1)
- handle_dahdi_exception(e1i_ts);
- else if (ret < 0)
- LOGP(DMI, LOGL_NOTICE, "%s write failed %d\n", __func__, ret);
-}
-
-static int handle_ts1_write(struct bsc_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 e1inp_sign_link *sign_link;
- struct msgb *msg;
-
- bfd->when &= ~BSC_FD_WRITE;
-
- /* get the next msg for this timeslot */
- msg = e1inp_tx_ts(e1i_ts, &sign_link);
- if (!msg) {
- /* no message after tx delay timer */
- return 0;
- }
-
- DEBUGP(DMI, "TX: %s\n", hexdump(msg->data, msg->len));
- lapd_transmit(e1i_ts->driver.dahdi.lapd, sign_link->tei,
- sign_link->sapi, msg->data, msg->len);
- msgb_free(msg);
-
- /* set tx delay timer for next event */
- e1i_ts->sign.tx_timer.cb = timeout_ts1_write;
- e1i_ts->sign.tx_timer.data = e1i_ts;
- bsc_schedule_timer(&e1i_ts->sign.tx_timer, 0, 50000);
-
- return 0;
-}
-
-
-static int invertbits = 1;
-
-static u_int8_t flip_table[256];
-
-static void init_flip_bits(void)
-{
- int i,k;
-
- for (i = 0 ; i < 256 ; i++) {
- u_int8_t sample = 0 ;
- for (k = 0; k<8; k++) {
- if ( i & 1 << k ) sample |= 0x80 >> k;
- }
- flip_table[i] = sample;
- }
-}
-
-static u_int8_t * flip_buf_bits ( u_int8_t * buf , int len)
-{
- int i;
- u_int8_t * start = buf;
-
- for (i = 0 ; i < len; i++) {
- buf[i] = flip_table[(u_int8_t)buf[i]];
- }
-
- return start;
-}
-
-#define D_BCHAN_TX_GRAN 160
-/* write to a B channel TS */
-static int handle_tsX_write(struct bsc_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];
- u_int8_t tx_buf[D_BCHAN_TX_GRAN];
- struct subch_mux *mx = &e1i_ts->trau.mux;
- int ret;
-
- ret = subchan_mux_out(mx, tx_buf, D_BCHAN_TX_GRAN);
-
- if (ret != D_BCHAN_TX_GRAN) {
- fprintf(stderr, "Huh, got ret of %d\n", ret);
- if (ret < 0)
- return ret;
- }
-
- DEBUGP(DMIB, "BCHAN TX: %s\n",
- hexdump(tx_buf, D_BCHAN_TX_GRAN));
-
- if (invertbits) {
- flip_buf_bits(tx_buf, ret);
- }
-
- ret = write(bfd->fd, tx_buf, ret);
- if (ret < D_BCHAN_TX_GRAN)
- fprintf(stderr, "send returns %d instead of %d\n", ret,
- D_BCHAN_TX_GRAN);
-
- return ret;
-}
-
-#define D_TSX_ALLOC_SIZE (D_BCHAN_TX_GRAN)
-/* FIXME: read from a B channel TS */
-static int handle_tsX_read(struct bsc_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(D_TSX_ALLOC_SIZE, "DAHDI TSx");
- int ret;
-
- if (!msg)
- return -ENOMEM;
-
- ret = read(bfd->fd, msg->data, D_TSX_ALLOC_SIZE);
- if (ret < 0 || ret != D_TSX_ALLOC_SIZE) {
- fprintf(stderr, "read error %d %s\n", ret, strerror(errno));
- return ret;
- }
-
- if (invertbits) {
- flip_buf_bits(msg->data, ret);
- }
-
- msgb_put(msg, ret);
-
- msg->l2h = msg->data;
- DEBUGP(DMIB, "BCHAN RX: %s\n",
- hexdump(msgb_l2(msg), ret));
- ret = e1inp_rx_ts(e1i_ts, msg, 0, 0);
- /* physical layer indicates that data has been sent,
- * we thus can send some more data */
- ret = handle_tsX_write(bfd);
- msgb_free(msg);
-
- return ret;
-}
-
-/* callback from select.c in case one of the fd's can be read/written */
-static int dahdi_fd_cb(struct bsc_fd *bfd, unsigned int what)
-{
- struct e1inp_line *line = bfd->data;
- unsigned int ts_nr = bfd->priv_nr;
- unsigned int idx = ts_nr-1;
- struct e1inp_ts *e1i_ts = &line->ts[idx];
- int rc = 0;
-
- switch (e1i_ts->type) {
- case E1INP_TS_TYPE_SIGN:
- if (what & BSC_FD_EXCEPT)
- handle_dahdi_exception(e1i_ts);
- if (what & BSC_FD_READ)
- rc = handle_ts1_read(bfd);
- if (what & BSC_FD_WRITE)
- rc = handle_ts1_write(bfd);
- break;
- case E1INP_TS_TYPE_TRAU:
- if (what & BSC_FD_EXCEPT)
- handle_dahdi_exception(e1i_ts);
- if (what & BSC_FD_READ)
- rc = handle_tsX_read(bfd);
- if (what & BSC_FD_WRITE)
- rc = handle_tsX_write(bfd);
- /* We never include the DAHDI B-Channel FD into the
- * writeset, since it doesn't support poll() based
- * write flow control */
- break;
- default:
- fprintf(stderr, "unknown E1 TS type %u\n", e1i_ts->type);
- break;
- }
-
- return rc;
-}
-
-static int dahdi_e1_line_update(struct e1inp_line *line);
-
-struct e1inp_driver dahdi_driver = {
- .name = "dahdi",
- .want_write = ts_want_write,
- .line_update = &dahdi_e1_line_update,
-};
-
-void dahdi_set_bufinfo(int fd, int as_sigchan)
-{
- struct dahdi_bufferinfo bi;
- int x = 0;
-
- if (ioctl(fd, DAHDI_GET_BUFINFO, &bi)) {
- fprintf(stderr, "Error getting bufinfo\n");
- exit(-1);
- }
-
- if (as_sigchan) {
- bi.numbufs = 4;
- bi.bufsize = 512;
- } else {
- bi.numbufs = 8;
- bi.bufsize = D_BCHAN_TX_GRAN;
- bi.txbufpolicy = DAHDI_POLICY_WHEN_FULL;
- }
-
- if (ioctl(fd, DAHDI_SET_BUFINFO, &bi)) {
- fprintf(stderr, "Error setting bufinfo\n");
- exit(-1);
- }
-
- if (!as_sigchan) {
- if (ioctl(fd, DAHDI_AUDIOMODE, &x)) {
- fprintf(stderr, "Error setting bufinfo\n");
- exit(-1);
- }
- } else {
- int one = 1;
- ioctl(fd, DAHDI_HDLCFCSMODE, &one);
- /* we cannot reliably check for the ioctl return value here
- * as this command will fail if the slot _already_ was a
- * signalling slot before :( */
- }
-}
-
-static int dahdi_e1_setup(struct e1inp_line *line)
-{
- int ts, ret;
-
- /* TS0 is CRC4, don't need any fd for it */
- for (ts = 1; ts < NUM_E1_TS; ts++) {
- unsigned int idx = ts-1;
- char openstr[128];
- struct e1inp_ts *e1i_ts = &line->ts[idx];
- struct bsc_fd *bfd = &e1i_ts->driver.dahdi.fd;
-
- bfd->data = line;
- bfd->priv_nr = ts;
- bfd->cb = dahdi_fd_cb;
- snprintf(openstr, sizeof(openstr), "/dev/dahdi/%d", ts);
-
- switch (e1i_ts->type) {
- case E1INP_TS_TYPE_NONE:
- continue;
- break;
- case E1INP_TS_TYPE_SIGN:
- bfd->fd = open(openstr, O_RDWR | O_NONBLOCK);
- if (bfd->fd == -1) {
- fprintf(stderr, "%s could not open %s %s\n",
- __func__, openstr, strerror(errno));
- exit(-1);
- }
- bfd->when = BSC_FD_READ | BSC_FD_EXCEPT;
- dahdi_set_bufinfo(bfd->fd, 1);
- e1i_ts->driver.dahdi.lapd = lapd_instance_alloc(1, dahdi_write_msg, bfd);
- break;
- case E1INP_TS_TYPE_TRAU:
- bfd->fd = open(openstr, O_RDWR | O_NONBLOCK);
- if (bfd->fd == -1) {
- fprintf(stderr, "%s could not open %s %s\n",
- __func__, openstr, strerror(errno));
- exit(-1);
- }
- dahdi_set_bufinfo(bfd->fd, 0);
- /* We never include the DAHDI B-Channel FD into the
- * writeset, since it doesn't support poll() based
- * write flow control */
- bfd->when = BSC_FD_READ | BSC_FD_EXCEPT;// | BSC_FD_WRITE;
- break;
- }
-
- if (bfd->fd < 0) {
- fprintf(stderr, "%s could not open %s %s\n",
- __func__, openstr, strerror(errno));
- return bfd->fd;
- }
-
- ret = bsc_register_fd(bfd);
- if (ret < 0) {
- fprintf(stderr, "could not register FD: %s\n",
- strerror(ret));
- return ret;
- }
- }
-
- return 0;
-}
-
-static int dahdi_e1_line_update(struct e1inp_line *line)
-{
- if (line->driver != &dahdi_driver)
- return -EINVAL;
-
- return dahdi_e1_setup(line);
-}
-
-int e1inp_dahdi_init(void)
-{
- init_flip_bits();
-
- /* register the driver with the core */
- return e1inp_driver_register(&dahdi_driver);
-}
-
-#endif /* HAVE_DAHDI_USER_H */
diff --git a/openbsc/src/input/ipaccess.c b/openbsc/src/input/ipaccess.c
deleted file mode 100644
index dcf8d1a53..000000000
--- a/openbsc/src/input/ipaccess.c
+++ /dev/null
@@ -1,797 +0,0 @@
-/* OpenBSC Abis input driver for ip.access */
-
-/* (C) 2009 by Harald Welte <laforge@gnumonks.org>
- * (C) 2010 by Holger Hans Peter Freyther
- * (C) 2010 by On-Waves
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <string.h>
-#include <time.h>
-#include <sys/fcntl.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <arpa/inet.h>
-
-#include <osmocore/select.h>
-#include <osmocore/tlv.h>
-#include <osmocore/msgb.h>
-#include <osmocore/talloc.h>
-#include <openbsc/debug.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/abis_nm.h>
-#include <openbsc/abis_rsl.h>
-#include <openbsc/subchan_demux.h>
-#include <openbsc/e1_input.h>
-#include <openbsc/ipaccess.h>
-#include <openbsc/socket.h>
-#include <openbsc/signal.h>
-
-#define PRIV_OML 1
-#define PRIV_RSL 2
-
-/* data structure for one E1 interface with A-bis */
-struct ia_e1_handle {
- struct bsc_fd listen_fd;
- struct bsc_fd rsl_listen_fd;
- struct gsm_network *gsmnet;
-};
-
-static struct ia_e1_handle *e1h;
-
-
-#define TS1_ALLOC_SIZE 900
-
-static const u_int8_t pong[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_PONG };
-static const u_int8_t id_ack[] = { 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_ACK };
-static const u_int8_t id_req[] = { 0, 17, IPAC_PROTO_IPACCESS, IPAC_MSGT_ID_GET,
- 0x01, IPAC_IDTAG_UNIT,
- 0x01, IPAC_IDTAG_MACADDR,
- 0x01, IPAC_IDTAG_LOCATION1,
- 0x01, IPAC_IDTAG_LOCATION2,
- 0x01, IPAC_IDTAG_EQUIPVERS,
- 0x01, IPAC_IDTAG_SWVERSION,
- 0x01, IPAC_IDTAG_UNITNAME,
- 0x01, IPAC_IDTAG_SERNR,
- };
-
-static const char *idtag_names[] = {
- [IPAC_IDTAG_SERNR] = "Serial_Number",
- [IPAC_IDTAG_UNITNAME] = "Unit_Name",
- [IPAC_IDTAG_LOCATION1] = "Location_1",
- [IPAC_IDTAG_LOCATION2] = "Location_2",
- [IPAC_IDTAG_EQUIPVERS] = "Equipment_Version",
- [IPAC_IDTAG_SWVERSION] = "Software_Version",
- [IPAC_IDTAG_IPADDR] = "IP_Address",
- [IPAC_IDTAG_MACADDR] = "MAC_Address",
- [IPAC_IDTAG_UNIT] = "Unit_ID",
-};
-
-static const char *ipac_idtag_name(int tag)
-{
- if (tag >= ARRAY_SIZE(idtag_names))
- return "unknown";
-
- return idtag_names[tag];
-}
-
-int ipaccess_idtag_parse(struct tlv_parsed *dec, unsigned char *buf, int len)
-{
- u_int8_t t_len;
- u_int8_t t_tag;
- u_int8_t *cur = buf;
-
- memset(dec, 0, sizeof(*dec));
-
- while (len >= 2) {
- len -= 2;
- t_len = *cur++;
- t_tag = *cur++;
-
- if (t_len > len + 1) {
- LOGP(DMI, LOGL_ERROR, "The tag does not fit: %d\n", t_len);
- return -1;
- }
-
- DEBUGPC(DMI, "%s='%s' ", ipac_idtag_name(t_tag), cur);
-
- dec->lv[t_tag].len = t_len;
- dec->lv[t_tag].val = cur;
-
- cur += t_len;
- len -= t_len;
- }
- return 0;
-}
-
-struct gsm_bts *find_bts_by_unitid(struct gsm_network *net,
- u_int16_t site_id, u_int16_t bts_id)
-{
- struct gsm_bts *bts;
-
- llist_for_each_entry(bts, &net->bts_list, list) {
-
- if (!is_ipaccess_bts(bts))
- continue;
-
- if (bts->ip_access.site_id == site_id &&
- bts->ip_access.bts_id == bts_id)
- return bts;
- }
-
- return NULL;
-}
-
-static int parse_unitid(const char *str, u_int16_t *site_id, u_int16_t *bts_id,
- u_int16_t *trx_id)
-{
- unsigned long ul;
- char *endptr;
- const char *nptr;
-
- nptr = str;
- ul = strtoul(nptr, &endptr, 10);
- if (endptr <= nptr)
- return -EINVAL;
- if (site_id)
- *site_id = ul & 0xffff;
-
- if (*endptr++ != '/')
- return -EINVAL;
-
- nptr = endptr;
- ul = strtoul(nptr, &endptr, 10);
- if (endptr <= nptr)
- return -EINVAL;
- if (bts_id)
- *bts_id = ul & 0xffff;
-
- if (*endptr++ != '/')
- return -EINVAL;
-
- nptr = endptr;
- ul = strtoul(nptr, &endptr, 10);
- if (endptr <= nptr)
- return -EINVAL;
- if (trx_id)
- *trx_id = ul & 0xffff;
-
- return 0;
-}
-
-/* send the id ack */
-int ipaccess_send_id_ack(int fd)
-{
- return write(fd, id_ack, sizeof(id_ack));
-}
-
-int ipaccess_send_id_req(int fd)
-{
- return write(fd, id_req, sizeof(id_req));
-}
-
-/* base handling of the ip.access protocol */
-int ipaccess_rcvmsg_base(struct msgb *msg,
- struct bsc_fd *bfd)
-{
- u_int8_t msg_type = *(msg->l2h);
- int ret = 0;
-
- switch (msg_type) {
- case IPAC_MSGT_PING:
- ret = write(bfd->fd, pong, sizeof(pong));
- break;
- case IPAC_MSGT_PONG:
- DEBUGP(DMI, "PONG!\n");
- break;
- case IPAC_MSGT_ID_ACK:
- DEBUGP(DMI, "ID_ACK? -> ACK!\n");
- ret = ipaccess_send_id_ack(bfd->fd);
- break;
- }
- return 0;
-}
-
-static int ipaccess_rcvmsg(struct e1inp_line *line, struct msgb *msg,
- struct bsc_fd *bfd)
-{
- struct tlv_parsed tlvp;
- u_int8_t msg_type = *(msg->l2h);
- u_int16_t site_id = 0, bts_id = 0, trx_id = 0;
- struct gsm_bts *bts;
- char *unitid;
- int len;
-
- /* handle base messages */
- ipaccess_rcvmsg_base(msg, bfd);
-
- switch (msg_type) {
- case IPAC_MSGT_ID_RESP:
- DEBUGP(DMI, "ID_RESP ");
- /* parse tags, search for Unit ID */
- ipaccess_idtag_parse(&tlvp, (u_int8_t *)msg->l2h + 2,
- msgb_l2len(msg)-2);
- DEBUGP(DMI, "\n");
-
- if (!TLVP_PRESENT(&tlvp, IPAC_IDTAG_UNIT))
- break;
-
- len = TLVP_LEN(&tlvp, IPAC_IDTAG_UNIT);
- if (len < 1)
- break;
-
- /* lookup BTS, create sign_link, ... */
- unitid = (char *) TLVP_VAL(&tlvp, IPAC_IDTAG_UNIT);
- unitid[len - 1] = '\0';
- parse_unitid(unitid, &site_id, &bts_id, &trx_id);
- bts = find_bts_by_unitid(e1h->gsmnet, site_id, bts_id);
- if (!bts) {
- LOGP(DINP, LOGL_ERROR, "Unable to find BTS configuration for "
- " %u/%u/%u, disconnecting\n", site_id, bts_id,
- trx_id);
- return -EIO;
- }
- DEBUGP(DINP, "Identified BTS %u/%u/%u\n", site_id, bts_id, trx_id);
- if (bfd->priv_nr == PRIV_OML) {
- /* drop any old oml connection */
- ipaccess_drop_oml(bts);
- bts->oml_link = e1inp_sign_link_create(&line->ts[PRIV_OML - 1],
- E1INP_SIGN_OML, bts->c0,
- bts->oml_tei, 0);
- } else if (bfd->priv_nr == PRIV_RSL) {
- struct e1inp_ts *e1i_ts;
- struct bsc_fd *newbfd;
- struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, trx_id);
-
- /* drop any old rsl connection */
- ipaccess_drop_rsl(trx);
-
- if (!bts->oml_link) {
- bsc_unregister_fd(bfd);
- close(bfd->fd);
- bfd->fd = -1;
- talloc_free(bfd);
- return 0;
- }
-
- bfd->data = line = bts->oml_link->ts->line;
- e1i_ts = &line->ts[PRIV_RSL + trx_id - 1];
- newbfd = &e1i_ts->driver.ipaccess.fd;
- e1inp_ts_config(e1i_ts, line, E1INP_TS_TYPE_SIGN);
-
- trx->rsl_link = e1inp_sign_link_create(e1i_ts,
- E1INP_SIGN_RSL, trx,
- trx->rsl_tei, 0);
- trx->rsl_link->ts->sign.delay = 0;
-
- /* get rid of our old temporary bfd */
- memcpy(newbfd, bfd, sizeof(*newbfd));
- newbfd->priv_nr = PRIV_RSL + trx_id;
- bsc_unregister_fd(bfd);
- bfd->fd = -1;
- talloc_free(bfd);
- bsc_register_fd(newbfd);
- }
- break;
- }
- return 0;
-}
-
-#define OML_UP 0x0001
-#define RSL_UP 0x0002
-
-/*
- * read one ipa message from the socket
- * return NULL in case of error
- */
-struct msgb *ipaccess_read_msg(struct bsc_fd *bfd, int *error)
-{
- struct msgb *msg = msgb_alloc(TS1_ALLOC_SIZE, "Abis/IP");
- struct ipaccess_head *hh;
- int len, ret = 0;
-
- if (!msg) {
- *error = -ENOMEM;
- return NULL;
- }
-
- /* first read our 3-byte header */
- hh = (struct ipaccess_head *) msg->data;
- ret = recv(bfd->fd, msg->data, sizeof(*hh), 0);
- if (ret == 0) {
- msgb_free(msg);
- *error = ret;
- return NULL;
- } else if (ret != sizeof(*hh)) {
- if (errno != EAGAIN)
- LOGP(DINP, LOGL_ERROR, "recv error %d %s\n", ret, strerror(errno));
- msgb_free(msg);
- *error = ret;
- return NULL;
- }
-
- msgb_put(msg, ret);
-
- /* then read te length as specified in header */
- msg->l2h = msg->data + sizeof(*hh);
- len = ntohs(hh->len);
-
- if (len < 0 || TS1_ALLOC_SIZE < len + sizeof(*hh)) {
- LOGP(DINP, LOGL_ERROR, "Can not read this packet. %d avail\n", len);
- msgb_free(msg);
- *error = -EIO;
- return NULL;
- }
-
- ret = recv(bfd->fd, msg->l2h, len, 0);
- if (ret < len) {
- LOGP(DINP, LOGL_ERROR, "short read! Got %d from %d\n", ret, len);
- msgb_free(msg);
- *error = -EIO;
- return NULL;
- }
- msgb_put(msg, ret);
-
- return msg;
-}
-
-int ipaccess_drop_oml(struct gsm_bts *bts)
-{
- struct gsm_bts_trx *trx;
- struct e1inp_ts *ts;
- struct e1inp_line *line;
- struct bsc_fd *bfd;
-
- if (!bts || !bts->oml_link)
- return -1;
-
- /* send OML down */
- ts = bts->oml_link->ts;
- line = ts->line;
- e1inp_event(ts, S_INP_TEI_DN, bts->oml_link->tei, bts->oml_link->sapi);
-
- bfd = &ts->driver.ipaccess.fd;
- bsc_unregister_fd(bfd);
- close(bfd->fd);
- bfd->fd = -1;
-
- /* clean up OML and RSL */
- e1inp_sign_link_destroy(bts->oml_link);
- bts->oml_link = NULL;
- bts->ip_access.flags = 0;
-
- /* drop all RSL connections too */
- llist_for_each_entry(trx, &bts->trx_list, list)
- ipaccess_drop_rsl(trx);
-
- /* kill the E1 line now... as we have no one left to use it */
- talloc_free(line);
-
- return -1;
-}
-
-static int ipaccess_drop(struct e1inp_ts *ts, struct bsc_fd *bfd)
-{
- struct e1inp_sign_link *link;
- int bts_nr;
-
- if (!ts) {
- /*
- * If we don't have a TS this means that this is a RSL
- * connection but we are not past the authentication
- * handling yet. So we can safely delete this bfd and
- * wait for a reconnect.
- */
- bsc_unregister_fd(bfd);
- close(bfd->fd);
- bfd->fd = -1;
- talloc_free(bfd);
- return -1;
- }
-
- /* attempt to find a signalling link */
- if (ts->type == E1INP_TS_TYPE_SIGN) {
- llist_for_each_entry(link, &ts->sign.sign_links, list) {
- bts_nr = link->trx->bts->bts_nr;
- /* we have issues just reconnecting RLS so we drop OML */
- ipaccess_drop_oml(link->trx->bts);
- return bts_nr;
- }
- }
-
- /* error case */
- LOGP(DINP, LOGL_ERROR, "Failed to find a signalling link for ts: %p\n", ts);
- bsc_unregister_fd(bfd);
- close(bfd->fd);
- bfd->fd = -1;
- return -1;
-}
-
-int ipaccess_drop_rsl(struct gsm_bts_trx *trx)
-{
- struct bsc_fd *bfd;
- struct e1inp_ts *ts;
-
- if (!trx || !trx->rsl_link)
- return -1;
-
- /* send RSL down */
- ts = trx->rsl_link->ts;
- e1inp_event(ts, S_INP_TEI_DN, trx->rsl_link->tei, trx->rsl_link->sapi);
-
- /* close the socket */
- bfd = &ts->driver.ipaccess.fd;
- bsc_unregister_fd(bfd);
- close(bfd->fd);
- bfd->fd = -1;
-
- /* destroy */
- e1inp_sign_link_destroy(trx->rsl_link);
- trx->rsl_link = NULL;
-
- return -1;
-}
-
-static int handle_ts1_read(struct bsc_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 e1inp_sign_link *link;
- struct msgb *msg;
- struct ipaccess_head *hh;
- int ret = 0, error;
-
- msg = ipaccess_read_msg(bfd, &error);
- if (!msg) {
- if (error == 0) {
- int ret = ipaccess_drop(e1i_ts, bfd);
- if (ret >= 0)
- LOGP(DINP, LOGL_NOTICE, "BTS %u disappeared, dead socket\n",
- ret);
- else
- LOGP(DINP, LOGL_NOTICE, "unknown BTS disappeared, dead socket\n");
- }
- return error;
- }
-
- DEBUGP(DMI, "RX %u: %s\n", ts_nr, hexdump(msgb_l2(msg), msgb_l2len(msg)));
-
- hh = (struct ipaccess_head *) msg->data;
- if (hh->proto == IPAC_PROTO_IPACCESS) {
- ret = ipaccess_rcvmsg(line, msg, bfd);
- if (ret < 0)
- ipaccess_drop(e1i_ts, bfd);
- msgb_free(msg);
- return ret;
- }
- /* BIG FAT WARNING: bfd might no longer exist here, since ipaccess_rcvmsg()
- * might have free'd it !!! */
-
- link = e1inp_lookup_sign_link(e1i_ts, hh->proto, 0);
- if (!link) {
- LOGP(DINP, LOGL_ERROR, "no matching signalling link for "
- "hh->proto=0x%02x\n", hh->proto);
- msgb_free(msg);
- return -EIO;
- }
- msg->trx = link->trx;
-
- switch (link->type) {
- case E1INP_SIGN_RSL:
- if (!(msg->trx->bts->ip_access.flags & (RSL_UP << msg->trx->nr))) {
- e1inp_event(e1i_ts, S_INP_TEI_UP, link->tei, link->sapi);
- msg->trx->bts->ip_access.flags |= (RSL_UP << msg->trx->nr);
- }
- ret = abis_rsl_rcvmsg(msg);
- break;
- case E1INP_SIGN_OML:
- if (!(msg->trx->bts->ip_access.flags & OML_UP)) {
- e1inp_event(e1i_ts, S_INP_TEI_UP, link->tei, link->sapi);
- msg->trx->bts->ip_access.flags |= OML_UP;
- }
- ret = abis_nm_rcvmsg(msg);
- break;
- default:
- LOGP(DINP, LOGL_NOTICE, "Unknown IP.access protocol proto=0x%02x\n", hh->proto);
- msgb_free(msg);
- break;
- }
- return ret;
-}
-
-void ipaccess_prepend_header(struct msgb *msg, int proto)
-{
- struct ipaccess_head *hh;
-
- /* prepend the ip.access header */
- hh = (struct ipaccess_head *) msgb_push(msg, sizeof(*hh));
- hh->len = htons(msg->len - sizeof(*hh));
- hh->proto = proto;
-}
-
-static int ts_want_write(struct e1inp_ts *e1i_ts)
-{
- e1i_ts->driver.ipaccess.fd.when |= BSC_FD_WRITE;
-
- return 0;
-}
-
-static void timeout_ts1_write(void *data)
-{
- struct e1inp_ts *e1i_ts = (struct e1inp_ts *)data;
-
- /* trigger write of ts1, due to tx delay timer */
- ts_want_write(e1i_ts);
-}
-
-static int handle_ts1_write(struct bsc_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 e1inp_sign_link *sign_link;
- struct msgb *msg;
- u_int8_t proto;
- int ret;
-
- bfd->when &= ~BSC_FD_WRITE;
-
- /* get the next msg for this timeslot */
- msg = e1inp_tx_ts(e1i_ts, &sign_link);
- if (!msg) {
- /* no message after tx delay timer */
- return 0;
- }
-
- switch (sign_link->type) {
- case E1INP_SIGN_OML:
- proto = IPAC_PROTO_OML;
- break;
- case E1INP_SIGN_RSL:
- proto = IPAC_PROTO_RSL;
- break;
- default:
- msgb_free(msg);
- bfd->when |= BSC_FD_WRITE; /* come back for more msg */
- return -EINVAL;
- }
-
- msg->l2h = msg->data;
- ipaccess_prepend_header(msg, sign_link->tei);
-
- DEBUGP(DMI, "TX %u: %s\n", ts_nr, hexdump(msg->l2h, msgb_l2len(msg)));
-
- ret = send(bfd->fd, msg->data, msg->len, 0);
- msgb_free(msg);
-
- /* set tx delay timer for next event */
- e1i_ts->sign.tx_timer.cb = timeout_ts1_write;
- e1i_ts->sign.tx_timer.data = e1i_ts;
-
- /* Reducing this might break the nanoBTS 900 init. */
- bsc_schedule_timer(&e1i_ts->sign.tx_timer, 0, e1i_ts->sign.delay);
-
- return ret;
-}
-
-/* callback from select.c in case one of the fd's can be read/written */
-static int ipaccess_fd_cb(struct bsc_fd *bfd, unsigned int what)
-{
- struct e1inp_line *line = bfd->data;
- unsigned int ts_nr = bfd->priv_nr;
- unsigned int idx = ts_nr-1;
- struct e1inp_ts *e1i_ts;
- int rc = 0;
-
- /* In case of early RSL we might not yet have a line */
-
- if (line)
- e1i_ts = &line->ts[idx];
-
- if (!line || e1i_ts->type == E1INP_TS_TYPE_SIGN) {
- if (what & BSC_FD_READ)
- rc = handle_ts1_read(bfd);
- if (what & BSC_FD_WRITE)
- rc = handle_ts1_write(bfd);
- } else
- LOGP(DINP, LOGL_ERROR, "unknown E1 TS type %u\n", e1i_ts->type);
-
- return rc;
-}
-
-struct e1inp_driver ipaccess_driver = {
- .name = "ip.access",
- .want_write = ts_want_write,
- .default_delay = 0,
-};
-
-/* callback of the OML listening filedescriptor */
-static int listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
-{
- int ret;
- int idx = 0;
- int i;
- struct e1inp_line *line;
- struct e1inp_ts *e1i_ts;
- struct bsc_fd *bfd;
- struct sockaddr_in sa;
- socklen_t sa_len = sizeof(sa);
-
- if (!(what & BSC_FD_READ))
- return 0;
-
- ret = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len);
- if (ret < 0) {
- perror("accept");
- return ret;
- }
- LOGP(DINP, LOGL_NOTICE, "accept()ed new OML link from %s\n",
- inet_ntoa(sa.sin_addr));
-
- line = talloc_zero(tall_bsc_ctx, struct e1inp_line);
- if (!line) {
- close(ret);
- return -ENOMEM;
- }
- line->driver = &ipaccess_driver;
- //line->driver_data = e1h;
- /* create virrtual E1 timeslots for signalling */
- e1inp_ts_config(&line->ts[1-1], line, E1INP_TS_TYPE_SIGN);
-
- /* initialize the fds */
- for (i = 0; i < ARRAY_SIZE(line->ts); ++i)
- line->ts[i].driver.ipaccess.fd.fd = -1;
-
- e1i_ts = &line->ts[idx];
-
- bfd = &e1i_ts->driver.ipaccess.fd;
- bfd->fd = ret;
- bfd->data = line;
- bfd->priv_nr = PRIV_OML;
- bfd->cb = ipaccess_fd_cb;
- bfd->when = BSC_FD_READ;
- ret = bsc_register_fd(bfd);
- if (ret < 0) {
- LOGP(DINP, LOGL_ERROR, "could not register FD\n");
- close(bfd->fd);
- talloc_free(line);
- return ret;
- }
-
- /* Request ID. FIXME: request LOCATION, HW/SW VErsion, Unit Name, Serno */
- ret = ipaccess_send_id_req(bfd->fd);
-
- return ret;
- //return e1inp_line_register(line);
-}
-
-static int rsl_listen_fd_cb(struct bsc_fd *listen_bfd, unsigned int what)
-{
- struct sockaddr_in sa;
- socklen_t sa_len = sizeof(sa);
- struct bsc_fd *bfd;
- int ret;
-
- if (!(what & BSC_FD_READ))
- return 0;
-
- bfd = talloc_zero(tall_bsc_ctx, struct bsc_fd);
- if (!bfd)
- return -ENOMEM;
-
- /* Some BTS has connected to us, but we don't know yet which line
- * (as created by the OML link) to associate it with. Thus, we
- * allocate a temporary bfd until we have received ID from BTS */
-
- bfd->fd = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len);
- if (bfd->fd < 0) {
- perror("accept");
- return bfd->fd;
- }
- LOGP(DINP, LOGL_NOTICE, "accept()ed new RSL link from %s\n", inet_ntoa(sa.sin_addr));
- bfd->priv_nr = PRIV_RSL;
- bfd->cb = ipaccess_fd_cb;
- bfd->when = BSC_FD_READ;
- ret = bsc_register_fd(bfd);
- if (ret < 0) {
- LOGP(DINP, LOGL_ERROR, "could not register FD\n");
- close(bfd->fd);
- talloc_free(bfd);
- return ret;
- }
- /* Request ID. FIXME: request LOCATION, HW/SW VErsion, Unit Name, Serno */
- ret = write(bfd->fd, id_req, sizeof(id_req));
-
- return 0;
-}
-
-/* Actively connect to a BTS. Currently used by ipaccess-config.c */
-int ipaccess_connect(struct e1inp_line *line, struct sockaddr_in *sa)
-{
- struct e1inp_ts *e1i_ts = &line->ts[0];
- struct bsc_fd *bfd = &e1i_ts->driver.ipaccess.fd;
- int ret, on = 1;
-
- bfd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- bfd->cb = ipaccess_fd_cb;
- bfd->when = BSC_FD_READ | BSC_FD_WRITE;
- bfd->data = line;
- bfd->priv_nr = PRIV_OML;
-
- if (bfd->fd < 0) {
- LOGP(DINP, LOGL_ERROR, "could not create TCP socket.\n");
- return -EIO;
- }
-
- setsockopt(bfd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
-
- ret = connect(bfd->fd, (struct sockaddr *) sa, sizeof(*sa));
- if (ret < 0) {
- LOGP(DINP, LOGL_ERROR, "could not connect socket\n");
- close(bfd->fd);
- return ret;
- }
-
- ret = bsc_register_fd(bfd);
- if (ret < 0) {
- close(bfd->fd);
- return ret;
- }
-
- line->driver = &ipaccess_driver;
-
- return ret;
- //return e1inp_line_register(line);
-}
-
-int ipaccess_setup(struct gsm_network *gsmnet)
-{
- int ret;
-
- /* register the driver with the core */
- /* FIXME: do this in the plugin initializer function */
- ret = e1inp_driver_register(&ipaccess_driver);
- if (ret)
- return ret;
-
- e1h = talloc_zero(tall_bsc_ctx, struct ia_e1_handle);
- if (!e1h)
- return -ENOMEM;
-
- e1h->gsmnet = gsmnet;
-
- /* Listen for OML connections */
- ret = make_sock(&e1h->listen_fd, IPPROTO_TCP, 0, IPA_TCP_PORT_OML,
- listen_fd_cb);
- if (ret < 0)
- return ret;
-
- /* Listen for RSL connections */
- ret = make_sock(&e1h->rsl_listen_fd, IPPROTO_TCP, 0,
- IPA_TCP_PORT_RSL, rsl_listen_fd_cb);
- if (ret < 0)
- return ret;
-
- return ret;
-}
diff --git a/openbsc/src/input/lapd.c b/openbsc/src/input/lapd.c
deleted file mode 100644
index 7bce6cc51..000000000
--- a/openbsc/src/input/lapd.c
+++ /dev/null
@@ -1,710 +0,0 @@
-/* OpenBSC minimal LAPD implementation */
-
-/* (C) 2009 by oystein@homelien.no
- * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
- * (C) 2010 by Digium and Matthew Fredrickson <creslin@digium.com>
- * (C) 2011 by Harald Welte <laforge@gnumonks.org>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- */
-
-/* TODO:
- * detect RR timeout and set SAP state back to SABM_RETRANSMIT
- * use of value_string
- * further code cleanup (spaghetti)
- */
-
-#include <stdio.h>
-#include <string.h>
-#include <assert.h>
-#include <errno.h>
-
-#include "lapd.h"
-
-#include <osmocore/linuxlist.h>
-#include <osmocore/talloc.h>
-#include <osmocore/msgb.h>
-#include <osmocore/timer.h>
-#include <openbsc/debug.h>
-
-#define SABM_INTERVAL 0, 300000
-
-typedef enum {
- LAPD_TEI_NONE = 0,
- LAPD_TEI_ASSIGNED,
- LAPD_TEI_ACTIVE,
-} lapd_tei_state;
-
-const char *lapd_tei_states[] = {
- "NONE",
- "ASSIGNED",
- "ACTIVE",
-};
-
-typedef enum {
- LAPD_TYPE_NONE = 0,
-
- LAPD_TYPE_I,
- LAPD_TYPE_S,
- LAPD_TYPE_U,
-} lapd_msg_type;
-
-typedef enum {
- /* commands/responses */
- LAPD_CMD_NONE = 0,
-
- LAPD_CMD_I,
- LAPD_CMD_RR,
- LAPD_CMD_RNR,
- LAPD_CMD_REJ,
-
- LAPD_CMD_SABME,
- LAPD_CMD_DM,
- LAPD_CMD_UI,
- LAPD_CMD_DISC,
- LAPD_CMD_UA,
- LAPD_CMD_FRMR,
- LAPD_CMD_XID,
-} lapd_cmd_type;
-
-const char *lapd_cmd_types[] = {
- "NONE",
-
- "I",
- "RR",
- "RNR",
- "REJ",
-
- "SABME",
- "DM",
- "UI",
- "DISC",
- "UA",
- "FRMR",
- "XID",
-
-};
-
-enum lapd_sap_state {
- SAP_STATE_INACTIVE,
- SAP_STATE_SABM_RETRANS,
- SAP_STATE_ACTIVE,
-};
-
-const char *lapd_sap_states[] = {
- "INACTIVE",
- "SABM_RETRANS",
- "ACTIVE",
-};
-
-const char *lapd_msg_types = "?ISU";
-
-/* structure representing an allocated TEI within a LAPD instance */
-struct lapd_tei {
- struct llist_head list;
- struct lapd_instance *li;
- uint8_t tei;
- lapd_tei_state state;
-
- struct llist_head sap_list;
-};
-
-/* Structure representing a SAP within a TEI. We use this for TE-mode to
- * re-transmit SABM */
-struct lapd_sap {
- struct llist_head list;
- struct lapd_tei *tei;
- uint8_t sapi;
- enum lapd_sap_state state;
-
- /* A valid N(R) value is one that is in the range V(A) ≤ N(R) ≤ V(S). */
- int vs; /* next to be transmitted */
- int va; /* last acked by peer */
- int vr; /* next expected to be received */
-
- struct timer_list sabme_timer; /* timer to re-transmit SABM message */
-};
-
-/* 3.5.2.2 Send state variable V(S)
- * Each point-to-point data link connection endpoint shall have an associated V(S) when using I frame
- * commands. V(S) denotes the sequence number of the next I frame to be transmitted. The V(S) can
- * take on the value 0 through n minus 1. The value of V(S) shall be incremented by 1 with each
- * successive I frame transmission, and shall not exceed V(A) by more than the maximum number of
- * outstanding I frames k. The value of k may be in the range of 1 ≤ k ≤ 127.
- *
- * 3.5.2.3 Acknowledge state variable V(A)
- * Each point-to-point data link connection endpoint shall have an associated V(A) when using I frame
- * commands and supervisory frame commands/responses. V(A) identifies the last I frame that has been
- * acknowledged by its peer [V(A) − 1 equals the N(S) of the last acknowledged I frame]. V(A) can
- * take on the value 0 through n minus 1. The value of V(A) shall be updated by the valid N(R) values
- * received from its peer (see 3.5.2.6). A valid N(R) value is one that is in the range V(A) ≤ N(R) ≤
- * V(S).
- *
- * 3.5.2.5 Receive state variable V(R)
- * Each point-to-point data link connection endpoint shall have an associated V(R) when using I frame
- * commands and supervisory frame commands/responses. V(R) denotes the sequence number of the
- * next in-sequence I frame expected to be received. V(R) can take on the value 0 through n minus 1.
- * The value of V(R) shall be incremented by one with the receipt of an error-free, in-sequence I frame
- * whose N(S) equals V(R).
- */
-#define LAPD_NS(sap) (sap->vs)
-#define LAPD_NR(sap) (sap->vr)
-
-/* 3.5.2.4 Send sequence number N(S)
- * Only I frames contain N(S), the send sequence number of transmitted I frames. At the time that an in-
- * sequence I frame is designated for transmission, the value of N(S) is set equal to V(S).
- *
- * 3.5.2.6 Receive sequence number N(R)
- * All I frames and supervisory frames contain N(R), the expected send sequence number of the next
- * received I frame. At the time that a frame of the above types is designated for transmission, the value
- * of N(R) is set equal to V(R). N(R) indicates that the data link layer entity transmitting the N(R) has
- * correctly received all I frames numbered up to and including N(R) − 1.
- */
-
-/* Resolve TEI structure from given numeric TEI */
-static struct lapd_tei *teip_from_tei(struct lapd_instance *li, uint8_t tei)
-{
- struct lapd_tei *lt;
-
- llist_for_each_entry(lt, &li->tei_list, list) {
- if (lt->tei == tei)
- return lt;
- }
- return NULL;
-};
-
-static void lapd_tei_set_state(struct lapd_tei *teip, int newstate)
-{
- DEBUGP(DMI, "state change on TEI %d: %s -> %s\n", teip->tei,
- lapd_tei_states[teip->state], lapd_tei_states[newstate]);
- teip->state = newstate;
-};
-
-/* Allocate a new TEI */
-struct lapd_tei *lapd_tei_alloc(struct lapd_instance *li, uint8_t tei)
-{
- struct lapd_tei *teip;
-
- teip = talloc_zero(li, struct lapd_tei);
- if (!teip)
- return NULL;
-
- teip->li = li;
- teip->tei = tei;
- llist_add(&teip->list, &li->tei_list);
- INIT_LLIST_HEAD(&teip->sap_list);
-
- lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED);
-
- return teip;
-}
-
-/* Find a SAP within a given TEI */
-static struct lapd_sap *lapd_sap_find(struct lapd_tei *teip, uint8_t sapi)
-{
- struct lapd_sap *sap;
-
- llist_for_each_entry(sap, &teip->sap_list, list) {
- if (sap->sapi == sapi)
- return sap;
- }
-
- return NULL;
-}
-
-static void sabme_timer_cb(void *_sap);
-
-/* Allocate a new SAP within a given TEI */
-static struct lapd_sap *lapd_sap_alloc(struct lapd_tei *teip, uint8_t sapi)
-{
- struct lapd_sap *sap = talloc_zero(teip, struct lapd_sap);
-
- LOGP(DMI, LOGL_INFO, "Allocating SAP for SAPI=%u / TEI=%u\n",
- sapi, teip->tei);
-
- sap->sapi = sapi;
- sap->tei = teip;
- sap->sabme_timer.cb = &sabme_timer_cb;
- sap->sabme_timer.data = sap;
-
- llist_add(&sap->list, &teip->sap_list);
-
- return sap;
-}
-
-static void lapd_sap_set_state(struct lapd_tei *teip, uint8_t sapi,
- enum lapd_sap_state newstate)
-{
- struct lapd_sap *sap = lapd_sap_find(teip, sapi);
- if (!sap)
- return;
-
- DEBUGP(DMI, "state change on TEI %u / SAPI %u: %s -> %s\n", teip->tei,
- sapi, lapd_sap_states[sap->state], lapd_sap_states[newstate]);
- switch (sap->state) {
- case SAP_STATE_SABM_RETRANS:
- if (newstate != SAP_STATE_SABM_RETRANS)
- bsc_del_timer(&sap->sabme_timer);
- break;
- default:
- if (newstate == SAP_STATE_SABM_RETRANS)
- bsc_schedule_timer(&sap->sabme_timer, SABM_INTERVAL);
- break;
- }
-
- sap->state = newstate;
-};
-
-/* Input function into TEI manager */
-static void lapd_tei_receive(struct lapd_instance *li, uint8_t *data, int len)
-{
- uint8_t entity = data[0];
- uint8_t ref = data[1];
- uint8_t mt = data[3];
- uint8_t action = data[4] >> 1;
- uint8_t e = data[4] & 1;
- uint8_t resp[8];
- struct lapd_tei *teip;
-
- DEBUGP(DMI, "TEIMGR: entity %x, ref %x, mt %x, action %x, e %x\n", entity, ref, mt, action, e);
-
- switch (mt) {
- case 0x01: /* IDENTITY REQUEST */
- DEBUGP(DMI, "TEIMGR: identity request for TEI %u\n", action);
-
- teip = teip_from_tei(li, action);
- if (!teip) {
- LOGP(DMI, LOGL_INFO, "TEI MGR: New TEI %u\n", action);
- lapd_tei_alloc(li, action);
- }
-
- /* Send ACCEPT */
- memmove(resp, "\xfe\xff\x03\x0f\x00\x00\x02\x00", 8);
- resp[7] = (action << 1) | 1;
- li->transmit_cb(resp, 8, li->cbdata);
-
- if (teip->state == LAPD_TEI_NONE)
- lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED);
- break;
- default:
- LOGP(DMI, LOGL_NOTICE, "TEIMGR: unknown mt %x action %x\n",
- mt, action);
- break;
- };
-};
-
-/* General input function for any data received for this LAPD instance */
-uint8_t *lapd_receive(struct lapd_instance *li, uint8_t * data, unsigned int len,
- int *ilen, lapd_mph_type *prim)
-{
- uint8_t sapi, cr, tei, command;
- int pf, ns, nr;
- uint8_t *contents;
- struct lapd_tei *teip;
- struct lapd_sap *sap;
-
- uint8_t resp[8];
- int l = 0;
-
- *ilen = 0;
- *prim = 0;
-
- if (len < 2) {
- DEBUGP(DMI, "len %d < 2\n", len);
- return NULL;
- };
-
- if ((data[0] & 1) != 0 || (data[1] & 1) != 1) {
- DEBUGP(DMI, "address field %x/%x not well formed\n", data[0],
- data[1]);
- return NULL;
- };
-
- sapi = data[0] >> 2;
- cr = (data[0] >> 1) & 1;
- tei = data[1] >> 1;
- command = li->network_side ^ cr;
- //DEBUGP(DMI, " address sapi %x tei %d cmd %d cr %d\n", sapi, tei, command, cr);
-
- if (len < 3) {
- DEBUGP(DMI, "len %d < 3\n", len);
- return NULL;
- };
-
- lapd_msg_type typ = 0;
- lapd_cmd_type cmd = 0;
- pf = -1;
- ns = -1;
- nr = -1;
- if ((data[2] & 1) == 0) {
- typ = LAPD_TYPE_I;
- assert(len >= 4);
- ns = data[2] >> 1;
- nr = data[3] >> 1;
- pf = data[3] & 1;
- cmd = LAPD_CMD_I;
- } else if ((data[2] & 3) == 1) {
- typ = LAPD_TYPE_S;
- assert(len >= 4);
- nr = data[3] >> 1;
- pf = data[3] & 1;
- switch (data[2]) {
- case 0x1:
- cmd = LAPD_CMD_RR;
- break;
- case 0x5:
- cmd = LAPD_CMD_RNR;
- break;
- case 0x9:
- cmd = LAPD_CMD_REJ;
- break;
- default:
- LOGP(DMI, LOGL_ERROR, "unknown LAPD S cmd %x\n", data[2]);
- return NULL;
- };
- } else if ((data[2] & 3) == 3) {
- typ = LAPD_TYPE_U;
- pf = (data[2] >> 4) & 1;
- int val = data[2] & ~(1 << 4);
- switch (val) {
- case 0x6f:
- cmd = LAPD_CMD_SABME;
- break;
- case 0x0f:
- cmd = LAPD_CMD_DM;
- break;
- case 0x03:
- cmd = LAPD_CMD_UI;
- break;
- case 0x43:
- cmd = LAPD_CMD_DISC;
- break;
- case 0x63:
- cmd = LAPD_CMD_UA;
- break;
- case 0x87:
- cmd = LAPD_CMD_FRMR;
- break;
- case 0xaf:
- cmd = LAPD_CMD_XID;
- break;
-
- default:
- LOGP(DMI, LOGL_ERROR, "unknown U cmd %x "
- "(pf %x data %x)\n", val, pf, data[2]);
- return NULL;
- };
- };
-
- contents = &data[4];
- if (typ == LAPD_TYPE_U)
- contents--;
- *ilen = len - (contents - data);
-
- if (tei == 127)
- lapd_tei_receive(li, contents, *ilen);
-
- teip = teip_from_tei(li, tei);
- if (!teip) {
- LOGP(DMI, LOGL_NOTICE, "Unknown TEI %u\n", tei);
- return NULL;
- }
-
- sap = lapd_sap_find(teip, sapi);
- if (!sap) {
- LOGP(DMI, LOGL_INFO, "No SAP for TEI=%u / SAPI=%u, "
- "allocating\n", tei, sapi);
- sap = lapd_sap_alloc(teip, sapi);
- }
-
- DEBUGP(DMI, "<- %c %s sapi %x tei %3d cmd %x pf %x ns %3d nr %3d "
- "ilen %d teip %p vs %d va %d vr %d len %d\n",
- lapd_msg_types[typ], lapd_cmd_types[cmd], sapi, tei, command, pf,
- ns, nr, *ilen, teip, sap->vs, sap->va, sap->vr, len);
-
- switch (cmd) {
- case LAPD_CMD_I:
- if (ns != sap->vr) {
- DEBUGP(DMI, "ns %d != vr %d\n", ns, sap->vr);
- if (ns == ((sap->vr - 1) & 0x7f)) {
- DEBUGP(DMI, "DOUBLE FRAME, ignoring\n");
- cmd = 0; // ignore
- } else {
- assert(0);
- };
- } else {
- //printf("IN SEQUENCE\n");
- sap->vr = (ns + 1) & 0x7f; // FIXME: hack!
- };
-
- break;
- case LAPD_CMD_UI:
- break;
- case LAPD_CMD_SABME:
- sap->vs = 0;
- sap->vr = 0;
- sap->va = 0;
-
- // ua
- resp[l++] = data[0];
- resp[l++] = (tei << 1) | 1;
- resp[l++] = 0x73;
- li->transmit_cb(resp, l, li->cbdata);
- if (teip->state != LAPD_TEI_ACTIVE) {
- if (teip->state == LAPD_TEI_ASSIGNED) {
- lapd_tei_set_state(teip,
- LAPD_TEI_ACTIVE);
- //printf("ASSIGNED and ACTIVE\n");
- } else {
-#if 0
- DEBUGP(DMI, "rr in strange state, send rej\n");
-
- // rej
- resp[l++] = (sap-> sapi << 2) | (li->network_side ? 0 : 2);
- resp[l++] = (tei << 1) | 1;
- resp[l++] = 0x09; //rej
- resp[l++] = ((sap->vr + 1) << 1) | 0;
- li->transmit_cb(resp, l, li->cbdata);
- pf = 0; // dont reply
-#endif
- };
- };
-
- *prim = LAPD_MPH_ACTIVATE_IND;
- break;
- case LAPD_CMD_UA:
- sap->vs = 0;
- sap->vr = 0;
- sap->va = 0;
- lapd_tei_set_state(teip, LAPD_TEI_ACTIVE);
- lapd_sap_set_state(teip, sapi, SAP_STATE_ACTIVE);
- *prim = LAPD_MPH_ACTIVATE_IND;
- break;
- case LAPD_CMD_RR:
- sap->va = (nr & 0x7f);
-#if 0
- if (teip->state != LAPD_TEI_ACTIVE) {
- if (teip->state == LAPD_TEI_ASSIGNED) {
- lapd_tei_set_state(teip, LAPD_TEI_ACTIVE);
- *prim = LAPD_MPH_ACTIVATE_IND;
- //printf("ASSIGNED and ACTIVE\n");
- } else {
-#if 0
- DEBUGP(DMI, "rr in strange " "state, send rej\n");
-
- // rej
- resp[l++] = (sap-> sapi << 2) | (li->network_side ? 0 : 2);
- resp[l++] = (tei << 1) | 1;
- resp[l++] = 0x09; //rej
- resp[l++] =
- ((sap->vr + 1) << 1) | 0;
- li->transmit_cb(resp, l, li->cbdata);
- pf = 0; // dont reply
-#endif
- };
- };
-#endif
- if (pf) {
- // interrogating us, send rr
- resp[l++] = data[0];
- resp[l++] = (tei << 1) | 1;
- resp[l++] = 0x01; // rr
- resp[l++] = (LAPD_NR(sap) << 1) | (data[3] & 1); // pf bit from req
-
- li->transmit_cb(resp, l, li->cbdata);
-
- };
- break;
- case LAPD_CMD_FRMR:
- // frame reject
-#if 0
- if (teip->state == LAPD_TEI_ACTIVE)
- *prim = LAPD_MPH_DEACTIVATE_IND;
- lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED);
-#endif
- LOGP(DMI, LOGL_NOTICE, "frame reject, ignoring\n");
- break;
- case LAPD_CMD_DISC:
- // disconnect
- resp[l++] = data[0];
- resp[l++] = (tei << 1) | 1;
- resp[l++] = 0x73;
- li->transmit_cb(resp, l, li->cbdata);
- lapd_tei_set_state(teip, LAPD_TEI_NONE);
- break;
- default:
- LOGP(DMI, LOGL_NOTICE, "unknown cmd for tei %d (cmd %x)\n",
- tei, cmd);
- break;
- }
-
- if (typ == LAPD_TYPE_I) {
- /* send rr
- * Thu Jan 22 19:17:13 2009 <4000> sangoma.c:340 read (62/25) 4: fa 33 01 0a
- * lapd <- S RR sapi 3e tei 25 cmd 0 pf 0 ns -1 nr 5 ilen 0 teip 0x613800 vs 7 va 5 vr 2 len 4
- */
-
- /* interrogating us, send rr */
- DEBUGP(DMI, "Sending RR response\n");
- resp[l++] = data[0];
- resp[l++] = (tei << 1) | 1;
- resp[l++] = 0x01; // rr
- resp[l++] = (LAPD_NR(sap) << 1) | (data[3] & 1); // pf bit from req
-
- li->transmit_cb(resp, l, li->cbdata);
-
- if (cmd != 0) {
- *prim = LAPD_DL_DATA_IND;
- return contents;
- }
- } else if (tei != 127 && typ == LAPD_TYPE_U && cmd == LAPD_CMD_UI) {
- *prim = LAPD_DL_UNITDATA_IND;
- return contents;
- }
-
- return NULL;
-};
-
-/* low-level function to send a single SABM message */
-static int lapd_send_sabm(struct lapd_instance *li, uint8_t tei, uint8_t sapi)
-{
- struct msgb *msg = msgb_alloc_headroom(1024, 128, "LAPD SABM");
- if (!msg)
- return -ENOMEM;
-
- DEBUGP(DMI, "Sending SABM for TEI=%u, SAPI=%u\n", tei, sapi);
-
- msgb_put_u8(msg, (sapi << 2) | (li->network_side ? 2 : 0));
- msgb_put_u8(msg, (tei << 1) | 1);
- msgb_put_u8(msg, 0x7F);
-
- li->transmit_cb(msg->data, msg->len, li->cbdata);
-
- msgb_free(msg);
-
- return 0;
-}
-
-/* timer call-back function for SABM re-transmission */
-static void sabme_timer_cb(void *_sap)
-{
- struct lapd_sap *sap = _sap;
-
- lapd_send_sabm(sap->tei->li, sap->tei->tei, sap->sapi);
-
- if (sap->state == SAP_STATE_SABM_RETRANS)
- bsc_schedule_timer(&sap->sabme_timer, SABM_INTERVAL);
-}
-
-/* Start a (user-side) SAP for the specified TEI/SAPI on the LAPD instance */
-int lapd_sap_start(struct lapd_instance *li, uint8_t tei, uint8_t sapi)
-{
- struct lapd_sap *sap;
- struct lapd_tei *teip;
-
- teip = teip_from_tei(li, tei);
- if (!teip)
- teip = lapd_tei_alloc(li, tei);
-
- sap = lapd_sap_find(teip, sapi);
- if (sap)
- return -EEXIST;
-
- sap = lapd_sap_alloc(teip, sapi);
-
- lapd_sap_set_state(teip, sapi, SAP_STATE_SABM_RETRANS);
-
- return 0;
-}
-
-/* Stop a (user-side) SAP for the specified TEI/SAPI on the LAPD instance */
-int lapd_sap_stop(struct lapd_instance *li, uint8_t tei, uint8_t sapi)
-{
- struct lapd_tei *teip;
- struct lapd_sap *sap;
-
- teip = teip_from_tei(li, tei);
- if (!teip)
- return -ENODEV;
-
- sap = lapd_sap_find(teip, sapi);
- if (!sap)
- return -ENODEV;
-
- lapd_sap_set_state(teip, sapi, SAP_STATE_INACTIVE);
-
- llist_del(&sap->list);
- talloc_free(sap);
-
- return 0;
-}
-
-/* Transmit Data (I-Frame) on the given LAPD Instance / TEI / SAPI */
-void lapd_transmit(struct lapd_instance *li, uint8_t tei, uint8_t sapi,
- uint8_t *data, unsigned int len)
-{
- struct lapd_tei *teip = teip_from_tei(li, tei);
- struct lapd_sap *sap;
-
- if (!teip) {
- LOGP(DMI, LOGL_ERROR, "Cannot transmit on non-existing "
- "TEI %u\n", tei);
- return;
- }
-
- sap = lapd_sap_find(teip, sapi);
- if (!sap) {
- LOGP(DMI, LOGL_INFO, "Tx on unknown SAPI=%u in TEI=%u, "
- "allocating\n", sapi, tei);
- sap = lapd_sap_alloc(teip, sapi);
- }
-
- /* prepend stuff */
- uint8_t buf[10000];
- memset(buf, 0, sizeof(buf));
- memmove(buf + 4, data, len);
- len += 4;
-
- buf[0] = (sapi << 2) | (li->network_side ? 2 : 0);
- buf[1] = (tei << 1) | 1;
- buf[2] = (LAPD_NS(sap) << 1);
- buf[3] = (LAPD_NR(sap) << 1) | 0;
-
- sap->vs = (sap->vs + 1) & 0x7f;
-
- li->transmit_cb(buf, len, li->cbdata);
-};
-
-/* Allocate a new LAPD instance */
-struct lapd_instance *lapd_instance_alloc(int network_side,
- void (*tx_cb)(uint8_t *data, int len,
- void *cbdata), void *cbdata)
-{
- struct lapd_instance *li;
-
- li = talloc_zero(NULL, struct lapd_instance);
- if (!li)
- return NULL;
-
- li->transmit_cb = tx_cb;
- li->cbdata = cbdata;
- li->network_side = network_side;
- INIT_LLIST_HEAD(&li->tei_list);
-
- return li;
-}
diff --git a/openbsc/src/input/lapd.h b/openbsc/src/input/lapd.h
deleted file mode 100644
index fd11edaa3..000000000
--- a/openbsc/src/input/lapd.h
+++ /dev/null
@@ -1,46 +0,0 @@
-#ifndef OPENBSC_LAPD_H
-#define OPENBSC_LAPD_H
-
-#include <stdint.h>
-
-#include <osmocore/linuxlist.h>
-
-typedef enum {
- LAPD_MPH_NONE = 0,
-
- LAPD_MPH_ACTIVATE_IND,
- LAPD_MPH_DEACTIVATE_IND,
-
- LAPD_DL_DATA_IND,
- LAPD_DL_UNITDATA_IND,
-
-} lapd_mph_type;
-
-struct lapd_instance {
- struct llist_head list; /* list of LAPD instances */
- int network_side;
-
- void (*transmit_cb)(uint8_t *data, int len, void *cbdata);
- void *cbdata;
-
- struct llist_head tei_list; /* list of TEI in this LAPD instance */
-};
-
-extern uint8_t *lapd_receive(struct lapd_instance *li, uint8_t *data, unsigned int len,
- int *ilen, lapd_mph_type *prim);
-
-extern void lapd_transmit(struct lapd_instance *li, uint8_t tei, uint8_t sapi,
- uint8_t *data, unsigned int len);
-
-struct lapd_instance *lapd_instance_alloc(int network_side,
- void (*tx_cb)(uint8_t *data, int len,
- void *cbdata), void *cbdata);
-
-
-/* Start a (user-side) SAP for the specified TEI/SAPI on the LAPD instance */
-int lapd_sap_start(struct lapd_instance *li, uint8_t tei, uint8_t sapi);
-
-/* Stop a (user-side) SAP for the specified TEI/SAPI on the LAPD instance */
-int lapd_sap_stop(struct lapd_instance *li, uint8_t tei, uint8_t sapi);
-
-#endif /* OPENBSC_LAPD_H */
diff --git a/openbsc/src/input/misdn.c b/openbsc/src/input/misdn.c
deleted file mode 100644
index 459887917..000000000
--- a/openbsc/src/input/misdn.c
+++ /dev/null
@@ -1,542 +0,0 @@
-/* OpenBSC Abis input driver for mISDNuser */
-
-/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org>
- * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <errno.h>
-#include <string.h>
-#include <time.h>
-#include <sys/fcntl.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <arpa/inet.h>
-#include <mISDNif.h>
-
-//#define AF_COMPATIBILITY_FUNC
-//#include <compat_af_isdn.h>
-#ifndef AF_ISDN
-#define AF_ISDN 34
-#define PF_ISDN AF_ISDN
-#endif
-
-#include <osmocore/select.h>
-#include <osmocore/msgb.h>
-#include <osmocore/talloc.h>
-#include <openbsc/debug.h>
-#include <openbsc/gsm_data.h>
-#include <openbsc/abis_nm.h>
-#include <openbsc/abis_rsl.h>
-#include <openbsc/subchan_demux.h>
-#include <openbsc/e1_input.h>
-#include <openbsc/signal.h>
-
-#define TS1_ALLOC_SIZE 300
-
-struct prim_name {
- unsigned int prim;
- const char *name;
-};
-
-const struct prim_name prim_names[] = {
- { PH_CONTROL_IND, "PH_CONTROL_IND" },
- { PH_DATA_IND, "PH_DATA_IND" },
- { PH_DATA_CNF, "PH_DATA_CNF" },
- { PH_ACTIVATE_IND, "PH_ACTIVATE_IND" },
- { DL_ESTABLISH_IND, "DL_ESTABLISH_IND" },
- { DL_ESTABLISH_CNF, "DL_ESTABLISH_CNF" },
- { DL_RELEASE_IND, "DL_RELEASE_IND" },
- { DL_RELEASE_CNF, "DL_RELEASE_CNF" },
- { DL_DATA_IND, "DL_DATA_IND" },
- { DL_UNITDATA_IND, "DL_UNITDATA_IND" },
- { DL_INFORMATION_IND, "DL_INFORMATION_IND" },
- { MPH_ACTIVATE_IND, "MPH_ACTIVATE_IND" },
- { MPH_DEACTIVATE_IND, "MPH_DEACTIVATE_IND" },
-};
-
-const char *get_prim_name(unsigned int prim)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(prim_names); i++) {
- if (prim_names[i].prim == prim)
- return prim_names[i].name;
- }
-
- return "UNKNOWN";
-}
-
-static int handle_ts1_read(struct bsc_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 e1inp_sign_link *link;
- struct msgb *msg = msgb_alloc(TS1_ALLOC_SIZE, "mISDN TS1");
- struct sockaddr_mISDN l2addr;
- struct mISDNhead *hh;
- socklen_t alen;
- int ret;
-
- if (!msg)
- return -ENOMEM;
-
- hh = (struct mISDNhead *) msg->data;
-
- alen = sizeof(l2addr);
- ret = recvfrom(bfd->fd, msg->data, 300, 0,
- (struct sockaddr *) &l2addr, &alen);
- if (ret < 0) {
- fprintf(stderr, "recvfrom error %s\n", strerror(errno));
- return ret;
- }
-
- if (alen != sizeof(l2addr)) {
- fprintf(stderr, "%s error len\n", __func__);
- return -EINVAL;
- }
-
- msgb_put(msg, ret);
-
- DEBUGP(DMI, "alen =%d, dev(%d) channel(%d) sapi(%d) tei(%d)\n",
- alen, l2addr.dev, l2addr.channel, l2addr.sapi, l2addr.tei);
-
- DEBUGP(DMI, "<= len = %d, prim(0x%x) id(0x%x): %s\n",
- ret, hh->prim, hh->id, get_prim_name(hh->prim));
-
- switch (hh->prim) {
- case DL_INFORMATION_IND:
- /* mISDN tells us which channel number is allocated for this
- * tuple of (SAPI, TEI). */
- DEBUGP(DMI, "DL_INFORMATION_IND: use channel(%d) sapi(%d) tei(%d) for now\n",
- l2addr.channel, l2addr.sapi, l2addr.tei);
- link = e1inp_lookup_sign_link(e1i_ts, l2addr.tei, l2addr.sapi);
- if (!link) {
- DEBUGPC(DMI, "mISDN message for unknown sign_link\n");
- msgb_free(msg);
- return -EINVAL;
- }
- /* save the channel number in the driver private struct */
- link->driver.misdn.channel = l2addr.channel;
- break;
- case DL_ESTABLISH_IND:
- DEBUGP(DMI, "DL_ESTABLISH_IND: channel(%d) sapi(%d) tei(%d)\n",
- l2addr.channel, l2addr.sapi, l2addr.tei);
- /* For some strange reason, sometimes the DL_INFORMATION_IND tells
- * us the wrong channel, and we only get the real channel number
- * during the DL_ESTABLISH_IND */
- link = e1inp_lookup_sign_link(e1i_ts, l2addr.tei, l2addr.sapi);
- if (!link) {
- DEBUGPC(DMI, "mISDN message for unknown sign_link\n");
- msgb_free(msg);
- return -EINVAL;
- }
- /* save the channel number in the driver private struct */
- link->driver.misdn.channel = l2addr.channel;
- ret = e1inp_event(e1i_ts, S_INP_TEI_UP, l2addr.tei, l2addr.sapi);
- break;
- case DL_RELEASE_IND:
- DEBUGP(DMI, "DL_RELEASE_IND: channel(%d) sapi(%d) tei(%d)\n",
- l2addr.channel, l2addr.sapi, l2addr.tei);
- ret = e1inp_event(e1i_ts, S_INP_TEI_DN, l2addr.tei, l2addr.sapi);
- break;
- case DL_DATA_IND:
- case DL_UNITDATA_IND:
- msg->l2h = msg->data + MISDN_HEADER_LEN;
- DEBUGP(DMI, "RX: %s\n", hexdump(msgb_l2(msg), ret - MISDN_HEADER_LEN));
- ret = e1inp_rx_ts(e1i_ts, msg, l2addr.tei, l2addr.sapi);
- break;
- case PH_ACTIVATE_IND:
- DEBUGP(DMI, "PH_ACTIVATE_IND: channel(%d) sapi(%d) tei(%d)\n",
- l2addr.channel, l2addr.sapi, l2addr.tei);
- break;
- case PH_DEACTIVATE_IND:
- DEBUGP(DMI, "PH_DEACTIVATE_IND: channel(%d) sapi(%d) tei(%d)\n",
- l2addr.channel, l2addr.sapi, l2addr.tei);
- break;
- default:
- break;
- }
- return ret;
-}
-
-static int ts_want_write(struct e1inp_ts *e1i_ts)
-{
- /* We never include the mISDN B-Channel FD into the
- * writeset, since it doesn't support poll() based
- * write flow control */
- if (e1i_ts->type == E1INP_TS_TYPE_TRAU)
- return 0;
-
- e1i_ts->driver.misdn.fd.when |= BSC_FD_WRITE;
-
- return 0;
-}
-
-static void timeout_ts1_write(void *data)
-{
- struct e1inp_ts *e1i_ts = (struct e1inp_ts *)data;
-
- /* trigger write of ts1, due to tx delay timer */
- ts_want_write(e1i_ts);
-}
-
-static int handle_ts1_write(struct bsc_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 e1inp_sign_link *sign_link;
- struct sockaddr_mISDN sa;
- struct msgb *msg;
- struct mISDNhead *hh;
- u_int8_t *l2_data;
- int ret;
-
- bfd->when &= ~BSC_FD_WRITE;
-
- /* get the next msg for this timeslot */
- msg = e1inp_tx_ts(e1i_ts, &sign_link);
- if (!msg) {
- /* no message after tx delay timer */
- return 0;
- }
-
- l2_data = msg->data;
-
- /* prepend the mISDNhead */
- hh = (struct mISDNhead *) msgb_push(msg, sizeof(*hh));
- hh->prim = DL_DATA_REQ;
-
- DEBUGP(DMI, "TX channel(%d) TEI(%d) SAPI(%d): %s\n",
- sign_link->driver.misdn.channel, sign_link->tei,
- sign_link->sapi, 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);
- msgb_free(msg);
-
- /* set tx delay timer for next event */
- e1i_ts->sign.tx_timer.cb = timeout_ts1_write;
- e1i_ts->sign.tx_timer.data = e1i_ts;
- bsc_schedule_timer(&e1i_ts->sign.tx_timer, 0, e1i_ts->sign.delay);
-
- return ret;
-}
-
-#define BCHAN_TX_GRAN 160
-/* write to a B channel TS */
-static int handle_tsX_write(struct bsc_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 mISDNhead *hh;
- u_int8_t tx_buf[BCHAN_TX_GRAN + sizeof(*hh)];
- struct subch_mux *mx = &e1i_ts->trau.mux;
- int ret;
-
- hh = (struct mISDNhead *) tx_buf;
- hh->prim = PH_DATA_REQ;
-
- subchan_mux_out(mx, tx_buf+sizeof(*hh), BCHAN_TX_GRAN);
-
- DEBUGP(DMIB, "BCHAN TX: %s\n",
- hexdump(tx_buf+sizeof(*hh), BCHAN_TX_GRAN));
-
- ret = send(bfd->fd, tx_buf, sizeof(*hh) + BCHAN_TX_GRAN, 0);
- if (ret < sizeof(*hh) + BCHAN_TX_GRAN)
- DEBUGP(DMIB, "send returns %d instead of %zu\n", ret,
- sizeof(*hh) + BCHAN_TX_GRAN);
-
- return ret;
-}
-
-#define TSX_ALLOC_SIZE 4096
-/* FIXME: read from a B channel TS */
-static int handle_tsX_read(struct bsc_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(TSX_ALLOC_SIZE, "mISDN TSx");
- struct mISDNhead *hh;
- int ret;
-
- if (!msg)
- return -ENOMEM;
-
- hh = (struct mISDNhead *) msg->data;
-
- ret = recv(bfd->fd, msg->data, TSX_ALLOC_SIZE, 0);
- if (ret < 0) {
- fprintf(stderr, "recvfrom error %s\n", strerror(errno));
- return ret;
- }
-
- msgb_put(msg, ret);
-
- if (hh->prim != PH_CONTROL_IND)
- DEBUGP(DMIB, "<= BCHAN len = %d, prim(0x%x) id(0x%x): %s\n",
- ret, hh->prim, hh->id, get_prim_name(hh->prim));
-
- switch (hh->prim) {
- case PH_DATA_IND:
- msg->l2h = msg->data + MISDN_HEADER_LEN;
- DEBUGP(DMIB, "BCHAN RX: %s\n",
- hexdump(msgb_l2(msg), ret - MISDN_HEADER_LEN));
- ret = e1inp_rx_ts(e1i_ts, msg, 0, 0);
- break;
- case PH_ACTIVATE_IND:
- case PH_DATA_CNF:
- /* physical layer indicates that data has been sent,
- * we thus can send some more data */
- ret = handle_tsX_write(bfd);
- default:
- break;
- }
- /* FIXME: why do we free signalling msgs in the caller, and trau not? */
- msgb_free(msg);
-
- return ret;
-}
-
-/* callback from select.c in case one of the fd's can be read/written */
-static int misdn_fd_cb(struct bsc_fd *bfd, unsigned int what)
-{
- struct e1inp_line *line = bfd->data;
- unsigned int ts_nr = bfd->priv_nr;
- unsigned int idx = ts_nr-1;
- struct e1inp_ts *e1i_ts = &line->ts[idx];
- int rc = 0;
-
- switch (e1i_ts->type) {
- case E1INP_TS_TYPE_SIGN:
- if (what & BSC_FD_READ)
- rc = handle_ts1_read(bfd);
- if (what & BSC_FD_WRITE)
- rc = handle_ts1_write(bfd);
- break;
- case E1INP_TS_TYPE_TRAU:
- if (what & BSC_FD_READ)
- rc = handle_tsX_read(bfd);
- /* We never include the mISDN B-Channel FD into the
- * writeset, since it doesn't support poll() based
- * write flow control */
- break;
- default:
- fprintf(stderr, "unknown E1 TS type %u\n", e1i_ts->type);
- break;
- }
-
- return rc;
-}
-
-static int activate_bchan(struct e1inp_line *line, int ts, int act)
-{
- struct mISDNhead hh;
- int ret;
- unsigned int idx = ts-1;
- struct e1inp_ts *e1i_ts = &line->ts[idx];
- struct bsc_fd *bfd = &e1i_ts->driver.misdn.fd;
-
- fprintf(stdout, "activate bchan\n");
- if (act)
- hh.prim = PH_ACTIVATE_REQ;
- else
- hh.prim = PH_DEACTIVATE_REQ;
-
- hh.id = MISDN_ID_ANY;
- ret = sendto(bfd->fd, &hh, sizeof(hh), 0, NULL, 0);
- if (ret < 0) {
- fprintf(stdout, "could not send ACTIVATE_RQ %s\n",
- strerror(errno));
- }
-
- return ret;
-}
-
-static int mi_e1_line_update(struct e1inp_line *line);
-
-struct e1inp_driver misdn_driver = {
- .name = "misdn",
- .want_write = ts_want_write,
- .default_delay = 50000,
- .line_update = &mi_e1_line_update,
-};
-
-static int mi_e1_setup(struct e1inp_line *line, int release_l2)
-{
- int ts, ret;
-
- /* TS0 is CRC4, don't need any fd for it */
- for (ts = 1; ts < NUM_E1_TS; ts++) {
- unsigned int idx = ts-1;
- struct e1inp_ts *e1i_ts = &line->ts[idx];
- struct bsc_fd *bfd = &e1i_ts->driver.misdn.fd;
- struct sockaddr_mISDN addr;
-
- bfd->data = line;
- bfd->priv_nr = ts;
- bfd->cb = misdn_fd_cb;
-
- switch (e1i_ts->type) {
- case E1INP_TS_TYPE_NONE:
- continue;
- break;
- case E1INP_TS_TYPE_SIGN:
- bfd->fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_LAPD_NT);
- bfd->when = BSC_FD_READ;
- break;
- case E1INP_TS_TYPE_TRAU:
- bfd->fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_B_RAW);
- /* We never include the mISDN B-Channel FD into the
- * writeset, since it doesn't support poll() based
- * write flow control */
- bfd->when = BSC_FD_READ;
- break;
- }
-
- if (bfd->fd < 0) {
- fprintf(stderr, "%s could not open socket %s\n",
- __func__, strerror(errno));
- return bfd->fd;
- }
-
- memset(&addr, 0, sizeof(addr));
- addr.family = AF_ISDN;
- 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;
- break;
- case E1INP_TS_TYPE_TRAU:
- addr.channel = ts;
- break;
- default:
- DEBUGP(DMI, "unsupported E1 TS type: %u\n",
- e1i_ts->type);
- break;
- }
-
- ret = bind(bfd->fd, (struct sockaddr *) &addr, sizeof(addr));
- if (ret < 0) {
- fprintf(stderr, "could not bind l2 socket %s\n",
- strerror(errno));
- return -EIO;
- }
-
- 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;
- }
- }
-
- /* FIXME: only activate B-Channels once we start to
- * use them to conserve CPU power */
- if (e1i_ts->type == E1INP_TS_TYPE_TRAU)
- activate_bchan(line, ts, 1);
-
- ret = bsc_register_fd(bfd);
- if (ret < 0) {
- fprintf(stderr, "could not register FD: %s\n",
- strerror(ret));
- return ret;
- }
- }
-
- return 0;
-}
-
-static int mi_e1_line_update(struct e1inp_line *line)
-{
- struct mISDN_devinfo devinfo;
- int sk, ret, cnt;
-
- if (line->driver != &misdn_driver)
- return -EINVAL;
-
- /* open the ISDN card device */
- sk = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE);
- if (sk < 0) {
- fprintf(stderr, "%s could not open socket %s\n",
- __func__, strerror(errno));
- return sk;
- }
-
- ret = ioctl(sk, IMGETCOUNT, &cnt);
- if (ret) {
- fprintf(stderr, "%s error getting interf count: %s\n",
- __func__, strerror(errno));
- close(sk);
- return -ENODEV;
- }
- //DEBUGP(DMI,"%d device%s found\n", cnt, (cnt==1)?"":"s");
- printf("%d device%s found\n", cnt, (cnt==1)?"":"s");
-#if 1
- devinfo.id = line->num;
- ret = ioctl(sk, IMGETDEVINFO, &devinfo);
- if (ret < 0) {
- fprintf(stdout, "error getting info for device %d: %s\n",
- line->num, strerror(errno));
- return -ENODEV;
- }
- fprintf(stdout, " id: %d\n", devinfo.id);
- fprintf(stdout, " Dprotocols: %08x\n", devinfo.Dprotocols);
- fprintf(stdout, " Bprotocols: %08x\n", devinfo.Bprotocols);
- fprintf(stdout, " protocol: %d\n", devinfo.protocol);
- fprintf(stdout, " nrbchan: %d\n", devinfo.nrbchan);
- fprintf(stdout, " name: %s\n", devinfo.name);
-#endif
-
- if (!(devinfo.Dprotocols & (1 << ISDN_P_NT_E1))) {
- fprintf(stderr, "error: card is not of type E1 (NT-mode)\n");
- return -EINVAL;
- }
-
- ret = mi_e1_setup(line, 1);
- if (ret)
- return ret;
-
- return 0;
-}
-
-void e1inp_misdn_init(void)
-{
- /* register the driver with the core */
- e1inp_driver_register(&misdn_driver);
-}