[BSC] Implement per-timeslot ARFCN lists for frequency hopping

We now compute the Cell Channel Description for SI 1 by bit-wise
OR of the ARFCN bitmask of each timeslot on all the TRX of the BTS.

Also, support generating a GSM 04.08 Channel Description IE for
the hopping case (with HSN/MAIO instead of ARFCN).

What's still missing now: Sending the 04.08 Mobile Allocation IE
diff --git a/openbsc/include/openbsc/gsm_04_08.h b/openbsc/include/openbsc/gsm_04_08.h
index 3fbc35f..b855aa5 100644
--- a/openbsc/include/openbsc/gsm_04_08.h
+++ b/openbsc/include/openbsc/gsm_04_08.h
@@ -63,5 +63,7 @@
 
 struct msgb *gsm48_create_mm_serv_rej(enum gsm48_reject_value value);
 struct msgb *gsm48_create_loc_upd_rej(uint8_t cause);
+void gsm48_lchan2chan_desc(struct gsm48_chan_desc *cd,
+			   const struct gsm_lchan *lchan);
 
 #endif
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index a1e881a..1074344 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -338,8 +338,11 @@
 	u_int8_t nm_chan_comb;
 
 	struct {
+		int enabled;
 		u_int8_t maio;
 		u_int8_t hsn;
+		struct bitvec arfcns;
+		u_int8_t arfcns_data[1024/8];
 	} hopping;
 
 	/* To which E1 subslot are we connected */
diff --git a/openbsc/src/abis_nm.c b/openbsc/src/abis_nm.c
index 8c6f518..31302f1 100644
--- a/openbsc/src/abis_nm.c
+++ b/openbsc/src/abis_nm.c
@@ -1976,11 +1976,23 @@
 			NM_OC_CHANNEL, bts->bts_nr,
 			ts->trx->nr, ts->nr);
 	msgb_tv_put(msg, NM_ATT_CHAN_COMB, chan_comb);
