Merge commit '074c9f904cb5e4f6ab014d76e4abc079c16fc5d7'
diff --git a/libosmocore/.gitignore b/libosmocore/.gitignore
index d61cdc5..06904fa 100644
--- a/libosmocore/.gitignore
+++ b/libosmocore/.gitignore
@@ -7,6 +7,7 @@
 *.la
 *.pc
 aclocal.m4
+m4/*.m4
 autom4te.cache
 config.h*
 config.sub
@@ -23,3 +24,7 @@
 
 .tarball-version
 .version
+
+tests/sms/sms_test
+tests/timer/timer_test
+
diff --git a/libosmocore/include/osmocore/Makefile.am b/libosmocore/include/osmocore/Makefile.am
index 1c3a33f..56f926b 100644
--- a/libosmocore/include/osmocore/Makefile.am
+++ b/libosmocore/include/osmocore/Makefile.am
@@ -1,7 +1,7 @@
 osmocore_HEADERS = signal.h linuxlist.h timer.h select.h msgb.h \
 		   tlv.h bitvec.h comp128.h statistics.h gsm_utils.h utils.h \
 		   gsmtap.h write_queue.h rsl.h gsm48.h rxlev_stat.h mncc.h \
-		   gsm48_ie.h logging.h
+		   gsm48_ie.h logging.h gsm0808.h
 
 if ENABLE_TALLOC
 osmocore_HEADERS += talloc.h
diff --git a/libosmocore/include/osmocore/gsm0808.h b/libosmocore/include/osmocore/gsm0808.h
new file mode 100644
index 0000000..a40713f
--- /dev/null
+++ b/libosmocore/include/osmocore/gsm0808.h
@@ -0,0 +1,41 @@
+/* (C) 2009,2010 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009,2010 by On-Waves
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU 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 OSMOCORE_GSM0808_H
+#define OSMOCORE_GSM0808_H
+
+#include "tlv.h"
+
+struct msgb;
+
+struct msgb *gsm0808_create_layer3(struct msgb *msg, uint16_t netcode, uint16_t countrycode, int lac, int ci);
+struct msgb *gsm0808_create_reset(void);
+struct msgb *gsm0808_create_clear_complete(void);
+struct msgb *gsm0808_create_cipher_complete(struct msgb *layer3, uint8_t alg_id);
+struct msgb *gsm0808_create_cipher_reject(uint8_t cause);
+struct msgb *gsm0808_create_classmark_update(const uint8_t *classmark, uint8_t length);
+struct msgb *gsm0808_create_sapi_reject(uint8_t link_id);
+struct msgb *gsm0808_create_assignment_completed(struct gsm_lchan *lchan, uint8_t rr_cause,
+						 uint8_t chosen_channel, uint8_t encr_alg_id,
+						 uint8_t speech_mode);
+struct msgb *gsm0808_create_assignment_failure(uint8_t cause, uint8_t *rr_cause);
+
+const struct tlv_definition *gsm0808_att_tlvdef();
+
+#endif
diff --git a/libosmocore/include/osmocore/gsm48.h b/libosmocore/include/osmocore/gsm48.h
index 1e96357..e3a1def 100644
--- a/libosmocore/include/osmocore/gsm48.h
+++ b/libosmocore/include/osmocore/gsm48.h
@@ -14,4 +14,8 @@
 int gsm48_generate_mid_from_tmsi(uint8_t *buf, uint32_t tmsi);
 int gsm48_generate_mid_from_imsi(uint8_t *buf, const char *imsi);
 
+/* Convert Mobile Identity (10.5.1.4) to string */
+int gsm48_mi_to_string(char *string, const int str_len,
+			const uint8_t *mi, const int mi_len);
+
 #endif
diff --git a/libosmocore/include/osmocore/gsm_utils.h b/libosmocore/include/osmocore/gsm_utils.h
index c87e967..195e865 100644
--- a/libosmocore/include/osmocore/gsm_utils.h
+++ b/libosmocore/include/osmocore/gsm_utils.h
@@ -27,6 +27,13 @@
 
 #include <stdint.h>
 
