From f047a4a04aa8f042dab3491c1a64cd416124cf0e Mon Sep 17 00:00:00 2001 From: Oliver Smith Date: Tue, 31 Jan 2023 13:10:03 +0100 Subject: [PATCH] gsm0808_enc/dec_channel_type: support data Related: OS#4393 Change-Id: Ib7b75c9d86aace329decf20003b68de459021c64 --- include/osmocom/gsm/protocol/gsm_08_08.h | 55 ++++++++++++ src/gsm/gsm0808_utils.c | 102 +++++++++++++++++---- tests/gsm0808/gsm0808_test.c | 108 ++++++++++++++++++++++- 3 files changed, 245 insertions(+), 20 deletions(-) diff --git a/include/osmocom/gsm/protocol/gsm_08_08.h b/include/osmocom/gsm/protocol/gsm_08_08.h index a33b01aa4..eacb95450 100644 --- a/include/osmocom/gsm/protocol/gsm_08_08.h +++ b/include/osmocom/gsm/protocol/gsm_08_08.h @@ -500,6 +500,42 @@ enum gsm0808_permitted_speech { 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 @@ struct gsm0808_speech_codec_list { 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 83d2ce75f..4993539f3 100644 --- a/src/gsm/gsm0808_utils.c +++ b/src/gsm/gsm0808_utils.c @@ -499,12 +499,6 @@ uint8_t gsm0808_enc_channel_type(struct msgb *msg, 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 @@ uint8_t gsm0808_enc_channel_type(struct msgb *msg, 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 @@ int gsm0808_dec_channel_type(struct gsm0808_channel_type *ct, 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 a9f131193..a03f27670 100644 --- a/tests/gsm0808/gsm0808_test.c +++ b/tests/gsm0808/gsm0808_test.c @@ -1086,6 +1086,88 @@ static void test_gsm0808_enc_dec_empty_speech_codec_list(void) 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 @@ static void test_gsm0808_dec_channel_type_err(void) 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 @@ int main(int argc, char **argv) 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();