Merge remote branch 'origin/master'
diff --git a/openbsc/include/openbsc/gsm_04_08.h b/openbsc/include/openbsc/gsm_04_08.h
index 186a53f..5bd860b 100644
--- a/openbsc/include/openbsc/gsm_04_08.h
+++ b/openbsc/include/openbsc/gsm_04_08.h
@@ -33,6 +33,23 @@
 	};
 } __attribute__ ((packed));
 
+/* Chapter 10.5.2.21aa */
+struct gsm48_multi_rate_conf {
+	u_int8_t smod : 2,
+		 spare: 1,
+		 icmi : 1,
+		 nscb : 1,
+		 ver : 3;
+	u_int8_t m4_75 : 1,
+		 m5_15 : 1,
+		 m5_90 : 1,
+		 m6_70 : 1,
+		 m7_40 : 1,
+		 m7_95 : 1,
+		 m10_2 : 1,
+		 m12_2 : 1;
+} __attribute__((packed));
+
 /* Chapter 10.5.2.30 */
 struct gsm48_req_ref {
 	u_int8_t ra;
@@ -411,6 +428,7 @@
 #define GSM_MI_TYPE_TMSI	0x04
 #define GSM_MI_ODD		0x08
 
+#define GSM48_IE_MUL_RATE_CFG	0x03	/* 10.5.2.21aa */
 #define GSM48_IE_MOBILE_ID	0x17
 #define GSM48_IE_NAME_LONG	0x43	/* 10.5.3.5a */
 #define GSM48_IE_NAME_SHORT	0x45	/* 10.5.3.5a */
@@ -634,7 +652,8 @@
 	CHREQ_T_VOICE_CALL_TCH_H,
 	CHREQ_T_DATA_CALL_TCH_H,
 	CHREQ_T_LOCATION_UPD,
-	CHREQ_T_PAG_R_ANY,
+	CHREQ_T_PAG_R_ANY_NECI0,
+	CHREQ_T_PAG_R_ANY_NECI1,
 	CHREQ_T_PAG_R_TCH_F,
 	CHREQ_T_PAG_R_TCH_FH,
 };
@@ -724,8 +743,8 @@
 int gsm0408_rcvmsg(struct msgb *msg, u_int8_t link_id);
 void gsm0408_generate_lai(struct gsm48_loc_area_id *lai48, u_int16_t mcc, 
 		u_int16_t mnc, u_int16_t lac);
-enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra);
-enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra);
+enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra, int neci);
+enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra, int neci);
 
 int gsm48_tx_mm_info(struct gsm_lchan *lchan);
 int gsm48_tx_mm_auth_req(struct gsm_lchan *lchan, u_int8_t *rand);
@@ -740,7 +759,7 @@
 int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv);
 int gsm48_send_rr_app_info(struct gsm_lchan *lchan, u_int8_t apdu_id,
 			   u_int8_t apdu_len, const u_int8_t *apdu);
-int gsm48_send_rr_ass_cmd(struct gsm_lchan *lchan, u_int8_t power_class);
+int gsm48_send_rr_ass_cmd(struct gsm_lchan *lchan, u_int8_t power_class, struct gsm48_multi_rate_conf *conf);
 
 int bsc_upqueue(struct gsm_network *net);
 
@@ -758,7 +777,7 @@
 int gsm48_paging_extract_mi(struct msgb *msg, char *mi_string, u_int8_t *mi_type);
 int gsm48_handle_paging_resp(struct msgb *msg, struct gsm_subscriber *subscr);
 
-int gsm48_lchan_modify(struct gsm_lchan *lchan, u_int8_t lchan_mode);
+int gsm48_lchan_modify(struct gsm_lchan *lchan, u_int8_t lchan_mode, struct gsm48_multi_rate_conf *conf);
 int gsm48_rx_rr_modif_ack(struct msgb *msg);
 
 #endif
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index 3040525..c1b7b05 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -410,6 +410,7 @@
 	char *name_short;
 	enum gsm_auth_policy auth_policy;
 	int a5_encryption;
