Merge branch 'zecke/features/amr'

Fix AMR support for the BSC, remove code duplication, make it
less prone to error.
diff --git a/openbsc/include/openbsc/bsc_api.h b/openbsc/include/openbsc/bsc_api.h
index a70d765..a3d12f2 100644
--- a/openbsc/include/openbsc/bsc_api.h
+++ b/openbsc/include/openbsc/bsc_api.h
@@ -40,7 +40,7 @@
 	 * not implemented AMR5.9 will be used.
 	 */
 	void (*mr_config)(struct gsm_subscriber_connection *conn,
-			  uint8_t *mr_ms_lv, uint8_t *mr_bts_lv);
+				struct gsm_lchan *lchan, int full_rate);
 };
 
 int bsc_api_init(struct gsm_network *network, struct bsc_api *api);
diff --git a/openbsc/include/openbsc/gsm_04_08.h b/openbsc/include/openbsc/gsm_04_08.h
index 02b2e3b..02d67f7 100644
--- a/openbsc/include/openbsc/gsm_04_08.h
+++ b/openbsc/include/openbsc/gsm_04_08.h
@@ -14,6 +14,7 @@
 struct gsm_trans;
 struct gsm_subscriber_connection;
 struct amr_multirate_conf;
+struct amr_mode;
 
 #define GSM48_ALLOC_SIZE	2048
 #define GSM48_ALLOC_HEADROOM	256
@@ -90,6 +91,6 @@
 void release_security_operation(struct gsm_subscriber_connection *conn);
 void allocate_security_operation(struct gsm_subscriber_connection *conn);
 
-int gsm48_multirate_config(uint8_t *lv, struct amr_multirate_conf *mr, int ms);
+int gsm48_multirate_config(uint8_t *lv, struct amr_multirate_conf *mr, struct amr_mode *modes);
 
 #endif
diff --git a/openbsc/include/openbsc/gsm_data_shared.h b/openbsc/include/openbsc/gsm_data_shared.h
index 5ff6c20..be3333c 100644
--- a/openbsc/include/openbsc/gsm_data_shared.h
+++ b/openbsc/include/openbsc/gsm_data_shared.h
@@ -150,14 +150,14 @@
 
 struct amr_mode {
 	uint8_t mode;
-	uint8_t threshold_ms;
-	uint8_t hysteresis_ms;
-	uint8_t threshold_bts;
-	uint8_t hysteresis_bts;
+	uint8_t threshold;
+	uint8_t hysteresis;
 };
+
 struct amr_multirate_conf {
 	uint8_t gsm48_ie[2];
-	struct amr_mode mode[4];
+	struct amr_mode ms_mode[4];
+	struct amr_mode bts_mode[4];
 	uint8_t num_modes;
 };
 /* /BTS ONLY */
diff --git a/openbsc/src/libbsc/abis_rsl.c b/openbsc/src/libbsc/abis_rsl.c
index 526b977..8e9258c 100644
--- a/openbsc/src/libbsc/abis_rsl.c
+++ b/openbsc/src/libbsc/abis_rsl.c
@@ -442,6 +442,13 @@
 	return 0;
 }
 
+static void mr_config_for_bts(struct gsm_lchan *lchan, struct msgb *msg)
+{
+	if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR)
+		msgb_tlv_put(msg, RSL_IE_MR_CONFIG, lchan->mr_bts_lv[0],
+			     lchan->mr_bts_lv + 1);
+}
+
 /* Chapter 8.4.1 */
 int rsl_chan_activate_lchan(struct gsm_lchan *lchan, uint8_t act_type,
 			    uint8_t ho_ref)
@@ -518,10 +525,7 @@
 	msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power);
 	msgb_tv_put(msg, RSL_IE_MS_POWER, lchan->ms_power);
 	msgb_tv_put(msg, RSL_IE_TIMING_ADVANCE, ta);
-
-	if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR)
-		msgb_tlv_put(msg, RSL_IE_MR_CONFIG, lchan->mr_bts_lv[0],
-			     lchan->mr_bts_lv + 1);
+	mr_config_for_bts(lchan, msg);
 
 	msg->dst = lchan->ts->trx->rsl_link;
 
@@ -557,11 +561,7 @@
 			msgb_tlv_put(msg, RSL_IE_ENCR_INFO, rc, encr_info);
 	}
 
-	if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR)
-{
-		msgb_tlv_put(msg, RSL_IE_MR_CONFIG, lchan->mr_bts_lv[0],
-			     lchan->mr_bts_lv + 1);
-}
+	mr_config_for_bts(lchan, msg);
 
 	msg->dst = lchan->ts->trx->rsl_link;
 
