From 79af67d7c02a25e36146369b60f7f25c9655c862 Mon Sep 17 00:00:00 2001 From: Jacob Erlbeck Date: Mon, 19 Jan 2015 08:27:34 +0100 Subject: gprs: Add GPRS timer conversion functions Currently, all GPRS timer values are hard-coded. To make these values configurable in seconds and to show them, conversion functions from and to seconds are needed. This patch adds gprs_tmr_to_secs and gprs_secs_to_tmr_floor. Due to the limited number of bits used to encode GPRS timer values, only a few durations can be represented. gprs_secs_to_tmr_floor therefore always returns the timer value that represents either the exact number (if an exact representation exists) or the next lower number for that an exact representation exists. Sponsored-by: On-Waves ehf --- openbsc/include/openbsc/gprs_utils.h | 5 +++ openbsc/include/openbsc/gsm_04_08_gprs.h | 5 ++- openbsc/src/gprs/gprs_utils.c | 45 +++++++++++++++++++++ openbsc/tests/gprs/gprs_test.c | 68 ++++++++++++++++++++++++++++++++ openbsc/tests/gprs/gprs_test.ok | 1 + 5 files changed, 123 insertions(+), 1 deletion(-) (limited to 'openbsc') diff --git a/openbsc/include/openbsc/gprs_utils.h b/openbsc/include/openbsc/gprs_utils.h index 60b55a178..6880e0587 100644 --- a/openbsc/include/openbsc/gprs_utils.h +++ b/openbsc/include/openbsc/gprs_utils.h @@ -31,6 +31,11 @@ int gprs_msgb_resize_area(struct msgb *msg, uint8_t *area, size_t old_size, size_t new_size); char *gprs_apn_to_str(char *out_str, const uint8_t *apn_enc, size_t rest_chars); int gprs_str_to_apn(uint8_t *apn_enc, size_t max_len, const char *str); + +/* GSM 04.08, 10.5.7.3 GPRS Timer */ +int gprs_tmr_to_secs(uint8_t tmr); +uint8_t gprs_secs_to_tmr_floor(int secs); + int gprs_is_mi_tmsi(const uint8_t *value, size_t value_len); int gprs_is_mi_imsi(const uint8_t *value, size_t value_len); int gprs_parse_mi_tmsi(const uint8_t *value, size_t value_len, uint32_t *tmsi); diff --git a/openbsc/include/openbsc/gsm_04_08_gprs.h b/openbsc/include/openbsc/gsm_04_08_gprs.h index fb30dff96..3eec98365 100644 --- a/openbsc/include/openbsc/gsm_04_08_gprs.h +++ b/openbsc/include/openbsc/gsm_04_08_gprs.h @@ -116,9 +116,12 @@ enum gsm48_gprs_tmr_unit { GPRS_TMR_2SECONDS = 0 << 5, GPRS_TMR_MINUTE = 1 << 5, GPRS_TMR_6MINUTE = 2 << 5, - GPRS_TMR_DEACTIVATED = 3 << 5, + GPRS_TMR_DEACTIVATED = 7 << 5, }; +#define GPRS_TMR_UNIT_MASK (7 << 5) +#define GPRS_TMR_FACT_MASK ((1 << 5)-1) + /* Chapter 9.4.2 / Table 9.4.2 */ struct gsm48_attach_ack { uint8_t att_result:4, /* 10.5.5.7 */ diff --git a/openbsc/src/gprs/gprs_utils.c b/openbsc/src/gprs/gprs_utils.c index 55bc629e8..2293f0254 100644 --- a/openbsc/src/gprs/gprs_utils.c +++ b/openbsc/src/gprs/gprs_utils.c @@ -20,6 +20,7 @@ * */ #include +#include #include #include @@ -172,6 +173,50 @@ int gprs_str_to_apn(uint8_t *apn_enc, size_t max_len, const char *str) return len; } +/* GSM 04.08, 10.5.7.3 GPRS Timer */ +int gprs_tmr_to_secs(uint8_t tmr) +{ + switch (tmr & GPRS_TMR_UNIT_MASK) { + case GPRS_TMR_2SECONDS: + return 2 * (tmr & GPRS_TMR_FACT_MASK); + default: + case GPRS_TMR_MINUTE: + return 60 * (tmr & GPRS_TMR_FACT_MASK); + case GPRS_TMR_6MINUTE: + return 360 * (tmr & GPRS_TMR_FACT_MASK); + case GPRS_TMR_DEACTIVATED: + return -1; + } +} + +/* This functions returns a tmr value such that + * - f is monotonic + * - f(s) <= s + * - f(s) == s if a tmr exists with s = gprs_tmr_to_secs(tmr) + * - the best possible resolution is used + * where + * f(s) = gprs_tmr_to_secs(gprs_secs_to_tmr_floor(s)) + */ +uint8_t gprs_secs_to_tmr_floor(int secs) +{ + if (secs < 0) + return GPRS_TMR_DEACTIVATED; + if (secs < 2 * 32) + return GPRS_TMR_2SECONDS | (secs / 2); + if (secs < 60 * 2) + /* Ensure monotonicity */ + return GPRS_TMR_2SECONDS | GPRS_TMR_FACT_MASK; + if (secs < 60 * 32) + return GPRS_TMR_MINUTE | (secs / 60); + if (secs < 360 * 6) + /* Ensure monotonicity */ + return GPRS_TMR_MINUTE | GPRS_TMR_FACT_MASK; + if (secs < 360 * 32) + return GPRS_TMR_6MINUTE | (secs / 360); + + return GPRS_TMR_6MINUTE | GPRS_TMR_FACT_MASK; +} + /* GSM 04.08, 10.5.1.4 */ int gprs_is_mi_tmsi(const uint8_t *value, size_t value_len) { diff --git a/openbsc/tests/gprs/gprs_test.c b/openbsc/tests/gprs/gprs_test.c index 37dc83285..e445ae7c8 100644 --- a/openbsc/tests/gprs/gprs_test.c +++ b/openbsc/tests/gprs/gprs_test.c @@ -614,6 +614,73 @@ static void test_gsup_messages_dec_enc(void) } } +static void test_gprs_timer_enc_dec(void) +{ + int i, u, secs, tmr; + const int upper_secs_test_limit = 12000; + int dec_secs, last_dec_secs = -1; + + printf("Test GPRS timer decoding/encoding\n"); + + /* Check gprs_tmr_to_secs with all 256 encoded values */ + for (u = 0; u <= GPRS_TMR_DEACTIVATED; u += 32) { + fprintf(stderr, "Testing decoding with timer value unit %u\n", + u / 32); + for (i = 0; i < 32; i++) { + switch (u) { + case GPRS_TMR_2SECONDS: + OSMO_ASSERT(gprs_tmr_to_secs(u + i) == 2 * i); + break; + + default: + case GPRS_TMR_MINUTE: + OSMO_ASSERT(gprs_tmr_to_secs(u + i) == 60 * i); + break; + + case GPRS_TMR_6MINUTE: + OSMO_ASSERT(gprs_tmr_to_secs(u + i) == 360 * i); + break; + + case GPRS_TMR_DEACTIVATED: + OSMO_ASSERT(gprs_tmr_to_secs(u + i) == -1); + break; + } + + OSMO_ASSERT(gprs_tmr_to_secs(u + i) < upper_secs_test_limit); + } + } + + /* Check gprs_secs_to_tmr_floor for secs that can exactly be + * represented as GPRS timer values */ + for (i = 0; i < GPRS_TMR_DEACTIVATED; i++) { + int j; + secs = gprs_tmr_to_secs(i); + tmr = gprs_secs_to_tmr_floor(secs); + OSMO_ASSERT(secs == gprs_tmr_to_secs(tmr)); + + /* Check that the highest resolution is used */ + for (j = 0; j < tmr; j++) + OSMO_ASSERT(secs != gprs_tmr_to_secs(j)); + } + OSMO_ASSERT(GPRS_TMR_DEACTIVATED == gprs_secs_to_tmr_floor(-1)); + + /* Check properties of gprs_secs_to_tmr_floor */ + for (secs = 0; secs <= upper_secs_test_limit; secs++) { + int tmr = gprs_secs_to_tmr_floor(secs); + int delta_secs = gprs_tmr_to_secs((tmr & ~0x1f) | 1); + dec_secs = gprs_tmr_to_secs(tmr); + + /* Check floor */ + OSMO_ASSERT(dec_secs <= secs); + /* Check monotonicity */ + OSMO_ASSERT(dec_secs >= last_dec_secs); + /* Check max distance (<= resolution) */ + OSMO_ASSERT(dec_secs - last_dec_secs <= delta_secs); + + last_dec_secs = dec_secs; + } +} + const struct log_info_cat default_categories[] = { [DGPRS] = { .name = "DGPRS", @@ -635,6 +702,7 @@ int main(int argc, char **argv) test_gsm_03_03_apn(); test_tlv_shift_functions(); test_gsup_messages_dec_enc(); + test_gprs_timer_enc_dec(); printf("Done.\n"); return EXIT_SUCCESS; diff --git a/openbsc/tests/gprs/gprs_test.ok b/openbsc/tests/gprs/gprs_test.ok index 4825b67cd..cf710769e 100644 --- a/openbsc/tests/gprs/gprs_test.ok +++ b/openbsc/tests/gprs/gprs_test.ok @@ -27,4 +27,5 @@ Test GSUP message decoding/encoding Testing Purge MS Request Testing Purge MS Error Testing Purge MS Result +Test GPRS timer decoding/encoding Done. -- cgit v1.2.3