summaryrefslogtreecommitdiffstats
path: root/openbsc
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2014-12-28 15:00:45 +0100
committerHarald Welte <laforge@gnumonks.org>2014-12-30 00:35:28 +0100
commit30f1f376383df3ae8d85e96542bf14d174c25d89 (patch)
treed2c7cf085cce2a44ad055b741b25ef8794f105a2 /openbsc
parent65be6de155407142ddab44faf8aee5f8d5ebf628 (diff)
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.
Diffstat (limited to 'openbsc')
-rw-r--r--openbsc/include/openbsc/abis_rsl.h3
-rw-r--r--openbsc/include/openbsc/gsm_data_shared.h6
-rw-r--r--openbsc/src/libbsc/abis_rsl.c21
-rw-r--r--openbsc/src/libbsc/bsc_vty.c50
-rw-r--r--openbsc/src/libbsc/chan_alloc.c23
-rw-r--r--openbsc/src/libbsc/system_information.c37
-rw-r--r--openbsc/src/libcommon/gsm_data_shared.c13
7 files changed, 132 insertions, 21 deletions
diff --git a/openbsc/include/openbsc/abis_rsl.h b/openbsc/include/openbsc/abis_rsl.h
index 0f188ee48..1609c4833 100644
--- a/openbsc/include/openbsc/abis_rsl.h
+++ b/openbsc/include/openbsc/abis_rsl.h
@@ -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);
diff --git a/openbsc/include/openbsc/gsm_data_shared.h b/openbsc/include/openbsc/gsm_data_shared.h
index 5d84969ae..30f6bc32f 100644
--- a/openbsc/include/openbsc/gsm_data_shared.h
+++ b/openbsc/include/openbsc/gsm_data_shared.h
@@ -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);
diff --git a/openbsc/src/libbsc/abis_rsl.c b/openbsc/src/libbsc/abis_rsl.c
index d9fe92c96..ae405c98c 100644
--- a/openbsc/src/libbsc/abis_rsl.c
+++ b/openbsc/src/libbsc/abis_rsl.c
@@ -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);
}
diff --git a/openbsc/src/libbsc/bsc_vty.c b/openbsc/src/libbsc/bsc_vty.c
index d6d66c638..0e38a7c86 100644
--- a/openbsc/src/libbsc/bsc_vty.c
+++ b/openbsc/src/libbsc/bsc_vty.c
@@ -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();
diff --git a/openbsc/src/libbsc/chan_alloc.c b/openbsc/src/libbsc/chan_alloc.c
index cd96c1b38..657aedcf7 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_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) {
diff --git a/openbsc/src/libbsc/system_information.c b/openbsc/src/libbsc/system_information.c
index 97827f2d9..29b9b191d 100644
--- a/openbsc/src/libbsc/system_information.c
+++ b/openbsc/src/libbsc/system_information.c
@@ -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)
diff --git a/openbsc/src/libcommon/gsm_data_shared.c b/openbsc/src/libcommon/gsm_data_shared.c
index 8687a1047..210d2284b 100644
--- a/openbsc/src/libcommon/gsm_data_shared.c
+++ b/openbsc/src/libcommon/gsm_data_shared.c
@@ -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;