#include #include #include #include #include /* Reasonable defaults for AMR-FR and AMR-HR rate configuration. * The values are taken from 3GPP TS 51.010-1 (version 13.11.0). * See 14.2.19.4.1 and 14.2.20.4.1 for AMR-FR and AMR-HR, respectively. * * ^ C/I (dB) | FR / HR | * | | * | | * MODE4 | | * = | ----+---- THR_MX_Up(3) | 20.5 / 18.0 | * | | | * | = ----+---- THR_MX_Dn(4) | 18.5 / 16.0 | * MODE3 | | * | = ----+---- THR_MX_Up(2) | 14.5 / 14.0 | * | | | * = | ----+---- THR_MX_Dn(3) | 12.5 / 12.0 | * MODE2 | | * = | ----+---- THR_MX_Up(1) | 8.5 / 10.0 | * | | | * | = ----+---- THR_MX_Dn(2) | 6.5 / 8.0 | * MODE1 | | * | | * | | */ static const struct gsm48_multi_rate_conf amr_fr_mr_cfg_def = { .m4_75 = 1, .m5_90 = 1, .m7_95 = 1, .m12_2 = 1, }; static const struct amr_mode amr_fr_bts_mode_def[] = { { .mode = 0, /* 4.75k */ .threshold = 13, /* THR_MX_Dn(2): 6.5 dB */ .hysteresis = 4, /* THR_MX_Up(1): 8.5 dB */ }, { .mode = 2, /* 5.90k */ .threshold = 25, /* THR_MX_Dn(3): 12.5 dB */ .hysteresis = 4, /* THR_MX_Up(2): 14.5 dB */ }, { .mode = 5, /* 7.95k */ .threshold = 37, /* THR_MX_Dn(4): 18.5 dB */ .hysteresis = 4, /* THR_MX_Up(3): 20.5 dB */ }, { .mode = 7, /* 12.2k */ /* this is the last mode, so no threshold */ }, }; static const struct gsm48_multi_rate_conf amr_hr_mr_cfg_def = { .m4_75 = 1, .m5_90 = 1, .m6_70 = 1, .m7_95 = 1, }; static const struct amr_mode amr_hr_bts_mode_def[] = { { .mode = 0, /* 4.75k */ .threshold = 16, /* THR_MX_Dn(2): 8.0 dB */ .hysteresis = 4, /* THR_MX_Up(1): 10.0 dB */ }, { .mode = 2, /* 5.90k */ .threshold = 24, /* THR_MX_Dn(3): 12.0 dB */ .hysteresis = 4, /* THR_MX_Up(2): 14.0 dB */ }, { .mode = 3, /* 6.70k */ .threshold = 32, /* THR_MX_Dn(4): 16.0 dB */ .hysteresis = 4, /* THR_MX_Up(3): 18.0 dB */ }, { .mode = 5, /* 7.95k */ /* this is the last mode, so no threshold */ }, }; void amr_log_mr_conf(int ss, int logl, const char *pfx, struct amr_multirate_conf *amr_mrc) { int i; LOGP(ss, logl, "%s AMR MR Conf: num_modes=%u", pfx, amr_mrc->num_modes); for (i = 0; i < amr_mrc->num_modes; i++) LOGPC(ss, logl, ", mode[%u] = %u/%u/%u", i, amr_mrc->mode[i].mode, amr_mrc->mode[i].threshold, amr_mrc->mode[i].hysteresis); LOGPC(ss, logl, "\n"); } static inline int get_amr_mode_idx(const struct amr_multirate_conf *amr_mrc, uint8_t cmi) { unsigned int i; for (i = 0; i < amr_mrc->num_modes; i++) { if (amr_mrc->mode[i].mode == cmi) return i; } return -EINVAL; } static inline uint8_t set_cmr_mode_idx(const struct amr_multirate_conf *amr_mrc, uint8_t cmr) { int rc; /* Codec Mode Request is in upper 4 bits of RTP payload header, * and we simply copy the CMR into the CMC */ if (cmr == 0xF) { /* FIXME: we need some state about the last codec mode */ return 0; } rc = get_amr_mode_idx(amr_mrc, cmr); if (rc < 0) { /* FIXME: we need some state about the last codec mode */ LOGP(DRTP, LOGL_INFO, "RTP->L1: overriding CMR %u\n", cmr); return 0; } return rc; } static inline uint8_t set_cmi_mode_idx(const struct amr_multirate_conf *amr_mrc, uint8_t cmi) { int rc = get_amr_mode_idx(amr_mrc, cmi); if (rc < 0) { LOGP(DRTP, LOGL_ERROR, "AMR CMI %u not part of AMR MR set\n", cmi); return 0; } return rc; } void amr_set_mode_pref(uint8_t *data, const struct amr_multirate_conf *amr_mrc, uint8_t cmi, uint8_t cmr) { data[0] = set_cmi_mode_idx(amr_mrc, cmi); data[1] = set_cmr_mode_idx(amr_mrc, cmr); } /* parse a GSM 04.08 MultiRate Config IE (10.5.2.21aa) in a more * comfortable internal data structure */ int amr_parse_mr_conf(struct amr_multirate_conf *amr_mrc, const uint8_t *mr_conf, unsigned int len) { uint8_t num_codecs = 0; int i, j = 0; if (len < 2) { LOGP(DRSL, LOGL_ERROR, "AMR Multirate IE is too short (%u)\n", len); goto ret_einval; } if ((mr_conf[0] >> 5) != 1) { LOGP(DRSL, LOGL_ERROR, "AMR Multirate Version %u unknown\n", (mr_conf[0] >> 5)); goto ret_einval; } /* check number of active codecs */ for (i = 0; i < 8; i++) { if (mr_conf[1] & (1 << i)) num_codecs++; } /* check for minimum length */ if (num_codecs == 0 || (num_codecs == 1 && len < 2) || (num_codecs == 2 && len < 4) || (num_codecs == 3 && len < 5) || (num_codecs == 4 && len < 6) || (num_codecs > 4)) { LOGP(DRSL, LOGL_ERROR, "AMR Multirate with %u modes len=%u " "not possible\n", num_codecs, len); goto ret_einval; } /* copy the first two octets of the IE */ amr_mrc->gsm48_ie[0] = mr_conf[0]; amr_mrc->gsm48_ie[1] = mr_conf[1]; amr_mrc->num_modes = num_codecs; for (i = 0; i < 8; i++) { if (mr_conf[1] & (1 << i)) { amr_mrc->mode[j++].mode = i; } } /* skip the first two octets of the IE */ mr_conf += 2; if (num_codecs >= 2) { amr_mrc->mode[0].threshold = mr_conf[0] & 0x3F; amr_mrc->mode[0].hysteresis = mr_conf[1] >> 4; } if (num_codecs >= 3) { amr_mrc->mode[1].threshold = ((mr_conf[1] & 0xF) << 2) | (mr_conf[2] >> 6); amr_mrc->mode[1].hysteresis = (mr_conf[2] >> 2) & 0xF; } if (num_codecs >= 4) { amr_mrc->mode[2].threshold = ((mr_conf[2] & 0x3) << 4) | (mr_conf[3] >> 4); amr_mrc->mode[2].hysteresis = mr_conf[3] & 0xF; } return num_codecs; ret_einval: return -EINVAL; } /*! \brief determine AMR initial codec mode for given logical channel * \returns integer between 0..3 for AMR codce mode 1..4 */ unsigned int amr_get_initial_mode(struct gsm_lchan *lchan) { struct amr_multirate_conf *amr_mrc = &lchan->tch.amr_mr; struct gsm48_multi_rate_conf *mr_conf = (struct gsm48_multi_rate_conf *) amr_mrc->gsm48_ie; if (mr_conf->icmi) { /* initial mode given, coding in TS 05.09 3.4.1 */ return mr_conf->smod; } else { /* implicit rule according to TS 05.09 Chapter 3.4.3 */ switch (amr_mrc->num_modes) { case 2: case 3: /* return the most robust */ return 0; case 4: /* return the second-most robust */ return 1; case 1: default: /* return the only mode we have */ return 0; } } } void amr_init_mr_conf_def(struct gsm_lchan *lchan) { const struct gsm48_multi_rate_conf *mr_cfg; const struct amr_mode *bts_mode; unsigned int num_modes; if (lchan->type == GSM_LCHAN_TCH_F) { num_modes = ARRAY_SIZE(amr_fr_bts_mode_def); bts_mode = &amr_fr_bts_mode_def[0]; mr_cfg = &amr_fr_mr_cfg_def; } else { num_modes = ARRAY_SIZE(amr_hr_bts_mode_def); bts_mode = &amr_hr_bts_mode_def[0]; mr_cfg = &amr_hr_mr_cfg_def; } memcpy(lchan->tch.amr_mr.gsm48_ie, mr_cfg, sizeof(lchan->tch.amr_mr.gsm48_ie)); memcpy(&lchan->tch.amr_mr.mode[0], &bts_mode[0], sizeof(lchan->tch.amr_mr.mode)); lchan->tch.amr_mr.num_modes = num_modes; }