aboutsummaryrefslogtreecommitdiffstats
path: root/src/amr.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/amr.c')
-rw-r--r--src/amr.c112
1 files changed, 112 insertions, 0 deletions
diff --git a/src/amr.c b/src/amr.c
index 06cf429..d3f7b38 100644
--- a/src/amr.c
+++ b/src/amr.c
@@ -12,6 +12,8 @@
#include <stdint.h>
#include <unistd.h>
+#include <string.h>
+#include <stdbool.h>
#include <osmocom/netif/amr.h>
/* According to TS 26.101:
@@ -62,3 +64,113 @@ int osmo_amr_ft_valid(uint8_t amr_ft)
return 1;
}
+
+/*! Check if an AMR frame is octet aligned by looking at the padding bits.
+ * \param[inout] payload user provided memory containing the AMR payload.
+ * \param[in] payload_len overall length of the AMR payload.
+ * \returns true when the payload is octet aligned. */
+bool osmo_amr_is_oa(uint8_t *payload, unsigned int payload_len)
+{
+ /* NOTE: The distinction between octet-aligned and bandwith-efficient
+ * mode normally relys on out of band methods that explicitly select
+ * one of the two modes. (See also RFC 3267, chapter 3.8). However the
+ * A interface in GSM does not provide ways to communicate which mode
+ * is used exactly used. The following functions uses some heuristics
+ * to check if an AMR payload is octet aligned or not. */
+
+ struct amr_hdr *oa_hdr = (struct amr_hdr *)payload;
+ unsigned int frame_len;
+
+ /* Broken payload? */
+ if (!payload || payload_len < 2)
+ return false;
+
+ /* In octet aligned mode, padding bits are specified to be
+ * set to zero. (However, there is a remaining risk that the FT0 or FT1
+ * is selected and the first two bits of the frame are zero as well,
+ * in this case a bandwith-efficient mode payload would look like an
+ * octet-aligned payload, thats why additional checks are required.) */
+ if (oa_hdr->pad1 != 0)
+ return false;
+ if (oa_hdr->pad2 != 0)
+ return false;
+
+ /* This implementation is limited to single-frame payloads only and
+ * since multi-frame payloads are not common in GSM anyway, we may
+ * include the final bit of the first header into this check. */
+ if (oa_hdr->f != 0)
+ return false;
+
+ /* Match the length of the received payload against the expected frame
+ * length that is defined by the frame type. */
+ if(!osmo_amr_ft_valid(oa_hdr->ft))
+ return false;
+ frame_len = osmo_amr_bytes(oa_hdr->ft);
+ if (frame_len != payload_len - 2)
+ return false;
+
+ return true;
+}
+
+/*! Convert an AMR frame from octet-aligned mode to bandwith-efficient mode.
+ * \param[inout] payload user provided memory containing the AMR payload.
+ * \param[in] payload_len overall length of the AMR payload.
+ * \returns resulting payload length, -1 on error. */
+int osmo_amr_oa_to_bwe(uint8_t *payload, unsigned int payload_len)
+{
+ struct amr_hdr *oa_hdr = (struct amr_hdr *)payload;
+ unsigned int frame_len = payload_len - 2;
+ unsigned int i;
+
+ /* This implementation is not capable to handle multi-frame
+ * packets, so we need to make sure that the frame we operate on
+ * contains only one payload. */
+ if (oa_hdr->f != 0)
+ return -1;
+
+ /* Move TOC close to CMR */
+ payload[0] |= (payload[1] >> 4) & 0x0f;
+ payload[1] = (payload[1] << 4) & 0xf0;
+
+ for (i = 0; i < frame_len; i++) {
+ payload[i + 1] |= payload[i + 2] >> 2;
+ payload[i + 2] = payload[i + 2] << 6;
+ }
+
+ /* The overall saving is one byte! */
+ return payload_len - 1;
+}
+
+/*! Convert an AMR frame from bandwith-efficient mode to octet-aligned mode.
+ * \param[inout] payload user provided memory containing the AMR payload.
+ * \param[in] payload_len overall length of the AMR payload.
+ * \param[in] payload_maxlen maximum length of the user provided memory.
+ * \returns resulting payload length, -1 on error. */
+int osmo_amr_bwe_to_oa(uint8_t *payload, unsigned int payload_len,
+ unsigned int payload_maxlen)
+{
+ uint8_t buf[256];
+ unsigned int frame_len = payload_len - 1;
+ unsigned int i;
+
+ memset(buf, 0, sizeof(buf));
+
+ if (payload_len + 1 > payload_maxlen)
+ return -1;
+
+ if (payload_len > sizeof(buf))
+ return -1;
+
+ buf[0] = payload[0] & 0xf0;
+ buf[1] = payload[0] << 4;
+ buf[1] |= (payload[1] >> 4) & 0x0c;
+
+ for (i = 0; i < frame_len -1; i++) {
+ buf[i + 2] = payload[i + 1] << 2;
+ buf[i + 2] |= payload[i + 2] >> 6;
+ }
+ buf[i + 2] = payload[i + 1] << 2;
+
+ memcpy(payload, buf, payload_len + 1);
+ return payload_len + 1;
+}