aboutsummaryrefslogtreecommitdiffstats
path: root/openbsc/src/libbsc
diff options
context:
space:
mode:
Diffstat (limited to 'openbsc/src/libbsc')
-rw-r--r--openbsc/src/libbsc/bsc_init.c6
-rw-r--r--openbsc/src/libbsc/bsc_vty.c78
-rw-r--r--openbsc/src/libbsc/rest_octets.c80
-rw-r--r--openbsc/src/libbsc/system_information.c152
4 files changed, 186 insertions, 130 deletions
diff --git a/openbsc/src/libbsc/bsc_init.c b/openbsc/src/libbsc/bsc_init.c
index 25f3fdcee..b95c7b08e 100644
--- a/openbsc/src/libbsc/bsc_init.c
+++ b/openbsc/src/libbsc/bsc_init.c
@@ -101,7 +101,7 @@ int bsc_shutdown_net(struct gsm_network *net)
static int rsl_si(struct gsm_bts_trx *trx, enum osmo_sysinfo_type i, int si_len)
{
struct gsm_bts *bts = trx->bts;
- int rc;
+ int rc, j;
DEBUGP(DRR, "SI%s: %s\n", get_value_string(osmo_sitype_strs, i),
osmo_hexdump(GSM_BTS_SI(bts, i), GSM_MACBLOCK_LEN));
@@ -114,6 +114,10 @@ static int rsl_si(struct gsm_bts_trx *trx, enum osmo_sysinfo_type i, int si_len)
rc = rsl_sacch_filling(trx, osmo_sitype2rsl(i),
GSM_BTS_SI(bts, i), si_len);
break;
+ case SYSINFO_TYPE_2quater:
+ for (j = 0; j <= bts->si2q_count; j++)
+ rc = rsl_bcch_info(trx, i, (const uint8_t *)GSM_BTS_SI2Q(bts, j), GSM_MACBLOCK_LEN);
+ break;
default:
rc = rsl_bcch_info(trx, osmo_sitype2rsl(i),
GSM_BTS_SI(bts, i), si_len);
diff --git a/openbsc/src/libbsc/bsc_vty.c b/openbsc/src/libbsc/bsc_vty.c
index b05d3d9da..2fc39ab4c 100644
--- a/openbsc/src/libbsc/bsc_vty.c
+++ b/openbsc/src/libbsc/bsc_vty.c
@@ -2774,6 +2774,7 @@ DEFUN(cfg_bts_neigh, cfg_bts_neigh_cmd,
return CMD_SUCCESS;
}
+/* help text should be kept in sync with EARFCN_*_INVALID defines */
DEFUN(cfg_bts_si2quater_neigh_add, cfg_bts_si2quater_neigh_add_cmd,
"si2quater neighbor-list add earfcn <0-65535> thresh-hi <0-31> "
"thresh-lo <0-32> prio <0-8> qrxlv <0-32> meas <0-8>",
@@ -2791,54 +2792,37 @@ DEFUN(cfg_bts_si2quater_neigh_add, cfg_bts_si2quater_neigh_add_cmd,
uint16_t arfcn = atoi(argv[0]);
uint8_t thresh_hi = atoi(argv[1]), thresh_lo = atoi(argv[2]),
prio = atoi(argv[3]), qrx = atoi(argv[4]), meas = atoi(argv[5]);
- int r = osmo_earfcn_add(e, arfcn,
- (meas < 8) ? meas : OSMO_EARFCN_MEAS_INVALID);
+ int r = bts_earfcn_add(bts, arfcn, thresh_hi, thresh_lo, prio, qrx, meas);
- if (r < 0) {
- vty_out(vty, "Unable to add ARFCN %u: %s%s", arfcn, strerror(-r),
- VTY_NEWLINE);
- return CMD_WARNING;
- }
-
- if (e->thresh_hi && thresh_hi != e->thresh_hi)
- vty_out(vty, "Warning: multiple threshold-high are not "
- "supported, overriding previous threshold %u%s",
- e->thresh_hi, VTY_NEWLINE);
-
- e->thresh_hi = thresh_hi;
-
- if (thresh_lo != 32) {
- if (e->thresh_lo_valid && e->thresh_lo != thresh_lo)
- vty_out(vty, "Warning: multiple threshold-low are not "
- "supported, overriding previous threshold %u%s",
- e->thresh_lo, VTY_NEWLINE);
- e->thresh_lo = thresh_lo;
- e->thresh_lo_valid = true;
- }
-
- if (qrx != 32) {
- if (e->qrxlm_valid && e->qrxlm != qrx)
- vty_out(vty, "Warning: multiple QRXLEVMIN are not "
- "supported, overriding previous value %u%s",
- e->qrxlm, VTY_NEWLINE);
- e->qrxlm = qrx;
- e->qrxlm_valid = true;
- }
-
- if (prio != 8) {
- if (e->prio_valid && e->prio != prio)
- vty_out(vty, "Warning: multiple priorities are not "
- "supported, overriding previous value %u%s",
- e->prio, VTY_NEWLINE);
- e->prio = prio;
- e->prio_valid = true;
+ switch (r) {
+ case 1:
+ vty_out(vty, "Warning: multiple threshold-high are not supported, overriding with %u%s",
+ thresh_hi, VTY_NEWLINE);
+ break;
+ case EARFCN_THRESH_LOW_INVALID:
+ vty_out(vty, "Warning: multiple threshold-low are not supported, overriding with %u%s",
+ thresh_lo, VTY_NEWLINE);
+ break;
+ case EARFCN_QRXLV_INVALID + 1:
+ vty_out(vty, "Warning: multiple QRXLEVMIN are not supported, overriding with %u%s",
+ qrx, VTY_NEWLINE);
+ break;
+ case EARFCN_PRIO_INVALID:
+ vty_out(vty, "Warning: multiple priorities are not supported, overriding with %u%s",
+ prio, VTY_NEWLINE);
+ break;
+ default:
+ if (r < 0) {
+ vty_out(vty, "Unable to add ARFCN %u: %s%s", arfcn, strerror(-r), VTY_NEWLINE);
+ return CMD_WARNING;
+ }
}
- if (si2q_num(bts) < 2) /* FIXME: use SI2Q_MAX_NUM */
+ if (si2q_num(bts) <= SI2Q_MAX_NUM)
return CMD_SUCCESS;
vty_out(vty, "Warning: not enough space in SI2quater (%u/%u used) for a given EARFCN %u%s",
- bts->si2q_count, 2, arfcn, VTY_NEWLINE); /* FIXME: use SI2Q_MAX_NUM */
+ bts->si2q_count, SI2Q_MAX_NUM, arfcn, VTY_NEWLINE);
osmo_earfcn_del(e, arfcn);
return CMD_WARNING;
@@ -2877,16 +2861,14 @@ DEFUN(cfg_bts_si2quater_uarfcn_add, cfg_bts_si2quater_uarfcn_add_cmd,
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);
+ vty_out(vty, "Unable to add UARFCN: max number of UARFCNs (%u) reached%s", MAX_EARFCN_LIST, VTY_NEWLINE);
return CMD_WARNING;
case -ENOSPC:
- vty_out(vty, "Warning: not enough space in si2quater for a "
- "given arfcn%s", VTY_NEWLINE);
+ vty_out(vty, "Warning: not enough space in SI2quater for a given UARFCN (%u, %u)%s",
+ arfcn, scramble, VTY_NEWLINE);
return CMD_WARNING;
case -EADDRINUSE:
- vty_out(vty, "Unable to add arfcn: (%u, %u) is already added%s",
- arfcn, scramble, VTY_NEWLINE);
+ vty_out(vty, "Unable to add UARFCN: (%u, %u) is already added%s", arfcn, scramble, VTY_NEWLINE);
return CMD_WARNING;
}
diff --git a/openbsc/src/libbsc/rest_octets.c b/openbsc/src/libbsc/rest_octets.c
index a6fdf46fe..fdab70a0c 100644
--- a/openbsc/src/libbsc/rest_octets.c
+++ b/openbsc/src/libbsc/rest_octets.c
@@ -65,6 +65,12 @@ static inline void append_eutran_neib_cell(struct bitvec *bv, struct gsm_bts *bt
unsigned i, skip = 0;
size_t offset = bts->e_offset;
uint8_t rem = budget - 6, earfcn_budget; /* account for mandatory stop bit and THRESH_E-UTRAN_high */
+
+ if (budget <= 6)
+ return;
+
+ OSMO_ASSERT(budget <= SI2Q_MAX_LEN);
+
/* first we have to properly adjust budget requirements */
if (e->prio_valid) /* E-UTRAN_PRIORITY: 3GPP TS 45.008*/
rem -= 4;
@@ -87,16 +93,17 @@ static inline void append_eutran_neib_cell(struct bitvec *bv, struct gsm_bts *bt
if (skip < offset) {
skip++; /* ignore EARFCNs added on previous calls */
} else {
- earfcn_budget = 17; /* computer budget per-EARFCN */
+ earfcn_budget = 17; /* compute budget per-EARFCN */
if (OSMO_EARFCN_MEAS_INVALID == e->meas_bw[i])
earfcn_budget++;
else
earfcn_budget += 4;
- if (rem - earfcn_budget < 0) {
+ if (rem - earfcn_budget < 0)
break;
- } else {
+ else {
bts->e_offset++;
+ rem -= earfcn_budget;
bitvec_set_bit(bv, 1); /* EARFCN: */
bitvec_set_uint(bv, e->arfcn[i], 16);
@@ -143,6 +150,12 @@ static inline void append_eutran_neib_cell(struct bitvec *bv, struct gsm_bts *bt
static inline void append_earfcn(struct bitvec *bv, struct gsm_bts *bts, uint8_t budget)
{
+ int rem = budget - 25;
+ if (rem <= 0)
+ return;
+
+ OSMO_ASSERT(budget <= SI2Q_MAX_LEN);
+
/* Additions in Rel-5: */
bitvec_set_bit(bv, H);
/* No 3G Additional Measurement Param. Descr. */
@@ -191,7 +204,7 @@ static inline void append_earfcn(struct bitvec *bv, struct gsm_bts *bts, uint8_t
bitvec_set_bit(bv, 1);
/* N. B: 25 bits are set in append_earfcn() - keep it in sync with budget adjustment below: */
- append_eutran_neib_cell(bv, bts, budget - 25);
+ append_eutran_neib_cell(bv, bts, rem);
/* stop bit - end of Repeated E-UTRAN Neighbour Cells sequence: */
bitvec_set_bit(bv, 0);
@@ -267,7 +280,12 @@ static inline int append_uarfcns(struct bitvec *bv, struct gsm_bts *bts, uint8_t
const uint16_t *u = bts->si_common.data.uarfcn_list, *sc = bts->si_common.data.scramble_list;
int i, j, k, rc, st = 0, a[bts->si_common.uarfcn_length];
uint16_t cu = u[bts->u_offset]; /* caller ensures that length is positive */
- uint8_t rem = budget - 7; /* account for constant bits right away */
+ uint8_t rem = budget - 7, offset_diff; /* account for constant bits right away */
+
+ OSMO_ASSERT(budget <= SI2Q_MAX_LEN);
+
+ if (budget <= 7)
+ return -ENOMEM;
/* 3G Neighbour Cell Description */
bitvec_set_bit(bv, 1);
@@ -282,20 +300,22 @@ static inline int append_uarfcns(struct bitvec *bv, struct gsm_bts *bts, uint8_t
bitvec_set_bit(bv, 0);
for (i = bts->u_offset; i < bts->si_common.uarfcn_length; i++) {
- for (j = st, k = 0; j < i; j++)
+ offset_diff = 0;
+ for (j = st, k = 0; j < i; j++) {
a[k++] = sc[j]; /* copy corresponding SCs */
-
+ offset_diff++; /* compute proper offset step */
+ }
if (u[i] != cu) { /* we've reached new UARFCN */
rc = append_utran_fdd_length(cu, a, bts->si_common.uarfcn_length, k);
if (rc < 0) { /* estimate bit length requirements */
return rc;
}
- if (rem - rc < 0) {
+ if (rem - rc <= 0)
break; /* we have ran out of budget in current SI2q */
- } else {
+ else {
rem -= append_utran_fdd(bv, cu, a, k);
- bts->u_offset++;
+ bts->u_offset += offset_diff;
}
cu = u[i];
st = i; /* update start position */
@@ -303,9 +323,11 @@ static inline int append_uarfcns(struct bitvec *bv, struct gsm_bts *bts, uint8_t
}
if (rem > 22) { /* add last UARFCN not covered by previous cycle if it could possibly fit into budget */
- for (i = st, k = 0; i < bts->si_common.uarfcn_length; i++)
+ offset_diff = 0;
+ for (i = st, k = 0; i < bts->si_common.uarfcn_length; i++) {
a[k++] = sc[i];
-
+ offset_diff++;
+ }
rc = append_utran_fdd_length(cu, a, bts->si_common.uarfcn_length, k);
if (rc < 0) {
return rc;
@@ -313,7 +335,7 @@ static inline int append_uarfcns(struct bitvec *bv, struct gsm_bts *bts, uint8_t
if (rem - rc >= 0) {
rem -= append_utran_fdd(bv, cu, a, k);
- bts->u_offset++;
+ bts->u_offset += offset_diff;
}
}
@@ -331,6 +353,10 @@ int rest_octets_si2quater(uint8_t *data, struct gsm_bts *bts)
{
int rc;
struct bitvec bv;
+
+ if (bts->si2q_count < bts->si2q_index)
+ return -EINVAL;
+
bv.data = data;
bv.data_len = 20;
bitvec_zero(&bv);
@@ -362,34 +388,28 @@ int rest_octets_si2quater(uint8_t *data, struct gsm_bts *bts)
/* No extension (length) */
bitvec_set_bit(&bv, 0);
- if (bts->si_common.uarfcn_length) {
- /* Even if we do not append EARFCN we still need to set 3 bits */
- rc = append_uarfcns(&bv, bts, SI2Q_MAX_LEN - (bv.cur_bit + 3));
+ rc = SI2Q_MAX_LEN - (bv.cur_bit + 3);
+ if (rc > 0 && bts->si_common.uarfcn_length - bts->u_offset > 0) {
+ rc = append_uarfcns(&bv, bts, rc);
if (rc < 0) {
- LOGP(DRR, LOGL_ERROR, "SI2quater: failed to append %zu UARFCNs due to range encoding failure: %s\n",
- bts->si_common.uarfcn_length, strerror(-rc));
+ LOGP(DRR, LOGL_ERROR, "SI2quater [%u/%u]: failed to append %zu UARFCNs due to range encoding "
+ "failure: %s\n",
+ bts->si2q_index, bts->si2q_count, bts->si_common.uarfcn_length, strerror(-rc));
return rc;
}
- } else { /* No 3G Neighbour Cell Description */
+ } 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 (si2q_earfcn_count(&bts->si_common.si2quater_neigh_list)) {
- append_earfcn(&bv, bts, SI2Q_MAX_LEN - bv.cur_bit);
-
- /* FIXME: remove following check once multiple SI2q are properly supported */
- if ((bts->e_offset != si2q_earfcn_count(&bts->si_common.si2quater_neigh_list)) ||
- si2q_earfcn_count(&bts->si_common.si2quater_neigh_list) > 5)
- return -ENOMEM;
- } else {
- /* No Additions in Rel-5: */
+ rc = SI2Q_MAX_LEN - bv.cur_bit;
+ if (rc > 0 && si2q_earfcn_count(&bts->si_common.si2quater_neigh_list) - bts->e_offset > 0)
+ append_earfcn(&bv, bts, rc);
+ else /* No Additions in Rel-5: */
bitvec_set_bit(&bv, L);
- }
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 a074a783a..dcabbbdd1 100644
--- a/openbsc/src/libbsc/system_information.c
+++ b/openbsc/src/libbsc/system_information.c
@@ -122,50 +122,73 @@ unsigned range512_q(unsigned m)
}
}
-static inline unsigned earfcn_size(const struct gsm_bts *bts)
+size_t si2q_earfcn_count(const struct osmo_earfcn_si2q *e)
{
- const struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list; /* EARFCN */
+ unsigned i, ret = 0;
+
+ if (!e)
+ return 0;
+
+ for (i = 0; i < e->length; i++)
+ if (e->arfcn[i] != OSMO_EARFCN_INVALID)
+ ret++;
- /* account for all the constant bits in append_earfcn() */
- return 25 + osmo_earfcn_bit_size_ext(e, bts->e_offset);
+ return ret;
}
-static inline unsigned uarfcn_size(const struct gsm_bts *bts)
+/* generate SI2quater messages, return rest octets length of last generated message or negative error code */
+static int make_si2quaters(struct gsm_bts *bts, bool counting)
{
- const uint16_t *u = bts->si_common.data.uarfcn_list;
- uint16_t cu = u[bts->u_offset]; /* UARFCN */
- /* account for all the constant bits in append_uarfcns() */
- unsigned s = 7, append = 22, r = 0, i, st = 0, j, k;
-
- for (i = bts->u_offset; i < bts->si_common.uarfcn_length; i++) {
- for (j = st, k = 0; j < i; j++, k++);
- if (u[i] != cu) { /* we've reached new UARFCN */
- r += (append + range1024_p(k));
- cu = u[i];
- st = i; /* update start position */
+ int rc;
+ bool memory_exceeded = true;
+ struct gsm48_system_information_type_2quater *si2q;
+
+ for (bts->si2q_index = 0; bts->si2q_index < SI2Q_MAX_NUM; bts->si2q_index++) {
+ si2q = GSM_BTS_SI2Q(bts, bts->si2q_index);
+ if (counting) { /* that's legitimate if we're called for counting purpose: */
+ if (bts->si2q_count < bts->si2q_index)
+ bts->si2q_count = bts->si2q_index;
+ } else {
+ 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);
+ if (rc < 0)
+ return rc;
+
+ if (bts->u_offset >= bts->si_common.uarfcn_length &&
+ bts->e_offset >= si2q_earfcn_count(&bts->si_common.si2quater_neigh_list)) {
+ memory_exceeded = false;
+ break;
}
}
- /* add last UARFCN not covered by previous cycle */
- for (i = st, k = 0; i < bts->si_common.uarfcn_length; i++, k++);
+ if (memory_exceeded)
+ return -ENOMEM;
- return s + r + append + range1024_p(k);
+ return rc;
}
+/* we generate SI2q rest octets twice to get proper estimation but it's one time cost anyway */
uint8_t si2q_num(struct gsm_bts *bts)
{
- size_t est, e_sz = 1, u_sz = 1;
-
- if (&bts->si_common.si2quater_neigh_list) /* EARFCN */
- e_sz = earfcn_size(bts);
+ int rc = make_si2quaters(bts, true);
+ uint8_t num = bts->si2q_index + 1; /* number of SI2quater messages */
- if (bts->si_common.uarfcn_length) /* UARFCN */
- u_sz = uarfcn_size(bts);
+ /* N. B: si2q_num() should NEVER be called during actualSI2q rest octets generation
+ we're not re-entrant because of the following code: */
+ bts->u_offset = 0;
+ bts->e_offset = 0;
- /* 2 bits are used in between UARFCN and EARFCN structs */
- est = 1 + (e_sz + u_sz) / (SI2Q_MAX_LEN - (SI2Q_MIN_LEN + 2));
+ if (rc < 0)
+ return 0xFF; /* return impossible index as an indicator of error in generating SI2quater */
- return est;
+ return num;
}
/* 3GPP TS 44.018, Table 9.1.54.1 - prepend diversity bit to scrambling code */
@@ -176,6 +199,44 @@ static inline uint16_t encode_fdd(uint16_t scramble, bool diversity)
return scramble;
}
+int bts_earfcn_add(struct gsm_bts *bts, uint16_t earfcn, uint8_t thresh_hi, uint8_t thresh_lo, uint8_t prio,
+ uint8_t qrx, uint8_t meas_bw)
+{
+ struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list;
+ int r = osmo_earfcn_add(e, earfcn, (meas_bw < EARFCN_MEAS_BW_INVALID) ? meas_bw : OSMO_EARFCN_MEAS_INVALID);
+
+ if (r < 0)
+ return r;
+
+ if (e->thresh_hi && thresh_hi != e->thresh_hi)
+ r = 1;
+
+ e->thresh_hi = thresh_hi;
+
+ if (thresh_lo != EARFCN_THRESH_LOW_INVALID) {
+ if (e->thresh_lo_valid && e->thresh_lo != thresh_lo)
+ r = EARFCN_THRESH_LOW_INVALID;
+ e->thresh_lo = thresh_lo;
+ e->thresh_lo_valid = true;
+ }
+
+ if (qrx != EARFCN_QRXLV_INVALID) {
+ if (e->qrxlm_valid && e->qrxlm != qrx)
+ r = EARFCN_QRXLV_INVALID + 1;
+ e->qrxlm = qrx;
+ e->qrxlm_valid = true;
+ }
+
+ if (prio != EARFCN_PRIO_INVALID) {
+ if (e->prio_valid && e->prio != prio)
+ r = EARFCN_PRIO_INVALID;
+ e->prio = prio;
+ e->prio_valid = true;
+ }
+
+ return r;
+}
+
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),
@@ -237,8 +298,10 @@ int bts_uarfcn_add(struct gsm_bts *bts, uint16_t arfcn, uint16_t scramble, bool
scl[k] = scr;
bts->si_common.uarfcn_length++;
- if (si2q_num(bts) < 2) /* FIXME: use SI2Q_MAX_NUM */
+ if (si2q_num(bts) <= SI2Q_MAX_NUM) {
+ bts->si2q_count = si2q_num(bts) - 1;
return 0;
+ }
bts_uarfcn_del(bts, arfcn, scramble);
return -ENOSPC;
@@ -689,39 +752,26 @@ static inline bool si2quater_not_needed(struct gsm_bts *bts)
return false;
}
-size_t si2q_earfcn_count(const struct osmo_earfcn_si2q *e)
-{
- unsigned i, ret = 0;
-
- if (!e)
- return 0;
-
- for (i = 0; i < e->length; i++)
- if (e->arfcn[i] != OSMO_EARFCN_INVALID)
- ret++;
-
- return ret;
-}
-
static int generate_si2quater(enum osmo_sysinfo_type t, struct gsm_bts *bts)
{
int rc;
- struct gsm48_system_information_type_2quater *si2q = GSM_BTS_SI2Q(bts);
+ struct gsm48_system_information_type_2quater *si2q;
if (si2quater_not_needed(bts)) /* generate rest_octets for SI2q only when necessary */
return GSM_MACBLOCK_LEN;
- 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;
+ bts->u_offset = 0;
+ bts->e_offset = 0;
+ bts->si2q_index = 0;
+ bts->si2q_count = si2q_num(bts) - 1;
- rc = rest_octets_si2quater(si2q->rest_octets, bts);
+ rc = make_si2quaters(bts, false);
if (rc < 0)
return rc;
+ OSMO_ASSERT(bts->si2q_count == bts->si2q_index);
+ OSMO_ASSERT(bts->si2q_count <= SI2Q_MAX_NUM);
+
return sizeof(*si2q) + rc;
}