[BSC] Hopping: Add MA IE to ASS CMD, IMM ASS CMD and HO CMD

Also, make sure the bit ordering in the pre-computed MA is correct,
as well as the cell channel description of the target cell being
present in the HO CMD.
diff --git a/openbsc/src/abis_rsl.c b/openbsc/src/abis_rsl.c
index f387354..e47900d 100644
--- a/openbsc/src/abis_rsl.c
+++ b/openbsc/src/abis_rsl.c
@@ -1203,7 +1203,10 @@
 	if (!lchan->ts->hopping.enabled) {
 		ia.mob_alloc_len = 0;
 	} else {
-		/* FIXME: Mobile Allocation in case of hopping */
+		uint8_t *ma;
+		ia.mob_alloc_len = lchan->ts->hopping.ma_len;
+		ma = msgb_put(msg, ia.mob_alloc_len);
+		memcpy(ma, lchan->ts->hopping.ma_data, ia.mob_alloc_len);
 	}
 
 	DEBUGP(DRSL, "%s Activating ARFCN(%u) SS(%u) lctype %s "
diff --git a/openbsc/src/bsc_init.c b/openbsc/src/bsc_init.c
index 0066811..8e4aafd 100644
--- a/openbsc/src/bsc_init.c
+++ b/openbsc/src/bsc_init.c
@@ -940,32 +940,51 @@
 	struct bitvec *cell_chan = &ts->trx->bts->si_common.cell_alloc;
 	struct bitvec *ts_arfcn = &ts->hopping.arfcns;
 	struct bitvec *ma = &ts->hopping.ma;
+	unsigned int num_cell_arfcns, bitnum, n_chan;
 	int i;
 
 	/* re-set the MA to all-zero */
 	ma->cur_bit = 0;
+	ts->hopping.ma_len = 0;
 	memset(ma->data, 0, ma->data_len);
 
 	if (!ts->hopping.enabled)
 		return 0;
 
+	/* count the number of ARFCNs in the cell channel allocation */
+	num_cell_arfcns = 0;
+	for (i = 1; i < 1024; i++) {
+		if (bitvec_get_bit_pos(cell_chan, i))
+			num_cell_arfcns++;
+	}
+
+	/* pad it to octet-aligned number of bits */
+	ts->hopping.ma_len = num_cell_arfcns / 8;
+	if (num_cell_arfcns % 8)
+		ts->hopping.ma_len++;
+
+	n_chan = 0;
 	for (i = 1; i < 1024; i++) {
 		if (!bitvec_get_bit_pos(cell_chan, i))
 			continue;
-		/* append a bit to the MA */
+		n_chan++;
+		/* set the corresponding bit in the MA */
+		bitnum = (ts->hopping.ma_len * 8) - 1 - n_chan;
 		if (bitvec_get_bit_pos(ts_arfcn, i))
-			bitvec_set_bit(ma, 1);
+			bitvec_set_bit_pos(ma, bitnum, 1);
 		else
-			bitvec_set_bit(ma, 0);
+			bitvec_set_bit_pos(ma, bitnum, 0);
 	}
 
 	/* ARFCN 0 is special: It is coded last in the bitmask */
 	if (bitvec_get_bit_pos(cell_chan, 0)) {
-		/* append a bit to the MA */
+		n_chan++;
+		/* set the corresponding bit in the MA */
+		bitnum = (ts->hopping.ma_len * 8) - 1 - n_chan;
 		if (bitvec_get_bit_pos(ts_arfcn, 0))
-			bitvec_set_bit(ma, 1);
+			bitvec_set_bit_pos(ma, bitnum, 1);
 		else
-			bitvec_set_bit(ma, 0);
+			bitvec_set_bit_pos(ma, bitnum, 0);
 	}
 
 	return 0;
diff --git a/openbsc/src/gsm_04_08_utils.c b/openbsc/src/gsm_04_08_utils.c
index f517020..0b009d1 100644
--- a/openbsc/src/gsm_04_08_utils.c
+++ b/openbsc/src/gsm_04_08_utils.c
@@ -332,6 +332,8 @@
 	}
 }
 
+#define GSM48_HOCMD_CCHDESC_LEN	16
+
 /* Chapter 9.1.15: Handover Command */
 int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan,
 		      u_int8_t power_command, u_int8_t ho_ref)
@@ -351,6 +353,22 @@
 	ho->ho_ref = ho_ref;
 	ho->power_command = power_command;
 
+	if (new_lchan->ts->hopping.enabled) {
+		struct gsm_bts *bts = new_lchan->ts->trx->bts;
+		struct gsm48_system_information_type_1 *si1;
+		uint8_t *cur;
+
+		si1 = GSM_BTS_SI(bts, SYSINFO_TYPE_1);
+		/* Copy the Cell Chan Desc (ARFCNS in this cell) */
+		msgb_put_u8(msg, GSM48_IE_CELL_CH_DESC);
+		cur = msgb_put(msg, GSM48_HOCMD_CCHDESC_LEN);
+		memcpy(cur, si1->cell_channel_description,
+			GSM48_HOCMD_CCHDESC_LEN);
+		/* Copy the Mobile Allocation */
+		msgb_tlv_put(msg, GSM48_IE_MA_BEFORE,
+			     new_lchan->ts->hopping.ma_len,
+			     new_lchan->ts->hopping.ma_data);
+	}
 	/* FIXME: optional bits for type of synchronization? */
 
 	return gsm48_sendmsg(msg);
@@ -381,8 +399,16 @@
 	gsm48_lchan2chan_desc(&ass->chan_desc, lchan);
 	ass->power_command = power_command;
 
+	/* optional: cell channel description */
+
 	msgb_tv_put(msg, GSM48_IE_CHANMODE_1, lchan->tch_mode);
 
+	/* mobile allocation in case of hopping */
+	if (lchan->ts->hopping.enabled) {
+		msgb_tlv_put(msg, GSM48_IE_MA_BEFORE, lchan->ts->hopping.ma_len,
+			     lchan->ts->hopping.ma_data);
+	}
+
 	/* in case of multi rate we need to attach a config */
 	if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) {
 		if (lchan->mr_conf.ver == 0) {
@@ -406,7 +432,6 @@
 	struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
 	struct gsm48_chan_mode_modify *cmm =
 		(struct gsm48_chan_mode_modify *) msgb_put(msg, sizeof(*cmm));
-	u_int16_t arfcn = lchan->ts->trx->arfcn & 0x3ff;
 
 	DEBUGP(DRR, "-> CHANNEL MODE MODIFY mode=0x%02x\n", mode);