+	int neci;
 
 	/* layer 4 */
 	int (*mncc_recv) (struct gsm_network *net, int msg_type, void *arg);
diff --git a/openbsc/include/openbsc/telnet_interface.h b/openbsc/include/openbsc/telnet_interface.h
index 97357d7..d3381e0 100644
--- a/openbsc/include/openbsc/telnet_interface.h
+++ b/openbsc/include/openbsc/telnet_interface.h
@@ -35,13 +35,6 @@
 	struct gsm_network *network;
 	struct bsc_fd fd;
 	struct vty *vty;
-
-	int bts;
-
-	int command;
-	char *imsi;
-	char commands[1024];
-	int read;
 };
 
 
diff --git a/openbsc/src/abis_rsl.c b/openbsc/src/abis_rsl.c
index acd41dd..2d00603 100644
--- a/openbsc/src/abis_rsl.c
+++ b/openbsc/src/abis_rsl.c
@@ -1133,13 +1133,14 @@
 
 	/* determine channel type (SDCCH/TCH_F/TCH_H) based on
 	 * request reference RA */
-	lctype = get_ctype_by_chreq(bts, rqd_ref->ra);
-	chreq_reason = get_reason_by_chreq(bts, rqd_ref->ra);
+	lctype = get_ctype_by_chreq(bts, rqd_ref->ra, bts->network->neci);
+	chreq_reason = get_reason_by_chreq(bts, rqd_ref->ra, bts->network->neci);
 
 	/* check availability / allocate channel */
 	lchan = lchan_alloc(bts, lctype);
 	if (!lchan) {
-		fprintf(stderr, "CHAN RQD: no resources\n");
+		DEBUGP(DRSL, "CHAN RQD: no resources for %u 0x%x\n",
+			lctype, rqd_ref->ra);
 		/* FIXME: send some kind of reject ?!? */
 		return -ENOMEM;
 	}
diff --git a/openbsc/src/bsc_init.c b/openbsc/src/bsc_init.c
index bb1e382..d11cde5 100644
--- a/openbsc/src/bsc_init.c
+++ b/openbsc/src/bsc_init.c
@@ -956,6 +956,10 @@
 	type_4->cell_sel_par.ms_txpwr_max_ccch =
 			ms_pwr_ctl_lvl(bts->band, bts->ms_max_power);
 
+	/* Set NECI to influence channel request */
+	type_3->cell_sel_par.neci = bts->network->neci;
+	type_4->cell_sel_par.neci = bts->network->neci;
+
 	if (bts->cell_barred) {
 		type_1->rach_control.cell_bar = 1;
 		type_2->rach_control.cell_bar = 1;
diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c
index 6a4abfc..7a8bcbd 100644
--- a/openbsc/src/gsm_04_08.c
+++ b/openbsc/src/gsm_04_08.c
@@ -3139,7 +3139,7 @@
 {
 	struct gsm_mncc *mode = arg;
 
-	return gsm48_lchan_modify(trans->lchan, mode->lchan_mode);
+	return gsm48_lchan_modify(trans->lchan, mode->lchan_mode, NULL);
 }
 
 static struct downstate {
diff --git a/openbsc/src/gsm_04_08_utils.c b/openbsc/src/gsm_04_08_utils.c
index 2545f33..053e999 100644
--- a/openbsc/src/gsm_04_08_utils.c
+++ b/openbsc/src/gsm_04_08_utils.c
@@ -255,7 +255,7 @@
 	{ 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H },
 	{ 0x00, 0xf0, CHREQ_T_LOCATION_UPD },
 	{ 0x10, 0xf0, CHREQ_T_SDCCH },
-	{ 0x80, 0xe0, CHREQ_T_PAG_R_ANY },
+	{ 0x80, 0xe0, CHREQ_T_PAG_R_ANY_NECI1 },
 	{ 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F },
 	{ 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH },
 };
@@ -267,7 +267,7 @@
 	{ 0xe0, 0xe0, CHREQ_T_TCH_F },
 	{ 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H },
 	{ 0x00, 0xe0, CHREQ_T_LOCATION_UPD },
-	{ 0x80, 0xe0, CHREQ_T_PAG_R_ANY },
+	{ 0x80, 0xe0, CHREQ_T_PAG_R_ANY_NECI0 },
 	{ 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F },
 	{ 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH },
 };
