Add basic support for CBCH / SMS-CB (Cell Brroadcast)

We can now configure the pyisical channel types for CBCH either in the
CCCH+SDCCH4 or in the SDCCH8 chanel combination.

Depending on whether a CBCH exists on the BTS, we also generate the SI4
with matching CBCH channel description to notify the phones of the
existance of the CBCH.

There is now a VTY command how a SMS-CB message can be sent to a given
BTS.

We do not yet have any logic at all for actual scheduling of multiple
CBCH RSL messages towards one or multiple BTSs yet, though.
diff --git a/openbsc/src/libbsc/abis_rsl.c b/openbsc/src/libbsc/abis_rsl.c
index d9fe92c..ae405c9 100644
--- a/openbsc/src/libbsc/abis_rsl.c
+++ b/openbsc/src/libbsc/abis_rsl.c
@@ -127,18 +127,21 @@
 				chan_nr, ts->pchan);
 	} else if ((cbits & 0x1c) == 0x04) {
 		lch_idx = cbits & 0x3;	/* SDCCH/4 */
-		if (ts->pchan != GSM_PCHAN_CCCH_SDCCH4)
+		if (ts->pchan != GSM_PCHAN_CCCH_SDCCH4 &&
+		    ts->pchan != GSM_PCHAN_CCCH_SDCCH4_CBCH)
 			LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n",
 				chan_nr, ts->pchan);
 	} else if ((cbits & 0x18) == 0x08) {
 		lch_idx = cbits & 0x7;	/* SDCCH/8 */
-		if (ts->pchan != GSM_PCHAN_SDCCH8_SACCH8C)
+		if (ts->pchan != GSM_PCHAN_SDCCH8_SACCH8C &&
+		    ts->pchan != GSM_PCHAN_SDCCH8_SACCH8C_CBCH)
 			LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n",
 				chan_nr, ts->pchan);
 	} else if (cbits == 0x10 || cbits == 0x11 || cbits == 0x12) {
 		lch_idx = 0;
 		if (ts->pchan != GSM_PCHAN_CCCH &&
-		    ts->pchan != GSM_PCHAN_CCCH_SDCCH4)
+		    ts->pchan != GSM_PCHAN_CCCH_SDCCH4 &&
+		    ts->pchan != GSM_PCHAN_CCCH_SDCCH4_CBCH)
 			LOGP(DRSL, LOGL_ERROR, "chan_nr=0x%02x but pchan=%u\n",
 				chan_nr, ts->pchan);
 		/* FIXME: we should not return first sdcch4 !!! */
@@ -2122,7 +2125,8 @@
 }
 
 int rsl_sms_cb_command(struct gsm_bts *bts, uint8_t chan_number,
-		       uint8_t cb_command, const uint8_t *data, int len)
+		       struct rsl_ie_cb_cmd_type cb_command,
+		       const uint8_t *data, int len)
 {
 	struct abis_rsl_dchan_hdr *dh;
 	struct msgb *cb_cmd;
@@ -2131,14 +2135,15 @@
 	if (!cb_cmd)
 		return -1;
 
-	dh = (struct abis_rsl_dchan_hdr *) msgb_put(cb_cmd, sizeof*dh);
+	dh = (struct abis_rsl_dchan_hdr *) msgb_put(cb_cmd, sizeof(*dh));
 	init_dchan_hdr(dh, RSL_MT_SMS_BC_CMD);
-	dh->chan_nr = RSL_CHAN_SDCCH4_ACCH; /* TODO: check the chan config */
+	dh->c.msg_discr = ABIS_RSL_MDISC_COM_CHAN;
+	dh->chan_nr = chan_number; /* TODO: check the chan config */
 
-	msgb_tv_put(cb_cmd, RSL_IE_CB_CMD_TYPE, cb_command);
+	msgb_tv_put(cb_cmd, RSL_IE_CB_CMD_TYPE, *(uint8_t*)&cb_command);
 	msgb_tlv_put(cb_cmd, RSL_IE_SMSCB_MSG, len, data);
 
-	cb_cmd->trx = bts->c0;
+	cb_cmd->dst = bts->c0->rsl_link;
 
 	return abis_rsl_sendmsg(cb_cmd);
 }
diff --git a/openbsc/src/libbsc/bsc_vty.c b/openbsc/src/libbsc/bsc_vty.c
index d6d66c6..0e38a7c 100644
--- a/openbsc/src/libbsc/bsc_vty.c
+++ b/openbsc/src/libbsc/bsc_vty.c
@@ -3246,6 +3246,55 @@
 	return CMD_SUCCESS;
 }
 
