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.
This commit is contained in:
Harald Welte 2014-12-28 15:00:45 +01:00
parent 65be6de155
commit 30f1f37638
7 changed files with 132 additions and 21 deletions

View File

@ -87,7 +87,8 @@ int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int dbm);
/* SMSCB functionality */
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);
/* some Nokia specific stuff */
int rsl_nokia_si_begin(struct gsm_bts_trx *trx);

View File

@ -735,9 +735,9 @@ struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts);
struct gsm_bts_trx *gsm_bts_trx_num(const struct gsm_bts *bts, int num);
const struct value_string gsm_pchant_names[10];
const struct value_string gsm_pchant_descs[10];
const struct value_string gsm_lchant_names[6];
const struct value_string gsm_pchant_names[12];
const struct value_string gsm_pchant_descs[12];
const struct value_string gsm_lchant_names[8];
const char *gsm_pchan_name(enum gsm_phys_chan_config c);
enum gsm_phys_chan_config gsm_pchan_parse(const char *name);
const char *gsm_lchant_name(enum gsm_chan_t c);

View File

@ -127,18 +127,21 @@ struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr)
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 abis_rsl_rcvmsg(struct msgb *msg)
}
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 @@ int rsl_sms_cb_command(struct gsm_bts *bts, uint8_t chan_number,
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);
}

View File

@ -3246,6 +3246,55 @@ DEFUN(drop_bts,
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 @@ int bsc_vty_init(const struct log_info *cat)
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();

View File

@ -65,7 +65,8 @@ struct gsm_bts_trx_ts *ts_c0_alloc(struct gsm_bts *bts,
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 @@ struct gsm_bts_trx_ts *ts_alloc(struct gsm_bts *bts,
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 @@ struct gsm_bts_trx_ts *ts_alloc(struct gsm_bts *bts,
from = 1; to = 7;
break;
case GSM_PCHAN_SDCCH8_SACCH8C:
case GSM_PCHAN_SDCCH8_SACCH8C_CBCH:
default:
return NULL;
}
@ -110,7 +113,7 @@ struct gsm_bts_trx_ts *ts_alloc(struct gsm_bts *bts,
/* 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 @@ static const uint8_t subslots_per_pchan[] = {
[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 @@ _lc_find_bts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan)
}
/* 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 @@ struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type,
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) {

View File

@ -555,11 +555,34 @@ static int generate_si3(uint8_t *output, struct gsm_bts *bts)
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 @@ static int generate_si4(uint8_t *output, struct gsm_bts *bts)
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)

View File

@ -51,7 +51,7 @@ static void gsm_mo_init(struct gsm_abis_mo *mo, struct gsm_bts *bts,
gsm_abis_mo_reset(mo);
}
const struct value_string gsm_pchant_names[10] = {
const struct value_string gsm_pchant_names[12] = {
{ GSM_PCHAN_NONE, "NONE" },
{ GSM_PCHAN_CCCH, "CCCH" },
{ GSM_PCHAN_CCCH_SDCCH4,"CCCH+SDCCH4" },
@ -61,10 +61,12 @@ const struct value_string gsm_pchant_names[10] = {
{ GSM_PCHAN_PDCH, "PDCH" },
{ GSM_PCHAN_TCH_F_PDCH, "TCH/F_PDCH" },
{ GSM_PCHAN_UNKNOWN, "UNKNOWN" },
{ GSM_PCHAN_CCCH_SDCCH4_CBCH, "CCCH+SDCCH4+CBCH" },
{ GSM_PCHAN_SDCCH8_SACCH8C, "SDCCH8+CBCH" },
{ 0, NULL }
};
const struct value_string gsm_pchant_descs[10] = {
const struct value_string gsm_pchant_descs[12] = {
{ GSM_PCHAN_NONE, "Physical Channel not configured" },
{ GSM_PCHAN_CCCH, "FCCH + SCH + BCCH + CCCH (Comb. IV)" },
{ GSM_PCHAN_CCCH_SDCCH4,
@ -75,6 +77,8 @@ const struct value_string gsm_pchant_descs[10] = {
{ GSM_PCHAN_PDCH, "Packet Data Channel for GPRS/EDGE" },
{ GSM_PCHAN_TCH_F_PDCH, "Dynamic TCH/F or GPRS PDCH" },
{ GSM_PCHAN_UNKNOWN, "Unknown / Unsupported channel combination" },
{ GSM_PCHAN_CCCH_SDCCH4_CBCH, "FCCH + SCH + BCCH + CCCH + CBCH + 3 SDCCH + 2 SACCH (Comb. V)" },
{ GSM_PCHAN_SDCCH8_SACCH8C, "7 SDCCH + 4 SACCH + CBCH (Comb. VII)" },
{ 0, NULL }
};
@ -88,12 +92,13 @@ enum gsm_phys_chan_config gsm_pchan_parse(const char *name)
return get_string_value(gsm_pchant_names, name);
}
const struct value_string gsm_lchant_names[6] = {
const struct value_string gsm_lchant_names[8] = {
{ GSM_LCHAN_NONE, "NONE" },
{ GSM_LCHAN_SDCCH, "SDCCH" },
{ GSM_LCHAN_TCH_F, "TCH/F" },
{ GSM_LCHAN_TCH_H, "TCH/H" },
{ GSM_LCHAN_UNKNOWN, "UNKNOWN" },
{ GSM_LCHAN_CBCH, "CBCH" },
{ 0, NULL }
};
@ -515,10 +520,12 @@ uint8_t gsm_ts2chan_nr(const struct gsm_bts_trx_ts *ts, uint8_t lchan_nr)
cbits += lchan_nr;
break;
case GSM_PCHAN_CCCH_SDCCH4:
case GSM_PCHAN_CCCH_SDCCH4_CBCH:
cbits = 0x04;
cbits += lchan_nr;
break;
case GSM_PCHAN_SDCCH8_SACCH8C:
case GSM_PCHAN_SDCCH8_SACCH8C_CBCH:
cbits = 0x08;
cbits += lchan_nr;
break;