@@ -282,7 +282,8 @@
 	[CHREQ_T_VOICE_CALL_TCH_H]	= GSM_LCHAN_TCH_H,
 	[CHREQ_T_DATA_CALL_TCH_H]	= GSM_LCHAN_TCH_H,
 	[CHREQ_T_LOCATION_UPD]		= GSM_LCHAN_SDCCH,
-	[CHREQ_T_PAG_R_ANY]		= GSM_LCHAN_SDCCH,
+	[CHREQ_T_PAG_R_ANY_NECI1]	= GSM_LCHAN_SDCCH,
+	[CHREQ_T_PAG_R_ANY_NECI0]	= GSM_LCHAN_SDCCH,
 	[CHREQ_T_PAG_R_TCH_F]		= GSM_LCHAN_TCH_F,
 	[CHREQ_T_PAG_R_TCH_FH]		= GSM_LCHAN_TCH_F,
 };
@@ -297,18 +298,29 @@
 	[CHREQ_T_VOICE_CALL_TCH_H]	= GSM_CHREQ_REASON_OTHER,
 	[CHREQ_T_DATA_CALL_TCH_H]	= GSM_CHREQ_REASON_OTHER,
 	[CHREQ_T_LOCATION_UPD]		= GSM_CHREQ_REASON_LOCATION_UPD,
-	[CHREQ_T_PAG_R_ANY]		= GSM_CHREQ_REASON_PAG,
+	[CHREQ_T_PAG_R_ANY_NECI1]	= GSM_CHREQ_REASON_PAG,
+	[CHREQ_T_PAG_R_ANY_NECI0]	= GSM_CHREQ_REASON_PAG,
 	[CHREQ_T_PAG_R_TCH_F]		= GSM_CHREQ_REASON_PAG,
 	[CHREQ_T_PAG_R_TCH_FH]		= GSM_CHREQ_REASON_PAG,
 };
 
-enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra)
+enum gsm_chan_t get_ctype_by_chreq(struct gsm_bts *bts, u_int8_t ra, int neci)
 {
 	int i;
-	/* FIXME: determine if we set NECI = 0 in the BTS SI4 */
+	int length;
+	const struct chreq *chreq;
 
-	for (i = 0; i < ARRAY_SIZE(chreq_type_neci0); i++) {
-		const struct chreq *chr = &chreq_type_neci0[i];
+	if (neci) {
+		chreq = chreq_type_neci1;
+		length = ARRAY_SIZE(chreq_type_neci1);
+	} else {
+		chreq = chreq_type_neci0;
+		length = ARRAY_SIZE(chreq_type_neci0);
+	}
+
+
+	for (i = 0; i < length; i++) {
+		const struct chreq *chr = &chreq[i];
 		if ((ra & chr->mask) == chr->val)
 			return ctype_by_chreq[chr->type];
 	}
@@ -316,13 +328,22 @@
 	return GSM_LCHAN_SDCCH;
 }
 
-enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra)
+enum gsm_chreq_reason_t get_reason_by_chreq(struct gsm_bts *bts, u_int8_t ra, int neci)
 {
 	int i;
-	/* FIXME: determine if we set NECI = 0 in the BTS SI4 */
+	int length;
+	const struct chreq *chreq;
 
-	for (i = 0; i < ARRAY_SIZE(chreq_type_neci0); i++) {
-		const struct chreq *chr = &chreq_type_neci0[i];
+	if (neci) {
+		chreq = chreq_type_neci1;
+		length = ARRAY_SIZE(chreq_type_neci1);
+	} else {
+		chreq = chreq_type_neci0;
+		length = ARRAY_SIZE(chreq_type_neci0);
+	}
+
+	for (i = 0; i < length; i++) {
+		const struct chreq *chr = &chreq[i];
 		if ((ra & chr->mask) == chr->val)
 			return reason_by_chreq[chr->type];
 	}
@@ -483,7 +504,8 @@
 }
 
 /* Chapter 9.1.2: Assignment Command */
-int gsm48_send_rr_ass_cmd(struct gsm_lchan *lchan, u_int8_t power_command)
+int gsm48_send_rr_ass_cmd(struct gsm_lchan *lchan, u_int8_t power_command,
+			  struct gsm48_multi_rate_conf *conf)
 {
 	struct msgb *msg = gsm48_msgb_alloc();
 	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
@@ -512,11 +534,24 @@
 	ass->chan_desc.h0.arfcn_low = arfcn & 0xff;
 	ass->power_command = power_command;
 
+	/* in case of multi rate we need to attach a config */
+	if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) {
+		if (!conf) {
+			DEBUGP(DRR, "BUG: Using multirate codec without multirate config.\n");
+		} else {
+			u_int8_t *data = msgb_put(msg, 4);
+			data[0] = GSM48_IE_MUL_RATE_CFG;
+			data[1] = 0x2;
+			memcpy(&data[2], conf, 2);
+		}
+	}
+
 	return gsm48_sendmsg(msg, NULL);
 }
 
 /* 9.1.5 Channel mode modify: Modify the mode on the MS side */