diff --git a/openbsc/src/libbsc/bsc_api.c b/openbsc/src/libbsc/bsc_api.c
index 78e7c87..504f044 100644
--- a/openbsc/src/libbsc/bsc_api.c
+++ b/openbsc/src/libbsc/bsc_api.c
@@ -163,7 +163,7 @@
 	struct gsm48_multi_rate_conf *mr_conf;
 
 	if (api->mr_config)
-		return api->mr_config(conn, lchan->mr_ms_lv, lchan->mr_bts_lv);
+		return api->mr_config(conn, lchan, full_rate);
 
 	if (full_rate)
 		mr = &lchan->ts->trx->bts->mr_full;
@@ -178,8 +178,8 @@
 		mr_conf->icmi = 1;
 		mr_conf->m5_90 = 1;
 	}
-	gsm48_multirate_config(lchan->mr_ms_lv, mr, 1);
-	gsm48_multirate_config(lchan->mr_bts_lv, mr, 0);
+	gsm48_multirate_config(lchan->mr_ms_lv, mr, mr->ms_mode);
+	gsm48_multirate_config(lchan->mr_bts_lv, mr, mr->bts_mode);
 }
 
 /*
diff --git a/openbsc/src/libbsc/bsc_vty.c b/openbsc/src/libbsc/bsc_vty.c
index 9b0f020..f8ef833 100644
--- a/openbsc/src/libbsc/bsc_vty.c
+++ b/openbsc/src/libbsc/bsc_vty.c
@@ -479,6 +479,21 @@
 		config_write_trx_single(vty, trx);
 }
 
+static void write_amr_modes(struct vty *vty, const char *prefix,
+	const char *name, struct amr_mode *modes, int num)
+{
+	int i;
+
+	vty_out(vty, "  %s threshold %s", prefix, name);
+	for (i = 0; i < num - 1; i++)
+		vty_out(vty, " %d", modes[i].threshold);
+	vty_out(vty, "%s", VTY_NEWLINE);
+	vty_out(vty, "  %s hysteresis %s", prefix, name);
+	for (i = 0; i < num - 1; i++)
+		vty_out(vty, " %d", modes[i].hysteresis);
+	vty_out(vty, "%s", VTY_NEWLINE);
+}
+
 static void config_write_bts_amr(struct vty *vty, struct gsm_bts *bts,
 	struct amr_multirate_conf *mr, int full)
 {
@@ -503,26 +518,8 @@
 	if (num > 4)
 		num = 4;
 	if (num > 1) {
-		vty_out(vty, "  %s threshold ms", prefix);
-		for (i = 0; i < num - 1; i++) {
-			vty_out(vty, " %d", mr->mode[i].threshold_ms);
-		}
-		vty_out(vty, "%s", VTY_NEWLINE);
-		vty_out(vty, "  %s hysteresis ms", prefix);
-		for (i = 0; i < num - 1; i++) {
-			vty_out(vty, " %d", mr->mode[i].hysteresis_ms);
-		}
-		vty_out(vty, "%s", VTY_NEWLINE);
-		vty_out(vty, "  %s threshold bts", prefix);
-		for (i = 0; i < num - 1; i++) {
-			vty_out(vty, " %d", mr->mode[i].threshold_bts);
-		}
-		vty_out(vty, "%s", VTY_NEWLINE);
-		vty_out(vty, "  %s hysteresis bts", prefix);
-		for (i = 0; i < num - 1; i++) {
-			vty_out(vty, " %d", mr->mode[i].hysteresis_bts);
-		}
-		vty_out(vty, "%s", VTY_NEWLINE);
+		write_amr_modes(vty, prefix, "ms", mr->ms_mode, num);
+		write_amr_modes(vty, prefix, "bts", mr->bts_mode, num);
 	}
 	vty_out(vty, "  %s start-mode ", prefix);
 	if (mr_conf->icmi) {
@@ -2953,30 +2950,24 @@
 {
 	struct gsm_bts *bts = vty->index;
 	struct amr_multirate_conf *mr = (full) ? &bts->mr_full: &bts->mr_half;
+	struct amr_mode *modes;
 	int i;
 
-	if (argv[0][0]=='m') {
-		for (i = 0; i < argc - 1; i++)
-			mr->mode[i].threshold_ms = atoi(argv[i + 1]);
-	} else {
-		for (i = 0; i < argc - 1; i++)
-			mr->mode[i].threshold_bts = atoi(argv[i + 1]);
-	}
+	modes = argv[0][0]=='m' ? mr->ms_mode : mr->bts_mode;
+	for (i = 0; i < argc - 1; i++)
+		modes[i].threshold = atoi(argv[i + 1]);
 }
 
 static void get_amr_hy_from_arg(struct vty *vty, int argc, const char *argv[], int full)
 {
 	struct gsm_bts *bts = vty->index;
 	struct amr_multirate_conf *mr = (full) ? &bts->mr_full: &bts->mr_half;
+	struct amr_mode *modes;
 	int i;
 
-	if (argv[0][0]=='m') {
-		for (i = 0; i < argc - 1; i++)
-			mr->mode[i].hysteresis_ms = atoi(argv[i + 1]);
-	} else {
-		for (i = 0; i < argc - 1; i++)
-			mr->mode[i].hysteresis_bts = atoi(argv[i + 1]);
-	}
+	modes = argv[0][0]=='m' ? mr->ms_mode : mr->bts_mode;
+	for (i = 0; i < argc - 1; i++)
+		modes[i].hysteresis = atoi(argv[i + 1]);
 }
 
 static void get_amr_start_from_arg(struct vty *vty, const char *argv[], int full)
diff --git a/openbsc/src/libbsc/gsm_04_08_utils.c b/openbsc/src/libbsc/gsm_04_08_utils.c
index ef82555..4901912 100644
--- a/openbsc/src/libbsc/gsm_04_08_utils.c
+++ b/openbsc/src/libbsc/gsm_04_08_utils.c
@@ -214,6 +214,13 @@
 	return GSM_CHREQ_REASON_OTHER;
 }
 
+static void mr_config_for_ms(struct gsm_lchan *lchan, struct msgb *msg)
+{
+	if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR)
+		msgb_tlv_put(msg, GSM48_IE_MUL_RATE_CFG, lchan->mr_ms_lv[0],
+			lchan->mr_ms_lv + 1);
+}
+
 /* 7.1.7 and 9.1.7: RR CHANnel RELease */
 int gsm48_send_rr_release(struct gsm_lchan *lchan)
 {
@@ -357,7 +364,7 @@
 	}
 }
 
