tbf,bts: Keep track of new TBF for dl/ul assignment in m_new_tbf

There are a couple of possibilities where one TBF is used to assign a
new one:

1. Assign a DL TBF from a UL TBF
2. Assign a UL TBF from a DL TBF
3. Assign a DL TBF from a DL TBF which is in wait-release state (T3193 is
   running)

In these cases the assignment is sent on the existing TBF and triggers
the assignement of the new TBF (with different TFI/direction).

The current code detects these situations by looking at dl/ul_ass_state
and then chosing the TBF with the opposite direction (DL/UL) that has
the same TLLI. This does not work in the case 3 above where a new DL TBF
is triggered for a DL TBF. The current code reuses the old TBF (and
TFI), but this violates the spec.

This patch introduces a m_new_tbf member which is set to the new TBF to
be assigned. When receiving a control ack the code looks up the
n_new_tbf member of the tbf that requested the control ack and completes
the ul/dl assignment. If the old TBF was in the wait release state
(T3193 is running) it is released.

From 3GPP TS 04.60 9.3.2.6:
"""
If the network has received the PACKET DOWNLINK ACK/NACK message with
the Final Ack Indicator bit set to '1' and has new data to transmit for
the mobile station, the network may establish a new downlink TBF for the
mobile station by sending the PACKET DOWNLINK ASSIGNMENT or PACKET
TIMESLOT RECONFIGURE message with the Control Ack bit set to '1' on
PACCH. In case the network establishes a new downlink TBF for the mobile
station, the network shall stop timer T3193.
"""

reuse_tbf() is modified to allocate a new TBF with a new TFI and trigger
a dl assignment for that TBF on the old TBF. All pending data is moved
to the new TBF.

Ticket: SYS#382
Sponsored-by: On-Waves ehf
diff --git a/src/bts.cpp b/src/bts.cpp
index 8665962..b660014 100644
--- a/src/bts.cpp
+++ b/src/bts.cpp
@@ -511,9 +511,12 @@
 		LOGP(DRLCMAC, LOGL_DEBUG, "Send dowlink assignment on "
 			"PACCH, because %s exists\n", tbf_name(old_tbf));
 		old_tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_SEND_ASS;
+
+		old_tbf->set_new_tbf(dl_tbf);
+		old_tbf->was_releasing = old_tbf->state_is(GPRS_RLCMAC_WAIT_RELEASE);
+
 		/* use TA from old TBF */
 		dl_tbf->ta = old_tbf->ta;
-		dl_tbf->was_releasing = dl_tbf->state_is(GPRS_RLCMAC_WAIT_RELEASE);
 		/* change state */
 		dl_tbf->set_state(GPRS_RLCMAC_ASSIGN);
 		dl_tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_PACCH);
@@ -530,6 +533,7 @@
 		dl_tbf->set_state(GPRS_RLCMAC_ASSIGN);
 		dl_tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_CCCH);
 		dl_tbf->assign_imsi(imsi);
+		dl_tbf->set_new_tbf(dl_tbf);
 		/* send immediate assignment */
 		dl_tbf->bts->snd_dl_ass(dl_tbf, 0, imsi);
 		dl_tbf->m_wait_confirm = 1;