+#define ADD_MODULO(sum, delta, modulo) do {	\
+	if ((sum += delta) >= modulo)		\
+		sum -= modulo;			\
+	} while (0)
+
+#define GSM_MAX_FN	(26*51*2048)
+
 struct gsm_time {
 	uint32_t	fn;	/* FN count */
 	uint16_t	t1;	/* FN div (26*51) */
diff --git a/libosmocore/include/osmocore/msgb.h b/libosmocore/include/osmocore/msgb.h
index 31db719..2841dc5 100644
--- a/libosmocore/include/osmocore/msgb.h
+++ b/libosmocore/include/osmocore/msgb.h
@@ -23,15 +23,11 @@
 #include <stdint.h>
 #include "linuxlist.h"
 
-struct bts_link;
-
 struct msgb {
 	struct llist_head list;
 
-	/* ptr to the physical E1 link to the BTS(s) */
-	struct gsm_bts_link *bts_link;
-
 	/* Part of which TRX logical channel we were received / transmitted */
+	/* FIXME: move them into the control buffer */
 	struct gsm_bts_trx *trx;
 	struct gsm_lchan *lchan;
 
@@ -41,17 +37,11 @@
 	unsigned char *l2h;
 	/* the layer 3 header. For OML: FOM; RSL: 04.08; GPRS: BSSGP */
 	unsigned char *l3h;
-
 	/* the layer 4 header */
-	union {
-		unsigned char *smsh;
-		unsigned char *llch;
-		unsigned char *l4h;
-	};
+	unsigned char *l4h;
 
-	/* the layer 5 header, GPRS: GMM header */
-	unsigned char *gmmh;
-	uint32_t tlli;
+	/* the 'control buffer', large enough to contain 5 pointers */
+	unsigned long cb[5];
 
 	uint16_t data_len;
 	uint16_t len;
@@ -71,7 +61,7 @@
 #define msgb_l1(m)	((void *)(m->l1h))
 #define msgb_l2(m)	((void *)(m->l2h))
 #define msgb_l3(m)	((void *)(m->l3h))
-#define msgb_sms(m)	((void *)(m->smsh))
+#define msgb_sms(m)	((void *)(m->l4h))
 
 static inline unsigned int msgb_l1len(const struct msgb *msgb)
 {
diff --git a/libosmocore/include/osmocore/protocol/gsm_04_08.h b/libosmocore/include/osmocore/protocol/gsm_04_08.h
index 801b9b5..47b98b2 100644
--- a/libosmocore/include/osmocore/protocol/gsm_04_08.h
+++ b/libosmocore/include/osmocore/protocol/gsm_04_08.h
@@ -685,6 +685,7 @@
 /* Chapter 5.1.2.2 */
 #define	GSM_CSTATE_NULL			0
 #define	GSM_CSTATE_INITIATED		1
+#define	GSM_CSTATE_MM_CONNECTION_PEND	2 /* see 10.5.4.6 */
 #define	GSM_CSTATE_MO_CALL_PROC		3
 #define	GSM_CSTATE_CALL_DELIVERED	4
 #define	GSM_CSTATE_CALL_PRESENT		6
diff --git a/libosmocore/include/osmocore/protocol/gsm_08_08.h b/libosmocore/include/osmocore/protocol/gsm_08_08.h
new file mode 100644
index 0000000..6b8f935
--- /dev/null
+++ b/libosmocore/include/osmocore/protocol/gsm_08_08.h
@@ -0,0 +1,303 @@
+/* From GSM08.08 */
+
+#ifndef GSM_0808_H
+#define GSM_0808_H
+
+#include <stdlib.h>
+
+/*
+ * this is from GSM 03.03 CGI but is copied in GSM 08.08
+ * in § 3.2.2.27 for Cell Identifier List
+ */
+enum CELL_IDENT {
+	CELL_IDENT_WHOLE_GLOBAL		= 0,
+	CELL_IDENT_LAC_AND_CI		= 1,
+	CELL_IDENT_CI			= 2,
+	CELL_IDENT_NO_CELL		= 3,
+	CELL_IDENT_LAI_AND_LAC		= 4,
+	CELL_IDENT_LAC			= 5,
+	CELL_IDENT_BSS			= 6,
+	CELL_IDENT_UTRAN_PLMN_LAC_RNC	= 8,
+	CELL_IDENT_UTRAN_RNC		= 9,
+	CELL_IDENT_UTRAN_LAC_RNC	= 10,
+};
+
+
+/* GSM 08.06 § 6.3 */
+enum BSSAP_MSG_TYPE {
+	BSSAP_MSG_BSS_MANAGEMENT    = 0x0,
+	BSSAP_MSG_DTAP		    = 0x1,
+};
+
+struct bssmap_header {
+	uint8_t type;
+	uint8_t length;
+} __attribute__((packed));
+
+struct dtap_header {
+	uint8_t type;
+	uint8_t link_id;
+	uint8_t length;
+} __attribute__((packed));
+
+
+enum BSS_MAP_MSG_TYPE {
+	BSS_MAP_MSG_RESERVED_0		= 0,
+
+	/* ASSIGNMENT MESSAGES */
+	BSS_MAP_MSG_ASSIGMENT_RQST	= 1,
+	BSS_MAP_MSG_ASSIGMENT_COMPLETE	= 2,
+	BSS_MAP_MSG_ASSIGMENT_FAILURE	= 3,
+
+	/*  HANDOVER MESSAGES */
+	BSS_MAP_MSG_HANDOVER_RQST		= 16,
+	BSS_MAP_MSG_HANDOVER_REQUIRED		= 17,
+	BSS_MAP_MSG_HANDOVER_RQST_ACKNOWLEDGE= 18,
+	BSS_MAP_MSG_HANDOVER_CMD		= 19,
+	BSS_MAP_MSG_HANDOVER_COMPLETE		= 20,
+	BSS_MAP_MSG_HANDOVER_SUCCEEDED		= 21,
+	BSS_MAP_MSG_HANDOVER_FAILURE		= 22,
+	BSS_MAP_MSG_HANDOVER_PERFORMED		= 23,
+	BSS_MAP_MSG_HANDOVER_CANDIDATE_ENQUIRE	= 24,
+	BSS_MAP_MSG_HANDOVER_CANDIDATE_RESPONSE	= 25,
+	BSS_MAP_MSG_HANDOVER_REQUIRED_REJECT	= 26,
+	BSS_MAP_MSG_HANDOVER_DETECT		= 27,
+
+	/* RELEASE MESSAGES */
+	BSS_MAP_MSG_CLEAR_CMD		= 32,
+	BSS_MAP_MSG_CLEAR_COMPLETE		= 33,
+	BSS_MAP_MSG_CLEAR_RQST		= 34,
+	BSS_MAP_MSG_RESERVED_1			= 35,
+	BSS_MAP_MSG_RESERVED_2			= 36,
+	BSS_MAP_MSG_SAPI_N_REJECT		= 37,
+	BSS_MAP_MSG_CONFUSION			= 38,
+
+	/* OTHER CONNECTION RELATED MESSAGES */
+	BSS_MAP_MSG_SUSPEND			= 40,
+	BSS_MAP_MSG_RESUME			= 41,
+	BSS_MAP_MSG_CONNECTION_ORIENTED_INFORMATION = 42,
+	BSS_MAP_MSG_PERFORM_LOCATION_RQST	= 43,
+	BSS_MAP_MSG_LSA_INFORMATION		= 44,
+	BSS_MAP_MSG_PERFORM_LOCATION_RESPONSE	= 45,
+	BSS_MAP_MSG_PERFORM_LOCATION_ABORT	= 46,
+	BSS_MAP_MSG_COMMON_ID			= 47,
+
+	/* GENERAL MESSAGES */
+	BSS_MAP_MSG_RESET			= 48,
+	BSS_MAP_MSG_RESET_ACKNOWLEDGE		= 49,
+	BSS_MAP_MSG_OVERLOAD			= 50,
+	BSS_MAP_MSG_RESERVED_3			= 51,
+	BSS_MAP_MSG_RESET_CIRCUIT		= 52,
+	BSS_MAP_MSG_RESET_CIRCUIT_ACKNOWLEDGE	= 53,
+	BSS_MAP_MSG_MSC_INVOKE_TRACE		= 54,
+	BSS_MAP_MSG_BSS_INVOKE_TRACE		= 55,
+	BSS_MAP_MSG_CONNECTIONLESS_INFORMATION	= 58,
+
+	/* TERRESTRIAL RESOURCE MESSAGES */
+	BSS_MAP_MSG_BLOCK			= 64,
+	BSS_MAP_MSG_BLOCKING_ACKNOWLEDGE	= 65,
+	BSS_MAP_MSG_UNBLOCK			= 66,
+	BSS_MAP_MSG_UNBLOCKING_ACKNOWLEDGE	= 67,
+	BSS_MAP_MSG_CIRCUIT_GROUP_BLOCK		= 68,
+	BSS_MAP_MSG_CIRCUIT_GROUP_BLOCKING_ACKNOWLEDGE	= 69,
+	BSS_MAP_MSG_CIRCUIT_GROUP_UNBLOCK	= 70,
+	BSS_MAP_MSG_CIRCUIT_GROUP_UNBLOCKING_ACKNOWLEDGE = 71,
+	BSS_MAP_MSG_UNEQUIPPED_CIRCUIT		= 72,
+	BSS_MAP_MSG_CHANGE_CIRCUIT		= 78,
+	BSS_MAP_MSG_CHANGE_CIRCUIT_ACKNOWLEDGE	= 79,
+
+	/* RADIO RESOURCE MESSAGES */
+	BSS_MAP_MSG_RESOURCE_RQST		= 80,
+	BSS_MAP_MSG_RESOURCE_INDICATION		= 81,
+	BSS_MAP_MSG_PAGING			= 82,
+	BSS_MAP_MSG_CIPHER_MODE_CMD		= 83,
+	BSS_MAP_MSG_CLASSMARK_UPDATE		= 84,
+	BSS_MAP_MSG_CIPHER_MODE_COMPLETE	= 85,
+	BSS_MAP_MSG_QUEUING_INDICATION		= 86,
+	BSS_MAP_MSG_COMPLETE_LAYER_3		= 87,
+	BSS_MAP_MSG_CLASSMARK_RQST		= 88,
+	BSS_MAP_MSG_CIPHER_MODE_REJECT		= 89,
+	BSS_MAP_MSG_LOAD_INDICATION		= 90,
+
+	/* VGCS/VBS */
+	BSS_MAP_MSG_VGCS_VBS_SETUP		= 4,
+	BSS_MAP_MSG_VGCS_VBS_SETUP_ACK		= 5,
+	BSS_MAP_MSG_VGCS_VBS_SETUP_REFUSE	= 6,
+	BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RQST	= 7,
+	BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RESULT	= 28,
+	BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_FAILURE	= 29,
+	BSS_MAP_MSG_VGCS_VBS_QUEUING_INDICATION	= 30,
+	BSS_MAP_MSG_UPLINK_RQST		= 31,
+	BSS_MAP_MSG_UPLINK_RQST_ACKNOWLEDGE	= 39,
+	BSS_MAP_MSG_UPLINK_RQST_CONFIRMATION	= 73,
+	BSS_MAP_MSG_UPLINK_RELEASE_INDICATION	= 74,
+	BSS_MAP_MSG_UPLINK_REJECT_CMD	= 75,
+	BSS_MAP_MSG_UPLINK_RELEASE_CMD	= 76,
+	BSS_MAP_MSG_UPLINK_SEIZED_CMD	= 77,
+};
+
+enum GSM0808_IE_CODING {
+	GSM0808_IE_CIRCUIT_IDENTITY_CODE	= 1,
+	GSM0808_IE_RESERVED_0			= 2,
+	GSM0808_IE_RESOURCE_AVAILABLE		= 3,
+	GSM0808_IE_CAUSE			= 4,
+	GSM0808_IE_CELL_IDENTIFIER		= 5,
+	GSM0808_IE_PRIORITY			= 6,
+	GSM0808_IE_LAYER_3_HEADER_INFORMATION	= 7,
+	GSM0808_IE_IMSI				= 8,
+	GSM0808_IE_TMSI				= 9,
+	GSM0808_IE_ENCRYPTION_INFORMATION	= 10,
+	GSM0808_IE_CHANNEL_TYPE			= 11,
+	GSM0808_IE_PERIODICITY			= 12,
+	GSM0808_IE_EXTENDED_RESOURCE_INDICATOR	= 13,
+	GSM0808_IE_NUMBER_OF_MSS		= 14,
+	GSM0808_IE_RESERVED_1			= 15,
+	GSM0808_IE_RESERVED_2			= 16,
+	GSM0808_IE_RESERVED_3			= 17,
+	GSM0808_IE_CLASSMARK_INFORMATION_T2	= 18,
+	GSM0808_IE_CLASSMARK_INFORMATION_T3	= 19,
+	GSM0808_IE_INTERFERENCE_BAND_TO_USE	= 20,
+	GSM0808_IE_RR_CAUSE			= 21,
+	GSM0808_IE_RESERVED_4			= 22,
+	GSM0808_IE_LAYER_3_INFORMATION		= 23,
+	GSM0808_IE_DLCI				= 24,
+	GSM0808_IE_DOWNLINK_DTX_FLAG		= 25,
+	GSM0808_IE_CELL_IDENTIFIER_LIST		= 26,
+	GSM0808_IE_RESPONSE_RQST		= 27,
+	GSM0808_IE_RESOURCE_INDICATION_METHOD	= 28,
+	GSM0808_IE_CLASSMARK_INFORMATION_TYPE_1	= 29,
+	GSM0808_IE_CIRCUIT_IDENTITY_CODE_LIST	= 30,
+	GSM0808_IE_DIAGNOSTIC			= 31,
+	GSM0808_IE_LAYER_3_MESSAGE_CONTENTS	= 32,
+	GSM0808_IE_CHOSEN_CHANNEL		= 33,
+	GSM0808_IE_TOTAL_RESOURCE_ACCESSIBLE	= 34,
+	GSM0808_IE_CIPHER_RESPONSE_MODE		= 35,
+	GSM0808_IE_CHANNEL_NEEDED		= 36,
+	GSM0808_IE_TRACE_TYPE			= 37,
+	GSM0808_IE_TRIGGERID			= 38,
+	GSM0808_IE_TRACE_REFERENCE		= 39,
+	GSM0808_IE_TRANSACTIONID		= 40,
+	GSM0808_IE_MOBILE_IDENTITY		= 41,
+	GSM0808_IE_OMCID			= 42,
+	GSM0808_IE_FORWARD_INDICATOR		= 43,
+	GSM0808_IE_CHOSEN_ENCR_ALG		= 44,
+	GSM0808_IE_CIRCUIT_POOL			= 45,
+	GSM0808_IE_CIRCUIT_POOL_LIST		= 46,
+	GSM0808_IE_TIME_INDICATION		= 47,
+	GSM0808_IE_RESOURCE_SITUATION		= 48,
+	GSM0808_IE_CURRENT_CHANNEL_TYPE_1	= 49,
+	GSM0808_IE_QUEUEING_INDICATOR		= 50,
+	GSM0808_IE_SPEECH_VERSION		= 64,
+	GSM0808_IE_ASSIGNMENT_REQUIREMENT	= 51,
+	GSM0808_IE_TALKER_FLAG			= 53,
+	GSM0808_IE_CONNECTION_RELEASE_RQSTED	= 54,
+	GSM0808_IE_GROUP_CALL_REFERENCE		= 55,
+	GSM0808_IE_EMLPP_PRIORITY		= 56,
+	GSM0808_IE_CONFIG_EVO_INDI		= 57,
+	GSM0808_IE_OLD_BSS_TO_NEW_BSS_INFORMATION	= 58,
+	GSM0808_IE_LSA_IDENTIFIER		= 59,
+	GSM0808_IE_LSA_IDENTIFIER_LIST		= 60,
+	GSM0808_IE_LSA_INFORMATION		= 61,
+	GSM0808_IE_LCS_QOS			= 62,
+	GSM0808_IE_LSA_ACCESS_CTRL_SUPPR	= 63,
+	GSM0808_IE_LCS_PRIORITY			= 67,
+	GSM0808_IE_LOCATION_TYPE		= 68,
+	GSM0808_IE_LOCATION_ESTIMATE		= 69,
+	GSM0808_IE_POSITIONING_DATA		= 70,
+	GSM0808_IE_LCS_CAUSE			= 71,
+	GSM0808_IE_LCS_CLIENT_TYPE		= 72,
+	GSM0808_IE_APDU				= 73,
+	GSM0808_IE_NETWORK_ELEMENT_IDENTITY	= 74,
+	GSM0808_IE_GPS_ASSISTANCE_DATA		= 75,
+	GSM0808_IE_DECIPHERING_KEYS		= 76,
+	GSM0808_IE_RETURN_ERROR_RQST		= 77,
+	GSM0808_IE_RETURN_ERROR_CAUSE		= 78,
+	GSM0808_IE_SEGMENTATION			= 79,
+	GSM0808_IE_SERVICE_HANDOVER		= 80,
+	GSM0808_IE_SOURCE_RNC_TO_TARGET_RNC_TRANSPARENT_UMTS	= 81,
+	GSM0808_IE_SOURCE_RNC_TO_TARGET_RNC_TRANSPARENT_CDMA2000= 82,
+	GSM0808_IE_RESERVED_5			= 65,
+	GSM0808_IE_RESERVED_6			= 66,
+};
+
+enum gsm0808_cause {
+	GSM0808_CAUSE_RADIO_INTERFACE_MESSAGE_FAILURE			= 0,
+	GSM0808_CAUSE_RADIO_INTERFACE_FAILURE				= 1,
+	GSM0808_CAUSE_UPLINK_QUALITY					= 2,
+	GSM0808_CAUSE_UPLINK_STRENGTH					= 3,
+	GSM0808_CAUSE_DOWNLINK_QUALITY					= 4,
+	GSM0808_CAUSE_DOWNLINK_STRENGTH					= 5,
+	GSM0808_CAUSE_DISTANCE						= 6,
+	GSM0808_CAUSE_O_AND_M_INTERVENTION				= 7,
+	GSM0808_CAUSE_RESPONSE_TO_MSC_INVOCATION			= 8,
+	GSM0808_CAUSE_CALL_CONTROL					= 9,
+	GSM0808_CAUSE_RADIO_INTERFACE_FAILURE_REVERSION			= 10,
+	GSM0808_CAUSE_HANDOVER_SUCCESSFUL				= 11,
+	GSM0808_CAUSE_BETTER_CELL					= 12,
+	GSM0808_CAUSE_DIRECTED_RETRY					= 13,
+	GSM0808_CAUSE_JOINED_GROUP_CALL_CHANNEL				= 14,
+	GSM0808_CAUSE_TRAFFIC						= 15,
+	GSM0808_CAUSE_EQUIPMENT_FAILURE					= 32,
+	GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE			= 33,
+	GSM0808_CAUSE_RQSTED_TERRESTRIAL_RESOURCE_UNAVAILABLE	= 34,
+	GSM0808_CAUSE_CCCH_OVERLOAD					= 35,
+	GSM0808_CAUSE_PROCESSOR_OVERLOAD				= 36,
+	GSM0808_CAUSE_BSS_NOT_EQUIPPED					= 37,
+	GSM0808_CAUSE_MS_NOT_EQUIPPED					= 38,
+	GSM0808_CAUSE_INVALID_CELL					= 39,
+	GSM0808_CAUSE_TRAFFIC_LOAD					= 40,
+	GSM0808_CAUSE_PREEMPTION					= 41,
+	GSM0808_CAUSE_RQSTED_TRANSCODING_RATE_ADAPTION_UNAVAILABLE	= 48,
+	GSM0808_CAUSE_CIRCUIT_POOL_MISMATCH				= 49,
+	GSM0808_CAUSE_SWITCH_CIRCUIT_POOL				= 50,
+	GSM0808_CAUSE_RQSTED_SPEECH_VERSION_UNAVAILABLE		= 51,
+	GSM0808_CAUSE_LSA_NOT_ALLOWED					= 52,
+	GSM0808_CAUSE_CIPHERING_ALGORITHM_NOT_SUPPORTED			= 64,
+	GSM0808_CAUSE_TERRESTRIAL_CIRCUIT_ALREADY_ALLOCATED		= 80,
+	GSM0808_CAUSE_INVALID_MESSAGE_CONTENTS				= 81,
+	GSM0808_CAUSE_INFORMATION_ELEMENT_OR_FIELD_MISSING		= 82,
+	GSM0808_CAUSE_INCORRECT_VALUE					= 83,
+	GSM0808_CAUSE_UNKNOWN_MESSAGE_TYPE				= 84,
+	GSM0808_CAUSE_UNKNOWN_INFORMATION_ELEMENT			= 85,
+	GSM0808_CAUSE_PROTOCOL_ERROR_BETWEEN_BSS_AND_MSC		= 96,
+};
+
+/* GSM 08.08 3.2.2.11 Channel Type */
+enum gsm0808_chan_indicator {
+	GSM0808_CHAN_SPEECH = 1,
+	GSM0808_CHAN_DATA   = 2,
+	GSM0808_CHAN_SIGN   = 3,
+};
+
+enum gsm0808_chan_rate_type_data {
+	GSM0808_DATA_FULL_BM	= 0x8,
+	GSM0808_DATA_HALF_LM	= 0x9,
+	GSM0808_DATA_FULL_RPREF	= 0xa,
+	GSM0808_DATA_HALF_PREF	= 0xb,
+	GSM0808_DATA_FULL_PREF_NO_CHANGE	= 0x1a,
+	GSM0808_DATA_HALF_PREF_NO_CHANGE	= 0x1b,
+	GSM0808_DATA_MULTI_MASK	= 0x20,
+	GSM0808_DATA_MULTI_MASK_NO_CHANGE	= 0x30,
+};
+
+enum gsm0808_chan_rate_type_speech {
+	GSM0808_SPEECH_FULL_BM	= 0x8,
+	GSM0808_SPEECH_HALF_LM	= 0x9,
+	GSM0808_SPEECH_FULL_PREF= 0xa,
+	GSM0808_SPEECH_HALF_PREF= 0xb,
+	GSM0808_SPEECH_FULL_PREF_NO_CHANGE	= 0x1a,
+	GSM0808_SPEECH_HALF_PREF_NO_CHANGE	= 0x1b,
+	GSM0808_SPEECH_PERM	= 0xf,
+	GSM0808_SPEECH_PERM_NO_CHANGE = 0x1f,
+};
+
+enum gsm0808_permitted_speech {
+	GSM0808_PERM_FR1	= 0x01,
+	GSM0808_PERM_FR2	= 0x11,
+	GSM0808_PERM_FR3	= 0x21,
+	GSM0808_PERM_HR1	= GSM0808_PERM_FR1 | 0x4,
+	GSM0808_PERM_HR2	= GSM0808_PERM_FR2 | 0x4,
+	GSM0808_PERM_HR3	= GSM0808_PERM_FR3 | 0x4,
+};
+
+#endif
diff --git a/libosmocore/include/osmocore/write_queue.h b/libosmocore/include/osmocore/write_queue.h
index 64d4159..ef244c3 100644
--- a/libosmocore/include/osmocore/write_queue.h
+++ b/libosmocore/include/osmocore/write_queue.h
@@ -35,6 +35,7 @@
 
 	int (*read_cb)(struct bsc_fd *fd);
 	int (*write_cb)(struct bsc_fd *fd, struct msgb *msg);
+	int (*except_cb)(struct bsc_fd *fd);
 };
 
 void write_queue_init(struct write_queue *queue, int max_length);
diff --git a/libosmocore/src/Makefile.am b/libosmocore/src/Makefile.am
index 1697807..20e99db 100644
--- a/libosmocore/src/Makefile.am
+++ b/libosmocore/src/Makefile.am
@@ -10,7 +10,7 @@
 libosmocore_la_SOURCES = timer.c select.c signal.c msgb.c rxlev_stat.c \
 			 tlv_parser.c bitvec.c comp128.c gsm_utils.c statistics.c \
 			 write_queue.c utils.c rsl.c gsm48.c gsm48_ie.c \
-			 logging.c
+			 logging.c gsm0808.c
 
 if ENABLE_TALLOC
 libosmocore_la_SOURCES += talloc.c
diff --git a/libosmocore/src/gsm0808.c b/libosmocore/src/gsm0808.c
new file mode 100644
index 0000000..7a7eb3a
--- /dev/null
+++ b/libosmocore/src/gsm0808.c
@@ -0,0 +1,295 @@
+/* (C) 2009,2010 by Holger Hans Peter Freyther <zecke@selfish.org>
+ * (C) 2009,2010 by On-Waves
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU 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 <osmocore/gsm0808.h>
+#include <osmocore/protocol/gsm_08_08.h>
+#include <osmocore/gsm48.h>
+
+#include <arpa/inet.h>
+
+#define BSSMAP_MSG_SIZE 512
+#define BSSMAP_MSG_HEADROOM 128
+
+struct msgb *gsm0808_create_layer3(struct msgb *msg_l3, uint16_t nc, uint16_t cc, int lac, int _ci)
+{
+	uint8_t *data;
+	uint16_t *ci;
+	struct msgb* msg;
+	struct gsm48_loc_area_id *lai;
+
+	msg  = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
+				   "bssmap cmpl l3");
+	if (!msg)
+		return NULL;
+
+	/* create the bssmap header */
+	msg->l3h = msgb_put(msg, 2);
+	msg->l3h[0] = 0x0;
+
+	/* create layer 3 header */
+	data = msgb_put(msg, 1);
+	data[0] = BSS_MAP_MSG_COMPLETE_LAYER_3;
+
+	/* create the cell header */
+	data = msgb_put(msg, 3);
+	data[0] = GSM0808_IE_CELL_IDENTIFIER;
+	data[1] = 1 + sizeof(*lai) + 2;
+	data[2] = CELL_IDENT_WHOLE_GLOBAL;
+
+	lai = (struct gsm48_loc_area_id *) msgb_put(msg, sizeof(*lai));
+	gsm48_generate_lai(lai, cc, nc, lac);
+
+	ci = (uint16_t *) msgb_put(msg, 2);
+	*ci = htons(_ci);
+
+	/* copy the layer3 data */
+	data = msgb_put(msg, msgb_l3len(msg_l3) + 2);
+	data[0] = GSM0808_IE_LAYER_3_INFORMATION;
+	data[1] = msgb_l3len(msg_l3);
+	memcpy(&data[2], msg_l3->l3h, data[1]);
+
+	/* update the size */
+	msg->l3h[1] = msgb_l3len(msg) - 2;
+
+	return msg;
+}
+
+struct msgb *gsm0808_create_reset(void)
+{
+	struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
+					       "bssmap: reset");
+	if (!msg)
+		return NULL;
+
+	msg->l3h = msgb_put(msg, 6);
+	msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
+	msg->l3h[1] = 0x04;
+	msg->l3h[2] = 0x30;
+	msg->l3h[3] = 0x04;
+	msg->l3h[4] = 0x01;
+	msg->l3h[5] = 0x20;
+	return msg;
+}
+
+struct msgb *gsm0808_create_clear_complete(void)
+{
+	struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
+					       "bssmap: clear complete");
+	if (!msg)
+		return NULL;
+
+	msg->l3h = msgb_put(msg, 3);
+	msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
+	msg->l3h[1] = 1;
+	msg->l3h[2] = BSS_MAP_MSG_CLEAR_COMPLETE;
+
+	return msg;
+}
+
+struct msgb *gsm0808_create_cipher_complete(struct msgb *layer3, uint8_t alg_id)
+{
+	struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
+					       "cipher-complete");
+	if (!msg)
+		return NULL;
+
+        /* send response with BSS override for A5/1... cheating */
+	msg->l3h = msgb_put(msg, 3);
+	msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
+	msg->l3h[1] = 0xff;
+	msg->l3h[2] = BSS_MAP_MSG_CIPHER_MODE_COMPLETE;
+
+	/* include layer3 in case we have at least two octets */
+	if (layer3 && msgb_l3len(layer3) > 2) {
+		msg->l4h = msgb_put(msg, msgb_l3len(layer3) + 2);
+		msg->l4h[0] = GSM0808_IE_LAYER_3_MESSAGE_CONTENTS;
+		msg->l4h[1] = msgb_l3len(layer3);
+		memcpy(&msg->l4h[2], layer3->l3h, msgb_l3len(layer3));
+	}
+
+	/* and the optional BSS message */
+	msg->l4h = msgb_put(msg, 2);
+	msg->l4h[0] = GSM0808_IE_CHOSEN_ENCR_ALG;
+	msg->l4h[1] = alg_id;
+
+	/* update the size */
+	msg->l3h[1] = msgb_l3len(msg) - 2;
+	return msg;
+}
+
+struct msgb *gsm0808_create_cipher_reject(uint8_t cause)
+{
+	struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
+					       "bssmap: clear complete");
+	if (!msg)
+		return NULL;
+
+	msg->l3h = msgb_put(msg, 3);
+	msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
+	msg->l3h[1] = 2;
+	msg->l3h[2] = BSS_MAP_MSG_CIPHER_MODE_REJECT;
+	msg->l3h[3] = cause;
+
+	return msg;
+}
+
+struct msgb *gsm0808_create_classmark_update(const uint8_t *classmark_data, uint8_t length)
+{
+	struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
+					       "classmark-update");
+	if (!msg)
+		return NULL;
+
+	msg->l3h = msgb_put(msg, 3);
+	msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
+	msg->l3h[1] = 0xff;
+	msg->l3h[2] = BSS_MAP_MSG_CLASSMARK_UPDATE;
+
+	msg->l4h = msgb_put(msg, length);
+	memcpy(msg->l4h, classmark_data, length);
+
+	/* update the size */
+	msg->l3h[1] = msgb_l3len(msg) - 2;
+	return msg;
+}
+
+struct msgb *gsm0808_create_sapi_reject(uint8_t link_id)
+{
+	struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
+					       "bssmap: sapi 'n' reject");
+	if (!msg)
+		return NULL;
+
+	msg->l3h = msgb_put(msg, 5);
+	msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
+	msg->l3h[1] = 3;
+	msg->l3h[2] = BSS_MAP_MSG_SAPI_N_REJECT;
+	msg->l3h[3] = link_id;
+	msg->l3h[4] = GSM0808_CAUSE_BSS_NOT_EQUIPPED;
+
+	return msg;
+}
+
+struct msgb *gsm0808_create_assignment_completed(struct gsm_lchan *lchan, uint8_t rr_cause,
+						 uint8_t chosen_channel, uint8_t encr_alg_id,
+						 uint8_t speech_mode)
+{
+	uint8_t *data;
+
+	struct msgb *msg = msgb_alloc(35, "bssmap: ass compl");
+	if (!msg)
+		return NULL;
+
+	msg->l3h = msgb_put(msg, 3);
+	msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
+	msg->l3h[1] = 0xff;
+	msg->l3h[2] = BSS_MAP_MSG_ASSIGMENT_COMPLETE;
+
+	/* write 3.2.2.22 */
+	data = msgb_put(msg, 2);
+	data[0] = GSM0808_IE_RR_CAUSE;
+	data[1] = rr_cause;
+
+	/* write cirtcuit identity  code 3.2.2.2 */
+	/* write cell identifier 3.2.2.17 */
+	/* write chosen channel 3.2.2.33 when BTS picked it */
+	data = msgb_put(msg, 2);
+	data[0] = GSM0808_IE_CHOSEN_CHANNEL;
+	data[1] = chosen_channel;
+
+	/* write chosen encryption algorithm 3.2.2.44 */
+	data = msgb_put(msg, 2);
+	data[0] = GSM0808_IE_CHOSEN_ENCR_ALG;
+	data[1] = encr_alg_id;
+
+	/* write circuit pool 3.2.2.45 */
+	/* write speech version chosen: 3.2.2.51 when BTS picked it */
+	if (speech_mode != 0) {
+		data = msgb_put(msg, 2);
+		data[0] = GSM0808_IE_SPEECH_VERSION;
+		data[1] = speech_mode;
+	}
+
+	/* write LSA identifier 3.2.2.15 */
+
+
+	/* update the size */
+	msg->l3h[1] = msgb_l3len(msg) - 2;
+	return msg;
+}
+
+struct msgb *gsm0808_create_assignment_failure(uint8_t cause, uint8_t *rr_cause)
+{
+	uint8_t *data;
+	struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
+					       "bssmap: ass fail");
+	if (!msg)
+		return NULL;
+
+	msg->l3h = msgb_put(msg, 6);
+	msg->l3h[0] = BSSAP_MSG_BSS_MANAGEMENT;
+	msg->l3h[1] = 0xff;
+	msg->l3h[2] = BSS_MAP_MSG_ASSIGMENT_FAILURE;
+	msg->l3h[3] = GSM0808_IE_CAUSE;
+	msg->l3h[4] = 1;
+	msg->l3h[5] = cause;
+
+	/* RR cause 3.2.2.22 */
+	if (rr_cause) {
+		data = msgb_put(msg, 2);
+		data[0] = GSM0808_IE_RR_CAUSE;
+		data[1] = *rr_cause;
+	}
+
+	/* Circuit pool 3.22.45 */
+	/* Circuit pool list 3.2.2.46 */
+
+	/* update the size */
+	msg->l3h[1] = msgb_l3len(msg) - 2;
+	return msg;
+}
+
+static const struct tlv_definition bss_att_tlvdef = {
+	.def = {
+		[GSM0808_IE_IMSI]		    = { TLV_TYPE_TLV },
+		[GSM0808_IE_TMSI]		    = { TLV_TYPE_TLV },
+		[GSM0808_IE_CELL_IDENTIFIER_LIST]   = { TLV_TYPE_TLV },
+		[GSM0808_IE_CHANNEL_NEEDED]	    = { TLV_TYPE_TV },
+		[GSM0808_IE_EMLPP_PRIORITY]	    = { TLV_TYPE_TV },
+		[GSM0808_IE_CHANNEL_TYPE]	    = { TLV_TYPE_TLV },
+		[GSM0808_IE_PRIORITY]		    = { TLV_TYPE_TLV },
+		[GSM0808_IE_CIRCUIT_IDENTITY_CODE]  = { TLV_TYPE_TV },
+		[GSM0808_IE_DOWNLINK_DTX_FLAG]	    = { TLV_TYPE_TV },
+		[GSM0808_IE_INTERFERENCE_BAND_TO_USE] = { TLV_TYPE_TV },
+		[GSM0808_IE_CLASSMARK_INFORMATION_T2] = { TLV_TYPE_TLV },
+		[GSM0808_IE_GROUP_CALL_REFERENCE]   = { TLV_TYPE_TLV },
+		[GSM0808_IE_TALKER_FLAG]	    = { TLV_TYPE_T },
+		[GSM0808_IE_CONFIG_EVO_INDI]	    = { TLV_TYPE_TV },
+		[GSM0808_IE_LSA_ACCESS_CTRL_SUPPR]  = { TLV_TYPE_TV },
+		[GSM0808_IE_SERVICE_HANDOVER]	    = { TLV_TYPE_TV},
+		[GSM0808_IE_ENCRYPTION_INFORMATION] = { TLV_TYPE_TLV },
+		[GSM0808_IE_CIPHER_RESPONSE_MODE]   = { TLV_TYPE_TV },
+	},
+};
+
+const struct tlv_definition *gsm0808_att_tlvdef()
+{
+	return &bss_att_tlvdef;
+}
diff --git a/libosmocore/src/gsm48.c b/libosmocore/src/gsm48.c
index 5761c67..783ff6a 100644
--- a/libosmocore/src/gsm48.c
+++ b/libosmocore/src/gsm48.c
@@ -97,10 +97,10 @@
 };
 
 /* FIXME: convert to value_string */
