summaryrefslogtreecommitdiffstats
path: root/openbsc/src/libbsc
diff options
context:
space:
mode:
Diffstat (limited to 'openbsc/src/libbsc')
-rw-r--r--openbsc/src/libbsc/abis_nm.c2
-rw-r--r--openbsc/src/libbsc/abis_rsl.c2
-rw-r--r--openbsc/src/libbsc/bsc_api.c6
-rw-r--r--openbsc/src/libbsc/bsc_ctrl_commands.c12
-rw-r--r--openbsc/src/libbsc/bsc_init.c10
-rw-r--r--openbsc/src/libbsc/bsc_vty.c172
-rw-r--r--openbsc/src/libbsc/gsm_04_08_utils.c15
-rw-r--r--openbsc/src/libbsc/rest_octets.c263
-rw-r--r--openbsc/src/libbsc/system_information.c222
9 files changed, 663 insertions, 41 deletions
diff --git a/openbsc/src/libbsc/abis_nm.c b/openbsc/src/libbsc/abis_nm.c
index c05e2f94f..3afc4c497 100644
--- a/openbsc/src/libbsc/abis_nm.c
+++ b/openbsc/src/libbsc/abis_nm.c
@@ -2598,7 +2598,7 @@ int abis_nm_ipaccess_restart(struct gsm_bts_trx *trx)
fill_om_fom_hdr(oh, 0, NM_MT_IPACC_RESTART, NM_OC_BASEB_TRANSC,
trx->bts->nr, trx->nr, 0xff);
- return abis_nm_sendmsg(trx->bts, msg);
+ return abis_nm_sendmsg_direct(trx->bts, msg);
}
int abis_nm_ipaccess_set_attr(struct gsm_bts *bts, uint8_t obj_class,
diff --git a/openbsc/src/libbsc/abis_rsl.c b/openbsc/src/libbsc/abis_rsl.c
index 512576455..36a663824 100644
--- a/openbsc/src/libbsc/abis_rsl.c
+++ b/openbsc/src/libbsc/abis_rsl.c
@@ -1345,8 +1345,6 @@ static void t3109_expired(void *data)
rsl_rf_chan_release(lchan, 1, SACCH_NONE);
}
-#define GSM48_LEN2PLEN(a) (((a) << 2) | 1)
-
/* Format an IMM ASS REJ according to 04.08 Chapter 9.1.20 */
static int rsl_send_imm_ass_rej(struct gsm_bts *bts,
unsigned int num_req_refs,
diff --git a/openbsc/src/libbsc/bsc_api.c b/openbsc/src/libbsc/bsc_api.c
index d9c34d035..a72f15121 100644
--- a/openbsc/src/libbsc/bsc_api.c
+++ b/openbsc/src/libbsc/bsc_api.c
@@ -395,11 +395,13 @@ int gsm0808_assign_req(struct gsm_subscriber_connection *conn, int chan_mode, in
if (handle_new_assignment(conn, chan_mode, full_rate) != 0)
goto error;
} else {
- LOGP(DMSC, LOGL_NOTICE,
- "Sending ChanModify for speech %d %d\n", chan_mode, full_rate);
if (chan_mode == GSM48_CMODE_SPEECH_AMR)
handle_mr_config(conn, conn->lchan, full_rate);
+ LOGP(DMSC, LOGL_NOTICE,
+ "Sending ChanModify for speech: %s on channel %s\n",
+ get_value_string(gsm48_chan_mode_names, chan_mode),
+ get_value_string(gsm_chan_t_names, conn->lchan->type));
gsm48_lchan_modify(conn->lchan, chan_mode);
}
diff --git a/openbsc/src/libbsc/bsc_ctrl_commands.c b/openbsc/src/libbsc/bsc_ctrl_commands.c
index b6b1c9a8b..7e8479734 100644
--- a/openbsc/src/libbsc/bsc_ctrl_commands.c
+++ b/openbsc/src/libbsc/bsc_ctrl_commands.c
@@ -90,7 +90,17 @@ static int set_net_apply_config(struct ctrl_cmd *cmd, void *data)
if (!is_ipaccess_bts(bts))
continue;
- ipaccess_drop_oml(bts);
+ /*
+ * The ip.access nanoBTS seems to be unrelaible on BSSGP
+ * so let's us just reboot it. For the sysmoBTS we can just
+ * restart the process as all state is gone.
+ */
+ if (!is_sysmobts_v2(bts) && strcmp(cmd->value, "restart") == 0) {
+ struct gsm_bts_trx *trx;
+ llist_for_each_entry_reverse(trx, &bts->trx_list, list)
+ abis_nm_ipaccess_restart(trx);
+ } else
+ ipaccess_drop_oml(bts);
}
cmd->reply = "Tried to drop the BTS";
diff --git a/openbsc/src/libbsc/bsc_init.c b/openbsc/src/libbsc/bsc_init.c
index 3ec3aa837..7abc4c666 100644
--- a/openbsc/src/libbsc/bsc_init.c
+++ b/openbsc/src/libbsc/bsc_init.c
@@ -140,6 +140,7 @@ int gsm_bts_trx_set_system_infos(struct gsm_bts_trx *trx)
gen_si[n_si++] = SYSINFO_TYPE_2;
gen_si[n_si++] = SYSINFO_TYPE_2bis;
gen_si[n_si++] = SYSINFO_TYPE_2ter;
+ gen_si[n_si++] = SYSINFO_TYPE_2quater;
gen_si[n_si++] = SYSINFO_TYPE_3;
gen_si[n_si++] = SYSINFO_TYPE_4;
@@ -191,9 +192,9 @@ int gsm_bts_trx_set_system_infos(struct gsm_bts_trx *trx)
return 0;
err_out:
- LOGP(DRR, LOGL_ERROR, "Cannot generate SI%s for BTS %u, most likely "
- "a problem with neighbor cell list generation\n",
- get_value_string(osmo_sitype_strs, i), bts->nr);
+ LOGP(DRR, LOGL_ERROR, "Cannot generate SI%s for BTS %u: error <%s>,"
+ "most likely a problem with neighbor cell list generation\n",
+ get_value_string(osmo_sitype_strs, i), bts->nr, strerror(-rc));
return rc;
}
@@ -373,6 +374,9 @@ static int bootstrap_bts(struct gsm_bts *bts)
{
int i, n;
+ if (!bts->model)
+ return -EFAULT;
+
if (bts->model->start && !bts->model->started) {
int ret = bts->model->start(bts->network);
if (ret < 0)
diff --git a/openbsc/src/libbsc/bsc_vty.c b/openbsc/src/libbsc/bsc_vty.c
index 049f73a62..864907515 100644
--- a/openbsc/src/libbsc/bsc_vty.c
+++ b/openbsc/src/libbsc/bsc_vty.c
@@ -603,6 +603,29 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
}
}
+ for (i = 0; i < MAX_EARFCN_LIST; i++) {
+ if (bts->si_common.si2quater_neigh_list.arfcn[i] !=
+ OSMO_EARFCN_INVALID) {
+ vty_out(vty, " si2quater neighbor-list add earfcn %u threshold %u",
+ bts->si_common.si2quater_neigh_list.arfcn[i],
+ bts->si_common.si2quater_neigh_list.thresh_hi);
+ if (bts->si_common.si2quater_neigh_list.meas_bw[i] !=
+ OSMO_EARFCN_MEAS_INVALID)
+ vty_out(vty, " %u",
+ bts->si_common.si2quater_neigh_list.meas_bw[i]);
+
+ vty_out(vty, "%s", VTY_NEWLINE);
+ }
+ }
+
+ for (i = 0; i < bts->si_common.uarfcn_length; i++) {
+ vty_out(vty, " si2quater neighbor-list add uarfcn %u %u %u%s",
+ bts->si_common.data.uarfcn_list[i],
+ bts->si_common.data.scramble_list[i] & ~(1 << 9),
+ (bts->si_common.data.scramble_list[i] >> 9) & 1,
+ VTY_NEWLINE);
+ }
+
vty_out(vty, " codec-support fr");
if (bts->codec.hr)
vty_out(vty, " hr");
@@ -2265,6 +2288,112 @@ DEFUN(cfg_bts_neigh, cfg_bts_neigh_cmd,
return CMD_SUCCESS;
}
+
+DEFUN(cfg_bts_si2quater_neigh_add, cfg_bts_si2quater_neigh_add_cmd,
+ "si2quater neighbor-list add earfcn <0-65535> threshold <0-31> "
+ "[<0-7>]", "SI2quater Neighbor List\n"
+ "SI2quater Neighbor List\n" "Add to manual SI2quater neighbor list\n"
+ "EARFCN of neighbor\n" "EARFCN of neighbor\n" "threshold high bits\n"
+ "threshold high bits\n" "measurement bandwidth\n")
+{
+ struct gsm_bts *bts = vty->index;
+ struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list;
+ uint16_t arfcn = atoi(argv[0]);
+ uint8_t meas = OSMO_EARFCN_MEAS_INVALID, thresh = atoi(argv[1]);
+ int r;
+
+ if (3 == argc)
+ meas = atoi(argv[2]);
+
+ r = osmo_earfcn_add(e, arfcn, meas);
+
+ if (r < 0) {
+ vty_out(vty, "Unable to add arfcn %u: %s%s", arfcn, strerror(r),
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ if (si2q_size_check(bts)) {
+ if (e->thresh_hi && thresh != e->thresh_hi)
+ vty_out(vty, "Warning: multiple thresholds are not "
+ "supported, overriding previous threshold %u%s",
+ e->thresh_hi, VTY_NEWLINE);
+
+ e->thresh_hi = thresh;
+ return CMD_SUCCESS;
+ }
+ vty_out(vty, "Warning: not enough space in si2quater for a given arfcn%s"
+ , VTY_NEWLINE);
+ osmo_earfcn_del(e, arfcn);
+ return CMD_WARNING;
+}
+
+DEFUN(cfg_bts_si2quater_neigh_del, cfg_bts_si2quater_neigh_del_cmd,
+ "si2quater neighbor-list del earfcn <0-65535>",
+ "SI2quater Neighbor List\n"
+ "SI2quater Neighbor List\n"
+ "Delete from SI2quater manual neighbor list\n"
+ "EARFCN of neighbor\n"
+ "EARFCN\n")
+{
+ struct gsm_bts *bts = vty->index;
+ struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list;
+ uint16_t arfcn = atoi(argv[0]);
+ int r = osmo_earfcn_del(e, arfcn);
+ if (r < 0) {
+ vty_out(vty, "Unable to delete arfcn %u: %s%s", arfcn,
+ strerror(-r), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_bts_si2quater_uarfcn_add, cfg_bts_si2quater_uarfcn_add_cmd,
+ "si2quater neighbor-list add uarfcn <0-16383> <0-511> <0-1>",
+ "SI2quater Neighbor List\n"
+ "SI2quater Neighbor List\n" "Add to manual SI2quater neighbor list\n"
+ "UARFCN of neighbor\n" "UARFCN of neighbor\n" "scrambling code\n"
+ "diversity bit\n")
+{
+ struct gsm_bts *bts = vty->index;
+ uint16_t arfcn = atoi(argv[0]), scramble = atoi(argv[1]);
+
+ switch(bts_uarfcn_add(bts, arfcn, scramble, atoi(argv[2]))) {
+ case -ENOMEM:
+ vty_out(vty, "Unable to add arfcn: max number of UARFCNs (%u) "
+ "reached%s", MAX_EARFCN_LIST, VTY_NEWLINE);
+ case -ENOSPC:
+ vty_out(vty, "Warning: not enough space in si2quater for a "
+ "given arfcn%s", VTY_NEWLINE);
+ case -EADDRINUSE:
+ vty_out(vty, "Unable to add arfcn: (%u, %u) is already added%s",
+ arfcn, scramble, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_bts_si2quater_uarfcn_del, cfg_bts_si2quater_uarfcn_del_cmd,
+ "si2quater neighbor-list del uarfcn <0-16383> <0-511>",
+ "SI2quater Neighbor List\n"
+ "SI2quater Neighbor List\n"
+ "Delete from SI2quater manual neighbor list\n"
+ "UARFCN of neighbor\n"
+ "UARFCN\n"
+ "scrambling code\n")
+{
+ struct gsm_bts *bts = vty->index;
+
+ if (bts_uarfcn_del(bts, atoi(argv[0]), atoi(argv[1])) < 0) {
+ vty_out(vty, "Unable to delete uarfcn: pair not found%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_bts_si5_neigh, cfg_bts_si5_neigh_cmd,
"si5 neighbor-list (add|del) arfcn <0-1023>",
"SI5 Neighbor List\n"
@@ -3118,6 +3247,44 @@ DEFUN(drop_bts,
return CMD_SUCCESS;
}
+DEFUN(restart_bts, restart_bts_cmd,
+ "restart-bts <0-65535>",
+ "Restart ip.access nanoBTS through OML\n"
+ "BTS Number\n")
+{
+ struct gsm_network *gsmnet;
+ struct gsm_bts_trx *trx;
+ struct gsm_bts *bts;
+ unsigned int bts_nr;
+
+ gsmnet = gsmnet_from_vty(vty);
+
+ bts_nr = atoi(argv[0]);
+ if (bts_nr >= gsmnet->num_bts) {
+ vty_out(vty, "BTS number must be between 0 and %d. It was %d.%s",
+ gsmnet->num_bts, bts_nr, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ bts = gsm_bts_num(gsmnet, bts_nr);
+ if (!bts) {
+ vty_out(vty, "BTS Nr. %d could not be found.%s", bts_nr, VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ if (!is_ipaccess_bts(bts) || is_sysmobts_v2(bts)) {
+ vty_out(vty, "This command only works for ipaccess nanoBTS.%s",
+ VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+
+ /* go from last TRX to c0 */
+ llist_for_each_entry_reverse(trx, &bts->trx_list, list)
+ abis_nm_ipaccess_restart(trx);
+
+ return CMD_SUCCESS;
+}
+
DEFUN(smscb_cmd, smscb_cmd_cmd,
"bts <0-255> smscb-command <1-4> HEXSTRING",
"BTS related commands\n" "BTS Number\n"
@@ -3320,6 +3487,10 @@ int bsc_vty_init(const struct log_info *cat, struct gsm_network *network)
install_element(BTS_NODE, &cfg_bts_neigh_mode_cmd);
install_element(BTS_NODE, &cfg_bts_neigh_cmd);
install_element(BTS_NODE, &cfg_bts_si5_neigh_cmd);
+ install_element(BTS_NODE, &cfg_bts_si2quater_neigh_add_cmd);
+ install_element(BTS_NODE, &cfg_bts_si2quater_neigh_del_cmd);
+ install_element(BTS_NODE, &cfg_bts_si2quater_uarfcn_add_cmd);
+ install_element(BTS_NODE, &cfg_bts_si2quater_uarfcn_del_cmd);
install_element(BTS_NODE, &cfg_bts_excl_rf_lock_cmd);
install_element(BTS_NODE, &cfg_bts_no_excl_rf_lock_cmd);
install_element(BTS_NODE, &cfg_bts_force_comb_si_cmd);
@@ -3380,6 +3551,7 @@ int bsc_vty_init(const struct log_info *cat, struct gsm_network *network)
install_element(TS_NODE, &cfg_ts_e1_subslot_cmd);
install_element(ENABLE_NODE, &drop_bts_cmd);
+ install_element(ENABLE_NODE, &restart_bts_cmd);
install_element(ENABLE_NODE, &pdch_act_cmd);
install_element(ENABLE_NODE, &smscb_cmd_cmd);
diff --git a/openbsc/src/libbsc/gsm_04_08_utils.c b/openbsc/src/libbsc/gsm_04_08_utils.c
index fd39c1c43..520a40ffc 100644
--- a/openbsc/src/libbsc/gsm_04_08_utils.c
+++ b/openbsc/src/libbsc/gsm_04_08_utils.c
@@ -440,7 +440,7 @@ int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan,
/*
* fill the channel information element, this code
* should probably be shared with rsl_rx_chan_rqd(),
- * gsm48_tx_chan_mode_modify. But beware that 10.5.2.5
+ * gsm48_lchan_modify(). But beware that 10.5.2.5
* 10.5.2.5.a have slightly different semantic for
* the chan_desc. But as long as multi-slot configurations
* are not used we seem to be fine.
@@ -465,7 +465,7 @@ int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan,
}
/* 9.1.5 Channel mode modify: Modify the mode on the MS side */
-int gsm48_tx_chan_mode_modify(struct gsm_lchan *lchan, uint8_t mode)
+int gsm48_lchan_modify(struct gsm_lchan *lchan, uint8_t mode)
{
struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CHN MOD");
struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
@@ -490,17 +490,6 @@ int gsm48_tx_chan_mode_modify(struct gsm_lchan *lchan, uint8_t mode)
return gsm48_sendmsg(msg);
}
-int gsm48_lchan_modify(struct gsm_lchan *lchan, uint8_t lchan_mode)
-{
- int rc;
-
- rc = gsm48_tx_chan_mode_modify(lchan, lchan_mode);
- if (rc < 0)
- return rc;
-
- return rc;
-}
-
int gsm48_rx_rr_modif_ack(struct msgb *msg)
{
int rc;
diff --git a/openbsc/src/libbsc/rest_octets.c b/openbsc/src/libbsc/rest_octets.c
index 4545794a9..aa286e578 100644
--- a/openbsc/src/libbsc/rest_octets.c
+++ b/openbsc/src/libbsc/rest_octets.c
@@ -24,10 +24,15 @@
#include <string.h>
#include <stdlib.h>
#include <errno.h>
+#include <stdbool.h>
+#include <openbsc/debug.h>
#include <openbsc/gsm_data.h>
#include <osmocom/core/bitvec.h>
+#include <osmocom/gsm/bitvec_gsm.h>
#include <openbsc/rest_octets.h>
+#include <openbsc/arfcn_range_encode.h>
+#include <openbsc/system_information.h>
/* generate SI1 rest octets */
int rest_octets_si1(uint8_t *data, uint8_t *nch_pos, int is1800_net)
@@ -53,6 +58,255 @@ int rest_octets_si1(uint8_t *data, uint8_t *nch_pos, int is1800_net)
return bv.data_len;
}
+/* Append Repeated E-UTRAN Neighbour Cell to bitvec:
+ * see 3GPP TS 44.018 Table 10.5.2.33b.1
+ */
+static inline void append_eutran_neib_cell(struct bitvec *bv,
+ const struct osmo_earfcn_si2q *e)
+{
+ unsigned i;
+ for (i = 0; i < e->length; i++) {
+ if (e->arfcn[i] != OSMO_EARFCN_INVALID) {
+ bitvec_set_bit(bv, 1); /* EARFCN: */
+ bitvec_set_uint(bv, e->arfcn[i], 16);
+
+ if (OSMO_EARFCN_MEAS_INVALID == e->meas_bw[i])
+ bitvec_set_bit(bv, 0);
+ else {
+ /* Measurement Bandwidth: 9.1.54 */
+ bitvec_set_bit(bv, 1);
+ bitvec_set_uint(bv, e->meas_bw[i], 3);
+ }
+ }
+ }
+
+ /* stop bit - end of EARFCN + Measurement Bandwidth sequence */
+ bitvec_set_bit(bv, 0);
+
+ if (e->prio_valid) {
+ /* E-UTRAN_PRIORITY: 3GPP TS 45.008*/
+ bitvec_set_bit(bv, 1);
+ bitvec_set_uint(bv, e->prio, 3);
+ } else
+ bitvec_set_bit(bv, 0);
+
+ /* THRESH_E-UTRAN_high */
+ bitvec_set_uint(bv, e->thresh_hi, 5);
+
+ if (e->thresh_lo_valid) {
+ /* THRESH_E-UTRAN_low: */
+ bitvec_set_bit(bv, 1);
+ bitvec_set_uint(bv, e->thresh_lo, 5);
+ } else
+ bitvec_set_bit(bv, 0);
+
+ if (e->qrxlm_valid) {
+ /* E-UTRAN_QRXLEVMIN: */
+ bitvec_set_bit(bv, 1);
+ bitvec_set_uint(bv, e->qrxlm, 5);
+ } else
+ bitvec_set_bit(bv, 0);
+}
+
+static inline void append_earfcn(struct bitvec *bv,
+ const struct osmo_earfcn_si2q *e)
+{
+ /* Additions in Rel-5: */
+ bitvec_set_bit(bv, H);
+ /* No 3G Additional Measurement Param. Descr. */
+ bitvec_set_bit(bv, 0);
+ /* No 3G ADDITIONAL MEASUREMENT Param. Descr. 2 */
+ bitvec_set_bit(bv, 0);
+ /* Additions in Rel-6: */
+ bitvec_set_bit(bv, H);
+ /* 3G_CCN_ACTIVE */
+ bitvec_set_bit(bv, 0);
+ /* Additions in Rel-7: */
+ bitvec_set_bit(bv, H);
+ /* No 700_REPORTING_OFFSET */
+ bitvec_set_bit(bv, 0);
+ /* No 810_REPORTING_OFFSET */
+ bitvec_set_bit(bv, 0);
+ /* Additions in Rel-8: */
+ bitvec_set_bit(bv, H);
+
+ /* Priority and E-UTRAN Parameters Description */
+ bitvec_set_bit(bv, 1);
+
+ /* No Serving Cell Priority Parameters Descr. */
+ bitvec_set_bit(bv, 0);
+ /* No 3G Priority Parameters Description */
+ bitvec_set_bit(bv, 0);
+ /* E-UTRAN Parameters Description */
+ bitvec_set_bit(bv, 1);
+
+ /* E-UTRAN_CCN_ACTIVE */
+ bitvec_set_bit(bv, 0);
+ /* E-UTRAN_Start: 9.1.54 */
+ bitvec_set_bit(bv, 1);
+ /* E-UTRAN_Stop: 9.1.54 */
+ bitvec_set_bit(bv, 1);
+
+ /* No E-UTRAN Measurement Parameters Descr. */
+ bitvec_set_bit(bv, 0);
+ /* No GPRS E-UTRAN Measurement Param. Descr. */
+ bitvec_set_bit(bv, 0);
+
+ /* Note: each of next 3 "repeated" structures might be repeated any
+ (0, 1, 2...) times - we only support 1 and 0 */
+
+ /* Repeated E-UTRAN Neighbour Cells */
+ bitvec_set_bit(bv, 1);
+
+ /* Note: we don't support different EARFCN arrays each with different
+ priority, threshold etc. */
+ append_eutran_neib_cell(bv, e);
+
+ /* stop bit - end of Repeated E-UTRAN Neighbour Cells sequence: */
+ bitvec_set_bit(bv, 0);
+
+ /* Note: following 2 repeated structs are not supported ATM */
+ /* stop bit - end of Repeated E-UTRAN Not Allowed Cells sequence: */
+ bitvec_set_bit(bv, 0);
+ /* stop bit - end of Repeated E-UTRAN PCID to TA mapping sequence: */
+ bitvec_set_bit(bv, 0);
+
+ /* Priority and E-UTRAN Parameters Description ends here */
+ /* No 3G CSG Description */
+ bitvec_set_bit(bv, 0);
+ /* No E-UTRAN CSG Description */
+ bitvec_set_bit(bv, 0);
+ /* No Additions in Rel-9: */
+ bitvec_set_bit(bv, L);
+}
+
+static inline void append_uarfcn(struct bitvec *bv, const uint16_t *u,
+ const uint16_t *sc, size_t length)
+{
+ int f0_inc, i, arfcns_used, w[RANGE_ENC_MAX_ARFCNS], a[length];
+ uint8_t chan_list[16] = {0};
+
+ /* 3G Neighbour Cell Description */
+ bitvec_set_bit(bv, 1);
+ /* No Index_Start_3G */
+ bitvec_set_bit(bv, 0);
+ /* No Absolute_Index_Start_EMR */
+ bitvec_set_bit(bv, 0);
+
+ /* UTRAN FDD Description */
+ bitvec_set_bit(bv, 1);
+ /* No Bandwidth_FDD */
+ bitvec_set_bit(bv, 0);
+
+ memset(w, 0, sizeof(w));
+ for (i = 0; i < length; i++)
+ a[i] = sc[i];
+
+ /* Note: we do not support repeating Neighbour Cells ATM */
+ /* Repeated UTRAN FDD Neighbour Cells */
+ bitvec_set_bit(bv, 1);
+
+ /* FDD-ARFCN */
+ bitvec_set_bit(bv, 0);
+ /* Note: we do not support multiple UARFCN values ATM: */
+ bitvec_set_uint(bv, u[0], 14);
+
+ arfcns_used = range_enc_filter_arfcns(a, length, 0, &f0_inc);
+ range_enc_arfcns(ARFCN_RANGE_1024, a, arfcns_used, w, 0);
+ range_enc_range1024(chan_list, 0, f0_inc, w);
+
+ /* FDD_Indic0: parameter value '0000000000' is not a member of the set */
+ bitvec_set_bit(bv, f0_inc);
+ /* NR_OF_FDD_CELLS */
+ bitvec_set_uint(bv, length, 5);
+
+ i = bv->cur_bit;
+ bitvec_add_range1024(bv, (struct gsm48_range_1024 *)chan_list);
+ bv->cur_bit = i + range1024_p(length);
+
+ /* stop bit - end of Repeated UTRAN FDD Neighbour Cells */
+ bitvec_set_bit(bv, 0);
+
+ /* UTRAN TDD Description */
+ bitvec_set_bit(bv, 0);
+}
+
+/* generate SI2quater rest octets: 3GPP TS 44.018 ยง 10.5.2.33b */
+int rest_octets_si2quater(uint8_t *data, const struct osmo_earfcn_si2q *e,
+ const uint16_t *u, const uint16_t *sc, size_t u_len)
+{
+ unsigned sz;
+ struct bitvec bv;
+ bv.data = data;
+ bv.data_len = 20;
+ bitvec_zero(&bv);
+
+ /* BA_IND */
+ bitvec_set_bit(&bv, 1);
+ /* 3G_BA_IND */
+ bitvec_set_bit(&bv, 1);
+ /* MP_CHANGE_MARK */
+ bitvec_set_bit(&bv, 0);
+
+ /* we do not support multiple si2quater messages at the moment: */
+ /* SI2quater_INDEX */
+ bitvec_set_uint(&bv, 0, 4);
+ /* SI2quater_COUNT */
+ bitvec_set_uint(&bv, 0, 4);
+
+ /* No Measurement_Parameters Description */
+ bitvec_set_bit(&bv, 0);
+ /* No GPRS_Real Time Difference Description */
+ bitvec_set_bit(&bv, 0);
+ /* No GPRS_BSIC Description */
+ bitvec_set_bit(&bv, 0);
+ /* No GPRS_REPORT PRIORITY Description */
+ bitvec_set_bit(&bv, 0);
+ /* No GPRS_MEASUREMENT_Parameters Description */
+ bitvec_set_bit(&bv, 0);
+ /* No NC Measurement Parameters */
+ bitvec_set_bit(&bv, 0);
+ /* No extension (length) */
+ bitvec_set_bit(&bv, 0);
+
+ if (u_len) {
+ sz = uarfcn_size(u, sc, u_len);
+ /* Even if we do not append EARFCN we still need to set 3 bits */
+ if (sz + bv.cur_bit + 3 > SI2Q_MAX_LEN) {
+ LOGP(DRR, LOGL_ERROR, "SI2quater: not enough memory to "
+ "add UARFCNs bits, current %u + required %u + "
+ "reminder %u > max %u\n", bv.cur_bit, sz, 3,
+ SI2Q_MAX_LEN);
+ return -ENOMEM;
+ }
+ append_uarfcn(&bv, u, sc, u_len);
+ } else { /* No 3G Neighbour Cell Description */
+ bitvec_set_bit(&bv, 0);
+ }
+
+ /* No 3G Measurement Parameters Description */
+ bitvec_set_bit(&bv, 0);
+ /* No GPRS_3G_MEASUREMENT Parameters Descr. */
+ bitvec_set_bit(&bv, 0);
+
+ if (e) {
+ sz = earfcn_size(e);
+ if (sz + bv.cur_bit > SI2Q_MAX_LEN) {
+ LOGP(DRR, LOGL_ERROR, "SI2quater: not enough memory to "
+ "add EARFCNs bits, current %u + required %u > max "
+ "%u\n", bv.cur_bit, sz, SI2Q_MAX_LEN);
+ return -ENOMEM;
+ }
+ append_earfcn(&bv, e);
+ } else {
+ /* No Additions in Rel-5: */
+ bitvec_set_bit(&bv, L);
+ }
+
+ bitvec_spare_padding(&bv, (bv.data_len * 8) - 1);
+ return bv.data_len;
+}
+
/* Append selection parameters to bitvec */
static void append_selection_params(struct bitvec *bv,
const struct gsm48_si_selection_params *sp)
@@ -129,6 +383,15 @@ int rest_octets_si3(uint8_t *data, const struct gsm48_si_ro_info *si3)
/* GPRS Indicator */
append_gprs_ind(&bv, &si3->gprs_ind);
+ /* 3G Early Classmark Sending Restriction controlled by
+ * early_cm_ctrl above */
+ bitvec_set_bit(&bv, H);
+
+ if (si3->si2quater_indicator) {
+ bitvec_set_bit(&bv, H); /* indicator struct present */
+ bitvec_set_uint(&bv, 0, 1); /* message is sent on BCCH Norm */
+ }
+
bitvec_spare_padding(&bv, (bv.data_len*8)-1);
return bv.data_len;
}
diff --git a/openbsc/src/libbsc/system_information.c b/openbsc/src/libbsc/system_information.c
index 1ee9d41c2..0d96621b2 100644
--- a/openbsc/src/libbsc/system_information.c
+++ b/openbsc/src/libbsc/system_information.c
@@ -25,6 +25,7 @@
#include <string.h>
#include <stdio.h>
#include <netinet/in.h>
+#include <stdbool.h>
#include <osmocom/core/bitvec.h>
#include <osmocom/core/utils.h>
@@ -67,8 +68,160 @@ static int is_dcs_net(const struct gsm_bts *bts)
return 1;
}
-static int use_arfcn(const struct gsm_bts *bts, const int bis, const int ter,
- const int pgsm, const int arfcn)
+/* Return p(n) for given NR_OF_TDD_CELLS - see Table 9.1.54.1a, 3GPP TS 44.018 */
+unsigned range1024_p(unsigned n)
+{
+ switch (n) {
+ case 0: return 0;
+ case 1: return 10;
+ case 2: return 19;
+ case 3: return 28;
+ case 4: return 36;
+ case 5: return 44;
+ case 6: return 52;
+ case 7: return 60;
+ case 8: return 67;
+ case 9: return 74;
+ case 10: return 81;
+ case 11: return 88;
+ case 12: return 95;
+ case 13: return 102;
+ case 14: return 109;
+ case 15: return 116;
+ case 16: return 122;
+ default: return 0;
+ }
+}
+
+/* Return q(m) for given NR_OF_TDD_CELLS - see Table 9.1.54.1b, 3GPP TS 44.018 */
+unsigned range512_q(unsigned m)
+{
+ switch (m) {
+ case 0: return 0;
+ case 1: return 9;
+ case 2: return 17;
+ case 3: return 25;
+ case 4: return 32;
+ case 5: return 39;
+ case 6: return 46;
+ case 7: return 53;
+ case 8: return 59;
+ case 9: return 65;
+ case 10: return 71;
+ case 11: return 77;
+ case 12: return 83;
+ case 13: return 89;
+ case 14: return 95;
+ case 15: return 101;
+ case 16: return 106;
+ case 17: return 111;
+ case 18: return 116;
+ case 19: return 121;
+ case 20: return 126;
+ default: return 0;
+ }
+}
+
+unsigned earfcn_size(const struct osmo_earfcn_si2q *e)
+{
+ /* account for all the constant bits in append_earfcn() */
+ return 25 + osmo_earfcn_bit_size(e);
+}
+
+unsigned uarfcn_size(const uint16_t *u, const uint16_t *sc, size_t u_len)
+{
+ /*account for all the constant bits in append_uarfcn() */
+ return 29 + range1024_p(u_len);
+}
+
+bool si2q_size_check(const struct gsm_bts *bts)
+{
+ const struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list;
+ const uint16_t *u = bts->si_common.data.uarfcn_list,
+ *sc = bts->si_common.data.scramble_list;
+ size_t len = bts->si_common.uarfcn_length;
+ unsigned e_sz = e ? earfcn_size(e) : 1,
+ u_sz = len ? uarfcn_size(u, sc, len) : 1;
+ /* 2 bits are used in between UARFCN and EARFCN structs */
+ if (SI2Q_MIN_LEN + u_sz + 2 + e_sz > SI2Q_MAX_LEN)
+ return false;
+ return true;
+}
+
+/* 3GPP TS 44.018, Table 9.1.54.1 - prepend diversity bit to scrambling code */
+uint16_t encode_fdd(uint16_t scramble, bool diversity)
+{
+ if (diversity)
+ return scramble | (1 << 9);
+ return scramble;
+}
+
+int bts_uarfcn_del(struct gsm_bts *bts, uint16_t arfcn, uint16_t scramble)
+{
+ uint16_t sc0 = encode_fdd(scramble, false), sc1 = encode_fdd(scramble, true),
+ *ual = bts->si_common.data.uarfcn_list,
+ *scl = bts->si_common.data.scramble_list;
+ size_t len = bts->si_common.uarfcn_length, i;
+ for (i = 0; i < len; i++) {
+ if (arfcn == ual[i] && (sc0 == scl[i] || sc1 == scl[i])) {
+ /* we rely on the assumption that (uarfcn, scramble)
+ tuple is unique in the lists */
+ if (i != len - 1) { /* move the tail if necessary */
+ memmove(ual + i, ual + i + 1, 2 * (len - i + 1));
+ memmove(scl + i, scl + i + 1, 2 * (len - i + 1));
+ }
+ break;
+ }
+ }
+
+ if (i == len)
+ return -EINVAL;
+
+ bts->si_common.uarfcn_length--;
+ return 0;
+}
+
+int bts_uarfcn_add(struct gsm_bts *bts, uint16_t arfcn, uint16_t scramble,
+ bool diversity)
+{
+ size_t len = bts->si_common.uarfcn_length, i, k;
+ uint16_t scr, chk,
+ *ual = bts->si_common.data.uarfcn_list,
+ *scl = bts->si_common.data.scramble_list,
+ scramble1 = encode_fdd(scramble, true),
+ scramble0 = encode_fdd(scramble, false);
+
+ scr = diversity ? scramble1 : scramble0;
+ chk = diversity ? scramble0 : scramble1;
+
+ if (len == MAX_EARFCN_LIST)
+ return -ENOMEM;
+
+ for (i = 0, k = 0; i < len; i++) {
+ if (arfcn == ual[i] && (scr == scl[i] || chk == scl[i]))
+ return -EADDRINUSE;
+ if (scr > scl[i])
+ k = i + 1;
+ }
+ /* we keep lists sorted by scramble code:
+ insert into appropriate position and move the tail */
+ if (len - k) {
+ memmove(ual + k + 1, ual + k, (len - k) * 2);
+ memmove(scl + k + 1, scl + k, (len - k) * 2);
+ }
+ ual[k] = arfcn;
+ scl[k] = scr;
+ bts->si_common.uarfcn_length++;
+
+ if (si2q_size_check(bts))
+ return 0;
+
+ bts_uarfcn_del(bts, arfcn, scramble);
+ return -ENOSPC;
+}
+
+static inline int use_arfcn(const struct gsm_bts *bts, const bool bis, const bool ter,
+ const bool pgsm, const int arfcn)
{
if (bts->force_combined_si)
return !bis && !ter;
@@ -135,9 +288,9 @@ static int freq_list_bmrel_set_arfcn(uint8_t *chan_list, unsigned int arfcn)
}
/* generate a variable bitmap */
-static int enc_freq_lst_var_bitmap(uint8_t *chan_list,
+static inline int enc_freq_lst_var_bitmap(uint8_t *chan_list,
struct bitvec *bv, const struct gsm_bts *bts,
- int bis, int ter, int min, int pgsm)
+ bool bis, bool ter, int min, bool pgsm)
{
int i;
@@ -164,9 +317,9 @@ static int enc_freq_lst_var_bitmap(uint8_t *chan_list,
}
/* generate a frequency list with the range 512 format */
-static int enc_freq_lst_range(uint8_t *chan_list,
+static inline int enc_freq_lst_range(uint8_t *chan_list,
struct bitvec *bv, const struct gsm_bts *bts,
- int bis, int ter, int pgsm)
+ bool bis, bool ter, bool pgsm)
{
int arfcns[RANGE_ENC_MAX_ARFCNS];
int w[RANGE_ENC_MAX_ARFCNS];
@@ -226,15 +379,15 @@ static int enc_freq_lst_range(uint8_t *chan_list,
/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */
static int bitvec2freq_list(uint8_t *chan_list, struct bitvec *bv,
- const struct gsm_bts *bts, int bis, int ter)
+ const struct gsm_bts *bts, bool bis, bool ter)
{
- int i, rc, min = -1, max = -1, pgsm = 0, arfcns = 0;
-
+ int i, rc, min = -1, max = -1, arfcns = 0;
+ bool pgsm = false;
memset(chan_list, 0, 16);
if (bts->band == GSM_BAND_900
&& bts->c0->arfcn >= 1 && bts->c0->arfcn <= 124)
- pgsm = 1;
+ pgsm = true;
/* P-GSM-only handsets only support 'bit map 0 format' */
if (!bis && !ter && pgsm) {
chan_list[0] = 0;
@@ -327,12 +480,12 @@ static int bitvec2freq_list(uint8_t *chan_list, struct bitvec *bv,
}
/* then we generate a GSM 04.08 frequency list from the bitvec */
- return bitvec2freq_list(chan_list, bv, bts, 0, 0);
+ return bitvec2freq_list(chan_list, bv, bts, false, false);
}
/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */
static int generate_bcch_chan_list(uint8_t *chan_list, struct gsm_bts *bts,
- int si5, int bis, int ter)
+ bool si5, bool bis, bool ter)
{
struct gsm_bts *cur_bts;
struct bitvec *bv;
@@ -422,7 +575,7 @@ static int generate_si2(uint8_t *output, struct gsm_bts *bts)
si2->header.skip_indicator = 0;
si2->header.system_information = GSM48_MT_RR_SYSINFO_2;
- rc = generate_bcch_chan_list(si2->bcch_frequency_list, bts, 0, 0, 0);
+ rc = generate_bcch_chan_list(si2->bcch_frequency_list, bts, false, false, false);
if (rc < 0)
return rc;
list_arfcn(si2->bcch_frequency_list, 0xce,
@@ -448,7 +601,7 @@ static int generate_si2bis(uint8_t *output, struct gsm_bts *bts)
si2b->header.skip_indicator = 0;
si2b->header.system_information = GSM48_MT_RR_SYSINFO_2bis;
- rc = generate_bcch_chan_list(si2b->bcch_frequency_list, bts, 0, 1, 0);
+ rc = generate_bcch_chan_list(si2b->bcch_frequency_list, bts, false, true, false);
if (rc < 0)
return rc;
n = list_arfcn(si2b->bcch_frequency_list, 0xce,
@@ -482,7 +635,7 @@ static int generate_si2ter(uint8_t *output, struct gsm_bts *bts)
si2t->header.skip_indicator = 0;
si2t->header.system_information = GSM48_MT_RR_SYSINFO_2ter;
- rc = generate_bcch_chan_list(si2t->ext_bcch_frequency_list, bts, 0, 0, 1);
+ rc = generate_bcch_chan_list(si2t->ext_bcch_frequency_list, bts, false, false, true);
if (rc < 0)
return rc;
n = list_arfcn(si2t->ext_bcch_frequency_list, 0x8e,
@@ -493,6 +646,30 @@ static int generate_si2ter(uint8_t *output, struct gsm_bts *bts)
return sizeof(*si2t);
}
+static int generate_si2quater(uint8_t *output, struct gsm_bts *bts)
+{
+ int rc;
+ struct gsm48_system_information_type_2quater *si2q =
+ (struct gsm48_system_information_type_2quater *) output;
+
+ memset(si2q, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN);
+
+ si2q->header.l2_plen = GSM48_LEN2PLEN(22);
+ si2q->header.rr_protocol_discriminator = GSM48_PDISC_RR;
+ si2q->header.skip_indicator = 0;
+ si2q->header.system_information = GSM48_MT_RR_SYSINFO_2quater;
+
+ rc = rest_octets_si2quater(si2q->rest_octets,
+ &bts->si_common.si2quater_neigh_list,
+ bts->si_common.data.uarfcn_list,
+ bts->si_common.data.scramble_list,
+ bts->si_common.uarfcn_length);
+ if (rc < 0)
+ return rc;
+
+ return sizeof(*si2q) + rc;
+}
+
static struct gsm48_si_ro_info si_info = {
.selection_params = {
.present = 0,
@@ -510,6 +687,7 @@ static struct gsm48_si_ro_info si_info = {
.ra_colour = 0,
.present = 1,
},
+ .si2quater_indicator = 0,
.lsa_params = {
.present = 0,
},
@@ -545,7 +723,12 @@ static int generate_si3(uint8_t *output, struct gsm_bts *bts)
} else {
si_info.si2ter_indicator = 0;
}
-
+ if ((bts->si_valid & (1 << SYSINFO_TYPE_2quater))) {
+ LOGP(DRR, LOGL_INFO, "SI 2quater is included.\n");
+ si_info.si2quater_indicator = 1;
+ } else {
+ si_info.si2quater_indicator = 0;
+ }
/* SI3 Rest Octets (10.5.2.34), containing
CBQ, CELL_RESELECT_OFFSET, TEMPORARY_OFFSET, PENALTY_TIME
Power Offset, 2ter Indicator, Early Classmark Sending,
@@ -624,7 +807,7 @@ static int generate_si5(uint8_t *output, struct gsm_bts *bts)
si5->rr_protocol_discriminator = GSM48_PDISC_RR;
si5->skip_indicator = 0;
si5->system_information = GSM48_MT_RR_SYSINFO_5;
- rc = generate_bcch_chan_list(si5->bcch_frequency_list, bts, 1, 0, 0);
+ rc = generate_bcch_chan_list(si5->bcch_frequency_list, bts, true, false, false);
if (rc < 0)
return rc;
list_arfcn(si5->bcch_frequency_list, 0xce,
@@ -659,7 +842,7 @@ static int generate_si5bis(uint8_t *output, struct gsm_bts *bts)
si5b->rr_protocol_discriminator = GSM48_PDISC_RR;
si5b->skip_indicator = 0;
si5b->system_information = GSM48_MT_RR_SYSINFO_5bis;
- rc = generate_bcch_chan_list(si5b->bcch_frequency_list, bts, 1, 1, 0);
+ rc = generate_bcch_chan_list(si5b->bcch_frequency_list, bts, true, true, false);
if (rc < 0)
return rc;
n = list_arfcn(si5b->bcch_frequency_list, 0xce,
@@ -703,7 +886,7 @@ static int generate_si5ter(uint8_t *output, struct gsm_bts *bts)
si5t->rr_protocol_discriminator = GSM48_PDISC_RR;
si5t->skip_indicator = 0;
si5t->system_information = GSM48_MT_RR_SYSINFO_5ter;
- rc = generate_bcch_chan_list(si5t->bcch_frequency_list, bts, 1, 0, 1);
+ rc = generate_bcch_chan_list(si5t->bcch_frequency_list, bts, true, false, true);
if (rc < 0)
return rc;
n = list_arfcn(si5t->bcch_frequency_list, 0x8e,
@@ -824,6 +1007,7 @@ static const gen_si_fn_t gen_si_fn[_MAX_SYSINFO_TYPE] = {
[SYSINFO_TYPE_2] = &generate_si2,
[SYSINFO_TYPE_2bis] = &generate_si2bis,
[SYSINFO_TYPE_2ter] = &generate_si2ter,
+ [SYSINFO_TYPE_2quater] = &generate_si2quater,
[SYSINFO_TYPE_3] = &generate_si3,
[SYSINFO_TYPE_4] = &generate_si4,
[SYSINFO_TYPE_5] = &generate_si5,