diff --git a/include/osmocom/gsm/gsm0808_utils.h b/include/osmocom/gsm/gsm0808_utils.h index 363fc3904..34eae4384 100644 --- a/include/osmocom/gsm/gsm0808_utils.h +++ b/include/osmocom/gsm/gsm0808_utils.h @@ -77,6 +77,7 @@ int gsm0808_dec_cell_id_list2(struct gsm0808_cell_id_list2 *cil, const uint8_t * int gsm0808_dec_cell_id_list(struct gsm0808_cell_id_list *cil, const uint8_t *elem, uint8_t len) OSMO_DEPRECATED("use gsm0808_dec_cell_id_list2 instead"); +int gsm0808_cell_id_list_add(struct gsm0808_cell_id_list2 *dst, const struct gsm0808_cell_id_list2 *src); int gsm0808_chan_type_to_speech_codec(uint8_t perm_spch); int gsm0808_speech_codec_from_chan_type(struct gsm0808_speech_codec *sc, uint8_t perm_spch); diff --git a/src/gsm/gsm0808_utils.c b/src/gsm/gsm0808_utils.c index e4872b878..996456e1e 100644 --- a/src/gsm/gsm0808_utils.c +++ b/src/gsm/gsm0808_utils.c @@ -909,6 +909,85 @@ int gsm0808_dec_cell_id_list(struct gsm0808_cell_id_list *cil, return (int)(elem - old_elem); } +static bool same_cell_id_list_entries(const struct gsm0808_cell_id_list2 *a, int ai, + const struct gsm0808_cell_id_list2 *b, int bi) +{ + struct gsm0808_cell_id_list2 tmp = { + .id_discr = a->id_discr, + .id_list_len = 1, + }; + uint8_t buf_a[32 + sizeof(struct msgb)]; + uint8_t buf_b[32 + sizeof(struct msgb)]; + struct msgb *msg_a = (void*)buf_a; + struct msgb *msg_b = (void*)buf_b; + + msg_a->data_len = 32; + msg_b->data_len = 32; + msgb_reset(msg_a); + msgb_reset(msg_b); + + if (a->id_discr != b->id_discr) + return false; + if (ai >= a->id_list_len + || bi >= b->id_list_len) + return false; + + tmp.id_list[0] = a->id_list[ai]; + gsm0808_enc_cell_id_list2(msg_a, &tmp); + + tmp.id_list[0] = b->id_list[bi]; + gsm0808_enc_cell_id_list2(msg_b, &tmp); + + if (msg_a->len != msg_b->len) + return false; + if (memcmp(msg_a->data, msg_b->data, msg_a->len)) + return false; + + return true; +} + +/*! Append entries from one Cell Identifier List to another. + * The cell identifier types must be identical between the two lists. + * \param dst[out] Append entries to this list. + * \param src[in] Append these entries to \a dst. + * \returns the nr of items added, or negative on error: -EINVAL if the id_discr mismatch + * between the lists, -ENOSPC if the destination list does not have enough space. If an error is + * returned, \a dst may have already been changed (particularly on -ENOSPC). Note that a return value + * of zero may occur when the src->id_list_len is zero, or when all entries from \a src already exist + * in \a dst, and does not indicate error per se. */ +int gsm0808_cell_id_list_add(struct gsm0808_cell_id_list2 *dst, const struct gsm0808_cell_id_list2 *src) +{ + int i, j; + int added = 0; + + if (dst->id_list_len == 0 + && dst->id_discr != CELL_IDENT_BSS) + dst->id_discr = src->id_discr; + else if (dst->id_discr != src->id_discr) + return -EINVAL; + + for (i = 0; i < src->id_list_len; i++) { + /* don't add duplicate entries */ + bool skip = false; + for (j = 0; j < dst->id_list_len; j++) { + if (same_cell_id_list_entries(dst, j, src, i)) { + skip = true; + break; + } + } + if (skip) + continue; + + if (dst->id_list_len >= ARRAY_SIZE(dst->id_list)) + return -ENOSPC; + + dst->id_list[dst->id_list_len++] = src->id_list[i]; + added ++; + } + + return added; +} + /*! Convert the representation of the permitted speech codec identifier * that is used in struct gsm0808_channel_type to the speech codec * representation we use in struct gsm0808_speech_codec. diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map index a6ea47dde..d99121e20 100644 --- a/src/gsm/libosmogsm.map +++ b/src/gsm/libosmogsm.map @@ -176,6 +176,7 @@ gsm0808_enc_cell_id_list; gsm0808_enc_cell_id_list2; gsm0808_dec_cell_id_list; gsm0808_dec_cell_id_list2; +gsm0808_cell_id_list_add; gsm0808_chan_type_to_speech_codec; gsm0808_speech_codec_from_chan_type; gsm0808_speech_codec_type_names; diff --git a/tests/gsm0808/gsm0808_test.c b/tests/gsm0808/gsm0808_test.c index 9730be2dc..f46709964 100644 --- a/tests/gsm0808/gsm0808_test.c +++ b/tests/gsm0808/gsm0808_test.c @@ -28,6 +28,7 @@ #include #include #include +#include #define VERIFY(msg, data, len) \ if (msgb_l3len(msg) != len) { \ @@ -1063,6 +1064,198 @@ static void test_gsm0808_enc_dec_cell_id_list_multi_global() msgb_free(msg); } +static void print_cil(const struct gsm0808_cell_id_list2 *cil) +{ + unsigned int i; + if (!cil) { + printf(" cell_id_list == NULL\n"); + return; + } + switch (cil->id_discr) { + case CELL_IDENT_WHOLE_GLOBAL: + printf(" cell_id_list cgi[%u] = {\n", cil->id_list_len); + for (i = 0; i < cil->id_list_len; i++) + printf(" %2d: %s\n", i, osmo_cgi_name(&cil->id_list[i].global)); + printf(" }\n"); + break; + case CELL_IDENT_LAC: + printf(" cell_id_list lac[%u] = {\n", cil->id_list_len); + for (i = 0; i < cil->id_list_len; i++) + printf(" %2d: %u\n", i, cil->id_list[i].lac); + printf(" }\n"); + break; + case CELL_IDENT_BSS: + printf(" cell_id_list bss[%u]\n", cil->id_list_len); + break; + case CELL_IDENT_NO_CELL: + printf(" cell_id_list no_cell[%u]\n", cil->id_list_len); + break; + default: + printf(" Unimplemented id_disc\n"); + } +} + +void test_cell_id_list_add() { + const struct gsm0808_cell_id_list2 cgi1 = { + .id_discr = CELL_IDENT_WHOLE_GLOBAL, + .id_list_len = 1, + .id_list = { + { + .global = { + .lai = { + .plmn = { .mcc = 1, .mnc = 2, .mnc_3_digits = false }, + .lac = 3, + }, + .cell_identity = 4, + } + }, + }, + }; + + const struct gsm0808_cell_id_list2 cgi2 = { + .id_discr = CELL_IDENT_WHOLE_GLOBAL, + .id_list_len = 2, + .id_list = { + { + .global = { + .lai = { + .plmn = { .mcc = 1, .mnc = 2, .mnc_3_digits = true }, + .lac = 3, + }, + .cell_identity = 4, + } + }, + { + .global = { + .lai = { + .plmn = { .mcc = 5, .mnc = 6, .mnc_3_digits = true }, + .lac = 7, + }, + .cell_identity = 8, + } + }, + }, + }; + + const struct gsm0808_cell_id_list2 cgi2a = { + .id_discr = CELL_IDENT_WHOLE_GLOBAL, + .id_list_len = 2, + .id_list = { + { + .global = cgi2.id_list[0].global + }, + { + .global = { + .lai = { + .plmn = { .mcc = 9, .mnc = 10, .mnc_3_digits = true }, + .lac = 11, + }, + .cell_identity = 12, + } + }, + }, + }; + + const struct gsm0808_cell_id_list2 cgi3 = { + .id_discr = CELL_IDENT_WHOLE_GLOBAL, + .id_list_len = 2, + .id_list = { + { + .global = { + .lai = { + .plmn = { .mcc = 13, .mnc = 14, .mnc_3_digits = true }, + .lac = 15, + }, + .cell_identity = 16, + } + }, + { + .global = { + .lai = { + .plmn = { .mcc = 16, .mnc = 17, .mnc_3_digits = true }, + .lac = 18, + }, + .cell_identity = 19, + } + }, + }, + }; + + + const struct gsm0808_cell_id_list2 lac1 = { + .id_discr = CELL_IDENT_LAC, + .id_list_len = 1, + .id_list = { + { + .lac = 123 + }, + }, + }; + + const struct gsm0808_cell_id_list2 lac2 = { + .id_discr = CELL_IDENT_LAC, + .id_list_len = 2, + .id_list = { + { + .lac = 456 + }, + { + .lac = 789 + }, + }, + }; + + struct gsm0808_cell_id_list2 cil = {}; + + printf("------- %s\n", __func__); + + print_cil(&cil); + +#define ADD_QUIET(other_cil, expect_rc) do { \ + int rc = gsm0808_cell_id_list_add(&cil, &other_cil); \ + printf("\ngsm0808_cell_id_list_add(&cil, &" #other_cil ") --> rc = %d\n", rc); \ + OSMO_ASSERT(rc == expect_rc); \ + } while(0) + +#define ADD(other_cil, expect_rc) ADD_QUIET(other_cil, expect_rc); print_cil(&cil) + + ADD(lac1, 1); + ADD(lac1, 0); + ADD(lac2, 2); + ADD(lac2, 0); + ADD(cil, 0); + ADD(cgi1, -EINVAL); + + printf("\ncan't add to BSS list\n"); + cil.id_list_len = 0; + cil.id_discr = CELL_IDENT_BSS; + print_cil(&cil); + ADD(lac1, -EINVAL); + + printf("\nother types (including NO_CELL) take on new type iff empty\n"); + cil.id_list_len = 0; + cil.id_discr = CELL_IDENT_NO_CELL; + print_cil(&cil); + ADD(cgi1, 1); + ADD(cgi1, 0); + ADD(cgi2, 2); + ADD(cgi2, 0); + + cil.id_list_len = GSM0808_CELL_ID_LIST2_MAXLEN - 1; + printf("\ncil.id_list_len = %u", cil.id_list_len); + ADD_QUIET(cgi2a, 1); + printf("cil.id_list_len = %u\n", cil.id_list_len); + + cil.id_list_len = GSM0808_CELL_ID_LIST2_MAXLEN - 1; + printf("\ncil.id_list_len = %u", cil.id_list_len); + ADD_QUIET(cgi3, -ENOSPC); + printf("cil.id_list_len = %u", cil.id_list_len); + ADD_QUIET(cgi2a, -ENOSPC); + printf("cil.id_list_len = %u\n", cil.id_list_len); + + printf("------- %s done\n", __func__); +} + int main(int argc, char **argv) { printf("Testing generation of GSM0808 messages\n"); @@ -1103,6 +1296,8 @@ int main(int argc, char **argv) test_gsm0808_enc_dec_cell_id_list_multi_lac_and_ci(); test_gsm0808_enc_dec_cell_id_list_multi_global(); + test_cell_id_list_add(); + printf("Done\n"); return EXIT_SUCCESS; } diff --git a/tests/gsm0808/gsm0808_test.ok b/tests/gsm0808/gsm0808_test.ok index e101d6574..a049dec55 100644 --- a/tests/gsm0808/gsm0808_test.ok +++ b/tests/gsm0808/gsm0808_test.ok @@ -19,4 +19,89 @@ Testing creating Clear Request Testing creating Paging Request Testing creating DTAP Testing prepend DTAP +------- test_cell_id_list_add + cell_id_list cgi[0] = { + } + +gsm0808_cell_id_list_add(&cil, &lac1) --> rc = 1 + cell_id_list lac[1] = { + 0: 123 + } + +gsm0808_cell_id_list_add(&cil, &lac1) --> rc = 0 + cell_id_list lac[1] = { + 0: 123 + } + +gsm0808_cell_id_list_add(&cil, &lac2) --> rc = 2 + cell_id_list lac[3] = { + 0: 123 + 1: 456 + 2: 789 + } + +gsm0808_cell_id_list_add(&cil, &lac2) --> rc = 0 + cell_id_list lac[3] = { + 0: 123 + 1: 456 + 2: 789 + } + +gsm0808_cell_id_list_add(&cil, &cil) --> rc = 0 + cell_id_list lac[3] = { + 0: 123 + 1: 456 + 2: 789 + } + +gsm0808_cell_id_list_add(&cil, &cgi1) --> rc = -22 + cell_id_list lac[3] = { + 0: 123 + 1: 456 + 2: 789 + } + +can't add to BSS list + cell_id_list bss[0] + +gsm0808_cell_id_list_add(&cil, &lac1) --> rc = -22 + cell_id_list bss[0] + +other types (including NO_CELL) take on new type iff empty + cell_id_list no_cell[0] + +gsm0808_cell_id_list_add(&cil, &cgi1) --> rc = 1 + cell_id_list cgi[1] = { + 0: 001-02-3-4 + } + +gsm0808_cell_id_list_add(&cil, &cgi1) --> rc = 0 + cell_id_list cgi[1] = { + 0: 001-02-3-4 + } + +gsm0808_cell_id_list_add(&cil, &cgi2) --> rc = 2 + cell_id_list cgi[3] = { + 0: 001-02-3-4 + 1: 001-002-3-4 + 2: 005-006-7-8 + } + +gsm0808_cell_id_list_add(&cil, &cgi2) --> rc = 0 + cell_id_list cgi[3] = { + 0: 001-02-3-4 + 1: 001-002-3-4 + 2: 005-006-7-8 + } + +cil.id_list_len = 126 +gsm0808_cell_id_list_add(&cil, &cgi2a) --> rc = 1 +cil.id_list_len = 127 + +cil.id_list_len = 126 +gsm0808_cell_id_list_add(&cil, &cgi3) --> rc = -28 +cil.id_list_len = 127 +gsm0808_cell_id_list_add(&cil, &cgi2a) --> rc = -28 +cil.id_list_len = 127 +------- test_cell_id_list_add done Done