summaryrefslogtreecommitdiffstats
path: root/src/gsm
diff options
context:
space:
mode:
Diffstat (limited to 'src/gsm')
-rw-r--r--src/gsm/gsm0808_utils.c94
1 files changed, 73 insertions, 21 deletions
diff --git a/src/gsm/gsm0808_utils.c b/src/gsm/gsm0808_utils.c
index dd14d3ca..2552c086 100644
--- a/src/gsm/gsm0808_utils.c
+++ b/src/gsm/gsm0808_utils.c
@@ -1346,42 +1346,94 @@ uint16_t gsm0808_sc_cfg_from_gsm48_mr_cfg(const struct gsm48_multi_rate_conf *cf
/*! Determine a GSM 04.08 AMR configuration struct from a set of speech codec
* configuration bits (S0-S15)
* \param[out] cfg AMR configuration in GSM 04.08 format.
- * \param[in] s15_s0 configuration bits (S0-S15). */
-void gsm48_mr_cfg_from_gsm0808_sc_cfg(struct gsm48_multi_rate_conf *cfg,
- uint16_t s15_s0)
+ * \param[in] s15_s0 configuration bits (S15-S0, non-ambiguous).
+ * \returns zero when successful; negative on error */
+int gsm48_mr_cfg_from_gsm0808_sc_cfg(struct gsm48_multi_rate_conf *cfg,
+ uint16_t s15_s0)
{
+ unsigned int count = 0;
+
+ /* Note: See also: 3GPP TS 28.062
+ * Table 7.11.3.1.3-2: Preferred Configurations for the Adaptive
+ * Multi-Rate Codec Types */
+
+ /* Note: The resulting multirate-configuration must not contain an
+ * active set of more than four codec rates. The active set also
+ * must contain at least one rate. */
+
memset(cfg, 0, sizeof(*cfg));
+ cfg->ver = 1;
+ cfg->icmi = 1;
/* Strip option bits */
s15_s0 &= 0x00ff;
- /* Rate 5,15k must always be present */
- cfg->m5_15 = 1;
+ /* Rate 5,15k can never be selected (see table) */
+ cfg->m5_15 = 0;
+
+ if (s15_s0 & GSM0808_SC_CFG_AMR_4_75_5_90_7_40_12_20 & 0xff) {
+ /* Table Table 7.11.3.1.3-2 lists one mode that selects 4
+ * rates at once (Config-NB-Code = 1). The rates selected
+ * are known to be compatible between GERAN and UTRAN, since
+ * an active set must not contain more than four rates at
+ * a time, we ignore all other settings as they are either
+ * redundaned or excess settings (invalid) */
+ cfg->m4_75 = 1;
+ cfg->m5_90 = 1;
+ cfg->m7_40 = 1;
+ cfg->m12_2 = 1;
+ count += 4;
+ }
- if ((s15_s0 & GSM0808_SC_CFG_DEFAULT_AMR_4_75 & 0xff) ==
- (GSM0808_SC_CFG_DEFAULT_AMR_4_75 & 0xff))
+ /* Check the bits in s15_s0 and set the flags for the
+ * respective rates. */
+ if (s15_s0 & GSM0808_SC_CFG_AMR_4_75 && !cfg->m4_75) {
+ if (count >= 4)
+ return -EINVAL;
cfg->m4_75 = 1;
- if ((s15_s0 & GSM0808_SC_CFG_DEFAULT_AMR_5_90 & 0xff) ==
- (GSM0808_SC_CFG_DEFAULT_AMR_5_90 & 0xff))
+ count++;
+ }
+ if (s15_s0 & GSM0808_SC_CFG_AMR_5_90 && !cfg->m5_90) {
+ if (count >= 4)
+ return -EINVAL;
cfg->m5_90 = 1;
- if ((s15_s0 & GSM0808_SC_CFG_DEFAULT_AMR_6_70 & 0xff) ==
- (GSM0808_SC_CFG_DEFAULT_AMR_6_70 & 0xff))
+ count++;
+ }
+ if (s15_s0 & GSM0808_SC_CFG_AMR_6_70) {
+ if (count >= 4)
+ return -EINVAL;
cfg->m6_70 = 1;
- if ((s15_s0 & GSM0808_SC_CFG_DEFAULT_AMR_7_40 & 0xff) ==
- (GSM0808_SC_CFG_DEFAULT_AMR_7_40 & 0xff))
+ count++;
+ }
+ if (s15_s0 & GSM0808_SC_CFG_AMR_7_40 && !cfg->m7_40) {
+ if (count >= 4)
+ return -EINVAL;
cfg->m7_40 = 1;
- if ((s15_s0 & GSM0808_SC_CFG_DEFAULT_AMR_7_95 & 0xff) ==
- (GSM0808_SC_CFG_DEFAULT_AMR_7_95 & 0xff))
+ count++;
+ }
+ if (s15_s0 & GSM0808_SC_CFG_AMR_7_95) {
+ if (count >= 4)
+ return -EINVAL;
cfg->m7_95 = 1;
- if ((s15_s0 & GSM0808_SC_CFG_DEFAULT_AMR_10_2 & 0xff) ==
- (GSM0808_SC_CFG_DEFAULT_AMR_10_2 & 0xff))
+ count++;
+ }
+ if (s15_s0 & GSM0808_SC_CFG_AMR_10_2) {
+ if (count >= 4)
+ return -EINVAL;
cfg->m10_2 = 1;
- if ((s15_s0 & GSM0808_SC_CFG_DEFAULT_AMR_12_2 & 0xff) ==
- (GSM0808_SC_CFG_DEFAULT_AMR_12_2 & 0xff))
+ count++;
+ }
+ if (s15_s0 & GSM0808_SC_CFG_AMR_12_2 && !cfg->m12_2) {
+ if (count >= 4)
+ return -EINVAL;
cfg->m12_2 = 1;
+ count++;
+ }
- cfg->ver = 1;
- cfg->icmi = 1;
+ if (count == 0)
+ return -EINVAL;
+
+ return 0;
}
int gsm0808_get_cipher_reject_cause(const struct tlv_parsed *tp)