aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src/libbsc
diff options
context:
space:
mode:
authorMax <msuraev@sysmocom.de>2016-04-20 15:57:13 +0200
committerHarald Welte <laforge@gnumonks.org>2016-04-22 14:55:33 +0200
commit26679e0475593aca645c7029e1aad899da73217a (patch)
treeb42d881edde2eccfbb350c26def3fd59d9de99db /openbsc/src/libbsc
parent27c3e76aa7e0912d3404b96a4a043e7208044d0f (diff)
Add basic UARFCN support
* add data structures, generation functions * vty interface for neightbor UARFCNs specific to SI2quater * vty test * unit test Fixes: OS#1666
Diffstat (limited to 'openbsc/src/libbsc')
-rw-r--r--openbsc/src/libbsc/bsc_vty.c53
-rw-r--r--openbsc/src/libbsc/rest_octets.c91
-rw-r--r--openbsc/src/libbsc/system_information.c139
3 files changed, 262 insertions, 21 deletions
diff --git a/openbsc/src/libbsc/bsc_vty.c b/openbsc/src/libbsc/bsc_vty.c
index 85b8394b1..cc4686577 100644
--- a/openbsc/src/libbsc/bsc_vty.c
+++ b/openbsc/src/libbsc/bsc_vty.c
@@ -707,6 +707,14 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts)
}
}
+ 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");
@@ -2813,6 +2821,49 @@ DEFUN(cfg_bts_si2quater_neigh_del, cfg_bts_si2quater_neigh_del_cmd,
return CMD_SUCCESS;
}
+DEFUN(cfg_bts_si2quater_uarfcn_add, cfg_bts_si2quater_uarfcn_add_cmd,
+ "si2quater neighbor-list add uarfcn <1900-2200> <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 -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 <1900-2200> <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"
@@ -3945,6 +3996,8 @@ int bsc_vty_init(const struct log_info *cat)
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);
diff --git a/openbsc/src/libbsc/rest_octets.c b/openbsc/src/libbsc/rest_octets.c
index 113af5ca9..b59430bcc 100644
--- a/openbsc/src/libbsc/rest_octets.c
+++ b/openbsc/src/libbsc/rest_octets.c
@@ -31,8 +31,7 @@
#include <osmocom/core/bitvec.h>
#include <openbsc/rest_octets.h>
#include <openbsc/arfcn_range_encode.h>
-
-#define SI2Q_MAX_LEN 160
+#include <openbsc/system_information.h>
/* generate SI1 rest octets */
int rest_octets_si1(uint8_t *data, uint8_t *nch_pos, int is1800_net)
@@ -108,14 +107,6 @@ static inline void append_eutran_neib_cell(struct bitvec *bv,
bitvec_set_bit(bv, 0);
}
-static inline int append_earfcn_size(const struct osmo_earfcn_si2q *e)
-{
- if (!e)
- return -EFAULT;
- /* account for all the constant bits */
- return 25 + osmo_earfcn_bit_size(e);
-}
-
static inline void append_earfcn(struct bitvec *bv,
const struct osmo_earfcn_si2q *e)
{
@@ -188,11 +179,62 @@ static inline void append_earfcn(struct bitvec *bv,
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,
- bool uarfcn, bool earfcn)
+ const uint16_t *u, const uint16_t *sc, size_t u_len)
{
- int rc;
+ unsigned sz;
struct bitvec bv;
bv.data = data;
bv.data_len = 20;
@@ -226,8 +268,17 @@ int rest_octets_si2quater(uint8_t *data, const struct osmo_earfcn_si2q *e,
/* No extension (length) */
bitvec_set_bit(&bv, 0);
- if (uarfcn) {
-
+ 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);
}
@@ -237,12 +288,14 @@ int rest_octets_si2quater(uint8_t *data, const struct osmo_earfcn_si2q *e,
/* No GPRS_3G_MEASUREMENT Parameters Descr. */
bitvec_set_bit(&bv, 0);
- if (earfcn) {
- rc = append_earfcn_size(e);
- if (rc < 0)
- return rc;
- if (rc + bv.cur_bit > SI2Q_MAX_LEN)
+ 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: */
diff --git a/openbsc/src/libbsc/system_information.c b/openbsc/src/libbsc/system_information.c
index 43a492af1..8952534c1 100644
--- a/openbsc/src/libbsc/system_information.c
+++ b/openbsc/src/libbsc/system_information.c
@@ -68,6 +68,139 @@ static int is_dcs_net(const struct gsm_bts *bts)
return 1;
}
+/* Return q(m) 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);
+}
+
+/* 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++;
+ return 0;
+}
+
static inline int use_arfcn(const struct gsm_bts *bts, const bool bis, const bool ter,
const bool pgsm, const int arfcn)
{
@@ -508,8 +641,10 @@ static int generate_si2quater(uint8_t *output, struct gsm_bts *bts)
si2q->header.system_information = GSM48_MT_RR_SYSINFO_2quater;
rc = rest_octets_si2quater(si2q->rest_octets,
- &bts->si_common.si2quater_neigh_list, false,
- true);
+ &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;