diff --git a/openbsc/include/openbsc/e1_input.h b/openbsc/include/openbsc/e1_input.h index ca8d5a65a..95b9dd1cc 100644 --- a/openbsc/include/openbsc/e1_input.h +++ b/openbsc/include/openbsc/e1_input.h @@ -87,7 +87,11 @@ struct e1inp_ts { /* ip.access driver has one fd for each ts */ struct bsc_fd fd; } ipaccess; - + struct { + /* DAHDI driver has one fd for each ts */ + struct bsc_fd fd; + struct lapd_instance *lapd; + } dahdi; } driver; }; diff --git a/openbsc/src/input/dahdi.c b/openbsc/src/input/dahdi.c index ebdaccfba..7a3c2beae 100644 --- a/openbsc/src/input/dahdi.c +++ b/openbsc/src/input/dahdi.c @@ -63,6 +63,7 @@ static int handle_ts1_read(struct bsc_fd *bfd) lapd_mph_type prim; unsigned int sapi, tei; int ilen, ret; + uint8_t *idata; if (!msg) return -ENOMEM; @@ -81,7 +82,9 @@ static int handle_ts1_read(struct bsc_fd *bfd) DEBUGP(DMI, "<= len = %d, sapi(%d) tei(%d)", ret, sapi, tei); - uint8_t *idata = lapd_receive(msg->data, msg->len, &ilen, &prim, bfd); + idata = lapd_receive(e1i_ts->driver.dahdi.lapd, msg->data, msg->len, &ilen, &prim); + if (!idata) + return -EIO; msgb_pull(msg, 2); @@ -126,7 +129,7 @@ static int ts_want_write(struct e1inp_ts *e1i_ts) return 0; } - e1i_ts->driver.misdn.fd.when |= BSC_FD_WRITE; + e1i_ts->driver.dahdi.fd.when |= BSC_FD_WRITE; return 0; } @@ -167,7 +170,7 @@ static int handle_ts1_write(struct bsc_fd *bfd) return 0; } - lapd_transmit(sign_link->tei, msg->data, msg->len, bfd); + lapd_transmit(e1i_ts->driver.dahdi.lapd, sign_link->tei, msg->data, msg->len); msgb_free(msg); /* set tx delay timer for next event */ @@ -353,7 +356,7 @@ void dahdi_set_bufinfo(int fd, int as_sigchan) } -static int dahdi_e1_setup(struct e1inp_line *line, int release_l2) +static int dahdi_e1_setup(struct e1inp_line *line) { int ts, ret; @@ -362,7 +365,7 @@ static int dahdi_e1_setup(struct e1inp_line *line, int release_l2) unsigned int idx = ts-1; char openstr[128]; struct e1inp_ts *e1i_ts = &line->ts[idx]; - struct bsc_fd *bfd = &e1i_ts->driver.misdn.fd; + struct bsc_fd *bfd = &e1i_ts->driver.dahdi.fd; bfd->data = line; bfd->priv_nr = ts; @@ -382,6 +385,7 @@ static int dahdi_e1_setup(struct e1inp_line *line, int release_l2) } bfd->when = BSC_FD_READ; dahdi_set_bufinfo(bfd->fd, 1); + e1i_ts->driver.dahdi.lapd = lapd_instance_alloc(dahdi_write_msg, bfd); break; case E1INP_TS_TYPE_TRAU: bfd->fd = open(openstr, O_RDWR | O_NONBLOCK); @@ -417,24 +421,16 @@ static int dahdi_e1_setup(struct e1inp_line *line, int release_l2) static int dahdi_e1_line_update(struct e1inp_line *line) { - int ret; - if (line->driver != &dahdi_driver) return -EINVAL; - init_flip_bits(); - - ret = dahdi_e1_setup(line, 1); - if (ret) - return ret; - - lapd_transmit_cb = dahdi_write_msg; - - return 0; + 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); } diff --git a/openbsc/src/input/lapd.c b/openbsc/src/input/lapd.c index 99069b44b..d488bfd40 100644 --- a/openbsc/src/input/lapd.c +++ b/openbsc/src/input/lapd.c @@ -1,7 +1,26 @@ +/* OpenBSC minimal LAPD implementation */ -/* - * minimal standalone network-side lap-d implementation - * oystein@homelien.no, 2009 +/* (C) 2009 by oystein@homelien.no + * (C) 2011 by Harald Welte + * (C) 2009 by Holger Hans Peter Freyther + * (C) 2010 by Digium and Matthew Fredrickson + * + * 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 @@ -9,7 +28,10 @@ #include #include "lapd.h" -#include "openbsc/debug.h" + +#include +#include +#include typedef enum { LAPD_TEI_NONE = 0, @@ -70,17 +92,18 @@ const char *lapd_cmd_types[] = { }; const char *lapd_msg_types = "?ISU"; -const int network_side = 1; /* 0 for user side */ -typedef struct { - int tei; - int sapi; +struct lapd_tei { + struct llist_head list; + + uint8_t tei; + uint8_t sapi; /* 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 */ lapd_tei_state state; -} lapd_tei_t; +}; /* 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 @@ -117,63 +140,59 @@ typedef struct { * 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. */ -void (*lapd_transmit_cb) (uint8_t * data, int len, void *cbdata); -static lapd_tei_t tei_list[] = { - {25, 62,}, - {1, 0,}, - {-1}, -}; - -static lapd_tei_t *teip_from_tei(int tei) +static struct lapd_tei *teip_from_tei(struct lapd_instance *li, uint8_t tei) { - lapd_tei_t *p; - for (p = tei_list; p->tei != -1; p++) { - if (p->tei == tei) - return p; - }; + 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(lapd_tei_t * teip, int newstate) +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; }; -static void lapd_tei_receive(uint8_t * data, int len, void *cbdata) +static void lapd_tei_receive(struct lapd_instance *li, uint8_t *data, int len) { int entity = data[0]; int ref = data[1]; int mt = data[3]; int action = data[4] >> 1; int e = data[4] & 1; + int tei; + uint8_t resp[8]; + struct lapd_tei *teip; + DEBUGP(DMI, "tei mgmt: entity %x, ref %x, mt %x, action %x, e %x\n", entity, ref, mt, action, e); switch (mt) { - case 0x01:{ // identity request - int tei = action; - uint8_t resp[8]; - lapd_tei_t *teip; + case 0x01: /* IDENTITY REQUEST */ + DEBUGP(DMI, "TEIMGR: identity request for TEI %u\n", tei); - DEBUGP(DMI, "tei mgmt: identity request, accepting " - "tei %d\n", tei); - memmove(resp, "\xfe\xff\x03\x0f\x00\x00\x02\x00", 8); - resp[7] = (tei << 1) | 1; - lapd_transmit_cb(resp, 8, cbdata); - - teip = teip_from_tei(tei); - if (!teip) { - LOGP(DMI, LOGL_NOTICE, "Message for unknown " - "TEI %u, LAPD code supports only " - "TEI 25, 1, 2\n", tei); - return; - } - if (teip->state == LAPD_TEI_NONE) - lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED); - break; + teip = teip_from_tei(li, tei); + if (!teip) { + LOGP(DMI, LOGL_INFO, "TEI MGR: New TEI %u\n", tei); + teip = talloc_zero(li, struct lapd_tei); + teip->tei = tei; + llist_add(&teip->list, &li->tei_list); + lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED); } + + /* Send ACCEPT */ + memmove(resp, "\xfe\xff\x03\x0f\x00\x00\x02\x00", 8); + resp[7] = (tei << 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, "tei mgmt: unknown mt %x action %x\n", mt, action); @@ -181,13 +200,13 @@ static void lapd_tei_receive(uint8_t * data, int len, void *cbdata) }; }; -uint8_t *lapd_receive(uint8_t * data, int len, int *ilen, lapd_mph_type * prim, - void *cbdata) +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; - lapd_tei_t *teip; + struct lapd_tei *teip; uint8_t resp[8]; int l = 0; @@ -209,7 +228,7 @@ uint8_t *lapd_receive(uint8_t * data, int len, int *ilen, lapd_mph_type * prim, sapi = data[0] >> 2; cr = (data[0] >> 1) & 1; tei = data[1] >> 1; - command = network_side ^ cr; + command = li->network_side ^ cr; //DEBUGP(DMI, " address sapi %x tei %d cmd %d cr %d\n", sapi, tei, command, cr); if (len < 3) { @@ -288,9 +307,9 @@ uint8_t *lapd_receive(uint8_t * data, int len, int *ilen, lapd_mph_type * prim, *ilen = len - (contents - data); if (tei == 127) - lapd_tei_receive(contents, *ilen, cbdata); + lapd_tei_receive(li, contents, *ilen); - teip = teip_from_tei(tei); + teip = teip_from_tei(li, tei); 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", @@ -330,7 +349,7 @@ uint8_t *lapd_receive(uint8_t * data, int len, int *ilen, lapd_mph_type * prim, resp[l++] = data[0]; resp[l++] = (tei << 1) | 1; resp[l++] = 0x73; - lapd_transmit_cb(resp, l, cbdata); + li->transmit_cb(resp, l, li->cbdata); if (teip->state != LAPD_TEI_ACTIVE) { if (teip->state == LAPD_TEI_ASSIGNED) { lapd_tei_set_state(teip, @@ -341,11 +360,11 @@ uint8_t *lapd_receive(uint8_t * data, int len, int *ilen, lapd_mph_type * prim, DEBUGP(DMI, "rr in strange state, send rej\n"); // rej - resp[l++] = (teip-> sapi << 2) | (network_side ? 0 : 2); + resp[l++] = (teip-> sapi << 2) | (li->network_side ? 0 : 2); resp[l++] = (tei << 1) | 1; resp[l++] = 0x09; //rej resp[l++] = ((teip->vr + 1) << 1) | 0; - lapd_transmit_cb(resp, l, cbdata); + li->transmit_cb(resp, l, li->cbdata); pf = 0; // dont reply #endif }; @@ -366,13 +385,12 @@ uint8_t *lapd_receive(uint8_t * data, int len, int *ilen, lapd_mph_type * prim, DEBUGP(DMI, "rr in strange " "state, send rej\n"); // rej - resp[l++] = (teip-> sapi << 2) | (network_side ? 0 : 2); + resp[l++] = (teip-> sapi << 2) | (li->network_side ? 0 : 2); resp[l++] = (tei << 1) | 1; resp[l++] = 0x09; //rej resp[l++] = ((teip->vr + 1) << 1) | 0; - lapd_transmit_cb(resp, l, - cbdata); + li->transmit_cb(resp, l, li->cbdata); pf = 0; // dont reply #endif }; @@ -385,7 +403,7 @@ uint8_t *lapd_receive(uint8_t * data, int len, int *ilen, lapd_mph_type * prim, resp[l++] = 0x01; // rr resp[l++] = (LAPD_NR(teip) << 1) | (data[3] & 1); // pf bit from req - lapd_transmit_cb(resp, l, cbdata); + li->transmit_cb(resp, l, li->cbdata); }; break; @@ -403,7 +421,7 @@ uint8_t *lapd_receive(uint8_t * data, int len, int *ilen, lapd_mph_type * prim, resp[l++] = data[0]; resp[l++] = (tei << 1) | 1; resp[l++] = 0x73; - lapd_transmit_cb(resp, l, cbdata); + li->transmit_cb(resp, l, li->cbdata); lapd_tei_set_state(teip, LAPD_TEI_NONE); break; default: @@ -425,7 +443,7 @@ uint8_t *lapd_receive(uint8_t * data, int len, int *ilen, lapd_mph_type * prim, resp[l++] = 0x01; // rr resp[l++] = (LAPD_NR(teip) << 1) | (data[3] & 1); // pf bit from req - lapd_transmit_cb(resp, l, cbdata); + li->transmit_cb(resp, l, li->cbdata); if (cmd != 0) { *prim = LAPD_DL_DATA_IND; @@ -439,12 +457,18 @@ uint8_t *lapd_receive(uint8_t * data, int len, int *ilen, lapd_mph_type * prim, return NULL; }; -void lapd_transmit(int tei, uint8_t * data, int len, void *cbdata) +void lapd_transmit(struct lapd_instance *li, uint8_t tei, + uint8_t *data, unsigned int len) { //printf("lapd_transmit %d, %d\n", tei, len); //hexdump(data, len); - lapd_tei_t *teip = teip_from_tei(tei); - //printf("teip %p\n", teip); + struct lapd_tei *teip = teip_from_tei(li, tei); + + if (!teip) { + LOGP(DMI, LOGL_ERROR, "Cannot transmit on non-existing " + "TEI %u\n", tei); + return; + } /* prepend stuff */ uint8_t buf[10000]; @@ -452,12 +476,29 @@ void lapd_transmit(int tei, uint8_t * data, int len, void *cbdata) memmove(buf + 4, data, len); len += 4; - buf[0] = (teip->sapi << 2) | (network_side ? 2 : 0); + buf[0] = (teip->sapi << 2) | (li->network_side ? 2 : 0); buf[1] = (teip->tei << 1) | 1; buf[2] = (LAPD_NS(teip) << 1); buf[3] = (LAPD_NR(teip) << 1) | 0; teip->vs = (teip->vs + 1) & 0x7f; - lapd_transmit_cb(buf, len, cbdata); + li->transmit_cb(buf, len, li->cbdata); }; + +struct lapd_instance *lapd_instance_alloc(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 = 1; + INIT_LLIST_HEAD(&li->tei_list); + + return li; +} diff --git a/openbsc/src/input/lapd.h b/openbsc/src/input/lapd.h index b0e6e6f66..f79d84f14 100644 --- a/openbsc/src/input/lapd.h +++ b/openbsc/src/input/lapd.h @@ -3,6 +3,8 @@ #include +#include + typedef enum { LAPD_MPH_NONE = 0, @@ -14,10 +16,22 @@ typedef enum { } lapd_mph_type; -extern uint8_t *lapd_receive(uint8_t *data, int len, int *ilen, lapd_mph_type *prim, void *cbdata); +struct lapd_instance { + struct llist_head list; /* list of LAPD instances */ + int network_side; -extern void (*lapd_transmit_cb)(uint8_t *data, int len, void *cbdata); + void (*transmit_cb)(uint8_t *data, int len, void *cbdata); + void *cbdata; -extern void lapd_transmit(int tei, uint8_t *data, int len, 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 *data, unsigned int len); + +struct lapd_instance *lapd_instance_alloc(void (*tx_cb)(uint8_t *data, int len, + void *cbdata), void *cbdata); #endif /* OPENBSC_LAPD_H */