gsm0808_enc/dec_channel_type: support data
Related: OS#4393
Change-Id: Ib7b75c9d86aace329decf20003b68de459021c64
diff --git a/include/osmocom/gsm/protocol/gsm_08_08.h b/include/osmocom/gsm/protocol/gsm_08_08.h
index a33b01a..eacb954 100644
--- a/include/osmocom/gsm/protocol/gsm_08_08.h
+++ b/include/osmocom/gsm/protocol/gsm_08_08.h
@@ -500,6 +500,42 @@
GSM0808_PERM_HR6 = 0x45, /*!< OHR AMR */
};
+/* 3GPP TS 48.008 3.2.2.11 Channel Type
+ * Transparent: Data Rate */
+enum gsm0808_data_rate_transp {
+ GSM0808_DATA_RATE_TRANSP_32000 = 0x3a,
+ GSM0808_DATA_RATE_TRANSP_28800 = 0x39,
+ GSM0808_DATA_RATE_TRANSP_14400 = 0x18,
+ GSM0808_DATA_RATE_TRANSP_09600 = 0x10,
+ GSM0808_DATA_RATE_TRANSP_04800 = 0x11,
+ GSM0808_DATA_RATE_TRANSP_02400 = 0x12,
+ GSM0808_DATA_RATE_TRANSP_01200 = 0x13,
+ GSM0808_DATA_RATE_TRANSP_00600 = 0x14,
+ GSM0808_DATA_RATE_TRANSP_01200_75 = 0x15,
+};
+
+/* 3GPP TS 48.008 3.2.2.11 Channel Type
+ * Non-Transparent: Radio Interface Data Rate (preferred) */
+enum gsm0808_data_rate_non_transp {
+ GSM0808_DATA_RATE_NON_TRANSP_12000_6000 = 0x00,
+ GSM0808_DATA_RATE_NON_TRANSP_43500 = 0x34,
+ GSM0808_DATA_RATE_NON_TRANSP_29000 = 0x31,
+ GSM0808_DATA_RATE_NON_TRANSP_14500 = 0x14,
+ GSM0808_DATA_RATE_NON_TRANSP_12000 = 0x10,
+ GSM0808_DATA_RATE_NON_TRANSP_06000 = 0x11,
+};
+
+/* 3GPP TS 48.008 3.2.2.11 Channel Type
+ * Non-Transparent: Allowed Radio Interface Data Rate (all possible allowed) */
+enum gsm0808_data_rate_allowed_r_if {
+ GSM0808_DATA_RATE_NON_TRANSP_ALLOWED_43500 = 0x40,
+ GSM0808_DATA_RATE_NON_TRANSP_ALLOWED_32000 = 0x20,
+ GSM0808_DATA_RATE_NON_TRANSP_ALLOWED_29000 = 0x10,
+ GSM0808_DATA_RATE_NON_TRANSP_ALLOWED_14500 = 0x08,
+ GSM0808_DATA_RATE_NON_TRANSP_ALLOWED_12000 = 0x02,
+ GSM0808_DATA_RATE_NON_TRANSP_ALLOWED_06000 = 0x01,
+};
+
extern const struct value_string gsm0808_permitted_speech_names[];
static inline const char *gsm0808_permitted_speech_name(enum gsm0808_permitted_speech val)
{ return get_value_string(gsm0808_permitted_speech_names, val); }
@@ -658,13 +694,32 @@
uint8_t len;
};
+/* 3GPP TS 48.008 3.2.2.11 Channel Type
+ * Asymmetry Preference (used for data, non-transparent service) */
+enum gsm0808_channel_type_asym_pref {
+ GSM0808_CT_ASYM_PREF_NOT_APPLICABLE = 0,
+ GSM0808_CT_ASYM_PREF_UL = 1,
+ GSM0808_CT_ASYM_PREF_DL = 2,
+ GSM0808_CT_ASYM_PREF_SPARE = 3,
+};
+
/* 3GPP TS 48.008 3.2.2.11 Channel Type */
#define CH_TYPE_PERM_SPCH_MAXLEN 9
struct gsm0808_channel_type {
uint8_t ch_indctr;
uint8_t ch_rate_type;
+
+ /* Speech only */
uint8_t perm_spch[CH_TYPE_PERM_SPCH_MAXLEN];
unsigned int perm_spch_len;
+
+ /* Data only */
+ bool data_transparent;
+ uint8_t data_rate;
+ bool data_rate_allowed_is_set;
+ uint8_t data_rate_allowed;
+ bool data_asym_pref_is_set;
+ enum gsm0808_channel_type_asym_pref data_asym_pref;
};
/* 3GPP TS 48.008 3.2.2.10 Encryption Information */
diff --git a/src/gsm/gsm0808_utils.c b/src/gsm/gsm0808_utils.c
index 83d2ce7..4993539 100644
--- a/src/gsm/gsm0808_utils.c
+++ b/src/gsm/gsm0808_utils.c
@@ -499,12 +499,6 @@
OSMO_ASSERT(ct->perm_spch_len <= CHANNEL_TYPE_ELEMENT_MAXLEN - 2);
- /* FIXME: Implement encoding support for Data
- * and Speech + CTM Text Telephony */
- if ((ct->ch_indctr & 0x0f) != GSM0808_CHAN_SPEECH
- && (ct->ch_indctr & 0x0f) != GSM0808_CHAN_SIGN)
- OSMO_ASSERT(false);
-
msgb_put_u8(msg, GSM0808_IE_CHANNEL_TYPE);
tlv_len = msgb_put(msg, 1);
old_tail = msg->tail;
@@ -512,12 +506,44 @@
msgb_put_u8(msg, ct->ch_indctr & 0x0f);
msgb_put_u8(msg, ct->ch_rate_type);
- for (i = 0; i < ct->perm_spch_len; i++) {
- byte = ct->perm_spch[i];
+ switch (ct->ch_indctr) {
+ case GSM0808_CHAN_DATA:
+ byte = ct->data_rate;
- if (i < ct->perm_spch_len - 1)
- byte |= 0x80;
+ if (ct->data_transparent)
+ byte |= 0x40; /* Set T/NT */
+
+ if (ct->data_rate_allowed_is_set) {
+ OSMO_ASSERT(!ct->data_transparent);
+ byte |= 0x80; /* Set ext */
+ msgb_put_u8(msg, byte);
+
+ byte = ct->data_rate_allowed;
+ if (ct->data_asym_pref_is_set) {
+ byte |= 0x80; /* Set ext */
+ msgb_put_u8(msg, byte);
+
+ /* Set asymmetry indication, rest is spare */
+ byte = ct->data_asym_pref << 5;
+ }
+ }
msgb_put_u8(msg, byte);
+ break;
+ case GSM0808_CHAN_SPEECH:
+ case GSM0808_CHAN_SPEECH_CTM_TEXT_TELEPHONY:
+ for (i = 0; i < ct->perm_spch_len; i++) {
+ byte = ct->perm_spch[i];
+
+ if (i < ct->perm_spch_len - 1)
+ byte |= 0x80;
+ msgb_put_u8(msg, byte);
+ }
+ break;
+ case GSM0808_CHAN_SIGN:
+ /* Octet 5 is spare */
+ break;
+ default:
+ OSMO_ASSERT(false);
}
*tlv_len = (uint8_t) (msg->tail - old_tail);
@@ -549,17 +575,57 @@
ct->ch_rate_type = (*elem) & 0x0f;
elem++;
- for (i = 0; i < ARRAY_SIZE(ct->perm_spch); i++) {
- if (elem - old_elem >= len)
- return -EOVERFLOW;
-
+ switch (ct->ch_indctr) {
+ case GSM0808_CHAN_DATA:
byte = *elem;
elem++;
- ct->perm_spch[i] = byte & 0x7f;
- if ((byte & 0x80) == 0x00)
- break;
+ ct->data_transparent = byte & 0x40; /* T/NT */
+ ct->data_rate = byte & 0x3f;
+
+ /* Optional extension for non-transparent service */
+ if (byte & 0x80) {
+ if (ct->data_transparent)
+ return -EINVAL;
+ if (elem - old_elem >= len)
+ return -EOVERFLOW;
+ byte = *elem;
+ elem++;
+
+ ct->data_rate_allowed_is_set = true;
+ ct->data_rate_allowed = byte & 0x7f;
+
+ /* Optional extension */
+ if (byte & 0x80) {
+ if (elem - old_elem >= len)
+ return -EOVERFLOW;
+ byte = *elem;
+ elem++;
+
+ ct->data_asym_pref_is_set = true;
+ ct->data_asym_pref = byte & 0x60 >> 5;
+ }
+ }
+ break;
+ case GSM0808_CHAN_SPEECH:
+ case GSM0808_CHAN_SPEECH_CTM_TEXT_TELEPHONY:
+ for (i = 0; i < ARRAY_SIZE(ct->perm_spch); i++) {
+ if (elem - old_elem >= len)
+ return -EOVERFLOW;
+
+ byte = *elem;
+ elem++;
+ ct->perm_spch[i] = byte & 0x7f;
+ if ((byte & 0x80) == 0x00)
+ break;
+ }
+ ct->perm_spch_len = i + 1;
+ break;
+ case GSM0808_CHAN_SIGN:
+ /* Octet 5 is spare */
+ break;
+ default:
+ return -ENOTSUP;
}
- ct->perm_spch_len = i + 1;
return (int)(elem - old_elem);
}
diff --git a/tests/gsm0808/gsm0808_test.c b/tests/gsm0808/gsm0808_test.c
index a9f1311..a03f276 100644
--- a/tests/gsm0808/gsm0808_test.c
+++ b/tests/gsm0808/gsm0808_test.c
@@ -1086,6 +1086,88 @@
msgb_free(msg);
}
+static void test_gsm0808_enc_dec_channel_type_data(void)
+{
+ struct gsm0808_channel_type enc_ct = {
+ .ch_indctr = GSM0808_CHAN_DATA,
+ .ch_rate_type = GSM0808_DATA_HALF_PREF,
+
+ .data_transparent = true,
+ .data_rate = GSM0808_DATA_RATE_TRANSP_04800,
+ };
+ struct gsm0808_channel_type dec_ct = {};
+ struct msgb *msg;
+ uint8_t ct_enc_expected[] = { GSM0808_IE_CHANNEL_TYPE,
+ 0x03, 0x02, 0x0b, 0x51,
+ };
+ uint8_t rc_enc;
+ int rc_dec;
+
+ msg = msgb_alloc(1024, "output buffer");
+ rc_enc = gsm0808_enc_channel_type(msg, &enc_ct);
+ OSMO_ASSERT(rc_enc == 5);
+ if (memcmp(ct_enc_expected, msg->data, msg->len)) {
+ printf(" got: %s\n", osmo_hexdump(msg->data, msg->len));
+ printf("expect: %s\n", osmo_hexdump(ct_enc_expected, sizeof(ct_enc_expected)));
+ OSMO_ASSERT(false);
+ }
+
+ rc_dec = gsm0808_dec_channel_type(&dec_ct, msg->data + 2, msg->len - 2);
+ OSMO_ASSERT(rc_dec == 3);
+ OSMO_ASSERT(dec_ct.ch_indctr == enc_ct.ch_indctr);
+ OSMO_ASSERT(dec_ct.ch_rate_type == enc_ct.ch_rate_type);
+ OSMO_ASSERT(dec_ct.data_transparent == enc_ct.data_transparent);
+ OSMO_ASSERT(dec_ct.data_rate == enc_ct.data_rate);
+ OSMO_ASSERT(dec_ct.data_rate_allowed_is_set == enc_ct.data_rate_allowed_is_set);
+ OSMO_ASSERT(dec_ct.data_asym_pref_is_set == enc_ct.data_asym_pref_is_set);
+
+ msgb_free(msg);
+}
+
+static void test_gsm0808_enc_dec_channel_type_data_asym_pref(void)
+{
+ struct gsm0808_channel_type enc_ct = {
+ .ch_indctr = GSM0808_CHAN_DATA,
+ .ch_rate_type = GSM0808_DATA_HALF_PREF,
+
+ .data_transparent = false,
+ .data_rate = GSM0808_DATA_RATE_NON_TRANSP_06000,
+ .data_rate_allowed_is_set = true,
+ .data_rate_allowed = GSM0808_DATA_RATE_NON_TRANSP_ALLOWED_06000
+ | GSM0808_DATA_RATE_NON_TRANSP_ALLOWED_12000
+ | GSM0808_DATA_RATE_NON_TRANSP_ALLOWED_14500,
+ .data_asym_pref_is_set = true,
+ .data_asym_pref = GSM0808_CT_ASYM_PREF_UL,
+ };
+ struct gsm0808_channel_type dec_ct = {};
+ struct msgb *msg;
+ uint8_t ct_enc_expected[] = { GSM0808_IE_CHANNEL_TYPE,
+ 0x05, 0x02, 0x0b, 0x91, 0x8b, 0x20,
+ };
+ uint8_t rc_enc;
+ int rc_dec;
+
+ msg = msgb_alloc(1024, "output buffer");
+ rc_enc = gsm0808_enc_channel_type(msg, &enc_ct);
+ OSMO_ASSERT(rc_enc == 7);
+ if (memcmp(ct_enc_expected, msg->data, msg->len)) {
+ printf(" got: %s\n", osmo_hexdump(msg->data, msg->len));
+ printf("expect: %s\n", osmo_hexdump(ct_enc_expected, sizeof(ct_enc_expected)));
+ OSMO_ASSERT(false);
+ }
+
+ rc_dec = gsm0808_dec_channel_type(&dec_ct, msg->data + 2, msg->len - 2);
+ OSMO_ASSERT(rc_dec == 5);
+ OSMO_ASSERT(dec_ct.ch_indctr == enc_ct.ch_indctr);
+ OSMO_ASSERT(dec_ct.ch_rate_type == enc_ct.ch_rate_type);
+ OSMO_ASSERT(dec_ct.data_transparent == enc_ct.data_transparent);
+ OSMO_ASSERT(dec_ct.data_rate == enc_ct.data_rate);
+ OSMO_ASSERT(dec_ct.data_rate_allowed_is_set == enc_ct.data_rate_allowed_is_set);
+ OSMO_ASSERT(dec_ct.data_asym_pref_is_set == enc_ct.data_asym_pref_is_set);
+
+ msgb_free(msg);
+}
+
static void test_gsm0808_enc_dec_channel_type_speech(void)
{
struct gsm0808_channel_type enc_ct = {
@@ -1122,9 +1204,29 @@
struct gsm0808_channel_type ct;
int rc;
+ /* Unknown channel indicator */
+ const uint8_t hex1[] = { 0x05, 0x0b, 0xa1, 0x25 };
+ rc = gsm0808_dec_channel_type(&ct, hex1, sizeof(hex1));
+ OSMO_ASSERT(rc == -ENOTSUP);
+
+ /* Data: ext in Octet 5 with transparent service */
+ const uint8_t hex2[] = { 0x02, 0x0b, 0xc0, 0x00 };
+ rc = gsm0808_dec_channel_type(&ct, hex2, sizeof(hex2));
+ OSMO_ASSERT(rc == -EINVAL);
+
+ /* Data: ext in Octet 5, but too short */
+ const uint8_t hex3[] = { 0x02, 0x0b, 0x80 };
+ rc = gsm0808_dec_channel_type(&ct, hex3, sizeof(hex3));
+ OSMO_ASSERT(rc == -EOVERFLOW);
+
+ /* Data: ext in Octet 5a, but too short */
+ const uint8_t hex4[] = { 0x02, 0x0b, 0x80, 0x80 };
+ rc = gsm0808_dec_channel_type(&ct, hex4, sizeof(hex4));
+ OSMO_ASSERT(rc == -EOVERFLOW);
+
/* Speech: extension bit is set in last byte */
- const uint8_t hex[] = { 0x01, 0x0b, 0xa1, 0xa5 };
- rc = gsm0808_dec_channel_type(&ct, hex, sizeof(hex));
+ const uint8_t hex5[] = { 0x01, 0x0b, 0xa1, 0xa5 };
+ rc = gsm0808_dec_channel_type(&ct, hex5, sizeof(hex5));
OSMO_ASSERT(rc == -EOVERFLOW);
}
@@ -2579,6 +2681,8 @@
test_gsm0808_enc_dec_speech_codec_with_cfg();
test_gsm0808_enc_dec_speech_codec_list();
test_gsm0808_enc_dec_empty_speech_codec_list();
+ test_gsm0808_enc_dec_channel_type_data();
+ test_gsm0808_enc_dec_channel_type_data_asym_pref();
test_gsm0808_enc_dec_channel_type_speech();
test_gsm0808_dec_channel_type_err();
test_gsm0808_enc_dec_encrypt_info();