@@ -729,7 +733,7 @@
 
 void gprs_rlcmac_pdch::rcv_control_ack(Packet_Control_Acknowledgement_t *packet, uint32_t fn)
 {
-	struct gprs_rlcmac_tbf *tbf;
+	struct gprs_rlcmac_tbf *tbf, *new_tbf;
 	uint32_t tlli = 0;
 
 	tlli = packet->TLLI;
@@ -766,25 +770,27 @@
 		/* reset N3105 */
 		tbf->n3105 = 0;
 		tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_NONE;
-		if (tbf->direction == GPRS_RLCMAC_UL_TBF)
-			tbf = bts()->dl_tbf_by_tlli(tbf->tlli());
-#warning "TBF is changing on the way... *sigh*"
-		if (!tbf) {
+
+		new_tbf = tbf->new_tbf();
+		if (!new_tbf) {
 			LOGP(DRLCMAC, LOGL_ERROR, "Got ACK, but DL "
 				"TBF is gone TLLI=0x%08x\n", tlli);
 			return;
 		}
-		tbf->set_state(GPRS_RLCMAC_FLOW);
+		if (tbf->state_is(GPRS_RLCMAC_WAIT_RELEASE))
+			tbf_free(tbf);
+
+		new_tbf->set_state(GPRS_RLCMAC_FLOW);
 		/* stop pending assignment timer */
-		tbf->stop_timer();
-		if ((tbf->state_flags &
+		new_tbf->stop_timer();
+		if ((new_tbf->state_flags &
 			(1 << GPRS_RLCMAC_FLAG_TO_DL_ASS))) {
-			tbf->state_flags &=
+			new_tbf->state_flags &=
 				~(1 << GPRS_RLCMAC_FLAG_TO_DL_ASS);
 			LOGP(DRLCMAC, LOGL_NOTICE, "Recovered downlink "
-				"assignment for %s\n", tbf_name(tbf));
+				"assignment for %s\n", tbf_name(new_tbf));
 		}
-		tbf_assign_control_ts(tbf);
+		tbf_assign_control_ts(new_tbf);
 		return;
 	}
 	if (tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_WAIT_ACK) {
@@ -792,23 +798,25 @@
 		/* reset N3105 */
 		tbf->n3105 = 0;
 		tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_NONE;
-#warning "TBF is changing on the way... *sigh*"
-		if (tbf->direction == GPRS_RLCMAC_DL_TBF)
-			tbf = bts()->ul_tbf_by_tlli(tbf->tlli());
-		if (!tbf) {
+
+		new_tbf = tbf->new_tbf();
+		if (!new_tbf) {
 			LOGP(DRLCMAC, LOGL_ERROR, "Got ACK, but UL "
 				"TBF is gone TLLI=0x%08x\n", tlli);
 			return;
 		}
-		tbf->set_state(GPRS_RLCMAC_FLOW);
-		if ((tbf->state_flags &
+		if (tbf->state_is(GPRS_RLCMAC_WAIT_RELEASE))
+			tbf_free(tbf);
+
+		new_tbf->set_state(GPRS_RLCMAC_FLOW);
+		if ((new_tbf->state_flags &
 			(1 << GPRS_RLCMAC_FLAG_TO_UL_ASS))) {
-			tbf->state_flags &=
+			new_tbf->state_flags &=
 				~(1 << GPRS_RLCMAC_FLAG_TO_UL_ASS);
 			LOGP(DRLCMAC, LOGL_NOTICE, "Recovered uplink "
-				"assignment for UL %s\n", tbf_name(tbf));
+				"assignment for UL %s\n", tbf_name(new_tbf));
 		}
-		tbf_assign_control_ts(tbf);
+		tbf_assign_control_ts(new_tbf);
 		return;
 	}
 	LOGP(DRLCMAC, LOGL_ERROR, "Error: received PACET CONTROL ACK "
@@ -858,7 +866,7 @@
 	if (ack_nack->Exist_Channel_Request_Description) {
 		LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF in ack "
 			"message, so we provide one:\n");
-		tbf_alloc_ul(bts_data(), tbf->trx->trx_no, tbf->ms_class, tbf->tlli(), tbf->ta, tbf);
+		tbf->set_new_tbf(tbf_alloc_ul(bts_data(), tbf->trx->trx_no, tbf->ms_class, tbf->tlli(), tbf->ta, tbf));
 		/* schedule uplink assignment */
 		tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_SEND_ASS;
 	}
@@ -920,6 +928,8 @@
 		ul_tbf = tbf_alloc_ul(bts_data(), trx_no(), ms_class, tlli, ta, NULL);
 		if (!ul_tbf)
 			return;
+
+		ul_tbf->set_new_tbf(ul_tbf);
 		/* set control ts to current MS's TS, until assignment complete */
 		LOGP(DRLCMAC, LOGL_DEBUG, "Change control TS to %d until assinment is complete.\n", ts_no);
 		ul_tbf->control_ts = ts_no;
diff --git a/src/tbf.cpp b/src/tbf.cpp
index ccf7362..2afe257 100644
--- a/src/tbf.cpp
+++ b/src/tbf.cpp
@@ -51,6 +51,11 @@
 	m_imsi[sizeof(m_imsi) - 1] = '\0';
 }
 
+void gprs_rlcmac_tbf::set_new_tbf(gprs_rlcmac_tbf *tbf)
+{
+	m_new_tbf = tbf;
+}
+
 gprs_rlcmac_ul_tbf *tbf_alloc_ul(struct gprs_rlcmac_bts *bts,
 	int8_t use_trx, uint8_t ms_class,
 	uint32_t tlli, uint8_t ta, struct gprs_rlcmac_tbf *dl_tbf)
@@ -557,10 +562,10 @@
 				"finished.\n");
 			return NULL;
 		}
-		#warning "THIS should probably go over the IMSI too"
-		new_dl_tbf = ul_tbf->bts->dl_tbf_by_tlli(m_tlli);
-	} else
-		new_dl_tbf = static_cast<gprs_rlcmac_dl_tbf *>(this);
+	}
+
+	new_dl_tbf = static_cast<gprs_rlcmac_dl_tbf *>(m_new_tbf);
+	new_dl_tbf->was_releasing = was_releasing;
 	if (!new_dl_tbf) {
 		LOGP(DRLCMACDL, LOGL_ERROR, "We have a schedule for downlink "
 			"assignment at uplink %s, but there is no downlink "
@@ -625,13 +630,7 @@
 			return NULL;
 	}
 
-	/* on down TBF we get the uplink TBF to be assigned. */
-#warning "Probably want to find by IMSI too"
-	if (direction == GPRS_RLCMAC_DL_TBF)
-		new_tbf = bts->ul_tbf_by_tlli(m_tlli);
-	else
-		new_tbf = static_cast<gprs_rlcmac_ul_tbf *>(this);
-
+	new_tbf = static_cast<gprs_rlcmac_ul_tbf *>(m_new_tbf);
 	if (!new_tbf) {
 		LOGP(DRLCMACUL, LOGL_ERROR, "We have a schedule for uplink "
 			"assignment at downlink %s, but there is no uplink "
diff --git a/src/tbf.h b/src/tbf.h
index c0f976d..69f1f05 100644
--- a/src/tbf.h
+++ b/src/tbf.h
@@ -142,6 +142,9 @@
 	const char *imsi() const;
 	void assign_imsi(const char *imsi);
 
+	void set_new_tbf(gprs_rlcmac_tbf *tbf);
+	gprs_rlcmac_tbf *new_tbf() const;
+
 	time_t created_ts() const;
 
 	/* attempt to make things a bit more fair */
@@ -165,6 +168,8 @@
 	enum gprs_rlcmac_tbf_ul_ass_state ul_ass_state;
 	enum gprs_rlcmac_tbf_ul_ack_state ul_ack_state;
 
+	gprs_rlcmac_tbf *m_new_tbf;
+
 	enum gprs_rlcmac_tbf_poll_state poll_state;
 	uint32_t poll_fn; /* frame number to poll */
 
@@ -289,6 +294,11 @@
 	return m_imsi;
 }
 
+inline gprs_rlcmac_tbf *gprs_rlcmac_tbf::new_tbf() const
+{
+	return m_new_tbf;
+}
+
 inline time_t gprs_rlcmac_tbf::created_ts() const
 {
 	return m_created_ts;
diff --git a/src/tbf_dl.cpp b/src/tbf_dl.cpp
index 8abea20..a715af7 100644
--- a/src/tbf_dl.cpp
+++ b/src/tbf_dl.cpp
@@ -643,10 +643,39 @@
 
 void gprs_rlcmac_dl_tbf::reuse_tbf(const uint8_t *data, const uint16_t len)
 {
+	uint8_t trx;
+	struct gprs_rlcmac_dl_tbf *new_tbf;
+	int8_t tfi; /* must be signed */
+	int rc;
+	struct msgb *msg;
+
 	bts->tbf_reused();
-	m_llc.put_frame(data, len);
+
+	tfi = bts->tfi_find_free(GPRS_RLCMAC_DL_TBF, &trx, this->trx->trx_no);
+	if (tfi < 0) {
+		LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource\n");
+		/* FIXME: send reject */
+		return;
+	}
+	new_tbf = tbf_alloc_dl_tbf(bts->bts_data(), NULL, tfi, trx, ms_class, 0);
+	if (!new_tbf) {
+		LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource\n");
+		/* FIXME: send reject */
+		return;
+	}
+
+	new_tbf->m_tlli = m_tlli;
+	new_tbf->m_tlli_valid = m_tlli_valid;
+	new_tbf->ta = ta;
+	new_tbf->assign_imsi(m_imsi);
+
+	/* Copy over all data to the new TBF */
+	new_tbf->m_llc.put_frame(data, len);
 	bts->llc_frame_sched();
 
+	while ((msg = m_llc.dequeue()))
+		new_tbf->m_llc.enqueue(msg);
+
 	/* reset rlc states */
 	m_tx_counter = 0;
 	m_wait_confirm = 0;
@@ -661,7 +690,7 @@
 	LOGP(DRLCMAC, LOGL_DEBUG, "%s Trigger dowlink assignment on PACCH, "
 		"because another LLC PDU has arrived in between\n",
 		tbf_name(this));
-	bts->trigger_dl_ass(this, this, NULL);
+	bts->trigger_dl_ass(new_tbf, this, NULL);
 }
 
 bool gprs_rlcmac_dl_tbf::dl_window_stalled() const