-	if (ts->hopping.hsn) {
+	if (ts->hopping.enabled) {
+		unsigned int i;
+		uint8_t *len;
+
 		msgb_tv_put(msg, NM_ATT_HSN, ts->hopping.hsn);
 		msgb_tv_put(msg, NM_ATT_MAIO, ts->hopping.maio);
-		/* FIXME: compute ARFCN list */
-		msgb_tlv16_put(msg, NM_ATT_ARFCN_LIST, 1, &arfcn);
+
+		/* build the ARFCN list */
+		msgb_put_u8(msg, NM_ATT_ARFCN_LIST);
+		len = msgb_put(msg, 1);
+		*len = 0;
+		for (i = 0; i < ts->hopping.arfcns.data_len*8; i++) {
+			if (bitvec_get_bit_pos(&ts->hopping.arfcns, i)) {
+				msgb_put_u16(msg, i);
+				*len += sizeof(uint16_t);
+			}
+		}
 	}
 	msgb_tv_put(msg, NM_ATT_TSC, bts->tsc);	/* training sequence */
 	if (bts->type == GSM_BTS_TYPE_BS11)
diff --git a/openbsc/src/abis_rsl.c b/openbsc/src/abis_rsl.c
index 451c80d..f387354 100644
--- a/openbsc/src/abis_rsl.c
+++ b/openbsc/src/abis_rsl.c
@@ -1195,15 +1195,16 @@
 	ia.proto_discr = GSM48_PDISC_RR;
 	ia.msg_type = GSM48_MT_RR_IMM_ASS;
 	ia.page_mode = GSM48_PM_SAME;
-	ia.chan_desc.chan_nr = lchan2chan_nr(lchan);
-	ia.chan_desc.h0.h = 0;
-	ia.chan_desc.h0.arfcn_high = arfcn >> 8;
-	ia.chan_desc.h0.arfcn_low = arfcn & 0xff;
-	ia.chan_desc.h0.tsc = bts->tsc;
+	gsm48_lchan2chan_desc(&ia.chan_desc, lchan);
+
 	/* use request reference extracted from CHAN_RQD */
 	memcpy(&ia.req_ref, rqd_ref, sizeof(ia.req_ref));
 	ia.timing_advance = rqd_ta;
-	ia.mob_alloc_len = 0;
+	if (!lchan->ts->hopping.enabled) {
+		ia.mob_alloc_len = 0;
+	} else {
+		/* FIXME: Mobile Allocation in case of hopping */
+	}
 
 	DEBUGP(DRSL, "%s Activating ARFCN(%u) SS(%u) lctype %s "
 		"r=%s ra=0x%02x\n", gsm_lchan_name(lchan), arfcn, subch,
diff --git a/openbsc/src/bsc_vty.c b/openbsc/src/bsc_vty.c
index 51db0b1..5eacb22 100644
--- a/openbsc/src/bsc_vty.c
+++ b/openbsc/src/bsc_vty.c
@@ -305,13 +305,21 @@
 	if (ts->pchan != GSM_PCHAN_NONE)
 		vty_out(vty, "     phys_chan_config %s%s",
 			gsm_pchan_name(ts->pchan), VTY_NEWLINE);
-	if (ts->hopping.hsn) {
-		vty_out(vty, "      hopping sequence number %u%s",
+	vty_out(vty, "     hopping enabled %u%s",
+		ts->hopping.enabled, VTY_NEWLINE);
+	if (ts->hopping.enabled) {
+		unsigned int i;
+		vty_out(vty, "     hopping sequence-number %u%s",
 			ts->hopping.hsn, VTY_NEWLINE);
-		vty_out(vty, "      hopping maio %u%s",
+		vty_out(vty, "     hopping maio %u%s",
 			ts->hopping.maio, VTY_NEWLINE);
-		/* FIXME: ARFCN list */
-	}
+		for (i = 0; i < ts->hopping.arfcns.data_len*8; i++) {
+			if (!bitvec_get_bit_pos(&ts->hopping.arfcns, i))
+				continue;
+			vty_out(vty, "     hopping arfcn add %u%s",
+				i, VTY_NEWLINE);
+		}
+	} else
 	config_write_e1_link(vty, &ts->e1_link, "     ");
 }
 
@@ -1984,9 +1992,25 @@
 	return CMD_SUCCESS;
 }
 
+#define HOPPING_STR "Configure frequency hopping\n"
+
+DEFUN(cfg_ts_hopping,
+      cfg_ts_hopping_cmd,
+      "hopping enabled (0|1)",
+	HOPPING_STR "Enable or disable frequency hopping\n"
+      "Disable frequency hopping\n" "Enable frequency hopping\n")
+{
+	struct gsm_bts_trx_ts *ts = vty->index;
+
+	ts->hopping.enabled = atoi(argv[0]);
+
+	return CMD_SUCCESS;
+}
+
 DEFUN(cfg_ts_hsn,
       cfg_ts_hsn_cmd,
-      "hopping sequence number <0-63>",
+      "hopping sequence-number <0-63>",
+	HOPPING_STR
       "Which hopping sequence to use for this channel")
 {
 	struct gsm_bts_trx_ts *ts = vty->index;
@@ -1999,6 +2023,7 @@
 DEFUN(cfg_ts_maio,
       cfg_ts_maio_cmd,
       "hopping maio <0-63>",
+	HOPPING_STR
       "Which hopping MAIO to use for this channel")
 {
 	struct gsm_bts_trx_ts *ts = vty->index;
@@ -2011,24 +2036,28 @@
 DEFUN(cfg_ts_arfcn_add,
       cfg_ts_arfcn_add_cmd,
       "hopping arfcn add <0-1023>",
-      "Add an entry to the hopping ARFCN list")
+	HOPPING_STR "Configure hopping ARFCN list\n"
+      "Add an entry to the hopping ARFCN list\n" "ARFCN\n")
 {
 	struct gsm_bts_trx_ts *ts = vty->index;
 	int arfcn = atoi(argv[0]);
 
-	/* FIXME */
+	bitvec_set_bit_pos(&ts->hopping.arfcns, arfcn, 1);
+
 	return CMD_SUCCESS;
 }
 
 DEFUN(cfg_ts_arfcn_del,
       cfg_ts_arfcn_del_cmd,
       "hopping arfcn del <0-1023>",
-      "Delete an entry to the hopping ARFCN list")
+	HOPPING_STR "Configure hopping ARFCN list\n"
+      "Delete an entry to the hopping ARFCN list\n" "ARFCN\n")
 {
 	struct gsm_bts_trx_ts *ts = vty->index;
 	int arfcn = atoi(argv[0]);
 
-	/* FIXME */
+	bitvec_set_bit_pos(&ts->hopping.arfcns, arfcn, 0);
+
 	return CMD_SUCCESS;
 }
 
@@ -2193,6 +2222,7 @@
 	install_element(TS_NODE, &ournode_exit_cmd);
 	install_element(TS_NODE, &ournode_end_cmd);
 	install_element(TS_NODE, &cfg_ts_pchan_cmd);
+	install_element(TS_NODE, &cfg_ts_hopping_cmd);
 	install_element(TS_NODE, &cfg_ts_hsn_cmd);
 	install_element(TS_NODE, &cfg_ts_maio_cmd);
 	install_element(TS_NODE, &cfg_ts_arfcn_add_cmd);
diff --git a/openbsc/src/gsm_04_08_utils.c b/openbsc/src/gsm_04_08_utils.c
index 3776565..f517020 100644
--- a/openbsc/src/gsm_04_08_utils.c
+++ b/openbsc/src/gsm_04_08_utils.c
@@ -312,16 +312,24 @@
 	cd->arfcn_lo = bts->c0->arfcn & 0xff;
 }
 
-static void gsm48_chan_desc(struct gsm48_chan_desc *cd,
-			    const struct gsm_lchan *lchan)
+void gsm48_lchan2chan_desc(struct gsm48_chan_desc *cd,
+			   const struct gsm_lchan *lchan)
 {
 	u_int16_t arfcn = lchan->ts->trx->arfcn & 0x3ff;
 
 	cd->chan_nr = lchan2chan_nr(lchan);
-	cd->h0.tsc = lchan->ts->trx->bts->tsc;
-	cd->h0.h = 0;
-	cd->h0.arfcn_high = arfcn >> 8;
-	cd->h0.arfcn_low = arfcn & 0xff;
+	if (!lchan->ts->hopping.enabled) {
+		cd->h0.tsc = lchan->ts->trx->bts->tsc;
+		cd->h0.h = 0;
+		cd->h0.arfcn_high = arfcn >> 8;
+		cd->h0.arfcn_low = arfcn & 0xff;
+	} else {
+		cd->h1.tsc = lchan->ts->trx->bts->tsc;
+		cd->h1.h = 1;
+		cd->h1.maio_high = lchan->ts->hopping.maio >> 2;
+		cd->h1.maio_low = lchan->ts->hopping.maio & 0x03;
+		cd->h1.hsn = lchan->ts->hopping.hsn;
+	}
 }
 
 /* Chapter 9.1.15: Handover Command */
@@ -339,7 +347,7 @@
 
 	/* mandatory bits */
 	gsm48_cell_desc(&ho->cell_desc, new_lchan->ts->trx->bts);
-	gsm48_chan_desc(&ho->chan_desc, new_lchan);
+	gsm48_lchan2chan_desc(&ho->chan_desc, new_lchan);
 	ho->ho_ref = ho_ref;
 	ho->power_command = power_command;
 
@@ -370,7 +378,7 @@
 	 * the chan_desc. But as long as multi-slot configurations
 	 * are not used we seem to be fine.
 	 */
-	gsm48_chan_desc(&ass->chan_desc, lchan);
+	gsm48_lchan2chan_desc(&ass->chan_desc, lchan);
 	ass->power_command = power_command;
 
 	msgb_tv_put(msg, GSM48_IE_CHANMODE_1, lchan->tch_mode);
@@ -409,11 +417,7 @@
 
 	/* fill the channel information element, this code
 	 * should probably be shared with rsl_rx_chan_rqd() */
-	cmm->chan_desc.chan_nr = lchan2chan_nr(lchan);
-	cmm->chan_desc.h0.tsc = lchan->ts->trx->bts->tsc;
-	cmm->chan_desc.h0.h = 0;
-	cmm->chan_desc.h0.arfcn_high = arfcn >> 8;
-	cmm->chan_desc.h0.arfcn_low = arfcn & 0xff;
+	gsm48_lchan2chan_desc(&cmm->chan_desc, lchan);
 	cmm->mode = mode;
 
 	/* in case of multi rate we need to attach a config */
diff --git a/openbsc/src/gsm_data.c b/openbsc/src/gsm_data.c
index 9de4c1f..ede1c00 100644
--- a/openbsc/src/gsm_data.c
+++ b/openbsc/src/gsm_data.c
@@ -154,6 +154,9 @@
 		ts->nr = k;
 		ts->pchan = GSM_PCHAN_NONE;
 
+		ts->hopping.arfcns.data_len = sizeof(ts->hopping.arfcns_data);
+		ts->hopping.arfcns.data = ts->hopping.arfcns_data;
+
 		for (l = 0; l < TS_MAX_LCHAN; l++) {
 			struct gsm_lchan *lchan;
 			lchan = &ts->lchan[l];
diff --git a/openbsc/src/system_information.c b/openbsc/src/system_information.c
index 337f756..508787d 100644
--- a/openbsc/src/system_information.c
+++ b/openbsc/src/system_information.c
@@ -177,9 +177,23 @@
 	struct gsm_bts_trx *trx;
 	struct bitvec *bv = &bts->si_common.cell_alloc;
 
+	/* Zero-initialize the bit-vector */
+	memset(&bv->data, 0, bv->data_len);
+
 	/* first we generate a bitvec of all TRX ARFCN's in our BTS */
-	llist_for_each_entry(trx, &bts->trx_list, list)
+	llist_for_each_entry(trx, &bts->trx_list, list) {
+		unsigned int i, j;
+		/* Always add the TRX's ARFCN */
 		bitvec_set_bit_pos(bv, trx->arfcn, 1);
+		for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
+			struct gsm_bts_trx_ts *ts = &trx->ts[i];
+			/* Add any ARFCNs present in hopping channels */
+			for (j = 0; j < 1024; j++) {
+				if (bitvec_get_bit_pos(&ts->hopping.arfcns, j))
+					bitvec_set_bit_pos(bv, j, 1);
+			}
+		}
+	}
 
 	/* then we generate a GSM 04.08 frequency list from the bitvec */
 	return bitvec2freq_list(chan_list, bv, bts);
@@ -191,6 +205,9 @@
 	struct gsm_bts *cur_bts;
 	struct bitvec *bv = &bts->si_common.neigh_list;
 
+	/* Zero-initialize the bit-vector */
+	memset(&bv->data, 0, bv->data_len);
+
 	/* first we generate a bitvec of the BCCH ARFCN's in our BSC */
 	llist_for_each_entry(cur_bts, &bts->network->bts_list, list) {
 		if (cur_bts == bts)