-int gsm48_tx_chan_mode_modify(struct gsm_lchan *lchan, u_int8_t mode)
+int gsm48_tx_chan_mode_modify(struct gsm_lchan *lchan, u_int8_t mode,
+			      struct gsm48_multi_rate_conf *conf)
 {
 	struct msgb *msg = gsm48_msgb_alloc();
 	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
@@ -540,14 +575,27 @@
 	cmm->chan_desc.h0.arfcn_low = arfcn & 0xff;
 	cmm->mode = mode;
 
+	/* in case of multi rate we need to attach a config */
+	if (mode == GSM48_CMODE_SPEECH_AMR) {
+		if (!conf) {
+			DEBUGP(DRR, "BUG: Using multirate codec without multirate config.\n");
+		} else {
+			u_int8_t *data = msgb_put(msg, 4);
+			data[0] = GSM48_IE_MUL_RATE_CFG;
+			data[1] = 0x2;
+			memcpy(&data[2], conf, 2);
+		}
+	}
+
 	return gsm48_sendmsg(msg, NULL);
 }
 
-int gsm48_lchan_modify(struct gsm_lchan *lchan, u_int8_t lchan_mode)
+int gsm48_lchan_modify(struct gsm_lchan *lchan, u_int8_t lchan_mode,
+		       struct gsm48_multi_rate_conf *conf)
 {
 	int rc;
 
-	rc = gsm48_tx_chan_mode_modify(lchan, lchan_mode);
+	rc = gsm48_tx_chan_mode_modify(lchan, lchan_mode, conf);
 	if (rc < 0)
 		return rc;
 
diff --git a/openbsc/src/input/ipaccess.c b/openbsc/src/input/ipaccess.c
index 34d9462..2d9f51e 100644
--- a/openbsc/src/input/ipaccess.c
+++ b/openbsc/src/input/ipaccess.c
@@ -543,7 +543,7 @@
 
 	/* Some BTS has connected to us, but we don't know yet which line
 	 * (as created by the OML link) to associate it with.  Thus, we
-	 * aloocate a temporary bfd until we have received ID from BTS */
+	 * allocate a temporary bfd until we have received ID from BTS */
 
 	bfd->fd = accept(listen_bfd->fd, (struct sockaddr *) &sa, &sa_len);
 	if (bfd->fd < 0) {
diff --git a/openbsc/src/ipaccess-config.c b/openbsc/src/ipaccess-config.c
index 1b2aa5e..c50a465 100644
--- a/openbsc/src/ipaccess-config.c
+++ b/openbsc/src/ipaccess-config.c
@@ -279,7 +279,7 @@
 	printf("  -o --oml-ip ip\n");
 	printf("  -r --restart\n");
 	printf("  -n flags/mask\tSet NVRAM attributes.\n");
-	printf("  -l --listen testnr \tPerform speciified test number\n");
+	printf("  -l --listen testnr \tPerform specified test number\n");
 	printf("  -h --help this text\n");
 	printf("  -s --stream-id ID\n");
 }
diff --git a/openbsc/src/paging.c b/openbsc/src/paging.c
index 87c7e7d..69902e8 100644
--- a/openbsc/src/paging.c
+++ b/openbsc/src/paging.c
@@ -197,6 +197,8 @@
 {
 	struct gsm_paging_request *req = (struct gsm_paging_request *)data;
 	struct paging_signal_data sig_data;
+	void *cbfn_param;
+	gsm_cbfn *cbfn;
 
 	DEBUGP(DPAG, "T3113 expired for request %p (%s)\n",
 		req, req->subscr->imsi);
@@ -205,11 +207,15 @@
 	sig_data.bts	= req->bts;
 	sig_data.lchan	= NULL;
 
-	dispatch_signal(SS_PAGING, S_PAGING_COMPLETED, &sig_data);
-	if (req->cbfn)
-		req->cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_EXPIRED, NULL, NULL,
-			  req->cbfn_param);
+	/* must be destroyed before calling cbfn, to prevent double free */
+	cbfn_param = req->cbfn_param;
+	cbfn = req->cbfn;
 	paging_remove_request(&req->bts->paging, req);
+
+	dispatch_signal(SS_PAGING, S_PAGING_COMPLETED, &sig_data);
+	if (cbfn)
+		cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_EXPIRED, NULL, NULL,
+			  cbfn_param);
 }
 
 static int _paging_request(struct gsm_bts *bts, struct gsm_subscriber *subscr,
diff --git a/openbsc/src/telnet_interface.c b/openbsc/src/telnet_interface.c
index d7c9055..128c34e 100644
--- a/openbsc/src/telnet_interface.c
+++ b/openbsc/src/telnet_interface.c
@@ -165,7 +165,6 @@
 	connection->fd.fd = new_connection;
 	connection->fd.when = BSC_FD_READ;
 	connection->fd.cb = client_data;
-	connection->bts = 0;
 	bsc_register_fd(&connection->fd);
 	llist_add_tail(&connection->entry, &active_connections);
 
diff --git a/openbsc/src/vty_interface.c b/openbsc/src/vty_interface.c
index f1e35e8..7ac0216 100644
--- a/openbsc/src/vty_interface.c
+++ b/openbsc/src/vty_interface.c
@@ -87,6 +87,8 @@
 		gsm_auth_policy_name(net->auth_policy), VTY_NEWLINE);
 	vty_out(vty, "  Encryption: A5/%u%s", net->a5_encryption,
 		VTY_NEWLINE);
