diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0a9f497
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,14 @@
+*.o
+Makefile
+Makefile.in
+aclocal.m4
+.*.swp
+config.log
+config.status
+configure
+depcomp
+missing
+install-sh
+.deps
+ylwrap
+
diff --git a/autogen.sh b/autogen.sh
new file mode 100644
index 0000000..4c38395
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,5 @@
+aclocal
+autoconf
+automake --add-missing
+
+
diff --git a/src/Makefile.am b/src/Makefile.am
index f89836c..0b1478c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -7,7 +7,8 @@ bsc_hack_SOURCES = bsc_hack.c abis_rsl.c abis_nm.c gsm_04_08.c gsm_data.c \
 		gsm_subscriber.c msgb.c select.c chan_alloc.c timer.c debug.c db.c \
 		gsm_04_11.c telnet_interface.c telnet_parser.l subchan_demux.c \
 		trau_frame.c trau_mux.c paging.c e1_config.c e1_input.c tlv_parser.c \
-		input/misdn.c input/ipaccess.c signal.c gsm_utils.c
+		input/misdn.c input/ipaccess.c signal.c gsm_utils.c \
+		input/lapd.c #input/sangoma.c
 bsc_hack_LDADD = -ldl -ldbi
 
 bs11_config_SOURCES = bs11_config.c abis_nm.c gsm_data.c msgb.c debug.c select.c timer.c rs232.c tlv_parser.c
diff --git a/src/bsc_hack.c b/src/bsc_hack.c
index fdd09ac..fa30c03 100644
--- a/src/bsc_hack.c
+++ b/src/bsc_hack.c
@@ -450,6 +450,7 @@ static void bootstrap_om_bs11(struct gsm_bts *bts)
 	/* end DB transmission */
 	abis_nm_bs11_db_transmission(bts, 0);
 
+#if 1
 	/* Reset BTS Site manager resource */
 	abis_nm_bs11_reset_resource(bts);
 
@@ -525,6 +526,7 @@ static void bootstrap_om_bs11(struct gsm_bts *bts)
 	/* Reset BTS Site manager resource */
 	abis_nm_bs11_reset_resource(bts);
 
+#endif
 	/* restart sending event reports */
 	abis_nm_event_reports(bts, 1);
 }
diff --git a/src/debug.c b/src/debug.c
index a3084e2..f171f3a 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -28,7 +28,7 @@
 
 #include <openbsc/debug.h>
 
