initial commit of current OpenBSC state

diff --git a/include/openbsc/abis_nm.h b/include/openbsc/abis_nm.h
new file mode 100644
index 0000000..1108aa4
--- /dev/null
+++ b/include/openbsc/abis_nm.h
@@ -0,0 +1,316 @@
+/* GSM Network Management messages on the A-bis interface 
+ * 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */
+
+/* (C) 2008 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.
+ *
+ */
+
+#ifndef _NM_H
+#define _NM_H
+
+#include <sys/types.h>
+
+/* PRIVATE */
+
+/* generic header in front of every OML message */
+struct abis_om_hdr {
+	u_int8_t	mdisc;
+	u_int8_t	placement;
+	u_int8_t	sequence;
+	u_int8_t	length;
+	u_int8_t	data[0];
+} __attribute__ ((packed));
+
+#define ABIS_OM_MDISC_FOM		0x80
+#define ABIS_OM_MDISC_MMI		0x40
+#define ABIS_OM_MDISC_TRAU		0x20
+#define ABIS_OM_MDISC_MANUF		0x10
+#define ABIS_OM_PLACEMENT_ONLY		0x80
+#define ABIS_OM_PLACEMENT_FIRST 	0x40
+#define ABIS_OM_PLACEMENT_MIDDLE	0x20
+#define ABIS_OM_PLACEMENT_LAST		0x10
+
+struct abis_om_obj_inst {
+	u_int8_t	bts_nr;
+	u_int8_t	trx_nr;
+	u_int8_t	ts_nr;
+} __attribute__ ((packed));
+
+struct abis_om_fom_hdr {
+	u_int8_t	msg_type;
+	u_int8_t	obj_class;
+	struct abis_om_obj_inst	obj_inst;
+} __attribute__ ((packed));
+
+#define ABIS_OM_FOM_HDR_SIZE	(sizeof(struct abis_om_hdr) + sizeof(struct abis_om_fom_hdr))
+
+/* Section 9.1: Message Types */
+enum abis_nm_msgtype {
+	/* SW Download Management Messages */
+	NM_MT_LOAD_INIT			= 0x01,
+	NM_MT_LOAD_INIT_ACK,
+	NM_MT_LOAD_INIT_NACK,
+	NM_MT_LOAD_SEG,
+	NM_MT_LOAD_SEG_ACK,
+	NM_MT_LOAD_ABORT,
+	NM_MT_LOAD_END,
+	NM_MT_LOAD_END_ACK,
+	NM_MT_LOAD_END_NACK,
+	NM_MT_SW_ACT_REQ,		/* BTS->BSC */
+	NM_MT_SW_ACT_REQ_ACK,
+	NM_MT_SW_ACT_REQ_NACK,
+	NM_MT_ACTIVATE_SW,		/* BSC->BTS */
+	NM_MT_ACTIVATE_SW_ACK,
+	NM_MT_ACTIVATE_SW_NACK,
+	NM_MT_SW_ACTIVATED_REP,		/* 0x10 */
+	/* A-bis Interface Management Messages */
+	NM_MT_ESTABLISH_TEI		= 0x21,
+	NM_MT_ESTABLISH_TEI_ACK,
+	NM_MT_ESTABLISH_TEI_NACK,
+	NM_MT_CONN_TERR_SIGN,
+	NM_MT_CONN_TERR_SIGN_ACK,
+	NM_MT_CONN_TERR_SIGN_NACK,
+	NM_MT_DISC_TERR_SIGN,
+	NM_MT_DISC_TERR_SIGN_ACK,
+	NM_MT_DISC_TERR_SIGN_NACK,
+	NM_MT_CONN_TERR_TRAF,
+	NM_MT_CONN_TERR_TRAF_ACK,
+	NM_MT_CONN_TERR_TRAF_NACK,
+	NM_MT_DISC_TERR_TRAF,
+	NM_MT_DISC_TERR_TRAF_ACK,
+	NM_MT_DISC_TERR_TRAF_NACK,
+	/* Transmission Management Messages */
+	NM_MT_CONN_MDROP_LINK		= 0x31,
+	NM_MT_CONN_MDROP_LINK_ACK,
+	NM_MT_CONN_MDROP_LINK_NACK,
+	NM_MT_DISC_MDROP_LINK,
+	NM_MT_DISC_MDROP_LINK_ACK,
+	NM_MT_DISC_MDROP_LINK_NACK,
+	/* Air Interface Management Messages */
+	NM_MT_SET_BTS_ATTR		= 0x41,
+	NM_MT_SET_BTS_ATTR_ACK,
+	NM_MT_SET_BTS_ATTR_NACK,
+	NM_MT_SET_RADIO_ATTR,
+	NM_MT_SET_RADIO_ATTR_ACK,
+	NM_MT_SET_RADIO_ATTR_NACK,
+	NM_MT_SET_CHAN_ATTR,
+	NM_MT_SET_CHAN_ATTR_ACK,
+	NM_MT_SET_CHAN_ATTR_NACK,
+	/* Test Management Messages */
+	NM_MT_PERF_TEST			= 0x51,
+	NM_MT_PERF_TESET_ACK,
+	NM_MT_PERF_TEST_NACK,
+	NM_MT_TEST_REP,
+	NM_MT_SEND_TEST_REP,
+	NM_MT_SEND_TEST_REP_ACK,
+	NM_MT_SEND_TEST_REP_NACK,
+	NM_MT_STOP_TEST,
+	NM_MT_STOP_TEST_ACK,
+	NM_MT_STOP_TEST_NACK,
+	/* State Management and Event Report Messages */
+	NM_MT_STATECHG_EVENT_REP	= 0x61,
+	NM_MT_FAILURE_EVENT_REP,
+	NM_MT_STOP_EVENT_REP,
+	NM_MT_STOP_EVENT_REP_ACK,
+	NM_MT_STOP_EVENT_REP_NACK,
+	NM_MT_REST_EVENT_REP,
+	NM_MT_REST_EVENT_REP_ACK,
+	NM_MT_REST_EVENT_REP_NACK,
+	NM_MT_CHG_ADM_STATE,
+	NM_MT_CHG_ADM_STATE_ACK,
+	NM_MT_CHG_ADM_STATE_NACK,
+	NM_MT_CHG_ADM_STATE_REQ,
+	NM_MT_CHG_ADM_STATE_REQ_ACK,
+	NM_MT_CHG_ADM_STATE_REQ_NACK,
+	NM_MT_REP_OUTST_ALARMS		= 0x93,
+	NM_MT_REP_OUTST_ALARMS_ACK,
+	NM_MT_REP_OUTST_ALARMS_NACK,
+	/* Equipment Management Messages */
+	NM_MT_CHANGEOVER		= 0x71,
+	NM_MT_CHANGEOVER_ACK,
+	NM_MT_CHANGEOVER_NACK,
+	NM_MT_OPSTART,
+	NM_MT_OPSTART_ACK,
+	NM_MT_OPSTART_NACK,
+	NM_MT_REINIT,
+	NM_MT_REINIT_ACK,
+	NM_MT_REINIT_NACK,
+	NM_MT_SET_SITE_OUT,
+	NM_MT_SET_SITE_OUT_ACK,
+	NM_MT_SET_SITE_OUT_NACK,
+	NM_MT_CHG_HW_CONF		= 0x90,
+	NM_MT_CHG_HW_CONF_ACK,
+	NM_MT_CHG_HW_CONF_NACK,
+	/* Measurement Management Messages */
+	NM_MT_MEAS_RES_REQ		= 0x8a,
+	NM_MT_MEAS_RES_RESP,
+	NM_MT_STOP_MEAS,
+	NM_MT_START_MEAS,
+	/* Other Messages */
+	NM_MT_GET_ATTR			= 0x81,
+	NM_MT_GET_ATTR_RESP,
+	NM_MT_GET_ATTR_NACK,
+	NM_MT_SET_ALARM_THRES,
+	NM_MT_SET_ALARM_THRES_ACK,
+	NM_MT_SET_ALARM_THRES_NACK,
+};
+
+/* Section 9.2: Object Class */
+enum abis_nm_obj_class {
+	NM_OC_SITE_MANAGER		= 0x00,
+	NM_OC_BTS,
+	NM_OC_RADIO_CARRIER,
+	NM_OC_BASEB_TRANSC,
+	NM_OC_CHANNEL,
+	/* RFU: 05-FE */
+	NM_OC_NULL			= 0xff,
+};
+
+/* Section 9.4: Attributes */
+enum abis_nm_attr {
+	NM_ATT_CHANNEL	= 0x01,
+	NM_ATT_ADD_INFO,
+	NM_ATT_ADD_TEXT,
+	NM_ATT_ADM_STATE,
+	NM_ATT_ARFCN_LIST,
+	NM_ATT_AUTON_REPORT,
+	NM_ATT_AVAIL_STATUS,
+	NM_ATT_BCCH_ARFCN,
+	NM_ATT_BSIC,
+	NM_ATT_BTS_AIR_TIMER,
+	NM_ATT_CCCH_L_I_P,
+	NM_ATT_CCCH_L_T,
+	NM_ATT_CHAN_COMB,
+	NM_ATT_CONN_FAIL_CRIT,
+	NM_ATT_DEST,
+	/* res */
+	NM_ATT_EVENT_TYPE	= 0x11,
+	NM_ATT_FILE_ID,
+	NM_ATT_FILE_VERSION,
+	NM_ATT_GSM_TIME,
+	NM_ATT_HSN,
+	NM_ATT_HW_CONFIG,
+	NM_ATT_HW_DESC,
+	NM_ATT_INTAVE_PARAM,
+	NM_ATT_INTERF_BOUND,
+	NM_ATT_LIST_REQ_ATTR,
+	NM_ATT_MAIO,
+	NM_ATT_MANUF_STATE,
+	NM_ATT_MANUF_THRESH,
+	NM_ATT_MANUF_ID,
+	NM_ATT_MAX_TA,
+	NM_ATT_MDROP_LINK,	/* 0x20 */
+	NM_ATT_MDROP_NEXT,
+	NM_ATT_NACK_CAUSES,
+	NM_ATT_NY1,
+	NM_ATT_OPER_STATE,
+	NM_ATT_OVERL_PERIOD,
+	NM_ATT_PHYS_CONF,
+	NM_ATT_POWER_CLASS,
+	NM_ATT_POWER_THRESH,
+	NM_ATT_PROB_CAUSE,
+	NM_ATT_RACH_B_THRESH,
+	NM_ATT_LDAVG_SLOTS,
+	NM_ATT_RAD_SUBC,
+	NM_ATT_RF_MAXPOWR_R,
+	NM_ATT_SITE_INPUTS,
+	NM_ATT_SITE_OUTPUTS,
+	NM_ATT_SOURCE,		/* 0x30 */
+	NM_ATT_SPEC_PROB,
+	NM_ATT_START_TIME,
+	NM_ATT_T200,
+	NM_ATT_TEI,
+	NM_ATT_TEST_DUR,
+	NM_ATT_TEST_NO,
+	NM_ATT_TEST_REPORT,
+	NM_ATT_VSWR_THRESH,
+	NM_ATT_WINDOW_SIZE,
+	/* Res  */
+	NM_ATT_TSC		= 0x40,
+	NM_ATT_SW_CONFIG,
+	NM_ATT_SW_DESCR,
+	NM_ATT_SEVERITY,
+	NM_ATT_GET_ARI,
+	NM_ATT_HW_CONF_CHG,
+	NM_ATT_OUTST_ALARM,
+	NM_ATT_FILE_DATA,
+	NM_ATT_MEAS_RES,
+	NM_ATT_MEAS_TYPE,
+};
+
+/* Section 9.4.4: Administrative State */
+enum abis_nm_adm_state {
+	NM_STATE_LOCKED		= 0x01,
+	NM_STATE_UNLOCKED	= 0x02,
+	NM_STATE_SHUTDOWN	= 0x03,
+	NM_STATE_NULL		= 0xff,
+};
+
+/* Section 9.4.13: Channel Combination */
+enum abis_nm_chan_comb {
+	NM_CHANC_TCHFull	= 0x00,
+	NM_CHANC_TCHHalf	= 0x01,
+	NM_CHANC_TCHHalf2	= 0x02,
+	NM_CHANC_SDCCH		= 0x03,
+	NM_CHANC_mainBCCH	= 0x04,
+	NM_CHANC_BCCCHComb	= 0x05,
+	NM_CHANC_BCCH		= 0x06,
+	NM_CHANC_BCCH_CBCH	= 0x07,
+	NM_CHANC_SDCCH_CBCH	= 0x08,
+};
+
+/* Section 9.4.1 */
+struct abis_nm_abis_channel {
+	u_int8_t	attrib;
+	u_int8_t	bts_port;
+	u_int8_t	timeslot;
+	u_int8_t	subslot;
+} __attribute__ ((packed));
+
+/* PUBLIC */
+
+struct msgb;
+
+struct abis_nm_cfg {
+	/* callback for unidirectional reports */
+	int (*report_cb)(struct msgb *,
+			 struct abis_om_fom_hdr *);
+	/* callback for software activate requests from BTS */
+	int (*sw_act_req)(struct msgb *);
+};
+
+extern int abis_nm_rx(struct msgb *msg);
+//extern struct abis_nm_h *abis_nm_init(struct abis_nm_cfg *cfg);
+//extern void abis_nm_fini(struct abis_nm_h *nmh);
+
+int abis_nm_rx(struct msgb *msg);
+int abis_nm_establish_tei(struct gsm_bts *bts, u_int8_t trx_nr,
+			  u_int8_t e1_port, u_int8_t e1_timeslot, u_int8_t e1_subslot,
+			  u_int8_t tei);
+int abis_nm_conn_terr_sign(struct gsm_bts_trx *trx,
+			   u_int8_t e1_port, u_int8_t e1_timeslot, u_int8_t e1_subslot);
+int abis_nm_conn_terr_traf(struct gsm_bts_trx_ts *ts,
+			   u_int8_t e1_port, u_int8_t e1_timeslot,
+			   u_int8_t e1_subslot);
+int abis_nm_set_channel_attr(struct gsm_bts_trx_ts *ts, u_int8_t chan_comb);
+int abis_nm_raw_msg(struct gsm_bts *bts, int len, u_int8_t *msg);
+int abis_nm_event_reports(struct gsm_bts *bts, int on);
+int abis_nm_reset_resource(struct gsm_bts *bts);
+int abis_nm_db_transaction(struct gsm_bts *bts, int begin);
+
+#endif /* _NM_H */
diff --git a/include/openbsc/abis_rsl.h b/include/openbsc/abis_rsl.h
new file mode 100644
index 0000000..9413f65
--- /dev/null
+++ b/include/openbsc/abis_rsl.h
@@ -0,0 +1,303 @@
+/* GSM Radio Signalling Link messages on the A-bis interface 
+ * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */
+
+/* (C) 2008 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.
+ *
+ */
+
+#ifndef _RSL_H
+#define _RSL_H
+
+struct abis_rsl_common_hdr {
+	u_int8_t	msg_discr;
+	u_int8_t	msg_type;
+	u_int8_t	data[0];
+} __attribute__ ((packed));
+
+/* Chapter 8.3 */
+struct abis_rsl_rll_hdr {
+	struct abis_rsl_common_hdr c;
+	u_int8_t	ie_chan;
+	u_int8_t	chan_nr;
+	u_int8_t	ie_link_id;
+	u_int8_t	link_id;
+	u_int8_t	data[0];
+} __attribute__ ((packed));
+
+/* Chapter 8.3 and 8.4 */
+struct abis_rsl_dchan_hdr {
+	struct abis_rsl_common_hdr c;
+	u_int8_t	ie_chan;
+	u_int8_t	chan_nr;
+	u_int8_t	data[0];
+} __attribute__ ((packed));
+
+
+/* Chapter 9.1 */
+#define ABIS_RSL_MDISC_RLL		0x02
+#define ABIS_RSL_MDISC_DED_CHAN		0x08
+#define ABIS_RSL_MDISC_COM_CHAN		0x0c
+#define ABIS_RSL_MDISC_TRX		0x10
+#define ABIS_RSL_MDISC_LOC		0x20
+
+#define ABIS_RSL_MDISC_IS_TRANSP(x)	(x & 0x01)
+
+/* Chapter 9.2 */
+enum abis_rsl_msgtype {
+	/* Radio Link Layer Management */
+	RSL_MT_DATA_REQ			= 0x01,
+	RSL_MT_DATA_IND,
+	RSL_MT_ERROR_IND,
+	RSL_MT_EST_REQ,
+	RSL_MT_EST_CONF,
+	RSL_MT_EST_IND,
+	RSL_MT_REL_REQ,
+	RSL_MT_REL_CONF,
+	RSL_MT_REL_IND,
+	RSL_MT_UNIT_DATA_REQ,
+	RSL_MT_UNIT_DATA_IND,		/* 0x0b */
+
+	/* Common Channel Management / TRX Management */
+	RSL_MT_BCCH_INFO			= 0x11,
+	RSL_MT_CCCH_LOAD_IND,
+	RSL_MT_CHAN_RQD,
+	RSL_MT_DELETE_IND,
+	RSL_MT_PAGING_CMD,
+	RSL_MT_IMMEDIATE_ASSIGN_CMD,
+	RSL_MT_SMS_BC_REQ,
+	/* empty */
+	RSL_MT_RF_RES_IND			= 0x19,
+	RSL_MT_SACCH_FILL,
+	RSL_MT_OVERLOAD,
+	RSL_MT_ERROR_REPORT,
+	RSL_MT_SMS_BC_CMD,
+	RSL_MT_CBCH_LOAD_IND,
+	RSL_MT_NOT_CMD,			/* 0x1f */
+
+	/* Dedicate Channel Management */
+	RSL_MT_CHAN_ACTIV			= 0x21,
+	RSL_MT_CHAN_ACTIV_ACK,
+	RSL_MT_CHAN_ACTIV_NACK,
+	RSL_MT_CONN_FAIL,
+	RSL_MT_DEACTIVATE_SACCH,
+	RSL_MT_ENCR_CMD,
+	RSL_MT_HANDO_DET,
+	RSL_MT_MEAS_RES,
+	RSL_MT_MODE_MODIFY_REQ,
+	RSL_MT_MODE_MODIFY_ACK,
+	RSL_MT_MODE_MODIFY_NACK,
+	RSL_MT_PHY_CONTEXT_REQ,
+	RSL_MT_PHY_CONTEXT_CONF,
+	RSL_MT_RF_CHAN_REL,
+	RSL_MT_MS_POWER_CONTROL,
+	RSL_MT_BS_POWER_CONTROL,
+	RSL_MT_PREPROC_CONFIG,
+	RSL_MT_PREPROC_MEAS_RES,
+	RSL_MT_RF_CHAN_REL_ACK,
+	RSL_MT_SACCH_INFO_MODIFY,
+	RSL_MT_TALKER_DET,
+	RSL_MT_LISTENER_DET,
+	RSL_MT_REMOTE_CODEC_CONF_REP,
+	RSL_MT_RTD_REP,
+	RSL_MT_PRE_HANDO_NOTIF,
+	RSL_MT_MR_CODEC_MOD_REQ,
+	RSL_MT_MR_CODEC_MOD_ACK,
+	RSL_MT_MR_CODEC_MOD_NACK,
+	RSL_MT_MR_CODEC_MOD_PER,
+	RSL_MT_TFO_REP,
+	RSL_MT_TFO_MOD_REQ,		/* 0x3f */
+};
+
+/* Chapter 9.3 */
+enum abis_rsl_ie {
+	RSL_IE_CHAN_NR			= 0x01,
+	RSL_IE_LINK_IDENT,
+	RSL_IE_ACT_TYPE,
+	RSL_IE_BS_POWER,
+	RSL_IE_CHAN_IDENT,
+	RSL_IE_CHAN_MODE,
+	RSL_IE_ENCR_INFO,
+	RSL_IE_FRAME_NUMBER,
+	RSL_IE_HANDO_REF,
+	RSL_IE_L1_INFO,
+	RSL_IE_L3_INFO,
+	RSL_IE_MS_IDENTITY,
+	RSL_IE_MS_POWER,
+	RSL_IE_PAGING_GROUP,
+	RSL_IE_PAGING_LOAD,
+	RSL_IE_PYHS_CONTEXT		= 0x10,
+	RSL_IE_ACCESS_DELAY,
+	RSL_IE_RACH_LOAD,
+	RSL_IE_REQ_REFERENCE,
+	RSL_IE_RELEASE_MODE,
+	RSL_IE_RESOURCE_INFO,
+	RSL_IE_RLM_CAUSE,
+	RSL_IE_STARTNG_TIME,
+	RSL_IE_TIMING_ADVANCE,
+	RSL_IE_UPLINK_MEAS,
+	RSL_IE_CAUSE,
+	RSL_IE_MEAS_RES_NR,
+	RSL_IE_MSG_ID,
+	/* reserved */
+	RSL_IE_SYSINFO_TYPE		= 0x1e,
+	RSL_IE_MS_POWER_PARAM,
+	RSL_IE_BS_POWER_PARAM,
+	RSL_IE_PREPROC_PARAM,
+	RSL_IE_PREPROC_MEAS,
+	RSL_IE_IMM_ASS_INFO,		/* Phase 1 (3.6.0), later Full below */
+	RSL_IE_SMSCB_INFO		= 0x24,
+	RSL_IE_MS_TIMING_OFFSET,
+	RSL_IE_ERR_MSG,
+	RSL_IE_FULL_BCCH_INFO,
+	RSL_IE_CHAN_NEEDED,
+	RSL_IE_CB_CMD_TYPE,
+	RSL_IE_SMSCB_MSG,
+	RSL_IE_FULL_IMM_ASS_INFO,
+	RSL_IE_SACCH_INFO,
+	RSL_IE_CBCH_LOAD_INFO,
+	RSL_IE_SMSCB_CHAN_INDICATOR,
+	RSL_IE_GROUP_CALL_REF,
+	RSL_IE_CHAN_DESC,
+	RSL_IE_NCH_DRX_INFO,
+	RSL_IE_CMD_INDICATOR,
+	RSL_IE_EMLPP_PRIO,
+	RSL_IE_UIC,
+	RSL_IE_MAIN_CHAN_REF,
+	RSL_IE_MR_CONFIG,
+	RSL_IE_MR_CONTROL,
+	RSL_IE_SUP_CODEC_TYPES,
+	RSL_IE_CODEC_CONFIG,
+	RSL_IE_RTD,
+	RSL_IE_TFO_STATUS,
+	RSL_IE_LLP_APDU,
+};
+
+/* Chapter 9.3.1 */
+#define RSL_CHAN_NR_MASK	0xf8
+#define RSL_CHAN_Bm_ACCHs	0x08
+#define RSL_CHAN_Lm_ACCHs	0x10	/* .. 0x18 */
+#define RSL_CHAN_SDCCH4_ACCH	0x20	/* .. 0x38 */
+#define RSL_CHAN_SDCCH8_ACCH	0x40	/* ...0x78 */
+#define RSL_CHAN_BCCH		0x80
+#define RSL_CHAN_RACH		0x88
+#define RSL_CHAN_PCH_AGCH	0x90
+
+/* Chapter 9.3.3 */
+#define RSL_ACT_TYPE_INITIAL	0x00
+#define RSL_ACT_TYPE_REACT	0x80
+#define RSL_ACT_INTRA_IMM_ASS	0x00
+#define RSL_ACT_INTRA_NORM_ASS	0x01
+#define RSL_ACT_INTER_ASYNC	0x02
+#define RSL_ACT_INTER_SYNC	0x03
+#define RSL_ACT_SECOND_ADD	0x04
+#define RSL_ACT_SECOND_MULTI	0x05
+
+/* Chapter 9.3.6 */
+struct rsl_ie_chan_mode {
+	u_int8_t dtx_dtu;
+	u_int8_t spd_ind;
+	u_int8_t chan_rt;
+	u_int8_t chan_rate;
+} __attribute__ ((packed));
+#define RSL_CMOD_DTXu		0x01	/* uplink */
+#define RSL_CMOD_DTXd		0x02	/* downlink */
+#define RSL_CMOD_SPD_SPEECH	0x01
+#define RSL_CMOD_SPD_DATA	0x02
+#define RSL_CMOD_SPD_SIGN	0x03
+#define RSL_CMOD_CRT_SDCCH	0x01
+#define RSL_CMOD_CRT_TCH_Bm	0x08	/* full-rate */
+#define RSL_CMOD_CRT_TCH_Lm	0x09	/* half-rate */
+/* FIXME: More CRT types */
+#define RSL_CMOD_SP_GSM1	0x01
+#define RSL_CMOD_SP_GSM2	0x11
+#define RSL_CMOD_SP_GSM3	0x21
+
+/* Chapter 9.3.5 */
+struct rsl_ie_chan_ident {
+	/* GSM 04.08 10.5.2.5 */
+	struct {
+		u_int8_t iei;
+		u_int8_t chan_nr;	/* enc_chan_nr */
+		u_int8_t oct3;
+		u_int8_t oct4;
+	} chan_desc;
+#if 0	/* spec says we need this but Abissim doesn't use it */
+	struct {
+		u_int8_t tag;
+		u_int8_t len;
+	} mobile_alloc;
+#endif
+} __attribute__ ((packed));
+
+/* Chapter 9.3.30 */
+#define RSL_SYSTEM_INFO_8	0x00
+#define RSL_SYSTEM_INFO_1	0x01
+#define RSL_SYSTEM_INFO_2	0x02
+#define RSL_SYSTEM_INFO_3	0x03
+#define RSL_SYSTEM_INFO_4	0x04
+#define RSL_SYSTEM_INFO_5	0x05
+#define RSL_SYSTEM_INFO_6	0x06
+#define RSL_SYSTEM_INFO_7	0x07
+#define RSL_SYSTEM_INFO_16	0x08
+#define RSL_SYSTEM_INFO_17	0x09
+#define RSL_SYSTEM_INFO_2bis	0x0a
+#define RSL_SYSTEM_INFO_2ter	0x0b
+#define RSL_SYSTEM_INFO_5bis	0x0d
+#define RSL_SYSTEM_INFO_5ter	0x0e
+#define RSL_SYSTEM_INFO_10	0x0f
+#define REL_EXT_MEAS_ORDER	0x47
+#define RSL_MEAS_INFO		0x48
+#define RSL_SYSTEM_INFO_13	0x28
+#define RSL_SYSTEM_INFO_2quater	0x29
+#define RSL_SYSTEM_INFO_9	0x2a
+#define RSL_SYSTEM_INFO_18	0x2b
+#define RSL_SYSTEM_INFO_19	0x2c
+#define RSL_SYSTEM_INFO_20	0x2d
+
+/* Chapter 9.3.40 */
+#define RSL_CHANNEED_ANY	0x00
+#define RSL_CHANNEED_SDCCH	0x01
+#define RSL_CHANNEED_TCH_F	0x02
+#define RSL_CHANNEED_TCH_ForH	0x03
+
+
+#include "msgb.h"
+
+int rsl_bcch_info(struct gsm_bts *bts, u_int8_t type,
+		  const u_int8_t *data, int len);
+int rsl_sacch_filling(struct gsm_bts *bts, u_int8_t type, 
+		      const u_int8_t *data, int len);
+int rsl_chan_activate(struct gsm_bts *bts, u_int8_t chan_nr,
+		      u_int8_t act_type,
+		      struct rsl_ie_chan_mode *chan_mode,
+		      struct rsl_ie_chan_ident *chan_ident,
+		      u_int8_t bs_power, u_int8_t ms_power,
+		      u_int8_t ta);
+int rsl_chan_activate_tch_f(struct gsm_bts_trx_ts *ts);
+int rsl_chan_activate_sdcch(struct gsm_bts_trx_ts *ts);
+int rsl_chan_release(struct gsm_bts_trx_ts *ts, u_int8_t chan_nr);
+int rsl_paging_cmd(struct gsm_bts *bts, u_int8_t paging_group, u_int8_t len,
+		   u_int8_t *ms_ident, u_int8_t chan_needed);
+int rsl_paging_cmd_imsi(struct gsm_bts *bts, u_int8_t chan_needed, const char *imsi_str);
+int rsl_imm_assign_cmd(struct gsm_bts *bts, u_int8_t len, u_int8_t *val);
+int rsl_data_request(struct gsm_bts *bts, struct msgb *msg);
+
+int abis_rsl_rx(struct msgb *msg);
+
+#endif /* RSL_MT_H */
+
diff --git a/include/openbsc/debug.h b/include/openbsc/debug.h
new file mode 100644
index 0000000..c1db120
--- /dev/null
+++ b/include/openbsc/debug.h
@@ -0,0 +1,15 @@
+#ifndef _DEBUG_H
+#define _DEBUG_H
+
+#define DRLL		0x0001
+#define DCC		0x0002
+#define DMM		0x0004
+#define DRR		0x0008
+
+#ifdef DEBUG
+#define DEBUGP(ss, args, ...)	debugp(ss, args, ...)
+#else
+#define DEBUGP(xss, args, ...) 
+#endif
+
+#endif /* _DEBUG_H */
diff --git a/include/openbsc/gsm_04_08.h b/include/openbsc/gsm_04_08.h
new file mode 100644
index 0000000..7c51969
--- /dev/null
+++ b/include/openbsc/gsm_04_08.h
@@ -0,0 +1,238 @@
+#ifndef _GSM_04_08_H
+#define _GSM_04_08_H
+
+/* GSM TS 04.08  definitions */
+
+/* Chapter 10.5.2.5 */
+struct gsm48_chan_desc {
+	u_int8_t chan_nr;
+	union {
+		struct {
+			u_int8_t maio_high:4,
+				 h:1,
+				 tsc:3;
+			u_int8_t hsn:6,
+				 maio_low:2;
+		} h1;
+		struct {
+			u_int8_t arfcn_high:2,
+				 spare:2,
+				 h:1,
+				 tsc:3;
+			u_int8_t arfcn_low;
+		} h0;
+	};
+};
+
+/* Chapter 10.5.2.30 */
+struct gsm48_req_ref {
+	u_int8_t ra;
+	u_int8_t t3_high:3,
+		 t1_:5;
+	u_int8_t t2:5,
+		 t3_low:3;
+};
+
+/* Chapter 9.1.18 */
+struct gsm48_imm_ass {
+	u_int8_t l2_plen;
+	u_int8_t proto_discr;
+	u_int8_t msg_type;
+	u_int8_t page_mode;
+	struct gsm48_chan_desc chan_desc;
+	struct gsm48_req_ref req_ref;
+	u_int8_t timing_advance;
+	u_int8_t mob_alloc_len;
+	u_int8_t mob_alloc[0];
+};
+
+struct gsm48_loc_area_id {
+	u_int8_t digits[3];	/* BCD! */
+	u_int16_t lac;
+} __attribute__ ((packed));
+
+/* Section 9.2.15 */
+struct gsm48_loc_upd_req {
+	u_int8_t type:4,
+		 key_seq:4;
+	struct gsm48_loc_area_id lai;
+	u_int8_t classmark1;
+	u_int8_t ie_mi;
+	u_int8_t mi_len;
+	u_int8_t mi[0];
+} __attribute__ ((packed));
+
+/* Section 10.1 */
+struct gsm48_hdr {
+	u_int8_t proto_discr;
+	u_int8_t msg_type;
+	u_int8_t data[0];
+} __attribute__ ((packed));
+
+/* Section 10.2 */
+#define GSM48_PDISC_CC		0x02
+#define GSM48_PDISC_MM		0x05
+#define GSM48_PDISC_RR		0x06
+#define GSM48_PDISC_MM_GPRS	0x08
+#define GSM48_PDISC_SM		0x0a
+
+/* Section 10.4 */
+#define GSM48_MT_RR_INIT_REQ		0x3c
+#define GSM48_MT_RR_ADD_ASS		0x3b
+#define GSM48_MT_RR_IMM_ASS		0x3f
+#define GSM48_MT_RR_IMM_ASS_EXT		0x39
+#define GSM48_MT_RR_IMM_ASS_REJ		0x3a
+
+#define GSM48_MT_RR_CIPH_M_CMD		0x35
+#define GSM48_MT_RR_CIPH_M_COMPL	0x32
+
+#define GSM48_MT_RR_CFG_CHG_CMD		0x30
+#define GSM48_MT_RR_CFG_CHG_ACK		0x31
+#define GSM48_MT_RR_CFG_CHG_REJ		0x33
+
+#define GSM48_MT_RR_ASS_CMD		0x2e
+#define GSM48_MT_RR_ASS_COMPL		0x29
+#define GSM48_MT_RR_ASS_FAIL		0x2f
+#define GSM48_MT_RR_HANDO_CMD		0x2b
+#define GSM48_MT_RR_HANDO_COMPL		0x2c
+#define GSM48_MT_RR_HANDO_FAIL		0x28
+#define GSM48_MT_RR_HANDO_INFO		0x2d
+
+#define GSM48_MT_RR_CELL_CHG_ORDER	0x08
+#define GSM48_MT_RR_PDCH_ASS_CMD	0x23
+
+#define GSM48_MT_RR_CHAN_REL		0x0d
+#define GSM48_MT_RR_PART_REL		0x0a
+#define GSM48_MT_RR_PART_REL_COMP	0x0f
+
+#define GSM48_MT_RR_PAG_REQ_1		0x21
+#define GSM48_MT_RR_PAG_REQ_2		0x22
+#define GSM48_MT_RR_PAG_REQ_3		0x24
+#define GSM48_MT_RR_PAG_RESP		0x27
+#define GSM48_MT_RR_NOTIF_NCH		0x20
+#define GSM48_MT_RR_NOTIF_FACCH		0x25
+#define GSM48_MT_RR_NOTIF_RESP		0x26
+
+#define GSM48_MT_RR_SYSINFO_8		0x18
+#define GSM48_MT_RR_SYSINFO_1		0x19
+#define GSM48_MT_RR_SYSINFO_2		0x1a
+#define GSM48_MT_RR_SYSINFO_3		0x1b
+#define GSM48_MT_RR_SYSINFO_4		0x1c
+#define GSM48_MT_RR_SYSINFO_5		0x1d
+#define GSM48_MT_RR_SYSINFO_6		0x1e
+#define GSM48_MT_RR_SYSINFO_7		0x1f
+
+#define GSM48_MT_RR_SYSINFO_2bis	0x02
+#define GSM48_MT_RR_SYSINFO_2ter	0x03
+#define GSM48_MT_RR_SYSINFO_5bis	0x05
+#define GSM48_MT_RR_SYSINFO_5ter	0x06
+#define GSM48_MT_RR_SYSINFO_9		0x04
+#define GSM48_MT_RR_SYSINFO_13		0x00
+
+#define GSM48_MT_RR_SYSINFO_16		0x3d
+#define GSM48_MT_RR_SYSINFO_17		0x3e
+
+#define GSM48_MT_RR_CHAN_MODE_MODIF	0x10
+#define GSM48_MT_RR_STATUS		0x12
+#define GSM48_MT_RR_CHAN_MODE_MODIF_ACK	0x17
+#define GSM48_MT_RR_FREQ_REDEF		0x14
+#define GSM48_MT_RR_MEAS_REP		0x15
+#define GSM48_MT_RR_CLSM_CHG		0x16
+#define GSM48_MT_RR_CLSM_ENQ		0x13
+#define GSM48_MT_RR_EXT_MEAS_REP	0x36
+#define GSM48_MT_RR_EXT_MEAS_REP_ORD	0x37
+#define GSM48_MT_RR_GPRS_SUSP_REQ	0x34
+
+#define GSM48_MT_RR_VGCS_UPL_GRANT	0x08
+#define GSM48_MT_RR_UPLINK_RELEASE	0x0e
+#define GSM48_MT_RR_UPLINK_FREE		0x0c
+#define GSM48_MT_RR_UPLINK_BUSY		0x2a
+#define GSM48_MT_RR_TALKER_IND		0x11
+
+#define GSM48_MT_RR_APP_INFO		0x38
+
+/* Table 10.2/3GPP TS 04.08 */
+#define GSM48_MT_MM_IMSI_DETACH_IND	0x01
+#define GSM48_MT_MM_LOC_UPD_ACCEPT	0x02
+#define GSM48_MT_MM_LOC_UPD_REJECT	0x04
+#define GSM48_MT_MM_LOC_UPD_REQUEST	0x08
+
+#define GSM48_MT_MM_AUTH_REJ		0x11
+#define GSM48_MT_MM_AUTH_REQ		0x12
+#define GSM48_MT_MM_AUTH_RESP		0x14
+#define GSM48_MT_MM_ID_REQ		0x18
+#define GSM48_MT_MM_ID_RESP		0x19
+#define GSM48_MT_MM_TMSI_REALL_CMD	0x1a
+#define GSM48_MT_MM_TMSI_REALL_COMPL	0x1b
+
+#define GSM48_MT_MM_CM_SERV_ACC		0x21
+#define GSM48_MT_MM_CM_SERV_REJ		0x22
+#define GSM48_MT_MM_CM_SERV_ABORT	0x23
+#define GSM48_MT_MM_CM_SERV_REQ		0x24
+#define GSM48_MT_MM_CM_SERV_PROMPT	0x25
+#define GSM48_MT_MM_CM_REEST_REQ	0x28
+#define GSM48_MT_MM_ABORT		0x29
+
+#define GSM48_MT_MM_NULL		0x30
+#define GSM48_MT_MM_STATUS		0x31
+#define GSM48_MT_MM_INFO		0x32
+
+/* Table 10.3/3GPP TS 04.08 */
+#define GSM48_MT_CC_ALERTING		0x01
+#define GSM48_MT_CC_CALL_CONF		0x08
+#define GSM48_MT_CC_CALL_PROC		0x02
+#define GSM48_MT_CC_CONNECT		0x07
+#define GSM48_MT_CC_CONNECT_ACK		0x0f
+#define GSM48_MT_CC_EMERG_SETUP		0x0e
+#define GSM48_MT_CC_PROGRESS		0x03
+#define GSM48_MT_CC_ESTAB		0x04
+#define GSM48_MT_CC_ESTAB_CONF		0x06
+#define GSM48_MT_CC_RECALL		0x0b
+#define GSM48_MT_CC_START_CC		0x09
+#define GSM48_MT_CC_SETUP		0x05
+
+#define GSM48_MT_CC_MODIFY		0x17
+#define GSM48_MT_CC_MODIFY_COMPL	0x1f
+#define GSM48_MT_CC_MODIFY_REJECT	0x13
+#define GSM48_MT_CC_USER_INFO		0x10
+#define GSM48_MT_CC_HOLD		0x18
+#define GSM48_MT_CC_HOLD_ACK		0x19
+#define GSM48_MT_CC_HOLD_REJ		0x1a
+#define GSM48_MT_CC_RETR		0x1c
+#define GSM48_MT_CC_RETR_ACK		0x1d
+#define GSM48_MT_CC_RETR_REJ		0x1e
+
+#define GSM48_MT_CC_DISCONNECT		0x25
+#define GSM48_MT_CC_RELEASE		0x2d
+#define GSM48_MT_CC_RELEASE_COMPL	0xea
+
+#define GSM48_MT_CC_CONG_CTRL		0x39
+#define GSM48_MT_CC_NOTIFY		0x3e
+#define GSM48_MT_CC_STATUS		0x3d
+#define GSM48_MT_CC_STATUS_ENQ		0x34
+#define GSM48_MT_CC_START_DTMF		0x35
+#define GSM48_MT_CC_STOP_DTMF		0x31
+#define GSM48_MT_CC_STOP_DTMF_ACK	0x32
+#define GSM48_MT_CC_START_DTMF_ACK	0x36
+#define GSM48_MT_CC_START_DTMF_REJ	0x37
+#define GSM48_MT_CC_FACILITY		0x3a
+
+/* FIXME: Table 10.4 / 10.4a (GPRS) */
+
+/* Section 10.5.2.26, Table 10.5.64 */
+#define GSM48_PM_MASK		0x03
+#define GSM48_PM_NORMAL		0x00
+#define GSM48_PM_EXTENDED	0x01
+#define GSM48_PM_REORG		0x02
+#define GSM48_PM_SAME		0x03
+
+/* Table 10.5.4 */
+#define GSM_MI_TYPE_MASK	0x07
+#define GSM_MI_TYPE_NONE	0x00
+#define GSM_MI_TYPE_IMSI	0x01
+#define GSM_MI_TYPE_IMEI	0x02
+#define GSM_MI_TYPE_IMEISV	0x03
+#define GSM_MI_TYPE_TMSI	0x04
+#define GSM_MI_ODD		0x08
+
+#endif
diff --git a/include/openbsc/gsm_data.h b/include/openbsc/gsm_data.h
new file mode 100644
index 0000000..51e246c
--- /dev/null
+++ b/include/openbsc/gsm_data.h
@@ -0,0 +1,74 @@
+#ifndef _GSM_DATA_H
+#define _GSM_DATA_H
+
+#include <sys/types.h>
+
+#define GSM_MAX_BTS	8
+#define BTS_MAX_TRX	8
+
+#define HARDCODED_ARFCN 123
+
+/* communications link with a BTS */
+struct gsm_bts_link {
+	struct gsm_bts *bts;
+};
+
+#define BTS_TRX_F_ACTIVATED	0x0001
+/* One Timeslot in a TRX */
+struct gsm_bts_trx_ts {
+	struct gsm_bts_trx *trx;
+	/* number of this timeslot at the TRX */
+	u_int8_t nr;
+
+	unsigned int flags;
+};
+
+/* One TRX in a BTS */
+struct gsm_bts_trx {
+	struct gsm_bts *bts;
+	/* number of this TRX in the BTS */
+	u_int8_t nr;
+
+	u_int16_t arfcn;
+	struct gsm_bts_trx_ts ts[8];
+};
+
+/* One BTS */
+struct gsm_bts {
+	struct gsm_network *network;
+	/* number of ths BTS in network */
+	u_int8_t nr;
+	/* location area code of this BTS */
+	u_int8_t location_area_code;
+
+	/* Abis network management O&M handle */
+	struct abis_nm_h *nmh;
+	/* number of this BTS on given E1 link */
+	u_int8_t bts_nr;
+
+	/* CCCH is on C0 */
+	struct gsm_bts_trx *c0;
+	/* transceivers */
+	int num_trx;
+	struct gsm_bts_trx trx[BTS_MAX_TRX+1];
+};
+
+struct gsm_ms {
+	unsigned long imei;
+};
+
+struct gsm_network {
+	/* global parameters */
+	u_int8_t country_code;
+	u_int8_t network_code;
+
+	unsigned int num_bts;
+	/* private lists */
+	struct gsm_bts	bts[GSM_MAX_BTS+1];
+	struct gsm_ms	*ms;
+	struct gsm_subscriber *subscriber;
+};
+
+struct gsm_network *gsm_network_init(unsigned int num_bts, u_int8_t country_code,
+				     u_int8_t network_code);
+#endif
diff --git a/include/openbsc/gsm_subscriber.h b/include/openbsc/gsm_subscriber.h
new file mode 100644
index 0000000..d5c6eff
--- /dev/null
+++ b/include/openbsc/gsm_subscriber.h
@@ -0,0 +1,16 @@
+#ifndef _GSM_SUBSCR_H
+#define _GSM_SUBSCR_H
+
+#include <sys/types.h>
+#include "gsm_data.h"
+
+struct gsm_subscriber {
+	u_int8_t *name;
+	u_int8_t tmsi[4];
+};
+
+struct gsm_subscriber *subscr_get_by_tmsi(u_int8_t *tmsi);
+struct gsm_subscriber *subscr_get_by_imsi(u_int8_t *imsi);
+int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts);
+
+#endif /* _GSM_SUBSCR_H */
diff --git a/include/openbsc/linuxlist.h b/include/openbsc/linuxlist.h
new file mode 100644
index 0000000..a89375e
--- /dev/null
+++ b/include/openbsc/linuxlist.h
@@ -0,0 +1,360 @@
+#ifndef _LINUX_LLIST_H
+#define _LINUX_LLIST_H
+
+#include <stddef.h>
+
+#ifndef inline
+#define inline __inline__
+#endif
+
+static inline void prefetch(const void *x) {;}
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ *
+ * @ptr:	the pointer to the member.
+ * @type:	the type of the container struct this is embedded in.
+ * @member:	the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({			\
+        const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
+        (type *)( (char *)__mptr - offsetof(type,member) );})
+
+
+/*
+ * These are non-NULL pointers that will result in page faults
+ * under normal circumstances, used to verify that nobody uses
+ * non-initialized llist entries.
+ */
+#define LLIST_POISON1  ((void *) 0x00100100)
+#define LLIST_POISON2  ((void *) 0x00200200)
+
+/*
+ * Simple doubly linked llist implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole llists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct llist_head {
+	struct llist_head *next, *prev;
+};
+
+#define LLIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LLIST_HEAD(name) \
+	struct llist_head name = LLIST_HEAD_INIT(name)
+
+#define INIT_LLIST_HEAD(ptr) do { \
+	(ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries. 
+ *
+ * This is only for internal llist manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __llist_add(struct llist_head *new,
+			      struct llist_head *prev,
+			      struct llist_head *next)
+{
+	next->prev = new;
+	new->next = next;
+	new->prev = prev;
+	prev->next = new;
+}
+
+/**
+ * llist_add - add a new entry
+ * @new: new entry to be added
+ * @head: llist head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void llist_add(struct llist_head *new, struct llist_head *head)
+{
+	__llist_add(new, head, head->next);
+}
+
+/**
+ * llist_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: llist head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void llist_add_tail(struct llist_head *new, struct llist_head *head)
+{
+	__llist_add(new, head->prev, head);
+}
+
+/*
+ * Delete a llist entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal llist manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __llist_del(struct llist_head * prev, struct llist_head * next)
+{
+	next->prev = prev;
+	prev->next = next;
+}
+
+/**
+ * llist_del - deletes entry from llist.
+ * @entry: the element to delete from the llist.
+ * Note: llist_empty on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void llist_del(struct llist_head *entry)
+{
+	__llist_del(entry->prev, entry->next);
+	entry->next = LLIST_POISON1;
+	entry->prev = LLIST_POISON2;
+}
+
+/**
+ * llist_del_init - deletes entry from llist and reinitialize it.
+ * @entry: the element to delete from the llist.
+ */
+static inline void llist_del_init(struct llist_head *entry)
+{
+	__llist_del(entry->prev, entry->next);
+	INIT_LLIST_HEAD(entry); 
+}
+
+/**
+ * llist_move - delete from one llist and add as another's head
+ * @llist: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void llist_move(struct llist_head *llist, struct llist_head *head)
+{
+        __llist_del(llist->prev, llist->next);
+        llist_add(llist, head);
+}
+
+/**
+ * llist_move_tail - delete from one llist and add as another's tail
+ * @llist: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void llist_move_tail(struct llist_head *llist,
+				  struct llist_head *head)
+{
+        __llist_del(llist->prev, llist->next);
+        llist_add_tail(llist, head);
+}
+
+/**
+ * llist_empty - tests whether a llist is empty
+ * @head: the llist to test.
+ */
+static inline int llist_empty(const struct llist_head *head)
+{
+	return head->next == head;
+}
+
+static inline void __llist_splice(struct llist_head *llist,
+				 struct llist_head *head)
+{
+	struct llist_head *first = llist->next;
+	struct llist_head *last = llist->prev;
+	struct llist_head *at = head->next;
+
+	first->prev = head;
+	head->next = first;
+
+	last->next = at;
+	at->prev = last;
+}
+
+/**
+ * llist_splice - join two llists
+ * @llist: the new llist to add.
+ * @head: the place to add it in the first llist.
+ */
+static inline void llist_splice(struct llist_head *llist, struct llist_head *head)
+{
+	if (!llist_empty(llist))
+		__llist_splice(llist, head);
+}
+
+/**
+ * llist_splice_init - join two llists and reinitialise the emptied llist.
+ * @llist: the new llist to add.
+ * @head: the place to add it in the first llist.
+ *
+ * The llist at @llist is reinitialised
+ */
+static inline void llist_splice_init(struct llist_head *llist,
+				    struct llist_head *head)
+{
+	if (!llist_empty(llist)) {
+		__llist_splice(llist, head);
+		INIT_LLIST_HEAD(llist);
+	}
+}
+
+/**
+ * llist_entry - get the struct for this entry
+ * @ptr:	the &struct llist_head pointer.
+ * @type:	the type of the struct this is embedded in.
+ * @member:	the name of the llist_struct within the struct.
+ */
+#define llist_entry(ptr, type, member) \
+	container_of(ptr, type, member)
+
+/**
+ * llist_for_each	-	iterate over a llist
+ * @pos:	the &struct llist_head to use as a loop counter.
+ * @head:	the head for your llist.
+ */
+#define llist_for_each(pos, head) \
+	for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+        	pos = pos->next, prefetch(pos->next))
+
+/**
+ * __llist_for_each	-	iterate over a llist
+ * @pos:	the &struct llist_head to use as a loop counter.
+ * @head:	the head for your llist.
+ *
+ * This variant differs from llist_for_each() in that it's the
+ * simplest possible llist iteration code, no prefetching is done.
+ * Use this for code that knows the llist to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __llist_for_each(pos, head) \
+	for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * llist_for_each_prev	-	iterate over a llist backwards
+ * @pos:	the &struct llist_head to use as a loop counter.
+ * @head:	the head for your llist.
+ */
+#define llist_for_each_prev(pos, head) \
+	for (pos = (head)->prev, prefetch(pos->prev); pos != (head); \
+        	pos = pos->prev, prefetch(pos->prev))
+        	
+/**
+ * llist_for_each_safe	-	iterate over a llist safe against removal of llist entry
+ * @pos:	the &struct llist_head to use as a loop counter.
+ * @n:		another &struct llist_head to use as temporary storage
+ * @head:	the head for your llist.
+ */
+#define llist_for_each_safe(pos, n, head) \
+	for (pos = (head)->next, n = pos->next; pos != (head); \
+		pos = n, n = pos->next)
+
+/**
+ * llist_for_each_entry	-	iterate over llist of given type
+ * @pos:	the type * to use as a loop counter.
+ * @head:	the head for your llist.
+ * @member:	the name of the llist_struct within the struct.
+ */
+#define llist_for_each_entry(pos, head, member)				\
+	for (pos = llist_entry((head)->next, typeof(*pos), member),	\
+		     prefetch(pos->member.next);			\
+	     &pos->member != (head); 					\
+	     pos = llist_entry(pos->member.next, typeof(*pos), member),	\
+		     prefetch(pos->member.next))
+
+/**
+ * llist_for_each_entry_reverse - iterate backwards over llist of given type.
+ * @pos:	the type * to use as a loop counter.
+ * @head:	the head for your llist.
+ * @member:	the name of the llist_struct within the struct.
+ */
+#define llist_for_each_entry_reverse(pos, head, member)			\
+	for (pos = llist_entry((head)->prev, typeof(*pos), member),	\
+		     prefetch(pos->member.prev);			\
+	     &pos->member != (head); 					\
+	     pos = llist_entry(pos->member.prev, typeof(*pos), member),	\
+		     prefetch(pos->member.prev))
+
+/**
+ * llist_for_each_entry_continue -	iterate over llist of given type
+ *			continuing after existing point
+ * @pos:	the type * to use as a loop counter.
+ * @head:	the head for your llist.
+ * @member:	the name of the llist_struct within the struct.
+ */
+#define llist_for_each_entry_continue(pos, head, member) 		\
+	for (pos = llist_entry(pos->member.next, typeof(*pos), member),	\
+		     prefetch(pos->member.next);			\
+	     &pos->member != (head);					\
+	     pos = llist_entry(pos->member.next, typeof(*pos), member),	\
+		     prefetch(pos->member.next))
+
+/**
+ * llist_for_each_entry_safe - iterate over llist of given type safe against removal of llist entry
+ * @pos:	the type * to use as a loop counter.
+ * @n:		another type * to use as temporary storage
+ * @head:	the head for your llist.
+ * @member:	the name of the llist_struct within the struct.
+ */
+#define llist_for_each_entry_safe(pos, n, head, member)			\
+	for (pos = llist_entry((head)->next, typeof(*pos), member),	\
+		n = llist_entry(pos->member.next, typeof(*pos), member);	\
+	     &pos->member != (head); 					\
+	     pos = n, n = llist_entry(n->member.next, typeof(*n), member))
+
+/**
+ * llist_for_each_rcu	-	iterate over an rcu-protected llist
+ * @pos:	the &struct llist_head to use as a loop counter.
+ * @head:	the head for your llist.
+ */
+#define llist_for_each_rcu(pos, head) \
+	for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+        	pos = pos->next, ({ smp_read_barrier_depends(); 0;}), prefetch(pos->next))
+        	
+#define __llist_for_each_rcu(pos, head) \
+	for (pos = (head)->next; pos != (head); \
+        	pos = pos->next, ({ smp_read_barrier_depends(); 0;}))
+        	
+/**
+ * llist_for_each_safe_rcu	-	iterate over an rcu-protected llist safe
+ *					against removal of llist entry
+ * @pos:	the &struct llist_head to use as a loop counter.
+ * @n:		another &struct llist_head to use as temporary storage
+ * @head:	the head for your llist.
+ */
+#define llist_for_each_safe_rcu(pos, n, head) \
+	for (pos = (head)->next, n = pos->next; pos != (head); \
+		pos = n, ({ smp_read_barrier_depends(); 0;}), n = pos->next)
+
+/**
+ * llist_for_each_entry_rcu	-	iterate over rcu llist of given type
+ * @pos:	the type * to use as a loop counter.
+ * @head:	the head for your llist.
+ * @member:	the name of the llist_struct within the struct.
+ */
+#define llist_for_each_entry_rcu(pos, head, member)			\
+	for (pos = llist_entry((head)->next, typeof(*pos), member),	\
+		     prefetch(pos->member.next);			\
+	     &pos->member != (head); 					\
+	     pos = llist_entry(pos->member.next, typeof(*pos), member),	\
+		     ({ smp_read_barrier_depends(); 0;}),		\
+		     prefetch(pos->member.next))
+
+
+/**
+ * llist_for_each_continue_rcu	-	iterate over an rcu-protected llist 
+ *			continuing after existing point.
+ * @pos:	the &struct llist_head to use as a loop counter.
+ * @head:	the head for your llist.
+ */
+#define llist_for_each_continue_rcu(pos, head) \
+	for ((pos) = (pos)->next, prefetch((pos)->next); (pos) != (head); \
+        	(pos) = (pos)->next, ({ smp_read_barrier_depends(); 0;}), prefetch((pos)->next))
+
+
+#endif
diff --git a/include/openbsc/msgb.h b/include/openbsc/msgb.h
new file mode 100644
index 0000000..b0740ed
--- /dev/null
+++ b/include/openbsc/msgb.h
@@ -0,0 +1,74 @@
+#ifndef _MSGB_H
+#define _MSGB_H
+
+/* (C) 2008 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.
+ *
+ */
+
+struct bts_link;
+
+struct msgb {
+	/* ptr to the incoming (RX) or outgoing (TX) BTS link */
+	struct gsm_bts_link *bts_link;
+
+	u_int8_t l2_off;
+	u_int8_t l3_off;
+
+	u_int16_t data_len;
+	u_int16_t len;
+
+	unsigned char *head;
+	unsigned char *tail;
+	unsigned char *data;
+	unsigned char _data[0];
+};
+
+extern struct msgb *msgb_alloc(u_int16_t size);
+extern void msgb_free(struct msgb *m);
+
+#define msgb_l2(m)	((void *)(m->data + m->l2_off))
+#define msgb_l3(m)	((void *)(m->data + m->l3_off))
+
+static inline unsigned int msgb_headlen(const struct msgb *msgb)
+{
+	return msgb->len - msgb->data_len;
+}
+static inline unsigned char *msgb_put(struct msgb *msgb, unsigned int len)
+{
+	unsigned char *tmp = msgb->tail;
+	msgb->tail += len;
+	msgb->len += len;
+	return tmp;
+}
+static inline unsigned char *msgb_push(struct msgb *msgb, unsigned int len)
+{
+	msgb->data -= len;
+	msgb->len += len;
+	return msgb->data;
+}
+static inline unsigned char *msgb_pull(struct msgb *msgb, unsigned int len)
+{
+	msgb->len -= len;
+	return msgb->data += len;
+}
+static inline int msgb_tailroom(const struct msgb *msgb)
+{
+	return (msgb->data + msgb->data_len) - msgb->tail;
+}
+
+#endif /* _MSGB_H */
diff --git a/include/openbsc/select.h b/include/openbsc/select.h
new file mode 100644
index 0000000..f98f72c
--- /dev/null
+++ b/include/openbsc/select.h
@@ -0,0 +1,17 @@
+#ifndef _BSC_SELECT_H
+#define _BSC_SELECT_H
+
+#define BSC_FD_READ	0x0001
+#define BSC_FD_WRITE	0x0002
+#define BSC_FD_EXCEPT	0x0004
+
+struct bsc_fd {
+	struct llist_head list;
+	int fd;
+	unsigned int when;
+	int (*cb)(struct bsc_fd *fd, unsigned int what);
+	void *data;
+	unsigned int priv_nr;
+};
+
+#endif /* _BSC_SELECT_H */
diff --git a/include/openbsc/tlv.h b/include/openbsc/tlv.h
new file mode 100644
index 0000000..4c00772
--- /dev/null
+++ b/include/openbsc/tlv.h
@@ -0,0 +1,66 @@
+#ifndef _TLV_H
+#define _TLV_H
+
+#include <sys/types.h>
+#include <string.h>
+
+#define TLV_GROSS_LEN(x)	(x+2)
+#define TLV16_GROSS_LEN(x)	((2*x)+2)
+
+static inline u_int8_t *tlv_put(u_int8_t *buf, u_int8_t tag, u_int8_t len,
+				const u_int8_t *val)
+{
+	*buf++ = tag;
+	*buf++ = len;
+	memcpy(buf, val, len);
+	return buf + len;
+}
+
+static inline u_int8_t *tlv16_put(u_int8_t *buf, u_int8_t tag, u_int8_t len,
+				const u_int16_t *val)
+{
+	*buf++ = tag;
+	*buf++ = len;
+	memcpy(buf, val, len*2);
+	return buf + len*2;
+}
+
+static inline u_int8_t *msgb_tlv16_put(struct msgb *msg, u_int8_t tag, u_int8_t len, const u_int16_t *val)
+{
+	u_int8_t *buf = msgb_put(msg, TLV16_GROSS_LEN(len));
+	return tlv16_put(buf, tag, len, val);
+}
+
+static inline u_int8_t *tv_put(u_int8_t *buf, u_int8_t tag, 
+				u_int8_t val)
+{
+	*buf++ = tag;
+	*buf++ = val;
+	return buf;
+}
+
+static inline u_int8_t *msgb_tlv_put(struct msgb *msg, u_int8_t tag, u_int8_t len, const u_int8_t *val)
+{
+	u_int8_t *buf = msgb_put(msg, TLV_GROSS_LEN(len));
+	return tlv_put(buf, tag, len, val);
+}
+
+static inline u_int8_t *msgb_tv_put(struct msgb *msg, u_int8_t tag, u_int8_t val)
+{
+	u_int8_t *buf = msgb_put(msg, 2);
+	return tv_put(buf, tag, val);
+}
+
+static inline u_int8_t *msgb_tlv_push(struct msgb *msg, u_int8_t tag, u_int8_t len, const u_int8_t *val)
+{
+	u_int8_t *buf = msgb_push(msg, TLV_GROSS_LEN(len));
+	return tlv_put(buf, tag, len, val);
+}
+
+static inline u_int8_t *msgb_tv_push(struct msgb *msg, u_int8_t tag, u_int8_t val)
+{
+	u_int8_t *buf = msgb_push(msg, 2);
+	return tv_put(buf, tag, val);
+}
+
+#endif /* _TLV_H */
diff --git a/src/abis_nm.c b/src/abis_nm.c
new file mode 100644
index 0000000..c873b95
--- /dev/null
+++ b/src/abis_nm.c
@@ -0,0 +1,419 @@
+/* GSM Network Management (OML) messages on the A-bis interface 
+ * 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */
+
+/* (C) 2008 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.
+ *
+ */
+
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "gsm_data.h"
+#include "debug.h"
+#include "msgb.h"
+#include "abis_nm.h"
+
+#define OM_ALLOC_SIZE	1024
+
+/* unidirectional messages from BTS to BSC */
+static const enum abis_nm_msgtype reports[] = {
+	NM_MT_SW_ACTIVATED_REP,
+	NM_MT_TEST_REP,
+	NM_MT_STATECHG_EVENT_REP,
+	NM_MT_FAILURE_EVENT_REP,
+};
+
+/* messages without ACK/NACK */
+static const enum abis_nm_msgtype no_ack_nack[] = {
+	NM_MT_MEAS_RES_REQ,
+	NM_MT_STOP_MEAS,
+	NM_MT_START_MEAS,
+};
+
+/* Attributes that the BSC can set, not only get, according to Section 9.4 */
+static const enum abis_nm_attr nm_att_settable[] = {
+	NM_ATT_ADD_INFO,
+	NM_ATT_ADD_TEXT,
+	NM_ATT_DEST,
+	NM_ATT_EVENT_TYPE,
+	NM_ATT_FILE_DATA,
+	NM_ATT_GET_ARI,
+	NM_ATT_HW_CONF_CHG,
+	NM_ATT_LIST_REQ_ATTR,
+	NM_ATT_MDROP_LINK,
+	NM_ATT_MDROP_NEXT,
+	NM_ATT_NACK_CAUSES,
+	NM_ATT_OUTST_ALARM,
+	NM_ATT_PHYS_CONF,
+	NM_ATT_PROB_CAUSE,
+	NM_ATT_RAD_SUBC,
+	NM_ATT_SOURCE,
+	NM_ATT_SPEC_PROB,
+	NM_ATT_START_TIME,
+	NM_ATT_TEST_DUR,
+	NM_ATT_TEST_NO,
+	NM_ATT_TEST_REPORT,
+	NM_ATT_WINDOW_SIZE,
+	NM_ATT_SEVERITY,
+	NM_ATT_MEAS_RES,
+	NM_ATT_MEAS_TYPE,
+};
+
+static int is_in_arr(enum abis_nm_msgtype mt, const enum abis_nm_msgtype *arr, int size)
+{
+	int i;
+
+	for (i = 0; i < size; i++) {
+		if (arr[i] == mt)
+			return 1;
+	}
+
+	return 0;
+}
+
+/* is this msgtype the usual ACK/NACK type ? */
+static int is_ack_nack(enum abis_nm_msgtype mt)
+{
+	return !is_in_arr(mt, no_ack_nack, ARRAY_SIZE(no_ack_nack));
+}
+
+/* is this msgtype a report ? */
+static int is_report(enum abis_nm_msgtype mt)
+{
+	return is_in_arr(mt, reports, ARRA_YSIZE(reports));
+}
+
+#define MT_ACK(x)	(x+1)
+#define MT_NACK(x)	(x+2)
+
+static void fill_om_hdr(struct abis_om_hdr *oh, u_int8_t len)
+{
+	oh->mdisc = ABIS_OM_MDISC_FOM;
+	oh->placement = ABIS_OM_PLACEMENT_ONLY;
+	oh->sequence = 0;
+	oh->length = len;
+}
+
+static void fill_om_fom_hdr(struct abis_om_hdr *oh, u_int8_t len,
+			    u_int8_t msg_type, u_int8_t obj_class,
+			    u_int8_t bts_nr, u_int8_t trx_nr, u_int8_t ts_nr)
+{
+	struct abis_om_fom_hdr *foh =
+			(struct abis_om_fom_hdr *) oh->data;
+
+	fill_om_hdr(oh, len);
+	foh->msg_type = msg_type;
+	foh->obj_class = obj_class;
+	foh->obj_inst.bts_nr = bts_nr;
+	foh->obj_inst.trx_nr = trx_nr;
+	foh->obj_inst.ts_nr = ts_nr;
+}
+
+/* Send a OML NM Message from BSC to BTS */
+int abis_nm_sendmsg(struct gsm_bts *bts, struct msgb *msg)
+{
+	/* FIXME */
+}
+
+/* Receive a OML NM Message from BTS */
+static int abis_nm_rcvmsg(struct msgb *mb)
+{
+	struct abis_om_fom_hdr *foh = msgb_l3(mb);
+	u_int8_t mt = foh->msg_type;
+
+	/* check for unsolicited message */
+	if (is_report(mt)) {
+		nmh->cfg->report_cb(mb, foh);
+		return 0;
+	}
+
+	/* check if last message is to be acked */
+	if (is_ack_nack(nmh->last_msgtype)) {
+		if (mt == MT_ACK(nmh->last_msgtype)) {
+			fprintf(stderr, "received ACK (0x%x)\n",
+				foh->msg_type);
+			/* we got our ACK, continue sending the next msg */
+		} else if (mt == MT_NACK(nmh->last_msgtype)) {
+			/* we got a NACK, signal this to the caller */
+			fprintf(stderr, "received NACK (0x%x)\n",
+				foh->msg_type);
+			/* FIXME: somehow signal this to the caller */
+		} else {
+			/* really strange things happen */
+			return -EINVAL;
+		}
+	}
+}
+
+/* High-Level API */
+/* Entry-point where L2 OML from BTS enters the NM code */
+int abis_nm_rx(struct msgb *msg)
+{
+	int rc;
+	struct abis_om_hdr *oh = msgb_l2(msg);
+	unsigned int l2_len = msg->tail - msg_l2(msg);
+
+	/* Various consistency checks */
+	if (oh->placement != ABIS_OM_PLACEMENT_ONLY) {
+		fprintf(stderr, "ABIS OML placement 0x%x not supported\n",
+			oh->placement);
+		return -EINVAL;
+	}
+	if (oh->sequence != 0) {
+		fprintf(stderr, "ABIS OML sequence 0x%x != 0x00\n",
+			oh->sequence);
+		return -EINVAL;
+	}
+	if (oh->length + sizeof(*oh) > l2_len) {
+		fprintf(stderr, "ABIS OML truncated message (%u > %u)\n",
+			oh->length + sizeof(*oh), l2_len);
+		return -EINVAL;
+	}
+	if (oh->length + sizeof(*oh) < l2_len)
+		fprintf(stderr, "ABIS OML message with extra trailer?!?\n");
+
+	msg->l3_off = ((unsigned char *)oh + sizeof(*oh)) - msg->head;
+
+	switch (oh->mdisc) {
+	case ABIS_OM_MDISC_FOM:
+		rc = abis_nm_rcvmsg(msg);
+		break;
+	case ABIS_OM_MDISC_MMI:
+	case ABIS_OM_MDISC_TRAU:
+	case ABIS_OM_MDISC_MANUF:
+	default:
+		fprintf(stderr, "unknown ABIS OML message discriminator 0x%x\n",
+			oh->mdisc);
+		return -EINVAL;
+	}
+
+	return rc;
+}
+
+#if 0
+/* initialized all resources */
+struct abis_nm_h *abis_nm_init(struct abis_nm_cfg *cfg)
+{
+	struct abis_nm_h *nmh;
+
+	nmh = malloc(sizeof(*nmh));
+	if (!nmh)
+		return NULL;
+
+	nmh->cfg = cfg;
+
+	return nmh;
+}
+
+/* free all resources */
+void abis_nm_fini(struct abis_nm_h *nmh)
+{
+	free(nmh);
+}
+#endif
+
+/* Here we are trying to define a high-level API that can be used by
+ * the actual BSC implementation.  However, the architecture is currently
+ * still under design.  Ideally the calls to this API would be synchronous,
+ * while the underlying stack behind the APi runs in a traditional select
+ * based state machine.
+ */
+
+/* 6.2 Software Load: FIXME */
+
+
+#if 0
+struct abis_nm_sw {
+	/* this will become part of the SW LOAD INITIATE */
+	u_int8_t obj_class;
+	u_int8_t obj_instance[3];
+	u_int8_t sw_description[255];
+	u_int16_t window_size;
+	/* the actual data that is to be sent subsequently */
+	unsigned char *sw;
+	unsigned int sw_len;
+};
+
+/* Load the specified software into the BTS */
+int abis_nm_sw_load(struct abis_nm_h *h, struct abis_nm_sw *sw);
+{
+	/* FIXME: Implementation */
+}
+
+/* Activate the specified software into the BTS */
+int abis_nm_sw_activate(struct abis_nm_h *h)
+{
+
+}
+#endif
+
+static fill_nm_channel(struct abis_nm_channel *ch, u_int8_t bts_port,
+		       u_int8_t ts_nr, u_int8_t subslot_nr)
+{
+	ch->attrib = NM_ATT_CHANNEL;
+	ch->bts_port = bts_port;
+	ch->timeslot = ts_nr;
+	ch->subslot = subslot_nr;	
+}
+
+int abis_nm_establish_tei(struct gsm_bts *bts, u_int8_t trx_nr,
+			  u_int8_t e1_port, u_int8_t e1_timeslot, u_int8_t e1_subslot,
+			  u_int8_t tei)
+{
+	struct abis_om_hdr *oh;
+	struct abis_nm_channel *ch;
+	u_int8_t *tei_attr;
+	u_int8_t len = 2 + sizeof(*ch);
+	struct mgsb *msg = msgb_alloc(OM_ALLOC_SIZE);
+
+	oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+	fill_om_fom_hdr(oh, len, NM_MT_ESTABLISH_TEI, NM_OC_RADIO_CARRIER,
+			bts->bts_nr, trx_nr, 0xff);
+	
+	msgb_tv_put(msgb, NM_ATT_TEI, tei);
+
+	ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch));
+	fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot);
+
+	return abis_nm_sendmsg(bts, msg);
+}
+
+/* connect signalling of one (BTS,TRX) to a particular timeslot on the E1 */
+int abis_nm_conn_terr_sign(struct gsm_bts_trx *trx,
+			   u_int8_t e1_port, u_int8_t e1_timeslot, u_int8_t e1_subslot)
+{
+	struct gsm_bts *bts = ts->trx->bts;
+	struct abis_om_hdr *oh;
+	struct abis_nm_channel *ch;
+	struct mgsb *msg = msgb_alloc(OM_ALLOC_SIZE);
+
+	oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+	fill_om_fom_hdr(oh, sizeof(*ch), NM_MT_CONN_TERR_SIGN,
+			NM_OC_RADIO_CARRIER, bts->bts_nr, trx->nr, 0xff);
+	
+	ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch));
+	fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot);
+
+	return abis_nm_sendmsg(bts, msg);
+}
+
+#if 0
+int abis_nm_disc_terr_sign(struct abis_nm_h *h, struct abis_om_obj_inst *inst,
+			   struct abis_nm_abis_channel *chan)
+{
+}
+#endif
+
+int abis_nm_conn_terr_traf(struct gsm_bts_trx_ts *ts,
+			   u_int8_t e1_port, u_int8_t e1_timeslot,
+			   u_int8_t e1_subslot)
+{
+	struct gsm_bts *bts = ts->trx->bts;
+	struct abis_om_hdr *oh;
+	struct abis_nm_channel *ch;
+	struct msgb *msg = msgb_alloc(OM_ALLOC_SIZE);
+
+	oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+	fill_om_fom_hdr(oh, sizeof(*ch), NM_MT_CONN_TERR_TRAF,
+			NM_OC_BASEB_TRANSC, bts->bts_nr, ts->trx->nr, ts->nr);
+
+	ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch));
+	fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot);
+
+	return abis_nm_sendmsg(bts, msg);
+}
+
+#if 0
+int abis_nm_disc_terr_traf(struct abis_nm_h *h, struct abis_om_obj_inst *inst,
+			   struct abis_nm_abis_channel *chan,
+			   u_int8_t subchan)
+{
+}
+#endif
+
+int abis_nm_set_channel_attr(struct gsm_bts_trx_ts *ts, u_int8_t chan_comb)
+{
+	struct gsm_bts *bts = ts->trx->bts;
+	struct abis_om_hdr *oh;
+	u_int8_t arfcn = htons(ts->trx->arfcn);
+	u_int8_t zero = 0x00;
+	struct msgb *msg = msgb_alloc(OM_ALLOC_SIZE);
+
+	oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+	fill_om_fom_hdr(oh, sizeof(*ch), NM_MT_SET_CHAN_ATTR,
+			NM_OC_BASEB_TRANSC, bts->bts_nr,
+			ts->trx->nr, ts->nr);
+	/* FIXME: don't send ARFCN list, hopping sequence, mAIO, ...*/
+	msgb_tlv16_put(msg, NM_ATT_ARFCN_LIST, 1, &arfcn);
+	msgb_tv_put(msg, NM_ATT_CHAN_COMB, chan_comb);
+	msgb_tv_put(msg, NM_ATT_HSN, 0x00);
+	msgb_tv_put(msg, NM_ATT_MAIO, 0x00);
+	msgb_tv_put(msg, NM_ATT_TSC, 0x07);	/* training sequence */
+	msgb_tlv_put(msg, 0x59, 1, &zero);
+
+	return abis_nm_sendmsg(bts, msg);
+}
+
+int abis_nm_raw_msg(struct gsm_bts *bts, int len, u_int8_t *msg)
+{
+	struct msgb *msg = msgb_alloc(OM_ALLOC_SIZE);
+	u_int8_t *data;
+
+	oh = (struct abis_om_hdr *) msgb_put(msg, sizeof(*oh));
+	fill_om_hdr(oh, len);
+	data = msgb_put(msg, len);
+	memcpy(msgb->data, msg, len);
+
+	return abis_nm_sendmsg(bts, msg);
+}
+
+/* Siemens specific commands */
+static int __simple_cmd(struct gsm_bts *bts, u_int8_t msg_type)
+{
+	struct abis_om_hdr *oh;
+	struct msg = msgb_alloc(OM_ALLOC_SIZE);
+
+	oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE);
+	fill_om_fom_hdr(oh, sizeof(*ch), msg_type, NM_OC_SITE_MANAGER,
+			0xff, 0xff, 0xff);
+
+	return abis_nm_sendmsg(bts, msg);
+}
+
+int abis_nm_event_reports(struct gsm_bts *bts, int on)
+{
+	if (on == 0)
+		return __simple_cmd(bts, 0x63);
+	else
+		return __simple_cmd(bts, 0x66);
+}
+
+int abis_nm_reset_resource(struct gsm_bts *bts)
+{
+	return __simple_cmd(bts, 0x74);
+}
+
+int abis_nm_db_transaction(struct gsm_bts *bts, int begin)
+{
+	if (begin)
+		return __simple_cmd(bts, 0xA3);
+	else
+		return __simple_cmd(bts, 0xA6);
+}
diff --git a/src/abis_rsl.c b/src/abis_rsl.c
new file mode 100644
index 0000000..f503684
--- /dev/null
+++ b/src/abis_rsl.c
@@ -0,0 +1,508 @@
+/* GSM Radio Signalling Link messages on the A-bis interface 
+ * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */
+
+/* (C) 2008 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.
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#include "gsm_data.h"
+#include "gsm_04_08.h"
+#include "abis_rsl.h"
+#include "debug.h"
+#include "tlv.h"
+
+#define RSL_ALLOC_SIZE	1024
+
+static u_int8_t mdisc_by_msgtype(u_int8_t msg_type)
+{
+	/* mask off the transparent bit ? */
+	msg_type &= 0xfe;
+
+	if (msg_type & 0xf0 == 0x00)
+		return ABIS_RSL_MDISC_RLL;
+	if (msg_type & 0xf0 == 0x10) {
+		if (msg_type >= 0x19 && msg_type <= 0x22)
+			return ABIS_RSL_MDISC_TRX;
+		else
+			return ABIS_RSL_MDISC_COM_CHAN;
+	}
+	if (msg_type & 0xc == 0x00)
+		return ABIS_RSL_MDISC_DED_CHAN;
+	
+	return ABIS_RSL_MDISC_LOC;
+}
+
+static inline void init_dchan_hdr(struct abis_rsl_dchan_hdr *dh,
+				  u_int8_t msg_type)
+{
+	dh->c.msg_discr = mdisc_by_msgtype(msg_type);
+	dh->c.msg_type = msg_type;
+	dh->ie_chan = RSL_IE_CHAN_NR;
+}
+
+static inline void init_llm_hdr(struct abis_rsl_rll_hdr *dh,
+				  u_int8_t msg_type)
+{
+	/* dh->c.msg_discr = mdisc_by_msgtype(msg_type); */
+	dh->c.msg_discr = ABIS_RSL_MDISC_RLL;
+	dh->c.msg_type = msg_type;
+	dh->ie_chan = RSL_IE_CHAN_NR;
+	dh->ie_link_id = RSL_IE_LINK_IDENT;
+}
+
+
+/* encode channel number as per Section 9.3.1 */
+u_int8_t rsl_enc_chan_nr(u_int8_t type, u_int8_t subch, u_int8_t timeslot)
+{
+	u_int8_t ret;
+
+	ret = (timeslot & 0x07) | type;
+	
+	switch (type) {
+	case RSL_CHAN_Lm_ACCHs:
+		subch &= 0x01;
+		break;
+	case RSL_CHAN_SDCCH4_ACCH:
+		subch &= 0x07;
+		break;
+	case RSL_CHAN_SDCCH8_ACCH:
+		subch &= 0x07;
+		break;
+	default:
+		/* no subchannels allowed */
+		subch = 0x00;
+		break;
+	}
+	ret |= (subch << 3);
+
+	return ret;
+}
+
+/* As per TS 03.03 Section 2.2, the IMSI has 'not more than 15 digits' */
+u_int64_t str_to_imsi(const char *imsi_str)
+{
+	u_int64_t ret;
+
+	ret = strtoull(imsi_str, NULL, 10);
+
+	return ret;
+}
+
+/* Table 5 Clause 7 TS 05.02 */
+unsigned int n_pag_blocks(int bs_ccch_sdcch_comb, unsigned int bs_ag_blks_res)
+{
+	if (!bs_ccch_sdcch_comb)
+		return 9 - bs_ag_blks_res;
+	else
+		return 3 - bs_ag_blks_res;
+}
+
+/* Chapter 6.5.2 of TS 05.02 */
+unsigned int get_ccch_group(u_int64_t imsi, unsigned int bs_cc_chans,
+			    unsigned int n_pag_blocks)
+{
+	return (imsi % 1000) % (bs_cc_chans * n_pag_blocks) / n_pag_blocks;
+}
+
+/* Chapter 6.5.2 of TS 05.02 */
+unsigned int get_paging_group(u_int64_t imsi, unsigned int bs_cc_chans,
+			      int n_pag_blocks)
+{
+	return (imsi % 1000) % (bs_cc_chans * n_pag_blocks) % n_pag_blocks;
+}
+
+/* Send a BCCH_INFO message as per Chapter 8.5.1 */
+int rsl_bcch_info(struct gsm_bts *bts, u_int8_t type,
+		  const u_int8_t *data, int len)
+{
+	struct abis_rsl_dchan_hdr *dh;
+	struct msgb *msg = msgb_alloc(RSL_ALLOC_SIZE);
+
+	dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof*dh);
+	init_dchan_hdr(dh, RSL_MT_BCCH_INFO);
+	dh->chan_nr = RSL_CHAN_BCCH;
+
+	msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type);
+	msgb_tlv_put(msg, RSL_IE_FULL_BCCH_INFO, len, data);
+
+	return abis_rsl_sendmsg(bts, msg);
+}
+
+int rsl_sacch_filling(struct gsm_bts *bts, u_int8_t type, 
+		      const u_int8_t *data, int len)
+{
+	struct abis_rsl_common_hdr *ch;
+	struct msgb *msg = msgb_alloc(RSL_ALLOC_SIZE);
+
+	ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch));
+	ch->msg_discr = ABIS_RSL_MDISC_TRX;
+	ch->msg_type = RSL_MT_SACCH_FILL;
+
+	msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type);
+	msgb_tlv_put(msg, RSL_IE_L3_INFO, len, data);
+
+	return abis_rsl_sendmsg(bts, msg);
+}
+
+/* Chapter 8.4.1 */
+int rsl_chan_activate(struct gsm_bts *bts, u_int8_t chan_nr,
+		      u_int8_t act_type,
+		      struct rsl_ie_chan_mode *chan_mode,
+		      struct rsl_ie_chan_ident *chan_ident,
+		      u_int8_t bs_power, u_int8_t ms_power,
+		      u_int8_t ta)
+{
+	struct abis_rsl_dchan_hdr *dh;
+	struct msgb *msg = msgb_alloc(RSL_ALLOC_SIZE);
+	u_int8_t encr_info = 0x01;
+
+	dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
+	init_dchan_hdr(dh, RSL_MT_CHAN_ACTIV);
+	dh->chan_nr = chan_nr;
+
+	msgb_tv_put(msg, RSL_IE_ACT_TYPE, act_type);
+	/* For compatibility with Phase 1 */
+	msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(*chan_mode),
+		     (u_int8_t *) chan_mode);
+	msgb_tlv_put(msg, RSL_IE_CHAN_IDENT, 4,
+		     (u_int8_t *) &chan_ident);
+	/* FIXME: this shoould be optional */
+	msgb_tlv_put(msg, RSL_IE_ENCR_INFO, 1,
+		     (u_int8_t *) &encr_info);
+	msgb_tv_put(msg, RSL_IE_BS_POWER, bs_power);
+	msgb_tv_put(msg, RSL_IE_MS_POWER, ms_power);
+	msgb_tv_put(msg, RSL_IE_TIMING_ADVANCE, ta);
+
+	return abis_rsl_sendmsg(bts, msg);
+}
+
+#define TSC	7
+
+int rsl_chan_activate_tch_f(struct gsm_bts_trx_ts *ts)
+{
+	u_int8_t chan_nr = rsl_enc_chan_nr(RSL_CHAN_Bm_ACCHs, 0, ts->nr);
+	u_int16_t arfcn = ts->trx->arfcn;
+	struct rsl_ie_chan_mode cm;
+	struct rsl_ie_chan_ident ci;
+
+	cm.dtx_dtu = 0;
+	cm.spd_ind = RSL_CMOD_SPD_SPEECH;
+	cm.chan_rt = RSL_CMOD_CRT_TCH_Bm;
+	cm.chan_rate = RSL_CMOD_SP_GSM1;
+
+	ci.chan_desc.iei = 0x64;
+	ci.chan_desc.chan_nr = chan_nr;
+	/* FIXME: this doesn't support hopping */
+	ci.chan_desc.oct3 = (TSC << 5) | ((arfcn & 0x3ff) >> 8);
+	ci.chan_desc.oct4 = arfcn & 0xff;
+#if 0
+	ci.mobile_alloc.tag = 0x72;
+	ci.mobile_alloc.len = 0;	/* as per Section 9.3.5 */
+#endif
+
+	return rsl_chan_activate(ts->trx->bts, chan_nr, 0x01, &cm, &ci, 0x01, 0x0f, 0x00);
+}
+
+int rsl_chan_activate_sdcch(struct gsm_bts_trx_ts *ts)
+{
+	u_int8_t chan_nr = rsl_enc_chan_nr(RSL_CHAN_SDCCH4_ACCH, 0, ts->nr);
+	u_int16_t arfcn = ts->trx->arfcn;
+	struct rsl_ie_chan_mode cm;
+	struct rsl_ie_chan_ident ci;
+
+	cm.dtx_dtu = 0x00;
+	cm.spd_ind = RSL_CMOD_SPD_SIGN;
+	cm.chan_rt = RSL_CMOD_CRT_SDCCH;
+	cm.chan_rate = 0x00;
+
+	ci.chan_desc.iei = 0x64;
+	ci.chan_desc.chan_nr = chan_nr;
+	ci.chan_desc.oct3 = (TSC << 5) | ((arfcn & 0x3ff) >> 8);
+	ci.chan_desc.oct4 = arfcn & 0xff;
+
+	/* FIXME: we're sending BS power IE, whcih Abissim doesn't */
+	return rsl_chan_activate(ts->trx->bts, chan_nr, 0x00, &cm, &ci, 0x01, 0x0f, 0x00);
+}
+
+int rsl_chan_release(struct gsm_bts_trx_ts *ts, u_int8_t chan_nr)
+{
+	struct abis_rsl_dchan_hdr *dh;
+	struct msgb *msg = msgb_alloc(RSL_ALLOC_SIZE);
+
+	dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
+	init_dchan_hdr(dh, RSL_MT_RF_CHAN_REL);
+	dh->chan_nr = chan_nr;
+
+	return abis_rsl_sendmsg(ts->trx->bts, msg);
+}
+
+int rsl_paging_cmd(struct gsm_bts *bts, u_int8_t paging_group, u_int8_t len,
+		   u_int8_t *ms_ident, u_int8_t chan_needed)
+{
+	struct abis_rsl_dchan_hdr *dh;
+	struct msgb *msg = msgb_alloc(RSL_ALLOC_SIZE);
+
+	dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
+	init_dchan_hdr(dh, RSL_MT_PAGING_CMD);
+	dh->chan_nr = RSL_CHAN_PCH_AGCH;
+
+	msgb_tv_put(msg, RSL_IE_PAGING_GROUP, paging_group);
+	msgb_tlv_put(msg, RSL_IE_MS_IDENTITY, len, ms_ident);
+	msgb_tv_put(msg, RSL_IE_CHAN_NEEDED, chan_needed);
+
+	return abis_rsl_sendmsg(bts, msg);
+}
+
+int imsi_str2bcd(u_int8_t *bcd_out, const char *str_in)
+{
+	int i, len = strlen(str_in);
+
+	for (i = 0; i < len; i++) {
+		int num = str_in[i] - 0x30;
+		if (num < 0 || num > 9)
+			return -1;
+		if (i % 2 == 0)
+			bcd_out[i/2] = num;
+		else
+			bcd_out[i/2] |= (num << 4);
+	}
+
+	return 0;
+}
+
+# if 0
+int rsl_paging_cmd_imsi(struct gsm_bts *bts, u_int8_t chan_needed, const char *imsi_str)
+{
+	/* FIXME: derive the MS Identity */
+	return rsl_paging_cmd(bts, paging_group, x, y, chan_needed);
+}
+#endif
+
+int rsl_imm_assign_cmd(struct gsm_bts *bts, u_int8_t len, u_int8_t *val)
+{
+	struct msgb *msg = msgb_alloc(RSL_ALLOC_SIZE);
+	struct abis_rsl_dchan_hdr *dh;
+
+	dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh));
+	init_dchan_hdr(dh, RSL_MT_IMMEDIATE_ASSIGN_CMD);
+	dh->chan_nr = RSL_CHAN_PCH_AGCH;
+
+	/* If phase 2, FULL_IMM_ASS_INFO */
+
+	msgb_tlv_put(msg, RSL_IE_IMM_ASS_INFO, len, val);
+
+	return abis_rsl_sendmsg(bts, msg);
+}
+
+/* Chapter 8.3.1 */
+int rsl_data_request(struct gsm_bts *bts, struct msgb *msg)
+{
+	/* FIXME: prepend RSL header to layer 3 message */
+	u_int8_t len = msg->len;
+	struct abis_rsl_rll_hdr *rh;
+
+	msgb_tv_push(msg, RSL_IE_L3_INFO, len);
+
+	rh = (struct abis_rsl_rll_hdr *) msgb_push(msg, sizeof(*rh));
+	init_llm_hdr(rh, RSL_MT_DATA_REQ);
+	rh->chan_nr = RSL_CHAN_SDCCH4_ACCH;	/* FIXME: don't harcode */
+
+	return abis_rsl_sendmsg(bts, msg);
+}
+
+static int abis_rsl_rx_dchan(struct msgb *msg)
+{
+	struct abis_rsl_common_hdr *rslh = msgb_l2(msg)	;
+
+	switch (rslh->msg_type) {
+	case RSL_MT_CHAN_ACTIV_ACK:
+	case RSL_MT_CHAN_ACTIV_NACK:
+	case RSL_MT_CONN_FAIL:
+	case RSL_MT_MEAS_RES:
+	case RSL_MT_MODE_MODIFY_ACK:
+	case RSL_MT_MODE_MODIFY_NACK:
+	case RSL_MT_PHY_CONTEXT_CONF:
+	case RSL_MT_PREPROC_MEAS_RES:
+	case RSL_MT_RF_CHAN_REL_ACK:
+	case RSL_MT_TALKER_DET:
+	case RSL_MT_LISTENER_DET:
+	case RSL_MT_REMOTE_CODEC_CONF_REP:
+	case RSL_MT_MR_CODEC_MOD_ACK:
+	case RSL_MT_MR_CODEC_MOD_NACK:
+	case RSL_MT_MR_CODEC_MOD_PER:
+		fprintf(stderr, "Unimplemented Abis RSL DChan msg 0x%02x\n",
+			rslh->msg_type);
+		break;
+	default:
+		fprintf(stderr, "unknown Abis RSL DChan msg 0x%02x\n",
+			rslh->msg_type);
+		return -EINVAL;
+	}
+}
+
+static int abis_rsl_rx_trx(struct msgb *msg)
+{
+	struct abis_rsl_common_hdr *rslh = msgb_l2(msg)	;
+
+	switch (rslh->msg_type) {
+	case RSL_MT_RF_RES_IND:
+		/* interference on idle channels of TRX */
+	case RSL_MT_OVERLOAD:
+		/* indicate CCCH / ACCH / processor overload */ 
+	case RSL_MT_ERROR_REPORT:
+		fprintf(stderr, "Unimplemented Abis RSL TRX message type 0x%02x\n",
+			rslh->msg_type);
+		break;
+	default:
+		fprintf(stderr, "Unknown Abis RSL TRX message type 0x%02x\n",
+			rslh->msg_type);
+		return -EINVAL;
+	}
+
+}
+
+static int rsl_rx_chan_rqd(struct msgb *msg)
+{
+	struct gsm_bts *bts = msg->bts_link->bts;
+	struct gsm48_imm_ass ia;
+	u_int16_t arfcn;
+	u_int8_t ts_number, subch;
+
+	/* MS has requested a channel on the RACH */
+	/* parse channel number, request reference, access delay */
+	/* FIXME: check permission/availability */
+	ts_number = 0;
+	arfcn = HARDCODED_ARFCN;
+	subch = 0;
+	
+	/* send CHANNEL ACTIVATION on RSL to BTS */
+	rsl_chan_activate_sdcch(&bts->trx[0].ts[ts_number]);
+
+	/* create IMMEDIATE ASSIGN 04.08 messge */
+	memset(&ia, 0, sizeof(ia));
+	ia.l2_plen = 0x2d;
+	ia.proto_discr = GSM48_PDISC_RR;
+	ia.msg_type = GSM48_MT_RR_IMM_ASS;
+	ia.page_mode = GSM48_PM_NORMAL;
+	ia.chan_desc.chan_nr = rsl_enc_chan_nr(RSL_CHAN_SDCCH4_ACCH, subch, ts_number);
+	ia.chan_desc.h0.h = 0;
+	ia.chan_desc.h0.arfcn_high = arfcn >> 8;
+	ia.chan_desc.h0.arfcn_low = arfcn & 0xff;
+	ia.chan_desc.h0.tsc = 7;
+	/* FIXME: use real request reference extracted from CHAN_RQD */
+	ia.req_ref.ra = 0x80 | 0x1e;
+	ia.req_ref.t2 = 0x0c;
+	ia.req_ref.t1_ = 0x12;
+	ia.req_ref.t3_low = 0x19 & 3;
+	ia.req_ref.t3_high = 0x19 >> 3;
+	ia.timing_advance = 0;
+	ia.mob_alloc_len = 0;
+
+	/* send IMMEDIATE ASSIGN CMD on RSL to BTS (to send on CCCH to MS) */
+	return rsl_imm_assign_cmd(bts, sizeof(ia), (u_int8_t *) &ia);
+}
+
+static int abis_rsl_rx_cchan(struct msgb *msg)
+{
+	struct abis_rsl_common_hdr *rslh = msgb_l2(msg)	;
+	int rc;
+
+	switch (rslh->msg_type) {
+	case RSL_MT_CHAN_RQD:
+		/* MS has requested a channel on the RACH */
+		rc = rsl_rx_chan_rqd(msg);
+		break;
+	case RSL_MT_DELETE_IND:
+		/* CCCH overloaded, IMM_ASSIGN was dropped */
+	case RSL_MT_CBCH_LOAD_IND:
+		/* current load on the CBCH */
+	case RSL_MT_CCCH_LOAD_IND:
+		/* current load on the CCCH */
+		fprintf(stderr, "Unimplemented Abis RSL TRX message type 0x%02x\n",
+			rslh->msg_type);
+		break;
+	default:
+		fprintf(stderr, "Unknown Abis RSL TRX message type 0x%02x\n",
+			rslh->msg_type);
+		return -EINVAL;
+	}
+}
+
+/*	ESTABLISH INDICATION, LOCATION AREA UPDATE REQUEST 
+	0x02, 0x06,
+	0x01, 0x20,
+	0x02, 0x00,
+	0x0b, 0x00, 0x0f, 0x05, 0x08, ... */
+
+static int abis_rsl_rx_rll(struct msgb *msg)
+{
+	struct abis_rsl_rll_hdr *rllh = msgb_l2(msg);
+	int rc;
+	
+	switch (rllh->c.msg_type) {
+	case RSL_MT_DATA_IND:
+		DEBUGP(DRLL, "DATA INDICATION\n");
+		/* FIXME: parse L3 info element */
+		rc = gsm0408_rcvmsg(msg);
+		break;
+	case RSL_MT_EST_IND:
+		DEBUGP(DRLL, "ESTABLISH INDICATION\n");
+		/* FIXME: parse L3 info element */
+		rc = gsm0408_rcvmsg(msg);
+		break;
+	case RSL_MT_ERROR_IND:
+	case RSL_MT_REL_IND:
+	case RSL_MT_UNIT_DATA_IND:
+		fprintf(stderr, "unimplemented Abis RLL message type 0x%02x\n",
+			rllh->c.msg_type);
+		break;
+	default:
+		fprintf(stderr, "unknown Abis RLL message type 0x%02x\n",
+			rllh->c.msg_type);
+	}
+}
+
+/* Entry-point where L2 RSL from BTS enters */
+int abis_rsl_rx(struct msgb *msg)
+{
+	struct abis_rsl_common_hdr *rslh = msgb_l2(msg)	;
+	unsigned int l2_len = (void *)msg->tail - msgb_l2(msg);
+	int rc;
+
+	switch (rslh->msg_discr & 0xfe) {
+	case ABIS_RSL_MDISC_RLL:
+		rc = abis_rsl_rx_rll(msg);
+		break;
+	case ABIS_RSL_MDISC_DED_CHAN:
+		rc = abis_rsl_rx_dchan(msg);
+		break;
+	case ABIS_RSL_MDISC_COM_CHAN:
+	case ABIS_RSL_MDISC_TRX:
+		rc = abis_rsl_rx_cchan(msg);
+		break;
+	case ABIS_RSL_MDISC_LOC:
+	default:
+		fprintf(stderr, "unknown RSL message discriminator 0x%02x\n",
+			rslh->msg_discr);
+		return -EINVAL;
+	}
+}
diff --git a/src/bsc_hack.c b/src/bsc_hack.c
new file mode 100644
index 0000000..0f237cc
--- /dev/null
+++ b/src/bsc_hack.c
@@ -0,0 +1,543 @@
+/* A hackish minimal BSC (+MSC +HLR) implementation */
+
+/* (C) 2008 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.
+ *
+ */
+
+
+
+#include "gsm_data.h"
+#include "abis_rsl.h"
+#include "abis_nm.h"
+
+/* global pointer to the gsm network data structure */
+static struct gsm_network *gsmnet;
+
+/* The following definitions are for OM and NM packets that we cannot yet
+ * generate by code but we just pass on */
+
+// BTS Site Manager, SET ATTRIBUTES
+
+/*
+  Object Class: BTS Site Manager
+  Instance 1: FF
+  Instance 2: FF
+  Instance 3: FF
+SET ATTRIBUTES
+  sAbisExternalTime: 2007/09/08   14:36:11
+  omLAPDRelTimer: 30sec
+  shortLAPDIntTimer: 5sec
+  emergencyTimer1: 10 minutes
+  emergencyTimer2: 0 minutes
+*/
+
+unsigned char msg_1[] = 
+{
+	0xD0, 0x00, 0xFF, 0xFF, 0xFF, 0x91, 0x07, 0xD7, 0x09, 0x08, 0x0E, 0x24,
+	0x0B, 0xCE, 0x02, 0x00, 0x1E, 0xE8, 0x01, 0x05, 0x42, 0x02, 0x00, 0x0A, 0x44,
+	0x02, 0x00, 0x00
+};
+
+// BTS, SET BTS ATTRIBUTES
+
+/*
+  Object Class: BTS
+  BTS relat. Number: 0 
+  Instance 2: FF
+  Instance 3: FF
+SET BTS ATTRIBUTES
+  bsIdentityCode / BSIC:
+    PLMN_colour_code: 7h
+    BS_colour_code:   7h
+  BTS Air Timer T3105: 4  ,unit 10 ms
+  btsIsHopping: FALSE
+  periodCCCHLoadIndication: 255sec
+  thresholdCCCHLoadIndication: 100%
+  cellAllocationNumber: 00h = GSM 900
+  enableInterferenceClass: 00h =  Disabled
+  fACCHQual: 6 (FACCH stealing flags minus 1)
+  intaveParameter: 31 SACCH multiframes
+  interferenceLevelBoundaries:
+    Interference Boundary 1: 0Ah 
+    Interference Boundary 2: 0Fh
+    Interference Boundary 3: 14h
+    Interference Boundary 4: 19h
+    Interference Boundary 5: 1Eh
+  mSTxPwrMax: 11
+      GSM range:     2=39dBm, 15=13dBm, stepsize 2 dBm 
+      DCS1800 range: 0=30dBm, 15=0dBm, stepsize 2 dBm 
+      PCS1900 range: 0=30dBm, 15=0dBm, stepsize 2 dBm 
+                    30=33dBm, 31=32dBm 
+  ny1:
+    Maximum number of repetitions for PHYSICAL INFORMATION message (GSM 04.08): 20
+  powerOutputThresholds:
+    Out Power Fault Threshold:     -10 dB
+    Red Out Power Threshold:       - 6 dB
+    Excessive Out Power Threshold:   5 dB
+  rACHBusyThreshold: -127 dBm 
+  rACHLoadAveragingSlots: 250 ,number of RACH burst periods
+  rfResourceIndicationPeriod: 125  SACCH multiframes 
+  T200:
+    SDCCH:                044 in  5 ms
+    FACCH/Full rate:      031 in  5 ms
+    FACCH/Half rate:      041 in  5 ms
+    SACCH with TCH SAPI0: 090 in 10 ms
+    SACCH with SDCCH:     090 in 10 ms
+    SDCCH with SAPI3:     090 in  5 ms
+    SACCH with TCH SAPI3: 135 in 10 ms
+  tSync: 9000 units of 10 msec
+  tTrau: 9000 units of 10 msec
+  enableUmLoopTest: 00h =  disabled
+  enableExcessiveDistance: 00h =  Disabled
+  excessiveDistance: 64km
+  hoppingMode: 00h = baseband hopping
+  cellType: 00h =  Standard Cell
+  BCCH ARFCN / bCCHFrequency: 1
+*/
+
+unsigned char msg_2[] = 
+{
+	0x41, 0x01, 0x00, 0xFF, 0xFF, 0x09, 0x3F, 0x0A, 0x04, 0x61, 0x00, 0x0B,
+	0xFF, 0x0C, 0x64, 0x62, 0x00, 0x66, 0x00, 0x6E, 0x06, 0x18, 0x1F, 0x19,
+	0x0A, 0x0F, 0x14, 0x19, 0x1E, 0x7B, 0x0B, 0x23, 0x14, 0x28, 0x00, 0x04,
+	0x03, 0x2A, 0x7F, 0x2B, 0x00, 0xFA, 0x8F, 0x7D, 0x33, 0x2C, 0x1F, 0x29,
+	0x5A, 0x5A, 0x5A, 0x87, 0x94, 0x23, 0x28, 0x95, 0x23, 0x28, 0x35, 0x01,
+	0x00, 0x46, 0x01, 0x00, 0x58, 0x01, 0x40, 0xC5, 0x01, 0x00, 0xF2, 0x01,
+	0x00, 0x08, 0x00, HARDCODED_ARFCN/*0x01*/, 
+};
+
+// Handover Recognition, SET ATTRIBUTES
+
+/*
+Illegal Contents GSM Formatted O&M Msg 
+  Object Class: Handover Recognition
+  BTS relat. Number: 0 
+  Instance 2: FF
+  Instance 3: FF
+SET ATTRIBUTES
+  enableDelayPowerBudgetHO: 00h = Disabled
+  enableDistanceHO: 00h =  Disabled
+  enableInternalInterCellHandover: 00h = Disabled
+  enableInternalIntraCellHandover: 00h =  Disabled
+  enablePowerBudgetHO: 00h = Disabled
+  enableRXLEVHO: 00h =  Disabled
+  enableRXQUALHO: 00h =  Disabled
+  hoAveragingDistance: 8  SACCH multiframes 
+  hoAveragingLev:
+    A_LEV_HO: 8  SACCH multiframes 
+    W_LEV_HO: 1  SACCH multiframes 
+  hoAveragingPowerBudget:  16  SACCH multiframes 
+  hoAveragingQual:
+    A_QUAL_HO: 8  SACCH multiframes 
+    W_QUAL_HO: 2  SACCH multiframes 
+  hoLowerThresholdLevDL: (10 - 110) dBm
+  hoLowerThresholdLevUL: (5 - 110) dBm
+  hoLowerThresholdQualDL: 06h =   6.4% < BER < 12.8%
+  hoLowerThresholdQualUL: 06h =   6.4% < BER < 12.8%
+  hoThresholdLevDLintra : (20 - 110) dBm
+  hoThresholdLevULintra: (20 - 110) dBm
+  hoThresholdMsRangeMax: 20 km 
+  nCell: 06h
+  timerHORequest: 3  ,unit 2 SACCH multiframes 
+*/
+
+unsigned char msg_3[] = 
+{
+	0xD0, 0xA1, 0x00, 0xFF, 0xFF, 0xD0, 0x00, 0x64, 0x00, 0x67, 0x00, 0x68,
+	0x00, 0x6A, 0x00, 0x6C, 0x00, 0x6D, 0x00, 0x6F, 0x08, 0x70, 0x08, 0x01,
+	0x71, 0x10, 0x10, 0x10, 0x72, 0x08, 0x02, 0x73, 0x0A, 0x74, 0x05, 0x75,
+	0x06, 0x76, 0x06, 0x78, 0x14, 0x79, 0x14, 0x7A, 0x14, 0x7D, 0x06, 0x92,
+	0x03, 0x20, 0x01, 0x00, 0x45, 0x01, 0x00, 0x48, 0x01, 0x00, 0x5A, 0x01,
+	0x00, 0x5B, 0x01, 0x05, 0x5E, 0x01, 0x1A, 0x5F, 0x01, 0x20, 0x9D, 0x01,
+	0x00, 0x47, 0x01, 0x00, 0x5C, 0x01, 0x64, 0x5D, 0x01, 0x1E, 0x97, 0x01,
+	0x20, 0xF7, 0x01, 0x3C,
+};
+
+// Power Control, SET ATTRIBUTES
+
+/*
+  Object Class: Power Control
+  BTS relat. Number: 0 
+  Instance 2: FF
+  Instance 3: FF
+SET ATTRIBUTES
+  enableMsPowerControl: 00h =  Disabled
+  enablePowerControlRLFW: 00h =  Disabled
+  pcAveragingLev:
+    A_LEV_PC: 4  SACCH multiframes 
+    W_LEV_PC: 1  SACCH multiframes 
+  pcAveragingQual:
+    A_QUAL_PC: 4  SACCH multiframes 
+    W_QUAL_PC: 2  SACCH multiframes 
+  pcLowerThresholdLevDL: 0Fh
+  pcLowerThresholdLevUL: 0Ah
+  pcLowerThresholdQualDL: 05h =   3.2% < BER <  6.4%
+  pcLowerThresholdQualUL: 05h =   3.2% < BER <  6.4%
+  pcRLFThreshold: 0Ch
+  pcUpperThresholdLevDL: 14h
+  pcUpperThresholdLevUL: 0Fh
+  pcUpperThresholdQualDL: 04h =   1.6% < BER <  3.2%
+  pcUpperThresholdQualUL: 04h =   1.6% < BER <  3.2%
+  powerConfirm: 2  ,unit 2 SACCH multiframes 
+  powerControlInterval: 2  ,unit 2 SACCH multiframes 
+  powerIncrStepSize: 02h = 4 dB
+  powerRedStepSize: 01h = 2 dB
+  radioLinkTimeoutBs: 64  SACCH multiframes 
+  enableBSPowerControl: 00h =  disabled
+*/
+
+unsigned char msg_4[] = 
+{
+	0xD0, 0xA2, 0x00, 0xFF, 0xFF, 0x69, 0x00, 0x6B, 0x00, 0x7E, 0x04, 0x01,
+	0x7F, 0x04, 0x02, 0x80, 0x0F, 0x81, 0x0A, 0x82, 0x05, 0x83, 0x05, 0x84,
+	0x0C, 0x85, 0x14, 0x86, 0x0F, 0x87, 0x04, 0x88, 0x04, 0x89, 0x02, 0x8A,
+	0x02, 0x8B, 0x02, 0x8C, 0x01, 0x8D, 0x40, 0x65, 0x01, 0x00 // set to 0x01 to enable BSPowerControl
+};
+
+
+// Transceiver, SET TRX ATTRIBUTES (TRX 0)
+
+/*
+  Object Class: Transceiver
+  BTS relat. Number: 0 
+  Tranceiver number: 0 
+  Instance 3: FF
+SET TRX ATTRIBUTES
+  aRFCNList (HEX):  0001
+  txPwrMaxReduction: 00h =   0dB
+  radioMeasGran: 254  SACCH multiframes 
+  radioMeasRep: 01h =  enabled
+  memberOfEmergencyConfig: 01h =  TRUE
+  trxArea: 00h = TRX doesn't belong to a concentric cell
+*/
+
+unsigned char msg_6[] = 
+{
+	0x44, 0x02, 0x00, 0x00, 0xFF, 0x05, 0x01, 0x00, HARDCODED_ARFCN /*0x01*/, 0x2D,
+	0x00, 0xDC, 0x01, 0xFE, 0xDD, 0x01, 0x01, 0x9B, 0x01, 0x01, 0x9F, 0x01, 0x00, 
+};
+
+
+static void bootstrap_om(struct gsm_bts *bts)
+{
+	struct gsm_bts_trx *trx = &bts->trx[0];
+
+	/* stop sending event reports */
+	abis_nm_event_reports(bts, 0);
+
+	/* begin DB transmission */
+	abis_nm_db_transmission(bts, 1);
+
+	abis_nm_raw_msg(bts, sizeof(msg_1), msg_1); /* set BTS SiteMgr attr*/
+	abis_nm_raw_msg(bts, sizeof(msg_2), msg_2); /* set BTS attr */
+	abis_nm_raw_msg(bts, sizeof(msg_3), msg_3); /* set BTS handover attr */
+	abis_nm_raw_msg(bts, sizeof(msg_4), msg_4); /* set BTS power control attr */
+
+	/* Connect signalling of bts0/trx0 to e1_0/ts1/64kbps */
+	abis_nm_conn_terr_sign(trx, 0, 1, 0xff);
+	abis_nm_raw_msg(bts, sizeof(msg_6), msg_6); /* SET TRX ATTRIBUTES */
+
+	/* Use TEI 1 for signalling */
+	abis_nm_establish_tei(bts, 0, 0, 1, 0xff, 0x01);
+	abis_nm_set_channel_attr(&trx->ts[0], NM_CHANC_SDCCH_CBCH);
+#if 0
+	/* TRX 1 */
+	abis_nm_conn_terr_sign(&bts->trx[1], 0, 1, 0xff);
+	/* FIXME: TRX ATTRIBUTE */
+	abis_nm_establish_tei(bts, 0, 0, 1, 0xff, 0x02);
+#endif
+
+	/* SET CHANNEL ATTRIBUTE TS1 */
+	abis_nm_set_channel_attr(&trx->ts[1], 0x09);
+	/* Connect traffic of bts0/trx0/ts1 to e1_0/ts2/b */
+	abis_nm_conn_terr_traf(&trx->ts[1], 0, 2, 1);
+	
+	/* SET CHANNEL ATTRIBUTE TS2 */
+	abis_nm_set_channel_attr(&trx->ts[2], 0x09);
+	/* Connect traffic of bts0/trx0/ts2 to e1_0/ts2/c */
+	abis_nm_conn_terr_traf(&trx->ts[2], 0, 2, 2);
+
+	/* SET CHANNEL ATTRIBUTE TS3 */
+	abis_nm_set_channel_attr(&trx->ts[3], 0x09);
+	/* Connect traffic of bts0/trx0/ts3 to e1_0/ts2/d */
+	abis_nm_conn_terr_traf(&trx->ts[3], 0, 2, 3);
+
+	/* SET CHANNEL ATTRIBUTE TS4 */
+	abis_nm_set_channel_attr(&trx->ts[4], 0x09);
+	/* Connect traffic of bts0/trx0/ts4 to e1_0/ts3/a */
+	abis_nm_conn_terr_traf(&trx->ts[4], 0, 3, 0);
+
+	/* SET CHANNEL ATTRIBUTE TS5 */
+	abis_nm_set_channel_attr(&trx->ts[5], 0x09);
+	/* Connect traffic of bts0/trx0/ts5 to e1_0/ts3/b */
+	abis_nm_conn_terr_traf(&trx->ts[5], 0, 3, 1);
+
+	/* SET CHANNEL ATTRIBUTE TS6 */
+	abis_nm_set_channel_attr(&trx->ts[6], 0x09);
+	/* Connect traffic of bts0/trx0/ts6 to e1_0/ts3/c */
+	abis_nm_conn_terr_traf(&trx->ts[6], 0, 3, 2);
+
+	/* SET CHANNEL ATTRIBUTE TS7 */
+	abis_nm_set_channel_attr(&trx->ts[7], 0x09);
+	/* Connect traffic of bts0/trx0/ts7 to e1_0/ts3/d */
+	abis_nm_conn_terr_traf(&trx->ts[7], 0, 3, 3);
+
+	/* end DB transmission */
+	abis_nm_db_transmission(bts, 0);
+
+	/* Reset BTS Site manager resource */
+	abis_nm_reset_resource(bts);
+
+	/* restart sending event reports */
+	abis_nm_event_reports(bts, 1);
+}
+
+
+
+struct bcch_info {
+	u_int8_t type;
+	u_int8_t len;
+	const u_int8_t *data;
+};
+
+/*
+SYSTEM INFORMATION TYPE 1
+  Cell channel description
+    Format-ID bit map 0
+    CA-ARFCN Bit 124...001 (Hex): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01
+  RACH Control Parameters
+    maximum 7 retransmissions
+    8 slots used to spread transmission
+    cell not barred for access
+    call reestablishment not allowed
+    Access Control Class = 0000
+*/
+static const u_int8_t si1[] = {
+	0x55, 0x06, 0x19, 0x04 /*0x00*/, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /*0x01*/,0xD5, 
+	0x00, 0x00, 0x2B
+};
+
+/*
+ SYSTEM INFORMATION TYPE 2
+  Neighbour Cells Description
+    EXT-IND: Carries the complete BA
+    BA-IND = 0
+    Format-ID bit map 0
+    CA-ARFCN Bit 124...001 (Hex): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+  NCC permitted (NCC) = FF
+  RACH Control Parameters
+    maximum 7 retransmissions
+    8 slots used to spread transmission
+    cell not barred for access
+    call reestablishment not allowed
+    Access Control Class = 0000
+*/
+static const u_int8_t si2[] = {
+	0x59, 0x06, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xD5, 0x00,
+	0x00
+};
+
+/*
+SYSTEM INFORMATION TYPE 3
+  Cell identity = 00001 (1h)
+  Location area identification
+    Mobile Country Code (MCC): 001
+    Mobile Network Code (MNC): 01
+    Location Area Code  (LAC): 00001 (1h)
+  Control Channel Description
+    Attach-detach: MSs in the cell are not allowed to apply IMSI attach /detach
+    0 blocks reserved for access grant
+    1 channel used for CCCH, with SDCCH
+    5 multiframes period for PAGING REQUEST
+    Time-out T3212 = 0
+  Cell Options BCCH
+    Power control indicator: not set
+    MSs shall not use uplink DTX
+    Radio link timeout = 36
+  Cell Selection Parameters
+    Cell reselect hysteresis = 6 dB RXLEV hysteresis for LA re-selection
+    max.TX power level MS may use for CCH = 2
+    Additional Reselect Parameter Indication (ACS) = only SYSTEM INFO 4: The SI rest octets, if present, shall be used to derive the value of PI and possibly C2 parameters
+    Half rate support (NECI): New establishment causes are not supported
+    min.RX signal level for MS = 0
+  RACH Control Parameters
+    maximum 7 retransmissions
+    8 slots used to spread transmission
+    cell not barred for access
+    call reestablishment not allowed
+    Access Control Class = 0000
+  SI 3 Rest Octets
+    Cell Bar Qualify (CBQ): 0
+    Cell Reselect Offset = 0 dB
+    Temporary Offset = 0 dB
+    Penalty Time = 20 s
+    System Information 2ter Indicator (2TI): 0 = not available
+    Early Classmark Sending Control (ECSC):  0 = forbidden
+    Scheduling Information is not sent in SYSTEM INFORMATION TYPE 9 on the BCCH
+*/
+unsigned char si3[] = {
+	0x49, 0x06, 0x1B, 0x00, 0x01, 0x00, 0xF1, 0x10, 0x00, 0x01,
+	0x01, 0x03, 0x00, 0x28, 0x62, 0x00, 0xD5, 0x00, 0x00, 0x80,
+	0x00, 0x00, 0x2B
+};
+
+/*
+SYSTEM INFORMATION TYPE 4
+  Location area identification
+    Mobile Country Code (MCC): 001
+    Mobile Network Code (MNC): 01
+    Location Area Code  (LAC): 00001 (1h)
+  Cell Selection Parameters
+    Cell reselect hysteresis = 6 dB RXLEV hysteresis for LA re-selection
+    max.TX power level MS may use for CCH = 2
+    Additional Reselect Parameter Indication (ACS) = only SYSTEM INFO 4: The SI rest octets, if present, shall be used to derive the value of PI and possibly C2 parameters
+    Half rate support (NECI): New establishment causes are not supported
+    min.RX signal level for MS = 0
+  RACH Control Parameters
+    maximum 7 retransmissions
+    8 slots used to spread transmission
+    cell not barred for access
+    call reestablishment not allowed
+    Access Control Class = 0000
+  Channel Description
+    Type = SDCCH/4[2]
+    Timeslot Number: 0
+    Training Sequence Code: 7h
+    ARFCN: 1
+  SI Rest Octets
+    Cell Bar Qualify (CBQ): 0
+    Cell Reselect Offset = 0 dB
+    Temporary Offset = 0 dB
+    Penalty Time = 20 s
+*/
+static const u_int8_t si4[] = {
+	0x41, 0x06, 0x1C, 0x00, 0xF1, 0x10, 0x00, 0x01, 0x62, 0x00, 
+	0xD5, 0x00, 0x00, 0x64, 0x30, 0xE0, HARDCODED_ARFCN/*0x01*/, 0x80, 0x00, 0x00, 
+	0x2B, 0x2B, 0x2B
+};
+
+/*
+ SYSTEM INFORMATION TYPE 5
+  Neighbour Cells Description
+    EXT-IND: Carries the complete BA
+    BA-IND = 0
+    Format-ID bit map 0
+    CA-ARFCN Bit 124...001 (Hex): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+*/
+
+static const u_int8_t si5[] = {
+	0x06, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
+};
+
+// SYSTEM INFORMATION TYPE 6
+
+/*
+SACCH FILLING
+  System Info Type: SYSTEM INFORMATION 6
+  L3 Information (Hex): 06 1E 00 01 xx xx 10 00 01 28 FF
+
+SYSTEM INFORMATION TYPE 6
+  Cell identity = 00001 (1h)
+  Location area identification
+    Mobile Country Code (MCC): 001
+    Mobile Network Code (MNC): 01
+    Location Area Code  (LAC): 00001 (1h)
+  Cell Options SACCH
+    Power control indicator: not set
+    MSs shall not use uplink DTX on a TCH-F. MS shall not use uplink DTX on TCH-H.
+    Radio link timeout = 36
+  NCC permitted (NCC) = FF
+*/
+
+static const u_int8_t si6[] = {
+	0x06, 0x1E, 0x00, 0x01, 0x00, 0xF1, 0x10, 0x00, 0x01, 0x28, 0xFF, 
+};
+
+
+
+static const struct bcch_info bcch_infos[] = {
+	{
+		.type = RSL_SYSTEM_INFO_1,
+		.len = sizeof(si1),
+		.data = si1,
+	}, {
+		.type = RSL_SYSTEM_INFO_2,
+		.len = sizeof(si2),
+		.data = si2,
+	}, {
+		.type = RSL_SYSTEM_INFO_3,
+		.len = sizeof(si3),
+		.data = si3,
+	}, {
+		.type = RSL_SYSTEM_INFO_4,
+		.len = sizeof(si4),
+		.data = si4,
+	},
+};
+
+/* set all system information types */
+static int set_system_infos(struct gsm_bts *bts)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(bcch_infos); i++) {
+		rsl_bcch_info(bts, bcch_infos[i].type,
+			      bcch_infos[i].data,
+			      bcch_infos[i].len);
+	}
+	rsl_sacch_filling(bts, RSL_SYSTEM_INFO_5, si5, sizeof(si5));
+	rsl_sacch_filling(bts, RSL_SYSTEM_INFO_6, si6, sizeof(si6));
+}
+
+static void activate_traffic_channels(struct gsm_bts_trx *trx)
+{
+	int i;
+
+	/* channel 0 is CCCH */
+	for (i = 1; i < 8; i++)
+		rsl_chan_activate_tch_f(&trx->ts[i]);
+}
+
+static void bootstrap_bts(struct gsm_bts *bts)
+{
+	bootstrap_om(bts);
+
+	set_system_infos(bts);
+
+	/* FIXME: defer this until the channels are used */
+	activate_traffic_channels(&bts->trx[0]);
+}
+
+static void bootstrap_network()
+{
+	struct gsm_bts *bts;
+
+	/* initialize our data structures */
+	gsmnet = gsm_network_init(1, 1, 1);
+	bts = &gsmnet->bts[0];
+	bts->location_area_code = 1;
+	bts->trx[0].arfcn = HARDCODED_ARFCN;
+
+	/* initialize the BTS */
+	bootstrap_bts(&gsmnet->bts[0]);
+
+
+}
diff --git a/src/gsm_04_08.c b/src/gsm_04_08.c
new file mode 100644
index 0000000..4165b78
--- /dev/null
+++ b/src/gsm_04_08.c
@@ -0,0 +1,306 @@
+/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface 
+ * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */
+
+/* (C) 2008 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.
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "msgb.h"
+#include "debug.h"
+#include "gsm_data.h"
+#include "gsm_subscriber.h"
+#include "gsm_04_08.h"
+
+#define GSM0408_ALLOC_SIZE	1024
+
+struct gsm_lai {
+	u_int16_t mcc;
+	u_int16_t mnc;
+	u_int16_t lac;
+};
+
+static void parse_lai(struct gsm_lai *lai, const struct gsm48_loc_area_id *lai48)
+{
+	u_int8_t dig[4];
+
+	/* MCC */
+	dig[1] = lai48->digits[0] & 0x0f;
+	dig[2] = lai48->digits[0] >> 4;
+	dig[3] = lai48->digits[1] & 0x0f;
+	lai->mcc = dig[3] * 100 + dig[2];
+
+	/* MNC */
+	dig[1] = lai48->digits[1] >> 4;
+	dig[2] = lai48->digits[2] & 0x0f;
+	dig[3] = lai48->digits[2] >> 4;
+	lai->mnc = dig[3] * 100 + dig[2];
+
+	lai->lac = lai48->lac;
+}
+
+static void to_bcd(u_int8_t *bcd, u_int16_t val)
+{
+	bcd[0] = val % 10;
+	val = val / 10;
+	bcd[1] = val % 10;
+	val = val / 10;
+	bcd[2] = val % 10;
+	val = val / 10;
+}
+
+static void generate_lai(struct gsm48_loc_area_id *lai48, u_int16_t mcc, 
+			 u_int16_t mnc, u_int16_t lac)
+{
+	u_int8_t bcd[3];
+
+	to_bcd(bcd, mcc);
+	lai48->digits[0] = bcd[0] | (bcd[1] << 4);
+	lai48->digits[1] = bcd[2];
+
+	to_bcd(bcd, mnc);
+	lai48->digits[2] |= bcd[2] << 4;
+	lai48->digits[3] = bcd[0] | (bcd[1] << 4);
+	
+	lai48->lac = lac;
+}
+
+#define TMSI_LEN	4
+#define MID_TMSI_LEN	(TMSI_LEN + 2)
+
+static void generate_mid_from_tmsi(u_int8_t *buf, u_int8_t *tmsi_bcd)
+{
+	buf[0] = MID_TMSI_LEN;
+	buf[1] = 0xf0 | GSM_MI_TYPE_TMSI;
+	buf[2] = tmsi_bcd[0];
+	buf[3] = tmsi_bcd[1];
+	buf[4] = tmsi_bcd[2];
+	buf[5] = tmsi_bcd[3];
+}
+
+static int gsm0408_sendmsg(struct msgb *msg)
+{
+	/* FIXME: set data pointer to beginning of L3 data object */
+
+	return rsl_data_request(msg);
+}
+
+static int gsm0408_rcv_cc(struct msgb *msg)
+{
+	struct gsm48_hdr *gh = msgb_l3(msg);
+
+	switch (gh->msg_type & 0xbf) {
+	case GSM48_MT_CC_CALL_CONF:
+		/* Response to SETUP */
+		DEBUGP(DCC, "CALL CONFIRM\n");
+		break;
+	case GSM48_MT_CC_RELEASE_COMPL:
+		DEBUGP(DCC, "RELEASE COMPLETE\n");
+		break;
+	case GSM48_MT_CC_ALERTING:
+		DEBUGP(DCC, "ALERTING\n");
+		break;
+	case GSM48_MT_CC_CONNECT:
+		DEBUGP(DCC, "CONNECT\n");
+		/* need to respond with CONNECT_ACK */
+		break;
+	case GSM48_MT_CC_RELEASE:
+		DEBUGP(DCC, "RELEASE\n");
+		/* need to respond with RELEASE_COMPLETE */
+		break;
+	case GSM48_MT_CC_EMERG_SETUP:
+		//DEBUGP(DCC, "EMERGENCY SETUP\n");
+	case GSM48_MT_CC_SETUP:
+		//DEBUGP(DCC, "SETUP\n");
+		/* FIXME: continue with CALL_PROCEEDING, ALERTING, CONNECT, RELEASE_COMPLETE */
+	default:
+		fprintf(stderr, "Unimplemented GSM 04.08 msg type 0x%02x\n",
+			gh->msg_type);
+		break;
+	}
+}
+
+/* Chapter 9.2.14 : Send LOCATION UPDATE REJECT */
+int gsm0408_loc_upd_rej(struct gsm_bts_link *bts_link, u_int8_t cause)
+{
+	struct msgb *msg = msgb_alloc(GSM0408_ALLOC_SIZE);
+	struct gsm48_hdr *gh;
+	
+	msg->bts_link = bts_link;
+
+	gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1);
+	gh->proto_discr = GSM48_PDISC_MM;
+	gh->msg_type = GSM48_MT_MM_LOC_UPD_ACCEPT;
+	gh->data[0] = cause;
+
+	DEBUGP(DMM, "-> LOCATION UPDATE REJECT\n");
+
+	return gsm0408_sendmsg(msg);
+}
+
+/* Chapter 9.2.13 : Send LOCATION UPDATE ACCEPT */
+int gsm0408_loc_upd_acc(struct gsm_bts_link *bts_link, u_int8_t *tmsi)
+{
+	struct gsm_bts *bts = bts_link->bts;
+	struct msgb *msg = msgb_alloc(GSM0408_ALLOC_SIZE);
+	struct gsm48_hdr *gh;
+	struct gsm48_loc_area_id *lai;
+	u_int8_t *mid;
+	
+	msg->bts_link = bts_link;
+
+	gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
+	gh->proto_discr = GSM48_PDISC_MM;
+	gh->msg_type = GSM48_MT_MM_LOC_UPD_ACCEPT;
+
+	lai = (struct gsm48_loc_area_id *) msgb_put(msg, sizeof(*lai));
+	generate_lai(lai, bts->network->country_code,
+		     bts->network->network_code, bts->location_area_code);
+
+	mid = msgb_put(msg, MID_TMSI_LEN);
+	generate_mid_from_tmsi(mid, tmsi);
+
+	DEBUGP(DMM, "-> LOCATION UPDATE ACCEPT\n");
+
+	return gsm0408_sendmsg(msg);
+}
+
+
+/* Chapter 9.2.15 */
+static int mm_loc_upd_req(struct msgb *msg)
+{
+	struct gsm_bts *bts = msg->bts_link->bts;
+	struct gsm48_loc_upd_req *lu;
+	struct gsm_subscriber *subscr;
+
+	u_int8_t mi_type = lu->mi[0] & GSM_MI_TYPE_MASK;
+
+	switch (mi_type) {
+	case GSM_MI_TYPE_IMSI:
+		/* look up subscriber based on IMSI */
+		subscr = subscr_get_by_imsi(&lu->mi[1]);
+		break;	
+	case GSM_MI_TYPE_TMSI:
+		/* look up the subscriber based on TMSI, request IMSI if it fails */
+		subscr = subscr_get_by_tmsi(&lu->mi[1]);
+		if (!subscr) {
+			/* FIXME: send IDENTITY REQUEST message to get IMSI */
+			//gsm0408_identity_request(...GSM_MI_TYPE_IMSI);
+		}
+		break;
+	case GSM_MI_TYPE_IMEI:
+	case GSM_MI_TYPE_IMEISV:
+		/* no sim card... FIXME: what to do ? */
+		fprintf(stderr, "Unimplemented mobile identity type\n");
+		break;
+	default:	
+		fprintf(stderr, "Unknown mobile identity type\n");
+		break;
+	}
+
+	if (!subscr) {
+		/* 0x16 is congestion */
+		gsm0408_loc_upd_rej(msg->bts_link, 0x16);
+		return -EINVAL;
+	}
+
+	subscr_update(subscr, bts);
+	return gsm0408_loc_upd_acc(msg->bts_link, subscr->tmsi);
+}
+
+static int gsm0408_rcv_mm(struct msgb *msg)
+{
+	struct gsm48_hdr *gh = msgb_l3(msg);
+	int rc;
+
+	switch (gh->msg_type & 0xbf) {
+	case GSM48_MT_MM_LOC_UPD_REQUEST:
+		DEBUGP(DMM, "LOCATION UPDATE REQUEST\n");
+		rc = mm_loc_upd_req(msg);
+		break;
+	case GSM48_MT_MM_ID_RESP:
+	case GSM48_MT_MM_TMSI_REALL_COMPL:
+	case GSM48_MT_MM_AUTH_RESP:
+	case GSM48_MT_MM_IMSI_DETACH_IND:
+	case GSM48_MT_MM_CM_SERV_REQ:
+	case GSM48_MT_MM_CM_REEST_REQ:
+		fprintf(stderr, "Unimplemented GSM 04.08 MM msg type 0x%02x\n",
+			gh->msg_type);
+		break;
+	default:
+		fprintf(stderr, "Unknown GSM 04.08 MM msg type 0x%02x\n",
+			gh->msg_type);
+		break;
+	}
+
+	return rc;
+}
+static int gsm0408_rcv_rr(struct msgb *msg)
+{
+	struct gsm48_hdr *gh = msgb_l3(msg);
+
+	switch (gh->msg_type) {
+	case GSM48_MT_RR_CLSM_CHG:
+		DEBUGP(DRR, "CLASSMARK CHANGE\n");
+		/* FIXME: what to do ?!? */
+		break;
+	case GSM48_MT_RR_PAG_RESP:
+	default:
+		fprintf(stderr, "Unimplemented GSM 04.08 msg type 0x%02x\n",
+			gh->msg_type);
+		break;
+	}
+
+	return 0;
+}
+
+/* here we pass in a msgb from the RSL->RLL.  We expect the l3 pointer to be set */
+int gsm0408_rcvmsg(struct msgb *msg)
+{
+	struct gsm48_hdr *gh = msgb_l3(msg);
+	u_int8_t pdisc = gh->proto_discr & 0x0f;
+	int rc;
+	
+	switch (pdisc) {
+	case GSM48_PDISC_CC:
+		rc = gsm0408_rcv_cc(msg);
+		break;
+	case GSM48_PDISC_MM:
+		rc = gsm0408_rcv_mm(msg);
+		break;
+	case GSM48_PDISC_RR:
+		rc = gsm0408_rcv_rr(msg);
+		break;
+	case GSM48_PDISC_MM_GPRS:
+	case GSM48_PDISC_SM:
+		fprintf(stderr, "Unimplemented GSM 04.08 discriminator 0x%02d\n",
+			pdisc);
+		break;
+	default:
+		fprintf(stderr, "Unknown GSM 04.08 discriminator 0x%02d\n",
+			pdisc);
+		break;
+	}
+
+	return rc;
+}
diff --git a/src/gsm_data.c b/src/gsm_data.c
new file mode 100644
index 0000000..a4d0e8b
--- /dev/null
+++ b/src/gsm_data.c
@@ -0,0 +1,68 @@
+/* (C) 2008 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.
+ *
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "gsm_data.h"
+
+struct gsm_network *gsm_network_init(unsigned int num_bts, u_int8_t country_code,
+				     u_int8_t network_code)
+{
+	int i;
+	struct gsm_network *net;
+
+	if (num_bts > GSM_MAX_BTS)
+		return NULL;
+
+	net = malloc(sizeof(*net));
+	if (!net)
+		return NULL;
+	memset(net, 0, sizeof(*net));	
+
+	net->country_code = country_code;
+	net->network_code = network_code;
+	net->num_bts = num_bts;
+
+	for (i = 0; i < num_bts; i++) {
+		struct gsm_bts *bts = &net->bts[i];
+		int j;
+		
+		bts->network = net;
+		bts->nr = i;
+
+		for (j = 0; j < BTS_MAX_TRX; j++) {
+			struct gsm_bts_trx *trx = &bts->trx[j];
+			int k;
+
+			trx->bts = bts;
+			trx->nr = j;
+
+			for (k = 0; k < 8; k++) {
+				struct gsm_bts_trx_ts *ts = &trx->ts[k];
+				
+				ts->trx = trx;
+				ts->nr = k;
+			}
+		}
+
+		bts->num_trx = 1;	/* FIXME */
+	}
+}
diff --git a/src/gsm_subscriber.c b/src/gsm_subscriber.c
new file mode 100644
index 0000000..76d2e35
--- /dev/null
+++ b/src/gsm_subscriber.c
@@ -0,0 +1,42 @@
+/* Dummy implementation of a subscriber database, roghly HLR/VLR functionality */
+
+/* (C) 2008 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.
+ *
+ */
+
+
+#include "gsm_subscriber.h"
+
+static struct gsm_subscriber subscr = {
+	.name = "Test User 1",
+	.tmsi = { 0x22, 0x33, 0x44, 0x55 },
+};
+
+struct gsm_subscriber *subscr_get_by_tmsi(u_int8_t *tmsi)
+{
+	return &subscr;
+}
+struct gsm_subscriber *subscr_get_by_imsi(u_int8_t *imsi)
+{
+	return &subscr;
+}
+
+int subscr_update(struct gsm_subscriber *s, struct gsm_bts *bts)
+{
+	return 0;
+}
diff --git a/src/misdn.c b/src/misdn.c
new file mode 100644
index 0000000..6b23223
--- /dev/null
+++ b/src/misdn.c
@@ -0,0 +1,281 @@
+/* OpenBSC Abis interface to mISDNuser
+ *
+ * (C) 2008 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.
+ *
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <mISDNif.h>
+
+#define AF_COMPATIBILITY_FUNC
+#include <compat_af_isdn.h>
+
+#define NUM_E1_TS	32
+
+/* 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;
+
+	struct gsm_fd fd[NUM_E1_TS];
+};
+
+#define SAPI_L2ML	0
+#define SAPI_OML	62
+#define SAPI_RSL	63
+
+#define TEI_L2ML	127
+#define TEI_OML		25
+#define TEI_RSL		1
+
+static void hexdump(unsigned char *buf, int len)
+{
+	int i;
+	for (i = 0; i < len; i++) {
+		fprintf(stdout, "%02x ", buf[i]);
+	}
+	fprintf(stdout, "\n");
+}
+
+#define TS1_ALLOC_SIZE	300
+
+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 l2dadr;
+	socklen_t alen;
+
+	if (!msg)
+		return -ENOMEM;
+
+	msg->bts = e1h->bts;
+
+	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);
+
+	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);
+
+	switch (hh->prim) {
+	case DL_INFORMATION_IND:
+		DEBUGP(DMI, "got DL_INFORMATION_IND\n");
+		struct sockaddr_mISDN *sa;
+		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:
+			continue;
+		}
+		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\n");
+		break;
+	case MPH_ACTIVATE_IND:
+		DEBUGP(DMI, "got MPH_ACTIVATE_IND\n");
+		break;
+	case MPH_DEACTIVATE_IND:
+		DEBUGP(DMI, "got MPH_DEACTIVATE_IND\n");
+		break;
+	case DL_DATA_IND:
+		DEBUGP(DMI, "got DL_DATA_IND\n");
+		msg->l2_off = MISDN_HEADER_LEN;
+		hexdump(msgb_l2(msg), ret - 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;
+}
+
+static int handle_ts1_write(struct bsc_fd *bfd)
+{
+	struct mi_e1_handle *e1h = bfd->data;
+
+	/* FIXME: dequeue a pending msgb for RSL / OML */
+	
+	/* prepend the mISDNhead */
+	hh = (struct mISDNhed *) msg_
+	hh->prim = DL_DATA_REQ;
+
+	/* FIXME: send it off */
+}
+
+static int handle_tsX_read(struct bsc_fd *bfd)
+{
+	/* FIXME: read from a B channel TS */
+}
+
+static int handle_TsX_write(struct bsc_fd *bfd)
+{
+	/* FIXME: write to a B channel TS */
+}
+
+/* callback from select.c in case one of the fd's can be read/written */
+static int misdn_fd_cb(struct gsm_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;
+}
+
+static int mi_setup(devinfo_t *di)
+{
+	int ts, sk, ret;
+	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");
+#if 0
+	devinfo.id = di->cardnr;
+	ret = ioctl(sk, IMGETDEVINFO, &devinfo);
+	if (ret < 0) {
+		fprintf(stdout, "error getting info for device %d: %s\n",
+			di->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 = i-1;
+		struct bsc_fd *bfd = &e1h->fd[idx];
+		struct sockaddr_mISDN addr;
+
+		if (ts == 1)
+			bfd->fd = socket(PF_ISDN, SOCK_RAW, ISDN_P_LAPD_NT);
+		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(stdout, "could not bind l2 socket %s\n",
+				strerror(errno));
+			return -EIO;
+		}
+	}
+}
+
diff --git a/src/msgb.c b/src/msgb.c
new file mode 100644
index 0000000..ab356de
--- /dev/null
+++ b/src/msgb.c
@@ -0,0 +1,50 @@
+/* (C) 2008 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.
+ *
+ */
+
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include "msgb.h"
+
+struct msgb *msgb_alloc(u_int16_t size)
+{
+	struct msgb *msg = malloc(sizeof(*msg) + size);
+
+	if (!msg)
+		return NULL;
+
+	msg->data_len = size;
+	msg->len = 0;
+	msg->data = msg->_data;
+
+	msg->head = msg->data;
+	msg->data = msg->data;
+	/* reset tail pointer */
+	msg->tail = msg->data - msg->head;
+	//msg->end = msg->tail + size;
+
+	return msg;
+}
+
+void msgb_free(struct msgb *m)
+{
+	free(m);
+}
diff --git a/src/select.c b/src/select.c
new file mode 100644
index 0000000..0d95cfb
--- /dev/null
+++ b/src/select.c
@@ -0,0 +1,97 @@
+/* select filedescriptor handling, taken from:
+ * userspace logging daemon for the iptables ULOG target
+ * of the linux 2.4 netfilter subsystem.
+ *
+ * (C) 2000-2008 by Harald Welte <laforge@gnumonks.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 
+ *  as published by the Free Software Foundation
+ *
+ *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <fcntl.h>
+#include <openbsc/select.h>
+#include <openbsc/linuxlist.h>
+
+static int maxfd = 0;
+static LLIST_HEAD(bsc_fds);
+
+int bsc_register_fd(struct bsc_fd *fd)
+{
+	int flags;
+
+	/* make FD nonblocking */
+	flags = fcntl(fd->fd, F_GETFL);
+	if (flags < 0)
+		return -1;
+	flags |= O_NONBLOCK;
+	flags = fcntl(fd->fd, F_SETFL, flags);
+	if (flags < 0)
+		return -1;
+
+	/* Register FD */
+	if (fd->fd > maxfd)
+		maxfd = fd->fd;
+
+	llist_add_tail(&fd->list, &bsc_fds);
+
+	return 0;
+}
+
+void bsc_unregister_fd(struct bsc_fd *fd)
+{
+	llist_del(&fd->list);
+}
+
+int bsc_select_main()
+{
+	struct bsc_fd *ufd;
+	fd_set readset, writeset, exceptset;
+	int i;
+
+	FD_ZERO(&readset);
+	FD_ZERO(&writeset);
+	FD_ZERO(&exceptset);
+
+	/* prepare read and write fdsets */
+	llist_for_each_entry(ufd, &bsc_fds, list) {
+		if (ufd->when & BSC_FD_READ)
+			FD_SET(ufd->fd, &readset);
+
+		if (ufd->when & BSC_FD_WRITE)
+			FD_SET(ufd->fd, &writeset);
+
+		if (ufd->when & BSC_FD_EXCEPT)
+			FD_SET(ufd->fd, &exceptset);
+	}
+
+	i = select(maxfd+1, &readset, &writeset, &exceptset, NULL);
+	if (i > 0) {
+		/* call registered callback functions */
+		llist_for_each_entry(ufd, &bsc_fds, list) {
+			int flags = 0;
+
+			if (FD_ISSET(ufd->fd, &readset))
+				flags |= BSC_FD_READ;
+
+			if (FD_ISSET(ufd->fd, &writeset))
+				flags |= BSC_FD_WRITE;
+
+			if (FD_ISSET(ufd->fd, &exceptset))
+				flags |= BSC_FD_EXCEPT;
+
+			if (flags)
+				ufd->cb(ufd, flags);
+		}
+	}
+	return i;
+}