-int gsm48_multirate_config(uint8_t *lv, struct amr_multirate_conf *mr, int ms)
+int gsm48_multirate_config(uint8_t *lv, struct amr_multirate_conf *mr, struct amr_mode *modes)
 {
 	int num = 0, i;
 
@@ -380,33 +387,19 @@
 	memcpy(lv + 1, mr->gsm48_ie, 2);
 	if (num == 1)
 		return 0;
-	if (ms) {
-		lv[3] = mr->mode[0].threshold_ms & 0x3f;
-		lv[4] = mr->mode[0].hysteresis_ms << 4;
-		if (num == 2)
-			return 0;
-		lv[4] |= (mr->mode[1].threshold_ms & 0x3f) >> 2;
-		lv[5] = mr->mode[1].threshold_ms << 6;
-		lv[5] |= (mr->mode[1].hysteresis_ms & 0x0f) << 2;
-		if (num == 3)
-			return 0;
-		lv[5] |= (mr->mode[2].threshold_ms & 0x3f) >> 4;
-		lv[6] = mr->mode[2].threshold_ms << 4;
-		lv[6] |= mr->mode[2].hysteresis_ms & 0x0f;
-	} else {
-		lv[3] = mr->mode[0].threshold_bts & 0x3f;
-		lv[4] = mr->mode[0].hysteresis_bts << 4;
-		if (num == 2)
-			return 0;
-		lv[4] |= (mr->mode[1].threshold_bts & 0x3f) >> 2;
-		lv[5] = mr->mode[1].threshold_bts << 6;
-		lv[5] |= (mr->mode[1].hysteresis_bts & 0x0f) << 2;
-		if (num == 3)
-			return 0;
-		lv[5] |= (mr->mode[2].threshold_bts & 0x3f) >> 4;
-		lv[6] = mr->mode[2].threshold_bts << 4;
-		lv[6] |= mr->mode[2].hysteresis_bts & 0x0f;
-	}
+
+	lv[3] = modes[0].threshold & 0x3f;
+	lv[4] = modes[0].hysteresis << 4;
+	if (num == 2)
+		return 0;
+	lv[4] |= (modes[1].threshold & 0x3f) >> 2;
+	lv[5] = modes[1].threshold << 6;
+	lv[5] |= (modes[1].hysteresis & 0x0f) << 2;
+	if (num == 3)
+		return 0;
+	lv[5] |= (modes[2].threshold & 0x3f) >> 4;
+	lv[6] = modes[2].threshold << 4;
+	lv[6] |= modes[2].hysteresis & 0x0f;
 
 	return 0;
 }
