[gsm48] When picking AMR we need to supply the multirate config

On channel mode modify and assignment command when using
the a multirate code the multirate configuration must be
present in the packet.
Add a parameter and add a warning when using it in a
broken way.
diff --git a/openbsc/include/openbsc/gsm_04_08.h b/openbsc/include/openbsc/gsm_04_08.h
index 3e9557b..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 */
@@ -741,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);
 
@@ -759,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/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 08aadf9..053e999 100644
--- a/openbsc/src/gsm_04_08_utils.c
+++ b/openbsc/src/gsm_04_08_utils.c
@@ -504,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));
@@ -533,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));
@@ -561,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;