Added timing advance support for up and downlink TBFs

The timing advance of any TBF is stored when it ends. Whenever a new TBF
with the same TLLI is created (downlink TBF), the stored TA is recalled.

This algorithm assumes that the mobile does not move too fast during
transfer. Also the mobile must start a connection in order to get correct
initial timing advance.

This algorithm does not implement the timing advance procedure as defined
in TS 04.60. To implement the standard timing advance procedure, the BTS
must decode RACH on certain bursts, the mobile is expected to send them.
This requires much more complexity to a transceiver like USRP/UmTRX or
Calypso BTS.

The algorithm was tested at TA >= 8 and works quite well.
diff --git a/src/gprs_rlcmac.cpp b/src/gprs_rlcmac.cpp
index 2c87317..2ea0a61 100644
--- a/src/gprs_rlcmac.cpp
+++ b/src/gprs_rlcmac.cpp
@@ -1819,3 +1819,107 @@
 
 	return 0;
 }
+
+
+/*
+ * timing advance memory
+ */
+
+/* enable to debug timing advance memory */
+//#define DEBUG_TA
+
+static LLIST_HEAD(gprs_rlcmac_ta_list);
+static int gprs_rlcmac_ta_num = 0;
+
+struct gprs_rlcmac_ta {
+	struct llist_head	list;
+	uint32_t		tlli;
+	uint8_t			ta;
+};
+
+/* remember timing advance of a given TLLI */
+int remember_timing_advance(uint32_t tlli, uint8_t ta)
+{
+	struct gprs_rlcmac_ta *ta_entry;
+
+	/* check for existing entry */
+	llist_for_each_entry(ta_entry, &gprs_rlcmac_ta_list, list) {
+		if (ta_entry->tlli == tlli) {
+#ifdef DEBUG_TA
+			fprintf(stderr, "update %08x %d\n", tlli, ta);
+#endif
+			ta_entry->ta = ta;
+			/* relink to end of list */
+			llist_del(&ta_entry->list);
+			llist_add_tail(&ta_entry->list, &gprs_rlcmac_ta_list);
+			return 0;
+		}
+	}
+
+#ifdef DEBUG_TA
+	fprintf(stderr, "remember %08x %d\n", tlli, ta);
+#endif
+	/* if list is full, remove oldest entry */
+	if (gprs_rlcmac_ta_num == 30) {
+		ta_entry = llist_entry(gprs_rlcmac_ta_list.next,
+			struct gprs_rlcmac_ta, list);
+	        llist_del(&ta_entry->list);
+		talloc_free(ta_entry);
+		gprs_rlcmac_ta_num--;
+	}
+
+	/* create new TA entry */
+	ta_entry = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_ta);
+	if (!ta_entry)
+		return -ENOMEM;
+
+	ta_entry->tlli = tlli;
+	ta_entry->ta = ta;
+	llist_add_tail(&ta_entry->list, &gprs_rlcmac_ta_list);
+	gprs_rlcmac_ta_num++;
+
+	return 0;
+}
+
+int recall_timing_advance(uint32_t tlli)
+{
+	struct gprs_rlcmac_ta *ta_entry;
+	uint8_t ta;
+
+	llist_for_each_entry(ta_entry, &gprs_rlcmac_ta_list, list) {
+		if (ta_entry->tlli == tlli) {
+			ta = ta_entry->ta;
+#ifdef DEBUG_TA
+			fprintf(stderr, "recall %08x %d\n", tlli, ta);
+#endif
+			return ta;
+		}
+	}
+#ifdef DEBUG_TA
+	fprintf(stderr, "no entry for %08x\n", tlli);
+#endif
+
+	return -EINVAL;
+}
+
+int flush_timing_advance(void)
+{
+	struct gprs_rlcmac_ta *ta_entry;
+	int count = 0;
+
+	while (!llist_empty(&gprs_rlcmac_ta_list)) {
+		ta_entry = llist_entry(gprs_rlcmac_ta_list.next,
+			struct gprs_rlcmac_ta, list);
+#ifdef DEBUG_TA
+		fprintf(stderr, "flush entry %08x %d\n", ta_entry->tlli,
+			ta_entry->ta);
+#endif
+	        llist_del(&ta_entry->list);
+		talloc_free(ta_entry);
+		count++;
+	}
+	gprs_rlcmac_ta_num = 0;
+
+	return count;
+}
+