-unsigned int debug_mask = 0xffffffff & ~(DMI|DMIB);
+unsigned int debug_mask = 0xffffffff;// & ~(DMI|DMIB);
 
 struct debug_info {
 	const char *name;
diff --git a/src/input/lapd.c b/src/input/lapd.c
new file mode 100644
index 0000000..5e8e936
--- /dev/null
+++ b/src/input/lapd.c
@@ -0,0 +1,396 @@
+
+/*
+ * minimal standalone network-side lap-d implementation
+ * oystein@homelien.no, 2009
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include "lapd.h"
+#include "openbsc/debug.h"
+
+#define	DEBUG_LAPD(f, args...)	{ printf("lapd "); printf(f, ## args); };
+
+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",
+
+
+};
+
+
+
+const char	*lapd_msg_types = "?ISU";
+const int	network_side = 1; // 0 for user side
+
+typedef struct {
+	int				tei;
+	int				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
+// 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(teip) (teip->vs)
+#define	LAPD_NR(teip) (teip->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.
+
+void (*lapd_transmit_cb)(uint8_t *data, int len, void *cbdata);
+
+static lapd_tei_t tei_list[] = {
+	{ 25, 62, },
+	{ 1, 0, },
+	{ -1 },
+};
+
+lapd_tei_t *teip_from_tei(int tei) {
+	lapd_tei_t *p;
+	for (p = tei_list; p->tei != -1; p++) {
+		if (p->tei == tei) return p;
+	};
+	return NULL;
+};
+
+void lapd_tei_set_state(lapd_tei_t *teip, int newstate) {
+	DEBUG_LAPD("state change on tei %d: %s -> %s\n", teip->tei, lapd_tei_states[teip->state], lapd_tei_states[newstate]);
+	teip->state = newstate;
+};
+
+void lapd_tei_receive(uint8_t *data, int len, void *cbdata) {
+	//DEBUG_LAPD("tei receive %p, %d\n", data, len);
+	int entity = data[0];
+	int	ref = data[1];
+	int mt = data[3];
+	int	action = data[4] >> 1;
+	int	e = data[4] & 1;
+	//DEBUG_LAPD("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;
+			DEBUG_LAPD("tei mgmt: identity request, accepting tei %d\n", tei);
+			//printf("tei: %d\n", tei);
+			uint8_t resp[8];
+			memmove(resp, "\xfe\xff\x03\x0f\x00\x00\x02\x00", 8);
+			resp[7] = (tei << 1) | 1;
+			lapd_transmit_cb(resp, 8, cbdata);
+			lapd_tei_t *teip = teip_from_tei (tei);
+			if (teip->state == LAPD_TEI_NONE)
+				lapd_tei_set_state(teip, LAPD_TEI_ASSIGNED);
+			break; }
+		default: 
+			DEBUG_LAPD("tei mgmt: unknown mt %x action %x\n", mt, action);
+			assert(0);
+	};
+};
+
+uint8_t *lapd_receive(uint8_t *data, int len, int *ilen, lapd_mph_type *prim, void *cbdata) {
+#if 0
+	DEBUG_LAPD("receive %p, %d\n", data, len);
+	hexdump(data, len);
+#endif
+
+	*ilen = 0;
+	*prim = 0;
+
+	if (len < 2) {
+		DEBUG_LAPD("len %d < 2\n", len);
+		return NULL;
+	};
+
+	if ((data[0] & 1) != 0 || (data[1] & 1) != 1) {
+		DEBUG_LAPD("address field %x/%x not well formed\n", data[0], data[1]);
+		return NULL;
+	};
+
+	int	sapi = data[0] >> 2;
+	int cr = (data[0] >> 1) & 1;
+	int tei = data[1] >> 1;
+	int command = network_side ^ cr;
+	//DEBUG_LAPD("  address sapi %x tei %d cmd %d cr %d\n", sapi, tei, command, cr);
+
+	if (len < 3) {
+		DEBUG_LAPD("len %d < 4\n", len);
+		return NULL;
+	};
+
+	lapd_msg_type typ = 0;
+	lapd_cmd_type cmd = 0;
+	int	pf = -1;
+	int	ns = -1;
+	int	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: 
+				DEBUG_LAPD("unknown S cmd %x\n", data[2]);
+				assert(0);
+		};
+	} 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: 
+				DEBUG_LAPD("unknown U cmd %x (pf %x data %x)\n", val, pf, data[2]);
+				assert(0);
+		};
+	};
+	
+	uint8_t *contents = &data[4];
+	if (typ == LAPD_TYPE_U) contents--;
+	*ilen = len - (contents - data);
+	
+	lapd_tei_t *teip = teip_from_tei(tei);
+	if (tei == 127)
+		lapd_tei_receive(contents, *ilen, cbdata);
+
+	DEBUG_LAPD("<- %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, teip ? teip->vs : -1, teip ? teip->va : -1, teip ? teip->vr : -1, len);
+
+	if (teip) {
+		switch (cmd) {
+			case LAPD_CMD_I: {
+				if (ns != teip->vr) {
+					DEBUG_LAPD("ns %d != vr %d\n", ns, teip->vr);
+					if (ns == teip->vr-1) {
+						DEBUG_LAPD("DOUBLE FRAME, ignoring\n");
+						cmd = 0; // ignore
+					} else {
+						assert(0);
+					};
+				} else {
+					//printf("IN SEQUENCE\n");
+				};
+				teip->vr = (ns + 1) & 0x7f; // FIXME: hack!
+
+				
+				break; }
+			case LAPD_CMD_SABME: {
+				teip->vs = 0;
+				teip->vr = 0;
+				teip->va = 0;
+
+				// ua
+				uint8_t resp[8];
+				int l = 0;
+				resp[l++] = data[0];
+				resp[l++] = (tei << 1) | 1;
+				resp[l++] = 0x73;
+				lapd_transmit_cb(resp, l, cbdata);
+				
+				break; }
+			case LAPD_CMD_RR: {
+				teip->va = (nr & 0x7f);
+				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
+						DEBUG_LAPD("rr in strange state, send rej\n");
+
+						// rej
+						uint8_t resp[8];
+						int l = 0;
+						resp[l++] = (teip->sapi << 2) | (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);
+						pf = 0; // dont reply
+#endif
+					};
+				};
+				if (pf) {
+					// interrogating us, send rr
+					uint8_t resp[8];
+					int l = 0;
+					resp[l++] = data[0];
+					resp[l++] = (tei << 1) | 1;
+					resp[l++] = 0x01; // rr
+					resp[l++] = (LAPD_NR(teip) << 1) | (data[3] & 1); // pf bit from req
+	
+					lapd_transmit_cb(resp, l, 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
+				DEBUG_LAPD("frame reject, ignoring\n");
+				assert(0);
+				break; }
+			case LAPD_CMD_DISC: { // disconnect
+				uint8_t resp[8];
+				int l = 0;
+				resp[l++] = data[0];
+				resp[l++] = (tei << 1) | 1;
+				resp[l++] = 0x73;
+				lapd_transmit_cb(resp, l, cbdata);
+				lapd_tei_set_state(teip, LAPD_TEI_NONE);
+				break; }
+			default: 
+				DEBUG_LAPD("unknown cmd for tei %d (cmd %x)\n", tei, cmd);
+				assert(0);
+		};
+	};
+
+	//if ((*prim == 0) && (ilen > 0) && (typ != LAPD_TYPE_S)) {
+	//if (cmd == LAPD_CMD_I) {
+	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
+		uint8_t resp[8];
+		int l = 0;
+		resp[l++] = data[0];
+		resp[l++] = (tei << 1) | 1;
+		resp[l++] = 0x01; // rr
+		resp[l++] = (LAPD_NR(teip) << 1) | (data[3] & 1); // pf bit from req
+
+		lapd_transmit_cb(resp, l, cbdata);
+
+		*prim = LAPD_DL_DATA_IND;
+		return contents;
+	};
+
+	return NULL;
+};
+
+void lapd_transmit(int tei, uint8_t *data, int len, void *cbdata) {
+	//printf("lapd_transmit %d, %d\n", tei, len);
+	//hexdump(data, len);
+	lapd_tei_t *teip = teip_from_tei(tei);
+	//printf("teip %p\n", teip);
+
+	// prepend stuff
+	uint8_t	buf[10000];
+	memset(buf, 0, sizeof(buf));
+	memmove(buf+4, data, len);
+	len += 4;
+
+	buf[0] = (teip->sapi << 2) | (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);
+};
+
diff --git a/src/input/lapd.h b/src/input/lapd.h
new file mode 100644
index 0000000..73195d6
--- /dev/null
+++ b/src/input/lapd.h
@@ -0,0 +1,19 @@
+
+#include <stdint.h>
+
+typedef enum {
+	LAPD_MPH_NONE	= 0,
+
+	LAPD_MPH_ACTIVATE_IND,
+	LAPD_MPH_DEACTIVATE_IND,
+
+	LAPD_DL_DATA_IND,
+
+} lapd_mph_type;
+
+extern uint8_t *lapd_receive(uint8_t *data, int len, int *ilen, lapd_mph_type *prim, void *cbdata);
+
+extern void (*lapd_transmit_cb)(uint8_t *data, int len, void *cbdata);
+
+extern void lapd_transmit(int tei, uint8_t *data, int len, void *cbdata);
+
diff --git a/src/input/sangoma.c b/src/input/sangoma.c
new file mode 100644
index 0000000..aa52425
--- /dev/null
+++ b/src/input/sangoma.c
@@ -0,0 +1,998 @@
+/* OpenBSC Sangoma cards E1 support layer */
+
+/* (C) 2008 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009 by Oystein Homelien <oystein@homelien.no>
+ *
+ * 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>
+#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>
+
+// FIXME: remove some
+#include <linux/if.h>
+#include <linux/if_wanpipe.h>
+#include <linux/wanpipe_defines.h>
+#include <linux/wanpipe_cfg.h>
+#include <linux/wanpipe.h>
+//#include <linux/sdla_xilinx.h>
+#include <linux/sdla_aft_te1.h>
+
+//#define AF_COMPATIBILITY_FUNC
+//#include <compat_af_isdn.h>
+#define AF_ISDN 34
+#define PF_ISDN AF_ISDN
+
+#include <openbsc/select.h>
+#include <openbsc/msgb.h>
+#include <openbsc/debug.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/abis_nm.h>
+#include <openbsc/abis_rsl.h>
+
+#include <lapd.h>
+
+#define NUM_E1_TS	32
+
+
+/*
+ * pcap writing of the misdn load
+ * pcap format is from http://wiki.wireshark.org/Development/LibpcapFileFormat
+ */
+#define DLT_LINUX_LAPD		177
+#define PCAP_INPUT		0
+#define PCAP_OUTPUT		1
+
+struct pcap_hdr {
+	u_int32_t magic_number;
+	u_int16_t version_major;
+	u_int16_t version_minor;
+	int32_t  thiszone;
+	u_int32_t sigfigs;
+	u_int32_t snaplen;
+	u_int32_t network;
+} __attribute__((packed));
+
+struct pcaprec_hdr {
+	u_int32_t ts_sec;
+	u_int32_t ts_usec;
+	u_int32_t incl_len;
+	u_int32_t orig_len;
+} __attribute__((packed));
+
+struct fake_linux_lapd_header {
+        u_int16_t pkttype;
+	u_int16_t hatype;
+	u_int16_t halen;
+	u_int64_t addr;
+	int16_t protocol;
+} __attribute__((packed));
+
+struct lapd_header {
+	u_int8_t ea1 : 1;
+	u_int8_t cr : 1;
+	u_int8_t sapi : 6;
+	u_int8_t ea2 : 1;
+	u_int8_t tei : 7;
+	u_int8_t control_foo; /* fake UM's ... */
+} __attribute__((packed));
+
+static_assert((int)&((struct fake_linux_lapd_header*)NULL)->hatype == 2,	hatype_offset);
+static_assert((int)&((struct fake_linux_lapd_header*)NULL)->halen == 4,		halen_offset);
+static_assert((int)&((struct fake_linux_lapd_header*)NULL)->addr == 6,		addr_offset);
+static_assert((int)&((struct fake_linux_lapd_header*)NULL)->protocol == 14,	proto_offset);
+static_assert(sizeof(struct fake_linux_lapd_header) == 16,			lapd_header_size);
+
+
+static int pcap_fd = -1;
+
+void mi_set_pcap_fd(int fd)
+{
+	int ret;
+	struct pcap_hdr header = {
+		.magic_number	= 0xa1b2c3d4,
+		.version_major	= 2,
+		.version_minor	= 4,
+		.thiszone	= 0,
+		.sigfigs	= 0,
+		.snaplen	= 65535,
+		.network	= DLT_LINUX_LAPD,
+	};
+
+	pcap_fd = fd;
+	ret = write(pcap_fd, &header, sizeof(header));
+}
+
+/* This currently only works for the D-Channel */
+static void write_pcap_packet(int direction, struct sockaddr_mISDN* addr,
+			      struct msgb *msg) {
+	if (pcap_fd < 0)
+		return;
+
+	int ret;
+	time_t cur_time;
+	struct tm *tm;
+
+	struct fake_linux_lapd_header header = {
+		.pkttype	= 4,
+		.hatype		= 0,
+		.halen		= 0,
+		.addr		= direction == PCAP_OUTPUT ? 0x0 : 0x1,
+		.protocol	= ntohs(48),
+	};
+
+	struct lapd_header lapd_header = {
+		.ea1		= 0,
+		.cr		= direction == PCAP_OUTPUT ? 1 : 0,
+		.sapi		= addr->sapi & 0x3F,
+		.ea2		= 1,
+		.tei		= addr->tei & 0x7F,
+		.control_foo	= 0x03 /* UI */,
+	};	
+
+	struct pcaprec_hdr payload_header = {
+		.ts_sec	    = 0,
+		.ts_usec    = 0,
+		.incl_len   = msg->len + sizeof(struct fake_linux_lapd_header)
+				+ sizeof(struct lapd_header)
+				- MISDN_HEADER_LEN,
+		.orig_len   = msg->len + sizeof(struct fake_linux_lapd_header)
+				+ sizeof(struct lapd_header)
+				- MISDN_HEADER_LEN,
+	};
+
+
+	cur_time = time(NULL);
+	tm = localtime(&cur_time);
+	payload_header.ts_sec = mktime(tm);
+
+	ret = write(pcap_fd, &payload_header, sizeof(payload_header));
+	ret = write(pcap_fd, &header, sizeof(header));
+	ret = write(pcap_fd, &lapd_header, sizeof(lapd_header));
+	ret = write(pcap_fd, msg->data + MISDN_HEADER_LEN,
+			msg->len - MISDN_HEADER_LEN);
+}
+
+/* data structure for one E1 interface with A-bis */
+struct mi_e1_handle {
+	struct gsm_bts *bts;
+	/* The mISDN card number of the card we use */
+	int cardnr;
+	/* The RSL adress */
+	struct sockaddr_mISDN l2addr;
+	/* The OML adress */
+	struct sockaddr_mISDN omladdr;
+	/* list (queue) of to-be-sent msgb's */
+	struct llist_head rsl_tx_list;
+	struct llist_head oml_tx_list;
+
+	void (*cb)(int event, struct gsm_bts *bts);
+	struct bsc_fd fd[NUM_E1_TS];
+
+	int ts2_fd;
+};
+
+/* FIXME: this needs to go */
+static struct mi_e1_handle *global_e1h;
+
+#define SAPI_L2ML	0
+#define SAPI_OML	62
+#define SAPI_RSL	0	/* 63 ? */
+
+#define TEI_L2ML	127
+#define TEI_OML		25
+#define TEI_RSL		1
+
+#define TS1_ALLOC_SIZE	300
+
+int sangoma_readmsg_socket(int sock, void *hdrbuf, int hdrlen, void *databuf, int datalen, int flag)
+{
+	struct msghdr msg;
+	struct iovec iov[2];
+
+	memset(&msg,0,sizeof(struct msghdr));
+
+	iov[0].iov_len=hdrlen;
+	iov[0].iov_base=hdrbuf;
+
+	iov[1].iov_len=datalen;
+	iov[1].iov_base=databuf;
+
+	msg.msg_iovlen=2;
+	msg.msg_iov=iov;
+
+	return recvmsg(sock,&msg,flag);
+}
+
+#define	PUT_DATA(msg, x)	{ *msg->tail = x; msgb_put(msg, 1); }
+
+static int sangoma_send(struct bsc_fd *bfd, struct msgb *msg) {
+	// send something
+	api_tx_hdr_t	hdr;
+	memset(&hdr, 0, sizeof(hdr));
+
+	int sapi = msg->data[0] >> 2;
+	int tei = msg->data[1] >> 1;
+	DEBUGP(SANGOMA, "WRITE (%d/%d) %3d: ", sapi, tei, msg->len);
+	if (debug_mask & SANGOMA) hexdump(msg->data, msg->len);
+
+	struct msghdr mhdr;
+	struct iovec iov[2];
+
+	memset(&mhdr, 0, sizeof(mhdr));
+
+	iov[0].iov_len = sizeof(api_tx_hdr_t);
+	iov[0].iov_base = &hdr;
+
+	iov[1].iov_len = msg->len;
+	iov[1].iov_base = msg->data;
+
+	mhdr.msg_iovlen = 2;
+	mhdr.msg_iov = iov;
+
+	int ret = sendmsg(bfd->fd, &mhdr, 0);
+
+	//printf("send: %d -> %d\n", olen, ret);
+	
+	return ret;
+};
+
+static int seq_nr = -1; // not inited
+static int seq_ns = -1; // not inited
+static int seq_active = 0;
+
+static int handle_ts1_read(struct bsc_fd *bfd)
+{
+	struct mi_e1_handle *e1h = bfd->data;
+	struct msgb *msg = msgb_alloc(TS1_ALLOC_SIZE);
+	struct sockaddr_mISDN l2addr;
+	struct mISDNhead *hh = malloc(sizeof(*hh));
+	int ret;
+
+	if (!msg)
+		return -ENOMEM;
+
+	memset(hh, 0, sizeof(*hh));
+
+#if 0
+	hh = (struct mISDNhead *) msg->data;
+
+	/* FIXME: Map TEI/SAPI to TRX */
+	msg->trx = e1h->bts->c0;
+
+	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))
+		return -EINVAL;
+
+	msgb_put(msg, ret);
+
+#endif
+
+	api_rx_hdr_t	rx_hdr;
+	struct msghdr mhdr;
+	struct iovec iov[2];
+
+	memset(&mhdr, 0, sizeof(mhdr));
+
+	iov[0].iov_len = sizeof(rx_hdr);
+	iov[0].iov_base = &rx_hdr;
+
+	iov[1].iov_len = msg->data_len;
+	iov[1].iov_base = msg->data;
+
+	mhdr.msg_iovlen = 2;
+	mhdr.msg_iov = iov;
+
+	ret = recvmsg(bfd->fd, &mhdr, 0);
+	int	rlen = ret - sizeof(rx_hdr);
+	msgb_put(msg, rlen);
+	//hexdump(msg->data, msg->len);
+	//printf("ret: %d\n", ret);
+
+	if (ret <= 0)
+		perror("readmsg");
+
+	if (rlen <= 0)
+		perror("short frame");
+
+	if (rx_hdr.error_flag) {
+		DEBUGP(SANGOMA, "error %x\n", rx_hdr.error_flag);
+		perror("sangoma rx error");
+		// error
+		// +16
+	};
+	int sapi = msg->data[0] >> 2;
+	int tei = msg->data[1] >> 1;
+#if 1
+	DEBUGP(SANGOMA, "read  (%d/%d) %3d: ", sapi, tei, rlen);
+	if (debug_mask & SANGOMA) hexdump(msg->data, rlen);
+#endif
+
+	int		ilen;
+	lapd_mph_type prim;
+	uint8_t *idata = lapd_receive(msg->data, msg->len, &ilen, &prim, bfd);
+	//if (idata) printf("lapd_receive: %p, %d, prim %d\n\n", idata, ilen, prim);
+	switch (prim) {
+		case 0: break;
+		case LAPD_MPH_ACTIVATE_IND: hh->prim = MPH_ACTIVATE_IND; break;
+		case LAPD_MPH_DEACTIVATE_IND: hh->prim = MPH_DEACTIVATE_IND; break;
+		case LAPD_DL_DATA_IND: hh->prim = DL_DATA_IND; break;
+		default: printf("ERROR: unknown prim\n");
+	};
+
+	int pass_on = (prim != 0);
+
+
+	//DEBUGP(SANGOMA, "  event_type %d, error_flag %x, channel %x\n", rx_hdr.event_type, rx_hdr.error_flag, rx_hdr.hdr_u.wp_api_event.channel);
+
+	//int	pass_on = 0; // pass to normal "misdn" processing, try to use what we already have
+
+#if 0	
+	// handle TEI management here
+
+	// read: Wed Jan 21 19:52:16 2009 <4000> sangoma.c:298 read, len 8: fc ff 03 0f 00 00 01 33 
+	// tei assignment request
+	if ((rlen == 8) && memcmp(msg->data, "\xfc\xff\x03\x0f\x00\x00\x01", 7) == 0) {
+		printf("1: TEI ASSIGN, sapi %d, tei %d\n", sapi, tei);
+
+		// write: tei assignment - action indicator identity assign
+		struct msgb *omsg = msgb_alloc(TS1_ALLOC_SIZE);
+		PUT_DATA(omsg, 0xfe);
+		PUT_DATA(omsg, 0xff);
+		PUT_DATA(omsg, 0x03);
+		PUT_DATA(omsg, 0x0f);
+		PUT_DATA(omsg, 0x00);
+		PUT_DATA(omsg, 0x00);
+		PUT_DATA(omsg, 0x02);
+		PUT_DATA(omsg, msg->data[7]);
+	
+		sangoma_send(bfd, omsg);
+
+
+	};
+	if (rlen == 3 && msg->data[0] == 0xf8 && msg->data[2] == 0x7f) {
+		// lapd stuff
+//Wed Jan 21 20:48:17 2009 <4000> sangoma.c:332 read   13: f8 33 00 00 80 80 00 05 10 00 ff ff ff 
+
+		// read: Wed Jan 21 20:11:07 2009 <4000> sangoma.c:310 read, len 3: f8 33 7f 
+		// SM/SABME
+		// send UA (unnumbered ack)
+		struct msgb *omsg = msgb_alloc(TS1_ALLOC_SIZE);
+		PUT_DATA(omsg, 0xf8);
+		PUT_DATA(omsg, msg->data[1]);
+		PUT_DATA(omsg, 0x73); // ua
+		printf("2: SABME\n");
+		sangoma_send(bfd, omsg);
+		seq_ns = 0;
+		seq_nr = 0;
+		// Wed Jan 21 21:17:24 2009 <4000> sangoma.c:238 WRITE   3: f8 33 73 
+	} else if (rlen == 4 && msg->data[0] == 0xf8 && msg->data[2] == 0x01) {
+		// Wed Jan 21 21:17:34 2009 <4000> sangoma.c:326 read    4: f8 33 01 01 
+
+		struct msgb *omsg = msgb_alloc(TS1_ALLOC_SIZE);
+		PUT_DATA(omsg, 0xf8); // sapi
+		PUT_DATA(omsg, msg->data[1]); // tei
+		PUT_DATA(omsg, 0x01); // S: ss=0, cont
+		PUT_DATA(omsg, (seq_nr << 1)| (msg->data[3] & 1)); // seq no
+		printf("3: RR, Receive Ready\n");
+		sangoma_send(bfd, omsg);
+		// Thu Jan 22 00:46:29 2009 <4000> sangoma.c:238 WRITE   4: f8 33 01 0d  <-- seq
+		hh->prim = MPH_ACTIVATE_IND;
+		if (!seq_active) {
+			pass_on = 1; // FIXME
+			seq_active = 1;
+		};
+	
+	} else if (rlen >= 4 && msg->data[0] == 0xfa && (msg->data[2] & 3) == 3) {
+		// Thu Jan 22 01:00:02 2009 <4000> sangoma.c:326 read    8: fa 33 97 01 03 00 01 08 
+
+		//pass_on = 1;
+		//hh->prim = DL_DATA_IND;
+		if (msg->data[2] == 0x97) {
+			printf("U: FRMR\n");
+
+		};
+		
+
+	} else if (rlen >= 4 && msg->data[0] == 0xfa && msg->data[2] == 9) {
+		// Thu Jan 22 01:47:35 2009 <4000> sangoma.c:331 read (62/25)   4: fa 33 09 00 
+		// Thu Jan 22 02:05:31 2009 <4000> sangoma.c:333 read (62/25)   4: fa 33 01 02 
+		// Thu Jan 22 02:05:31 2009 <4000> sangoma.c:238 WRITE  11: fa 33 00 00 00 05 a3 00 ff ff ff 
+		// Thu Jan 22 02:05:31 2009 <4000> sangoma.c:333 read (62/25)   4: fa 33 09 02 
+		//
+		// 9=rej, 7 >> 
+		int nr = msg->data[3] >> 1;
+		int pf = msg->data[3] & 1;
+		printf("REJ: nr=%d, pf=%d\n", nr, pf);
+		seq_ns = nr;
+
+	} else if (rlen >= 4 && msg->data[0] == 0xf8 && (msg->data[2] & 1) == 0) {
+		/*
+		 * Thu Jan 22 05:52:27 2009 <4000> sangoma.c:336 read  (62/25)  38: f8 33 00 04 80 80 00 1e dc a3 ff ff ff c6 01 00 c7 07 07 d9 01 16 05 31 03 c8 01 03 c9 08 41 44 4d 49 4e 49 53 00 
+		 * Thu Jan 22 05:52:27 2009 <4000> sangoma.c:336 read  (62/25)  38: f8 33 00 05 80 80 00 1e dc a3 ff ff ff c6 01 00 c7 07 07 d9 01 16 05 31 03 c8 01 03 c9 08 41 44 4d 49 4e 49 53 00 
+		 * Thu Jan 22 05:52:27 2009 <4000> sangoma.c:336 read  (62/25)  38: f8 33 00 05 80 80 00 1e dc a3 ff ff ff c6 01 00 c7 07 07 d9 01 16 05 31 03 c8 01 03 c9 08 41 44 4d 49 4e 49 53 00 
+		 * Thu Jan 22 05:52:27 2009 <4000> sangoma.c:336 read  (62/25)  38: f8 33 00 05 80 80 00 1e dc a3 ff ff ff c6 01 00 c7 07 07 d9 01 16 05 31 03 c8 01 03 c9 08 41 44 4d 49 4e 49 53 00 
+		 * */
+
+		printf("I: \n");
+		hh->prim = DL_DATA_IND;
+		pass_on = 1;
+
+
+	} else if (rlen == 4 && msg->data[0] == 0xfa) {
+		// FIXME: MUST BE LAST, could be special frame.
+		// Thu Jan 22 02:27:35 2009 <4000> sangoma.c:333 read (62/25)   4: fa 33 01 04 
+		// keep seq_nr..  data[
+		//seq_nr = msg->data[2] >> 1;
+		//printf("ack, nr=%d\n", seq_nr);
+
+	} else if ((rlen == 8) && memcmp(msg->data, "\xfa\x33\x97\x01\x0b\x00\x01\x08", 8) == 0) {
+
+		// Wed Jan 21 23:57:49 2009 <4000> sangoma.c:326 read    8: fa 33 97 01 0b 00 01 08 
+		// Wed Jan 21 23:59:18 2009 <4000> sangoma.c:326 read    8: fa 33 97 01 0b 00 01 08 
+		printf("4  ??\n");
+	};
+
+/*
+ * Thu Jan 22 00:03:06 2009 <4000> sangoma.c:326 read    8: fc ff 03 0f 00 00 01 33 
+ * 1 TEI ASSIGN, sapi 0, tei 25
+ * Thu Jan 22 00:03:06 2009 <4000> sangoma.c:238 WRITE   8: fe ff 03 0f 00 00 02 33 
+ * Thu Jan 22 00:03:06 2009 <4000> sangoma.c:326 read    3: f8 33 7f 
+ * 2 SM/SABME
+ * Thu Jan 22 00:03:06 2009 <4000> sangoma.c:238 WRITE   3: f8 33 73 
+ * Thu Jan 22 00:03:16 2009 <4000> sangoma.c:326 read    4: f8 33 01 01 
+ * 3 RR
+ * Thu Jan 22 00:03:16 2009 <4000> sangoma.c:238 WRITE   4: f8 33 01 0b 
+ * Thu Jan 22 00:03:16 2009 <4000> sangoma.c:326 read    8: fa 33 97 01 0b 00 01 08 
+ * Thu Jan 22 00:03:16 2009 <4000> sangoma.c:326 read    3: f8 33 7f 
+ * 2 SM/SABME
+ * Thu Jan 22 00:03:16 2009 <4000> sangoma.c:238 WRITE   3: f8 33 73 
+ * Thu Jan 22 00:03:27 2009 <4000> sangoma.c:326 read    4: f8 33 01 01 
+ * 3 RR
+ * Thu Jan 22 00:03:27 2009 <4000> sangoma.c:238 WRITE   4: f8 33 01 0b 
+ * Thu Jan 22 00:03:27 2009 <4000> sangoma.c:326 read    8: fa 33 97 01 0b 00 01 08 
+ * Thu Jan 22 00:03:27 2009 <4000> sangoma.c:326 read    3: f8 33 7f 
+ * 2 SM/SABME
+ * Thu Jan 22 00:03:27 2009 <4000> sangoma.c:238 WRITE   3: f8 33 73 
+ */
+
+
+/*
+ * Thu Jan 22 02:11:48 2009 <4000> sangoma.c:333 read (62/25)   4: fa 33 01 34 
+ * Thu Jan 22 02:11:48 2009 <4000> sangoma.c:238 WRITE  11: fa 33 34 00 00 05 74 00 ff ff ff 
+ * Thu Jan 22 02:11:48 2009 <4000> sangoma.c:333 read (62/25)   4: fa 33 01 36 
+ * */
+#endif
+
+	if (rlen > 4 && msg->data[0] == 0xf8 && msg->data[2] == 0x00) {
+		//pass_on = 1;
+	};
+	
+	if (!pass_on) {
+		free(hh);
+		return 0;
+	};
+/*
+ * Thu Jan 22 14:52:53 2009 <4000> sangoma.c:336 read  (62/25)  13: f8 33 00 02 80 80 00 05 64 00 ff ff ff 
+ * I: 
+ * Thu Jan 22 14:52:53 2009 <1000> sangoma.c:584 got DL_DATA_IND
+ * RX: 00 05 64 00 ff ff ff 
+ * ABIS OML placement 0x5 not supported
+ * */
+	l2addr.sapi = msg->data[0] >> 2;
+	l2addr.tei = msg->data[1] >> 1;
+	msgb_pull(msg, 2);
+	int alen = msg->len;
+	//hh->prim = DL_INFORMATION_IND;
+/*
+ * Thu Jan 22 01:09:35 2009 <1000> sangoma.c:489 got MPH_ACTIVATE_IND
+ * bootstrapping OML
+ * OML TX: 80 80 00 05 63 00 ff ff ff 
+ * Thu Jan 22 01:09:35 2009 <4000> sangoma.c:328 read    3: f8 33 7f 
+ * 2 SM/SABME
+ * Thu Jan 22 01:09:35 2009 <4000> sangoma.c:238 WRITE   3: f8 33 73 
+ * OML TX: 80 80 00 05 a3 00 ff ff ff 
+ * OML TX: 80 80 00 05 a6 00 ff ff ff 
+ * OML TX: 80 80 00 05 74 00 ff ff ff 
+ * OML TX: 80 80 00 05 a3 00 ff ff ff 
+ * OML TX: 80 80 00 1c d0 00 ff ff ff 91 07 d7 09 08 0e 24 0b ce 02 00 1e e8 01 05 42 02 00 0a 44 02 00 00 
+ * OML TX: 80 80 00 4c 41 01 00 ff ff 09 3f 0a 04 61 00 0b ff 0c 64 62 00 66 00 6e 06 18 1f 19 0a 0f 14 19 1e 7b 0b 23 14 28 00 04 03 2a 7f 2b 00 fa 8f 7d 33 2c 1f 29 5a 5a 5a 87 94 23 28 95 23 28 35 01 00 46 01 00 58 01 40 c5 01 00 f2 01 00 08 00 7b 
+ * OML TX: 80 80 00 58 d0 a1 00 ff ff d0 00 64 00 67 00 68 00 6a 00 6c 00 6d 00 6f 08 70 08 01 71 10 10 10 72 08 02 73 0a 74 05 75 06 76 06 78 14 79 14 7a 14 7d 06 92 03 20 01 00 45 01 00 48 01 00 5a 01 00 5b 01 05 5e 01 1a 5f 01 20 9d 01 00 47 01 00 5c 01 64 5d 01 1e 97 01 20 f7 01 3c 
+ * OML TX: 80 80 00 2e d0 a2 00 ff ff 69 00 6b 00 7e 04 01 7f 04 02 80 0f 81 0a 82 05 83 05 84 0c 85 14 86 0f 87 04 88 04 89 02 8a 02 8b 02 8c 01 8d 40 65 01 00 
+ * OML TX: 80 80 00 09 24 02 00 00 ff 01 00 01 ff 
+ * OML TX: 80 80 00 17 44 02 00 00 ff 05 01 00 7b 2d 00 dc 01 fe dd 01 01 9b 01 01 9f 01 00 
+ * OML TX: 80 80 00 0b 21 02 00 00 ff 34 01 01 00 01 ff 
+ * OML TX: 80 80 00 14 47 03 00 00 00 05 01 00 7b 0d 08 15 00 1b 00 40 07 59 01 00 
+ * OML TX: 80 80 00 14 47 03 00 00 01 05 01 00 7b 0d 09 15 00 1b 00 40 07 59 01 00 
+ * OML TX: 80 80 00 09 2a 03 00 00 01 01 00 02 01 
+ * OML TX: 80 80 00 14 47 03 00 00 02 05 01 00 7b 0d 09 15 00 1b 00 40 07 59 01 00 
+ * OML TX: 80 80 00 09 2a 03 00 00 02 01 00 02 02 
+ * OML TX: 80 80 00 14 47 03 00 00 03 05 01 00 7b 0d 09 15 00 1b 00 40 07 59 01 00 
+ * OML TX: 80 80 00 09 2a 03 00 00 03 01 00 02 03 
+ * OML TX: 80 80 00 14 47 03 00 00 04 05 01 00 7b 0d 09 15 00 1b 00 40 07 59 01 00 
+ * OML TX: 80 80 00 09 2a 03 00 00 04 01 00 03 00 
+ * OML TX: 80 80 00 14 47 03 00 00 05 05 01 00 7b 0d 09 15 00 1b 00 40 07 59 01 00 
+ * OML TX: 80 80 00 09 2a 03 00 00 05 01 00 03 01 
+ * OML TX: 80 80 00 14 47 03 00 00 06 05 01 00 7b 0d 09 15 00 1b 00 40 07 59 01 00 
+ * OML TX: 80 80 00 09 2a 03 00 00 06 01 00 03 02 
+ * OML TX: 80 80 00 14 47 03 00 00 07 05 01 00 7b 0d 09 15 00 1b 00 40 07 59 01 00 
+ * OML TX: 80 80 00 09 2a 03 00 00 07 01 00 03 03 
+ * OML TX: 80 80 00 05 a6 00 ff ff ff 
+ * OML TX: 80 80 00 05 74 00 ff ff ff 
+ * OML TX: 80 80 00 05 66 00 ff ff ff 
+ */
+
+#if 0
+	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)\n",
+		ret, hh->prim, hh->id);
+#endif
+
+	switch (hh->prim) {
+	case DL_INFORMATION_IND:
+		DEBUGP(DMI, "got DL_INFORMATION_IND\n");
+		struct sockaddr_mISDN *sa = NULL;
+		char *lstr = "UNKN";
+
+		switch (l2addr.tei) {
+		case TEI_OML:
+			sa = &e1h->omladdr;
+			lstr = "OML";
+			break;
+		case TEI_RSL:
+			sa = &e1h->l2addr;
+			lstr = "RSL";
+			break;
+		default:
+			break;
+		}
+		if (sa) {
+			DEBUGP(DMI, "%s use channel(%d) sapi(%d) tei(%d) for now\n",
+				lstr, l2addr.channel, l2addr.sapi, l2addr.tei);
+			memcpy(sa, &l2addr, sizeof(l2addr));
+		}
+		break;
+	case DL_ESTABLISH_IND:
+		DEBUGP(DMI, "got DL_ESTABLISH_IND\n");
+		break;
+	case DL_ESTABLISH_CNF:
+		DEBUGP(DMI, "got DL_ESTABLISH_CNF\n");
+		break;
+	case DL_RELEASE_IND:
+		DEBUGP(DMI, "got DL_RELEASE_IND: E1 Layer 1 disappeared?\n");
+		break;
+	case MPH_ACTIVATE_IND:
+		DEBUGP(DMI, "got MPH_ACTIVATE_IND\n");
+		printf("tei %d, sapi %d\n", l2addr.tei, l2addr.sapi);
+		if (l2addr.tei == TEI_OML && l2addr.sapi == SAPI_OML)
+			e1h->cb(EVT_E1_OML_UP, e1h->bts);
+		else if (l2addr.tei == TEI_RSL && l2addr.sapi == SAPI_RSL)
+			e1h->cb(EVT_E1_RSL_UP, e1h->bts);
+		break;
+	case MPH_DEACTIVATE_IND:
+		DEBUGP(DMI, "got MPH_DEACTIVATE_IND: TEI link closed?\n");
+		if (l2addr.tei == TEI_OML && l2addr.sapi == SAPI_OML)
+			e1h->cb(EVT_E1_OML_DN, e1h->bts);
+		else if (l2addr.tei == TEI_RSL && l2addr.sapi == SAPI_RSL)
+			e1h->cb(EVT_E1_RSL_DN, e1h->bts);
+		break;
+	case DL_DATA_IND:
+		DEBUGP(DMI, "got DL_DATA_IND\n");
+		write_pcap_packet(PCAP_INPUT, &l2addr, msg);
+
+		ret = msg->len;
+		msg->l2h = msg->data + 2;// + MISDN_HEADER_LEN;
+
+		if (debug_mask & DMI) { 
+			fprintf(stdout, "RX: ");
+			hexdump(msgb_l2(msg), ret - (msg->l2h - msg->data));// - MISDN_HEADER_LEN);
+		}
+		switch (l2addr.tei) {
+		case TEI_OML:
+			ret = abis_nm_rcvmsg(msg);
+			break;
+		case TEI_RSL:
+			ret = abis_rsl_rcvmsg(msg);
+			break;
+		default:
+			fprintf(stderr, "DATA_IND for unknown TEI\n");
+			break;
+		}
+		break;
+	default:
+		DEBUGP(DMI, "got unexpected 0x%x prim\n", hh->prim);
+		break;
+	}
+	return ret;
+}
+
+void sangoma_transmit(uint8_t *data, int len, void *cbdata) {
+	//printf("sangoma_transmit %d\n", len);
+	//hexdump(data, len);
+	struct msgb *msg = msgb_alloc(TS1_ALLOC_SIZE);
+	memmove(msg->data, data, len);
+	msgb_put(msg, len);
+	sangoma_send(cbdata, msg);
+};
+
+static int handle_ts1_write(struct bsc_fd *bfd)
+{
+	struct mi_e1_handle *e1h = bfd->data;
+	struct msgb *msg;
+	struct mISDNhead *hh;
+	int ret, no_oml = 0;
+
+	msg = msgb_dequeue(&e1h->oml_tx_list);
+	if (!msg)
+		no_oml = 1;
+	else {
+#if 0
+		uint8_t *prepend = msgb_push(msg, 4);
+		prepend[0] = (SAPI_OML << 2) | 0x2;
+		prepend[1] = (TEI_OML << 1) | 1;
+		prepend[2] = seq_ns << 1;
+		prepend[3] = seq_nr << 1;
+
+		sangoma_send(bfd, msg);
+		seq_ns++;
+#endif
+		lapd_transmit(TEI_OML, msg->data, msg->len, bfd);
+
+		write_pcap_packet(PCAP_OUTPUT, &e1h->omladdr, msg);
+		ret = sendto(bfd->fd, msg->data, msg->len, 0,
+			     (struct sockaddr *)&e1h->omladdr,
+			     sizeof(e1h->omladdr));
+		msgb_free(msg);
+		usleep(100000);
+		/* we always dequeue all OML messages */
+		return ret;
+	}
+
+	msg = msgb_dequeue(&e1h->rsl_tx_list);
+	if (!msg) {
+		if (no_oml)
+			bfd->when &= ~BSC_FD_WRITE;
+	} else {
+#if 0
+		uint8_t *prepend = msgb_push(msg, 2);
+		prepend[0] = (SAPI_RSL << 2) | 0x2;
+		prepend[1] = (TEI_RSL << 1) | 1;
+		prepend[2] = seq_ns << 1;
+		prepend[3] = seq_nr << 1;
+
+		sangoma_send(bfd, msg);
+		seq_ns++;
+#endif
+		lapd_transmit(TEI_RSL, msg->data, msg->len, bfd);
+
+		write_pcap_packet(PCAP_OUTPUT, &e1h->l2addr, msg);
+		ret = sendto(bfd->fd, msg->data, msg->len, 0,
+			     (struct sockaddr *)&e1h->l2addr,
+			     sizeof(e1h->l2addr));
+		msgb_free(msg);
+		usleep(10000);
+		//sleep(1);
+	}
+
+	return ret;
+}
+
+#define TSX_ALLOC_SIZE 4096
+
+/* FIXME: read from a B channel TS */
+static int handle_tsX_read(struct bsc_fd *bfd)
+{
+	struct mi_e1_handle *e1h = bfd->data;
+	struct msgb *msg = msgb_alloc(TSX_ALLOC_SIZE);
+	struct mISDNhead *hh;
+	int ret, dummy;
+
+	if (!msg)
+		return -ENOMEM;
+
+	hh = (struct mISDNhead *) msg->data;
+
+	/* FIXME: Map TEI/SAPI to TRX */
+	msg->trx = e1h->bts->c0;
+
+	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);
+
+	DEBUGP(DMIB, "<= BCHAN len = %d, prim(0x%x) id(0x%x)\n", ret, hh->prim, hh->id);
+
+	switch (hh->prim) {
+	case PH_CONTROL_IND:
+		DEBUGP(DMIB, "got PH_CONTROL_IND\n");
+		break;
+	case PH_DATA_IND:
+		DEBUGP(DMIB, "got PH_DATA_IND\n");
+
+		msg->l2h = msg->data + MISDN_HEADER_LEN;
+		
+		if (debug_mask & DMIB) {
+			fprintf(stdout, "BCHAN RX: ");
+			hexdump(msgb_l2(msg), ret - MISDN_HEADER_LEN);
+		}
+		if (!e1h->ts2_fd)
+			e1h->ts2_fd = open("/tmp/ts2.dump", O_WRONLY|O_APPEND|O_CREAT, 0660);
+		
+		dummy = write(e1h->ts2_fd, msgb_l2(msg), ret - MISDN_HEADER_LEN);
+
+		break;
+	default:
+		DEBUGP(DMIB, "got unexpected 0x%x prim\n", hh->prim);
+		break;
+	}
+	/* FIXME: don't free it if we still hold reference! */
+	msgb_free(msg);
+
+	return ret;
+}
+
+static int handle_tsX_write(struct bsc_fd *bfd)
+{
+	/* FIXME: write to a B channel TS */
+	return -1;
+}
+
+/* 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)
+{
+	unsigned int e1_ts = bfd->priv_nr;
+	int rc = 0;
+
+	switch (e1_ts) {
+	case 1:
+		if (what & BSC_FD_READ)
+			rc = handle_ts1_read(bfd);
+		if (what & BSC_FD_WRITE)
+			rc = handle_ts1_write(bfd);
+		break;
+	default:
+		if (what & BSC_FD_READ)
+			rc = handle_tsX_read(bfd);
+		if (what & BSC_FD_WRITE)
+			rc = handle_tsX_write(bfd);
+		break;
+	}
+
+	return rc;
+}
+
+int abis_rsl_sendmsg(struct msgb *msg)
+{
+	struct mi_e1_handle *e1h = global_e1h;
+
+	msg->l2h = msg->data;
+	msgb_enqueue(&e1h->rsl_tx_list, msg);
+	e1h->fd[0].when |= BSC_FD_WRITE;
+
+	return 0;
+}
+
+int _abis_nm_sendmsg(struct msgb *msg)
+{
+	struct mi_e1_handle *e1h = global_e1h;
+
+	msg->l2h = msg->data;
+	msgb_enqueue(&e1h->oml_tx_list, msg);
+	e1h->fd[0].when |= BSC_FD_WRITE;
+
+	return 0;
+}
+
+static int activate_bchan(struct mi_e1_handle *e1h, int ts)
+{
+	struct mISDNhead hh;
+	int ret;
+	struct bsc_fd *bfd = &e1h->fd[ts-1];
+
+	fprintf(stdout, "activate bchan\n");
+	hh.prim = PH_ACTIVATE_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 0;
+	}
+
+	return ret;
+}
+
+static int mi_e1_setup(struct mi_e1_handle *e1h)
+{
+	int ts, sk, ret, cnt;
+	struct mISDN_devinfo devinfo;
+
+	sk = socket(PF_ISDN, SOCK_RAW, ISDN_P_BASE);
+	if (sk < 0) {
+		fprintf(stderr, "could not open socket %s\n", strerror(errno));
+		return sk;
+	}
+
+	ret = ioctl(sk, IMGETCOUNT, &cnt);
+	if (ret) {
+		fprintf(stderr, "error getting interf count: %s\n",
+			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 = e1h->cardnr;
+	ret = ioctl(sk, IMGETDEVINFO, &devinfo);
+	if (ret < 0) {
+		fprintf(stdout, "error getting info for device %d: %s\n",
+			e1h->cardnr, 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
+
+	/* TS0 is CRC4, don't need any fd for it */
+	for (ts = 1; ts < NUM_E1_TS; ts++) {
+		unsigned int idx = ts-1;
+		struct bsc_fd *bfd = &e1h->fd[idx];
+		struct sockaddr_mISDN addr;
+
+		bfd->data = e1h;
+		bfd->priv_nr = ts;
+		bfd->cb = misdn_fd_cb;
+
+		if (ts == 1) {
+			bfd->fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_LAPD_NT);
+			bfd->when = BSC_FD_READ;
+		} else 
+			bfd->fd = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_B_RAW);
+
+		if (bfd->fd < 0) {
+			fprintf(stderr, "could not open socket %s\n",
+				strerror(errno));
+			return bfd->fd;
+		}
+
+		memset(&addr, 0, sizeof(addr));
+		addr.family = AF_ISDN;
+		addr.dev = e1h->cardnr;
+		if (ts == 1) {
+			addr.channel = 0;
+			addr.sapi = 0;/* SAPI not supported yet in kernel */
+			addr.tei = TEI_L2ML;
+		} else
+			addr.channel = ts; 
+
+		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 (ts == 2) {
+			bfd->when = BSC_FD_READ;
+			activate_bchan(e1h, ts);
+		}
+
+		ret = bsc_register_fd(bfd);
+		if (ret < 0) {
+			fprintf(stderr, "could not register FD: %s\n",
+				strerror(ret));
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int sangoma_e1_setup(struct mi_e1_handle *e1h)
+{
+	// /etc/wanpipe/api/aft/aft_api -c wanpipe2 -i w2g1 -r -rxcnt 10 -verbose
+	DEBUGP(SANGOMA, "setup.\n");
+
+	struct wan_sockaddr_ll 	sa;
+
+	int ts = 1;
+	unsigned int idx = ts-1;
+	struct bsc_fd *bfd = &e1h->fd[idx];
+
+	bfd->data = e1h;
+	bfd->priv_nr = ts;
+	bfd->cb = misdn_fd_cb;
+
+	memset(&sa, 0, sizeof(struct wan_sockaddr_ll));
+   	if ((bfd->fd = socket(AF_WANPIPE, SOCK_RAW, 0)) < 0)
+		perror("socket");
+	bfd->when = BSC_FD_READ;
+
+	strcpy((char*)sa.sll_card, "wanpipe2");
+	strcpy((char*)sa.sll_device, "w2g1");
+	sa.sll_protocol = htons(PVC_PROT);
+	sa.sll_family = AF_WANPIPE;
+	
+	if (bind(bfd->fd, (struct sockaddr *)&sa, sizeof(struct wan_sockaddr_ll)) < 0)
+		perror("bind");
+
+	int	customer_id;
+	if (ioctl(bfd->fd, SIOC_AFT_CUSTOMER_ID, &customer_id))
+		perror("customer");
+
+	int ret = bsc_register_fd(bfd);
+	if (ret < 0) {
+		fprintf(stderr, "could not register FD: %s\n",
+			strerror(ret));
+		return ret;
+	}
+
+	DEBUGP(SANGOMA, "done registering, fd=%d customer=%d\n", bfd->fd, customer_id);
+
+	//sangoma_send(bfd);
+	return 0;
+}
+
+int mi_setup(struct gsm_bts *bts, int cardnr, 
+	     void (cb)(int event, struct gsm_bts *bts))
+{
+	struct mi_e1_handle *e1h;
+
+	e1h = malloc(sizeof(*e1h));
+	memset(e1h, 0, sizeof(*e1h));
+
+	e1h->cardnr = cardnr;
+	e1h->bts = bts;
+	e1h->cb = cb;
+	INIT_LLIST_HEAD(&e1h->oml_tx_list);
+	INIT_LLIST_HEAD(&e1h->rsl_tx_list);
+
+	global_e1h = e1h;
+	lapd_transmit_cb = sangoma_transmit;
+
+	//return mi_e1_setup(e1h);
+	return sangoma_e1_setup(e1h);
+}
