diff options
author | Max <msuraev@sysmocom.de> | 2016-04-20 15:57:13 +0200 |
---|---|---|
committer | Harald Welte <laforge@gnumonks.org> | 2016-04-22 14:55:33 +0200 |
commit | 26679e0475593aca645c7029e1aad899da73217a (patch) | |
tree | b42d881edde2eccfbb350c26def3fd59d9de99db /openbsc/src/libbsc | |
parent | 27c3e76aa7e0912d3404b96a4a043e7208044d0f (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.c | 53 | ||||
-rw-r--r-- | openbsc/src/libbsc/rest_octets.c | 91 | ||||
-rw-r--r-- | openbsc/src/libbsc/system_information.c | 139 |
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; |