-static const char *cc_state_names[32] = {
+static const char *cc_state_names[33] = {
 	"NULL",
 	"INITIATED",
-	"illegal state 2",
+	"MM_CONNECTION_PEND",
 	"MO_CALL_PROC",
 	"CALL_DELIVERED",
 	"illegal state 5",
@@ -261,3 +261,47 @@
 
 	return 2 + buf[1];
 }
+
+/* Convert Mobile Identity (10.5.1.4) to string */
+int gsm48_mi_to_string(char *string, const int str_len, const uint8_t *mi,
+		       const int mi_len)
+{
+	int i;
+	uint8_t mi_type;
+	char *str_cur = string;
+	uint32_t tmsi;
+
+	mi_type = mi[0] & GSM_MI_TYPE_MASK;
+
+	switch (mi_type) {
+	case GSM_MI_TYPE_NONE:
+		break;
+	case GSM_MI_TYPE_TMSI:
+		/* Table 10.5.4.3, reverse generate_mid_from_tmsi */
+		if (mi_len == GSM48_TMSI_LEN && mi[0] == (0xf0 | GSM_MI_TYPE_TMSI)) {
+			memcpy(&tmsi, &mi[1], 4);
+			tmsi = ntohl(tmsi);
+			return snprintf(string, str_len, "%u", tmsi);
+		}
+		break;
+	case GSM_MI_TYPE_IMSI:
+	case GSM_MI_TYPE_IMEI:
+	case GSM_MI_TYPE_IMEISV:
+		*str_cur++ = bcd2char(mi[0] >> 4);
+
+                for (i = 1; i < mi_len; i++) {
+			if (str_cur + 2 >= string + str_len)
+				return str_cur - string;
+			*str_cur++ = bcd2char(mi[i] & 0xf);
+			/* skip last nibble in last input byte when GSM_EVEN */
+			if( (i != mi_len-1) || (mi[0] & GSM_MI_ODD))
+				*str_cur++ = bcd2char(mi[i] >> 4);
+		}
+		break;
+	default:
+		break;
+	}
+	*str_cur++ = '\0';
+
+	return str_cur - string;
+}
diff --git a/libosmocore/src/gsm48_ie.c b/libosmocore/src/gsm48_ie.c
index 4ca5fb8..3c2a1f7 100644
--- a/libosmocore/src/gsm48_ie.c
+++ b/libosmocore/src/gsm48_ie.c
@@ -2,7 +2,7 @@
  * 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>
- * (C) 2008-2010 by Andreas Eversberg
+ * (C) 2009-2010 by Andreas Eversberg
  *
  * All Rights Reserved
  *
diff --git a/libosmocore/src/logging.c b/libosmocore/src/logging.c
index 508ccfd..7c50877 100644
--- a/libosmocore/src/logging.c
+++ b/libosmocore/src/logging.c
@@ -20,11 +20,16 @@
  *
  */
 
+#include "../config.h"
+
 #include <stdarg.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+
+#ifdef HAVE_STRINGS_H
 #include <strings.h>
+#endif
 #include <time.h>
 #include <errno.h>
 
@@ -302,31 +307,46 @@
 	target->categories[category].loglevel = level;
 }
 
