aboutsummaryrefslogtreecommitdiffstats
path: root/src/osmo-bsc/gsm_04_08_rr.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/osmo-bsc/gsm_04_08_rr.c')
-rw-r--r--src/osmo-bsc/gsm_04_08_rr.c112
1 files changed, 89 insertions, 23 deletions
diff --git a/src/osmo-bsc/gsm_04_08_rr.c b/src/osmo-bsc/gsm_04_08_rr.c
index 35044a320..4659c1ac9 100644
--- a/src/osmo-bsc/gsm_04_08_rr.c
+++ b/src/osmo-bsc/gsm_04_08_rr.c
@@ -321,47 +321,113 @@ static void gsm48_cell_desc(struct gsm48_cell_desc *cd,
cd->arfcn_lo = bts->c0->arfcn & 0xff;
}
-/*! \brief Encode a TS 04.08 multirate config LV according to 10.5.2.21aa
- * \param[out] lv caller-allocated buffer of 7 bytes. First octet is IS length
- * \param[in] mr multi-rate configuration to encode
- * \param[in] modes array describing the AMR modes
- * \returns 0 on success */
-int gsm48_multirate_config(uint8_t *lv, const struct amr_multirate_conf *mr, const struct amr_mode *modes)
+/*! \brief Encode a TS 04.08 multirate config LV according to 10.5.2.21aa.
+ * \param[out] lv caller-allocated buffer of 7 bytes. First octet is is length.
+ * \param[in] mr_conf multi-rate configuration to encode (selected modes).
+ * \param[in] modes array describing the AMR modes.
+ * \param[in] num_modes length of the modes array.
+ * \returns 0 on success, -EINVAL on failure. */
+int gsm48_multirate_config(uint8_t *lv,
+ const struct gsm48_multi_rate_conf *mr_conf,
+ const struct amr_mode *modes, unsigned int num_modes)
{
- int num = 0, i;
+ int num = 0;
+ unsigned int i;
+ unsigned int k;
+ unsigned int m = 0;
+ bool mode_valid;
+ uint8_t *gsm48_ie = (uint8_t *) mr_conf;
+ const struct amr_mode *modes_selected[4];
+
+ /* Check if modes for consistency (order and duplicates) */
+ for (i = 0; i < num_modes; i++) {
+ if (i > 0 && modes[i - 1].mode > modes[i].mode) {
+ LOGP(DRR, LOGL_ERROR,
+ "BUG: Multirate codec with inconsistant config (mode order).\n");
+ return -EINVAL;
+ }
+ if (i > 0 && modes[i - 1].mode == modes[i].mode) {
+ LOGP(DRR, LOGL_ERROR,
+ "BUG: Multirate codec with inconsistant config (duplicate modes).\n");
+ return -EINVAL;
+ }
+ }
+ /* Check if the active set that is defined in mr_conf has at least one
+ * mode but not more than 4 modes set */
for (i = 0; i < 8; i++) {
- if (((mr->gsm48_ie[1] >> i) & 1))
+ if (((gsm48_ie[1] >> i) & 1))
num++;
}
if (num > 4) {
- LOGP(DRR, LOGL_ERROR, "BUG: Using multirate codec with too "
- "many modes in config.\n");
- num = 4;
+ LOGP(DRR, LOGL_ERROR,
+ "BUG: Multirate codec with too many modes in config.\n");
+ return -EINVAL;
}
if (num < 1) {
- LOGP(DRR, LOGL_ERROR, "BUG: Using multirate codec with no "
- "mode in config.\n");
- num = 1;
+ LOGP(DRR, LOGL_ERROR,
+ "BUG: Multirate codec with no mode in config.\n");
+ return -EINVAL;
}
+ /* Do not accept excess hysteresis or threshold values */
+ for (i = 0; i < num_modes; i++) {
+ if (modes[i].threshold >= 64) {
+ LOGP(DRR, LOGL_ERROR,
+ "BUG: Multirate codec with excessive threshold values.\n");
+ return -EINVAL;
+ }
+ if (modes[i].hysteresis >= 16) {
+ LOGP(DRR, LOGL_ERROR,
+ "BUG: Multirate codec with excessive hysteresis values.\n");
+ return -EINVAL;
+ }
+ }
+
+ /* Scan through the selected modes and find a matching threshold/
+ * hysteresis value for that mode. */
+ for (i = 0; i < 8; i++) {
+ if (((gsm48_ie[1] >> i) & 1)) {
+ mode_valid = false;
+ for (k = 0; k < num_modes; k++) {
+ if (modes[k].mode == i) {
+ mode_valid = true;
+ modes_selected[m] = &modes[k];
+ m++;
+ }
+ }
+ if (!mode_valid) {
+ LOGP(DRR, LOGL_ERROR,
+ "BUG: Multirate codec with inconsistant config (no mode defined).\n");
+ return -EINVAL;
+ }
+ }
+ }
+ OSMO_ASSERT(m <= 4);
+
+ /* When the caller is not interested in any result, skip the actual
+ * composition of the IE (dry run) */
+ if (!lv)
+ return 0;
+
+ /* Compose output buffer */
lv[0] = (num == 1) ? 2 : (num + 2);
- memcpy(lv + 1, mr->gsm48_ie, 2);
+ memcpy(lv + 1, gsm48_ie, 2);
if (num == 1)
return 0;
- lv[3] = modes[0].threshold & 0x3f;
- lv[4] = modes[0].hysteresis << 4;
+ lv[3] = modes_selected[0]->threshold & 0x3f;
+ lv[4] = modes_selected[0]->hysteresis << 4;
if (num == 2)
return 0;
- lv[4] |= (modes[1].threshold & 0x3f) >> 2;
- lv[5] = modes[1].threshold << 6;
- lv[5] |= (modes[1].hysteresis & 0x0f) << 2;
+ lv[4] |= (modes_selected[1]->threshold & 0x3f) >> 2;
+ lv[5] = modes_selected[1]->threshold << 6;
+ lv[5] |= (modes_selected[1]->hysteresis & 0x0f) << 2;
if (num == 3)
return 0;
- lv[5] |= (modes[2].threshold & 0x3f) >> 4;
- lv[6] = modes[2].threshold << 4;
- lv[6] |= modes[2].hysteresis & 0x0f;
+ lv[5] |= (modes_selected[2]->threshold & 0x3f) >> 4;
+ lv[6] = modes_selected[2]->threshold << 4;
+ lv[6] |= modes_selected[2]->hysteresis & 0x0f;
return 0;
}