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
diff --git a/openbsc/src/gprs/gprs_utils.c b/openbsc/src/gprs/gprs_utils.c
index 55bc629..2293f02 100644
--- a/openbsc/src/gprs/gprs_utils.c
+++ b/openbsc/src/gprs/gprs_utils.c
@@ -20,6 +20,7 @@
  *
  */
 #include <openbsc/gprs_utils.h>
+#include <openbsc/gsm_04_08_gprs.h>
 
 #include <osmocom/core/msgb.h>
 #include <osmocom/gprs/gprs_ns.h>
@@ -172,6 +173,50 @@
 	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)
 {