@@ -489,9 +482,7 @@
 	}
 
 	/* in case of multi rate we need to attach a config */
-	if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR)
-		msgb_tlv_put(msg, GSM48_IE_MUL_RATE_CFG, lchan->mr_ms_lv[0],
-			lchan->mr_ms_lv + 1);
+	mr_config_for_ms(lchan, msg);
 
 	return gsm48_sendmsg(msg);
 }
@@ -517,9 +508,7 @@
 	cmm->mode = mode;
 
 	/* in case of multi rate we need to attach a config */
-	if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR)
-		msgb_tlv_put(msg, GSM48_IE_MUL_RATE_CFG, lchan->mr_ms_lv[0],
-			lchan->mr_ms_lv + 1);
+	mr_config_for_ms(lchan, msg);
 
 	return gsm48_sendmsg(msg);
 }
diff --git a/openbsc/src/osmo-bsc/osmo_bsc_api.c b/openbsc/src/osmo-bsc/osmo_bsc_api.c
index 00a10b3..fbeed77 100644
--- a/openbsc/src/osmo-bsc/osmo_bsc_api.c
+++ b/openbsc/src/osmo-bsc/osmo_bsc_api.c
@@ -491,9 +491,10 @@
 }
 
 static void bsc_mr_config(struct gsm_subscriber_connection *conn,
-			  struct gsm48_multi_rate_conf *conf)
+				struct gsm_lchan *lchan, int full_rate)
 {
 	struct osmo_msc_data *msc;
+	struct gsm48_multi_rate_conf *ms_conf, *bts_conf;
 
 	if (!conn->sccp_con) {
 		LOGP(DMSC, LOGL_ERROR,
@@ -504,18 +505,31 @@
 
 	msc = conn->sccp_con->msc;
 
-	conf->ver = 1;
-	conf->icmi = 1;
+	/* initialize the data structure */
+	lchan->mr_ms_lv[0] = sizeof(*ms_conf);
+	lchan->mr_bts_lv[0] = sizeof(*bts_conf);
+	ms_conf = (struct gsm48_multi_rate_conf *) &lchan->mr_ms_lv[1];
+	bts_conf = (struct gsm48_multi_rate_conf *) &lchan->mr_bts_lv[1];
+	memset(ms_conf, 0, sizeof(*ms_conf));
+	memset(bts_conf, 0, sizeof(*bts_conf));
+
+	bts_conf->ver = ms_conf->ver = 1;
+	bts_conf->icmi = ms_conf->icmi = 1;
 
 	/* maybe gcc see's it is copy of _one_ byte */
-	conf->m4_75 = msc->amr_conf.m4_75;
-	conf->m5_15 = msc->amr_conf.m5_15;
-	conf->m5_90 = msc->amr_conf.m5_90;
-	conf->m6_70 = msc->amr_conf.m6_70;
-	conf->m7_40 = msc->amr_conf.m7_40;
-	conf->m7_95 = msc->amr_conf.m7_95;
-	conf->m10_2 = msc->amr_conf.m10_2;
-	conf->m12_2 = msc->amr_conf.m12_2;
+	bts_conf->m4_75 = ms_conf->m4_75 = msc->amr_conf.m4_75;
+	bts_conf->m5_15 = ms_conf->m5_15 = msc->amr_conf.m5_15;
+	bts_conf->m5_90 = ms_conf->m5_90 = msc->amr_conf.m5_90;
+	bts_conf->m6_70 = ms_conf->m6_70 = msc->amr_conf.m6_70;
+	bts_conf->m7_40 = ms_conf->m7_40 = msc->amr_conf.m7_40;
+	bts_conf->m7_95 = ms_conf->m7_95 = msc->amr_conf.m7_95;
+	if (full_rate) {
+		bts_conf->m10_2 = ms_conf->m10_2 = msc->amr_conf.m10_2;
+		bts_conf->m12_2 = ms_conf->m12_2 = msc->amr_conf.m12_2;
+	}
+
+	/* now copy this into the bts structure */
+	memcpy(lchan->mr_bts_lv, lchan->mr_ms_lv, sizeof(lchan->mr_ms_lv));
 }
 
 static struct bsc_api bsc_handler = {