+DEFUN(smscb_cmd, smscb_cmd_cmd,
+	"bts <0-255> smscb-command <1-4> HEXSTRING",
+	"BTS related commands\n" "BTS Number\n"
+	"SMS Cell Broadcast\n" "Last Valid Block\n"
+	"Hex Encoded SMSCB message (up to 88 octets)\n")
+{
+	struct gsm_bts *bts;
+	int bts_nr = atoi(argv[0]);
+	int last_block = atoi(argv[1]);
+	struct rsl_ie_cb_cmd_type cb_cmd;
+	uint8_t buf[88];
+	int rc;
+
+	bts = gsm_bts_num(bsc_gsmnet, bts_nr);
+	if (!bts) {
+		vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+	rc = osmo_hexparse(argv[2], buf, sizeof(buf));
+	if (rc < 0 || rc > sizeof(buf)) {
+		vty_out(vty, "Error parsing HEXSTRING%s", VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	cb_cmd.spare = 0;
+	cb_cmd.def_bcast = 0;
+	cb_cmd.command = RSL_CB_CMD_TYPE_NORMAL;
+
+	switch (last_block) {
+	case 1:
+		cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_1;
+		break;
+	case 2:
+		cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_2;
+		break;
+	case 3:
+		cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_3;
+		break;
+	case 4:
+		cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_4;
+		break;
+	}
+
+	rsl_sms_cb_command(bts, RSL_CHAN_SDCCH4_ACCH, cb_cmd, buf, rc);
+
+	return CMD_SUCCESS;
+}
+
+
 DEFUN(pdch_act, pdch_act_cmd,
 	"bts <0-255> trx <0-255> timeslot <0-7> pdch (activate|deactivate)",
 	"BTS related commands\n" "BTS Number\n" "Transceiver\n" "Transceiver Number\n"
@@ -3474,6 +3523,7 @@
 
 	install_element(ENABLE_NODE, &drop_bts_cmd);
 	install_element(ENABLE_NODE, &pdch_act_cmd);
+	install_element(ENABLE_NODE, &smscb_cmd_cmd);
 
 	abis_nm_vty_init();
 	abis_om2k_vty_init();
diff --git a/openbsc/src/libbsc/chan_alloc.c b/openbsc/src/libbsc/chan_alloc.c
index cd96c1b..657aedc 100644
--- a/openbsc/src/libbsc/chan_alloc.c
+++ b/openbsc/src/libbsc/chan_alloc.c
@@ -65,7 +65,8 @@
 	struct gsm_bts_trx_ts *ts = &trx->ts[0];
 
 	if (pchan != GSM_PCHAN_CCCH &&
-	    pchan != GSM_PCHAN_CCCH_SDCCH4)
+	    pchan != GSM_PCHAN_CCCH_SDCCH4 &&
+	    pchan != GSM_PCHAN_CCCH_SDCCH4_CBCH)
 		return NULL;
 
 	if (ts->pchan != GSM_PCHAN_NONE)
@@ -96,6 +97,7 @@
 			switch (pchan) {
 			case GSM_PCHAN_CCCH:
 			case GSM_PCHAN_CCCH_SDCCH4:
+			case GSM_PCHAN_CCCH_SDCCH4_CBCH:
 				from = 0; to = 0;
 				break;
 			case GSM_PCHAN_TCH_F:
@@ -103,6 +105,7 @@
 				from = 1; to = 7;
 				break;
 			case GSM_PCHAN_SDCCH8_SACCH8C:
+			case GSM_PCHAN_SDCCH8_SACCH8C_CBCH:
 			default:
 				return NULL;
 			}
@@ -110,7 +113,7 @@
 			/* Every secondary TRX is configured for TCH/F
 			 * and TCH/H only */
 			switch (pchan) {
-			case GSM_PCHAN_SDCCH8_SACCH8C:
+			case GSM_PCHAN_SDCCH8_SACCH8C_CBCH:
 				from = 1; to = 1;
 			case GSM_PCHAN_TCH_F:
 			case GSM_PCHAN_TCH_H:
@@ -153,6 +156,8 @@
 	[GSM_PCHAN_SDCCH8_SACCH8C] = 8,
 	/* FIXME: what about dynamic TCH_F_TCH_H ? */
 	[GSM_PCHAN_TCH_F_PDCH] = 1,
+	[GSM_PCHAN_CCCH_SDCCH4_CBCH] = 4,
+	[GSM_PCHAN_SDCCH8_SACCH8C_CBCH] = 8,
 };
 
 static struct gsm_lchan *
@@ -211,7 +216,9 @@
 	}
 
 	/* we cannot allocate more of these */
-	if (pchan == GSM_PCHAN_CCCH_SDCCH4)
+	if (pchan == GSM_PCHAN_CCCH_SDCCH4 ||
+	    pchan == GSM_PCHAN_CCCH_SDCCH4_CBCH ||
+	    pchan == GSM_PCHAN_SDCCH8_SACCH8C_CBCH)
 		return NULL;
 
 	/* if we've reached here, we need to allocate a new physical
@@ -229,21 +236,29 @@
 			      int allow_bigger)
 {
 	struct gsm_lchan *lchan = NULL;
-	enum gsm_phys_chan_config first, second;
+	enum gsm_phys_chan_config first, first_cbch, second, second_cbch;
 
 	switch (type) {
 	case GSM_LCHAN_SDCCH:
 		if (bts->chan_alloc_reverse) {
 			first = GSM_PCHAN_SDCCH8_SACCH8C;
+			first_cbch = GSM_PCHAN_SDCCH8_SACCH8C_CBCH;
 			second = GSM_PCHAN_CCCH_SDCCH4;
+			second_cbch = GSM_PCHAN_CCCH_SDCCH4_CBCH;
 		} else {
 			first = GSM_PCHAN_CCCH_SDCCH4;
+			first_cbch = GSM_PCHAN_CCCH_SDCCH4_CBCH;
 			second = GSM_PCHAN_SDCCH8_SACCH8C;
+			second_cbch = GSM_PCHAN_SDCCH8_SACCH8C_CBCH;
 		}
 
 		lchan = _lc_find_bts(bts, first);
 		if (lchan == NULL)
+			lchan = _lc_find_bts(bts, first_cbch);
+		if (lchan == NULL)
 			lchan = _lc_find_bts(bts, second);
+		if (lchan == NULL)
+			lchan = _lc_find_bts(bts, second_cbch);
 
 		/* allow to assign bigger channels */
 		if (allow_bigger) {
diff --git a/openbsc/src/libbsc/system_information.c b/openbsc/src/libbsc/system_information.c
index 97827f2..29b9b19 100644
--- a/openbsc/src/libbsc/system_information.c
+++ b/openbsc/src/libbsc/system_information.c
@@ -555,11 +555,34 @@
 	return sizeof(*si3) + rc;
 }
 
+/* return the gsm_lchan for the CBCH (if it exists at all) */
+static struct gsm_lchan *bts_get_cbch(struct gsm_bts *bts)
+{
+	struct gsm_lchan *lchan = NULL;
+	struct gsm_bts_trx *trx = bts->c0;
+
+	if (trx->ts[0].pchan == GSM_PCHAN_CCCH_SDCCH4_CBCH)
+		lchan = &trx->ts[0].lchan[2];
+	else {
+		int i;
+		for (i = 0; i < 8; i++) {
+			if (trx->ts[i].pchan == GSM_PCHAN_SDCCH8_SACCH8C_CBCH) {
+				lchan = &trx->ts[i].lchan[2];
+				break;
+			}
+		}
+	}
+
+	return lchan;
+}
+
 static int generate_si4(uint8_t *output, struct gsm_bts *bts)
 {
 	int rc;
 	struct gsm48_system_information_type_4 *si4 =
 		(struct gsm48_system_information_type_4 *) output;
+	struct gsm_lchan *cbch_lchan;
+	uint8_t *restoct = si4->data;
 
 	/* length of all IEs present except SI4 rest octets and l2_plen */
 	int l2_plen = sizeof(*si4) - 1;
@@ -577,15 +600,25 @@
 	si4->rach_control = bts->si_common.rach_control;
 
 	/* Optional: CBCH Channel Description + CBCH Mobile Allocation */
+	cbch_lchan = bts_get_cbch(bts);
+	if (cbch_lchan) {
+		struct gsm48_chan_desc cd;
+		gsm48_lchan2chan_desc(&cd, cbch_lchan);
+		tv_fixed_put(si4->data, GSM48_IE_CBCH_CHAN_DESC, 4,
+			     (uint8_t *) &cd);
+		l2_plen += 4 + 1;
+		restoct += 4 + 1;
+		/* we don't use hopping and thus don't need a CBCH MA */
+	}
 
 	si4->header.l2_plen = (l2_plen << 2) | 1;
 
 	/* SI4 Rest Octets (10.5.2.35), containing
 		Optional Power offset, GPRS Indicator,
 		Cell Identity, LSA ID, Selection Parameter */
-	rc = rest_octets_si4(si4->data, &si_info);
+	rc = rest_octets_si4(restoct, &si_info);
 
-	return sizeof(*si4) + rc;
+	return l2_plen + 1 + rc;
 }
 
 static int generate_si5(uint8_t *output, struct gsm_bts *bts)