[LAPD] Support multiple instances of LAPD

We cannot afford static/global state, as we may have multiple E1
lines, each having its own LAPD instance.  Furthermore, we might
even have multiple LAPD instances on the same E1 line (think of
a multi-drop setup).

This also implements dynamic TEI allocation, i.e. no hardcoded
TEI list anymore.
diff --git a/openbsc/include/openbsc/e1_input.h b/openbsc/include/openbsc/e1_input.h
index ca8d5a6..95b9dd1 100644
--- a/openbsc/include/openbsc/e1_input.h
+++ b/openbsc/include/openbsc/e1_input.h
@@ -87,7 +87,11 @@
 			/* 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 ebdaccf..7a3c2be 100644
--- a/openbsc/src/input/dahdi.c
+++ b/openbsc/src/input/dahdi.c
@@ -63,6 +63,7 @@
 	lapd_mph_type prim;
 	unsigned int sapi, tei;
 	int ilen, ret;
+	uint8_t *idata;
 
 	if (!msg)
 		return -ENOMEM;
@@ -81,7 +82,9 @@
 
 	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 @@
 		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 @@
 		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 @@
 
 }
 
-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 @@
 		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 @@
 			}
 			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_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 99069b4..d488bfd 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 <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 <stdio.h>
@@ -9,7 +28,10 @@
 #include <assert.h>
 
 #include "lapd.h"
-#include "openbsc/debug.h"
+
+#include <osmocore/linuxlist.h>
+#include <osmocore/talloc.h>
+#include <openbsc/debug.h>
 
 typedef enum {
 	LAPD_TEI_NONE = 0,
@@ -70,17 +92,18 @@
 };
 
 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 @@
  * 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 @@
 	};
 };
 
-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 @@
 	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 @@
 	*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 @@
 		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 @@
 				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 @@
 				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 @@
 			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 @@
 		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 @@
 		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 @@
 	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 @@
 	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 b0e6e6f..f79d84f 100644
--- a/openbsc/src/input/lapd.h
+++ b/openbsc/src/input/lapd.h
@@ -3,6 +3,8 @@
 
 #include <stdint.h>
 
+#include <osmocore/linuxlist.h>
+
 typedef enum {
 	LAPD_MPH_NONE	= 0,
 
@@ -14,10 +16,22 @@
 
 } 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 */