+	vty_out(vty, "  NECI (TCH/H): %u%s", net->neci,
+		VTY_NEWLINE);
 }
 
 DEFUN(show_net, show_net_cmd, "show network",
@@ -784,11 +786,20 @@
       "encryption a5 (0|1|2)",
       "Enable or disable encryption (A5) for this network\n")
 {
-	gsmnet->auth_policy = atoi(argv[0]);
+	gsmnet->a5_encryption= atoi(argv[0]);
 
 	return CMD_SUCCESS;
 }
 
+DEFUN(cfg_net_neci,
+      cfg_net_neci_cmd,
+      "neci (0|1)",
+      "Set if NECI of cell selection is to be set")
+{
+	gsmnet->neci = atoi(argv[0]);
+	return CMD_SUCCESS;
+}
+
 /* per-BTS configuration */
 DEFUN(cfg_bts,
       cfg_bts_cmd,
@@ -1228,6 +1239,7 @@
 	install_element(GSMNET_NODE, &cfg_net_name_long_cmd);
 	install_element(GSMNET_NODE, &cfg_net_auth_policy_cmd);
 	install_element(GSMNET_NODE, &cfg_net_encryption_cmd);
+	install_element(GSMNET_NODE, &cfg_net_neci_cmd);
 
 	install_element(GSMNET_NODE, &cfg_bts_cmd);
 	install_node(&bts_node, config_write_bts);