+/* since C89/C99 says stderr is a macro, we can safely do this! */
+#ifdef stderr
 static void _stderr_output(struct log_target *target, const char *log)
 {
 	fprintf(target->tgt_stdout.out, "%s", log);
 	fflush(target->tgt_stdout.out);
 }
+#endif
 
 struct log_target *log_target_create(void)
 {
 	struct log_target *target;
+	unsigned int i;
 
 	target = talloc_zero(tall_log_ctx, struct log_target);
 	if (!target)
 		return NULL;
 
 	INIT_LLIST_HEAD(&target->entry);
-	memcpy(target->categories, log_info->cat,
-		sizeof(struct log_category)*log_info->num_cat);
+
+	/* initialize the per-category enabled/loglevel from defaults */
+	for (i = 0; i < log_info->num_cat; i++) {
+		struct log_category *cat = &target->categories[i];
+		cat->enabled = log_info->cat[i].enabled;
+		cat->loglevel = log_info->cat[i].loglevel;
+	}
+
+	/* global settings */
 	target->use_color = 1;
 	target->print_timestamp = 0;
+
+	/* global log level */
 	target->loglevel = 0;
 	return target;
 }
 
 struct log_target *log_target_create_stderr(void)
 {
+/* since C89/C99 says stderr is a macro, we can safely do this! */
+#ifdef stderr
 	struct log_target *target;
 
 	target = log_target_create();
@@ -336,6 +356,9 @@
 	target->tgt_stdout.out = stderr;
 	target->output = _stderr_output;
 	return target;
+#else
+	return NULL;
+#endif /* stderr */
 }
 
 void log_init(const struct log_info *cat)
diff --git a/libosmocore/src/msgb.c b/libosmocore/src/msgb.c
index 60af373..9117a0a 100644
--- a/libosmocore/src/msgb.c
+++ b/libosmocore/src/msgb.c
@@ -80,10 +80,9 @@
 	msg->head = msg->_data;
 	msg->tail = msg->_data;
 
-	msg->bts_link = NULL;
 	msg->trx = NULL;
 	msg->lchan = NULL;
 	msg->l2h = NULL;
 	msg->l3h = NULL;
-	msg->smsh = NULL;
+	msg->l4h = NULL;
 }
diff --git a/libosmocore/src/select.c b/libosmocore/src/select.c
index 9517778..2f6afa7 100644
--- a/libosmocore/src/select.c
+++ b/libosmocore/src/select.c
@@ -121,7 +121,8 @@
 		/* ugly, ugly hack. If more than one filedescriptors were
 		 * unregistered, they might have been consecutive and
 		 * llist_for_each_entry_safe() is no longer safe */
-		if (unregistered_count > 1)
+		/* this seems to happen with the last element of the list as well */
+		if (unregistered_count >= 1)
 			goto restart;
 	}
 	return work;
diff --git a/libosmocore/src/write_queue.c b/libosmocore/src/write_queue.c
index a0ac2d6..618a8c0 100644
--- a/libosmocore/src/write_queue.c
+++ b/libosmocore/src/write_queue.c
@@ -32,6 +32,9 @@
 	if (what & BSC_FD_READ)
 		queue->read_cb(fd);
 
+	if (what & BSC_FD_EXCEPT)
+		queue->except_cb(fd);
+
 	if (what & BSC_FD_WRITE) {
 		struct msgb *msg;