Replace PollController with newly added PDCH UL Controller

TbfTest is updated to submit empty blocks to have somehow meaningful
output (at least as meaningful test results as before, not much). That's
because we must update bts->curr_fn to have polls expire.

Related: OS#5020
Change-Id: I683ca738ce5a133c49c36a1d94439a942d64a831
diff --git a/src/Makefile.am b/src/Makefile.am
index ab9aeeb..e1225b0 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -63,7 +63,6 @@
 	bts.cpp \
 	pdch.cpp \
 	pdch_ul_controller.c \
-	poll_controller.cpp \
 	encoding.cpp \
 	sba.c \
 	decoding.cpp \
@@ -102,7 +101,6 @@
 	bts.h \
 	pdch.h \
 	pdch_ul_controller.h \
-	poll_controller.h \
 	encoding.h \
 	sba.h \
 	rlc.h \
diff --git a/src/bts.cpp b/src/bts.cpp
index a7d475c..9f78c8b 100644
--- a/src/bts.cpp
+++ b/src/bts.cpp
@@ -19,7 +19,6 @@
  */
 
 #include <bts.h>
-#include <poll_controller.h>
 #include <tbf.h>
 #include <tbf_ul.h>
 #include <encoding.h>
@@ -212,7 +211,6 @@
 	 * m_ms_store's destructor */
 	bts->ms_store->cleanup();
 	delete bts->ms_store;
-	delete bts->pollController;
 
 	if (bts->ratectrs) {
 		rate_ctr_group_free(bts->ratectrs);
@@ -244,7 +242,6 @@
 	bts->pcu = pcu;
 	bts->nr = bts_nr;
 
-	bts->pollController = new PollController(*bts);
 	bts->ms_store = new GprsMsStorage(bts);
 
 	bts->cur_fn = 0;
@@ -295,16 +292,9 @@
 	/* The UL frame numbers lag 3 behind the DL frames and the data
 	 * indication is only sent after all 4 frames of the block have been
 	 * received. Sometimes there is an idle frame between the end of one
-	 * and start of another frame (every 3 blocks).  So the timeout should
-	 * definitely be there if we're more than 8 frames past poll_fn. Let's
-	 * stay on the safe side and say 13 or more. An additional delay can
-	 * happen due to the block processing time in the DSP, so the delay of
-	 * decoded blocks relative to the timing clock can be much larger.
-	 * Values up to 50 frames have been observed under load. */
-	const static int max_delay = 60;
+	 * and start of another frame (every 3 blocks). */
 
 	bts->cur_fn = fn;
-	bts->pollController->expireTimedout(bts->cur_fn, max_delay);
 }
 
 static inline int delta_fn(int fn, int to)
@@ -341,8 +331,6 @@
 	if (delay < fn_update_ok_min_delay || delay > fn_update_ok_max_delay ||
 		bts_current_frame_number(bts) == 0)
 		bts->cur_fn = fn;
-
-	bts->pollController->expireTimedout(fn, max_delay);
 }
 
 int bts_add_paging(struct gprs_rlcmac_bts *bts, uint8_t chan_needed, const struct osmo_mobile_identity *mi)
@@ -485,36 +473,6 @@
 	return false;
 }
 
-struct gprs_rlcmac_dl_tbf *bts_dl_tbf_by_poll_fn(struct gprs_rlcmac_bts *bts, uint32_t fn, uint8_t trx, uint8_t ts)
-{
-	struct llist_item *pos;
-	struct gprs_rlcmac_tbf *tbf;
-
-	/* only one TBF can poll on specific TS/FN, because scheduler can only
-	 * schedule one downlink control block (with polling) at a FN per TS */
-	llist_for_each_entry(pos, &bts->dl_tbfs, list) {
-		tbf = (struct gprs_rlcmac_tbf *)pos->entry;
-		if (tbf_check(tbf, fn, trx, ts))
-			return as_dl_tbf(tbf);
-	}
-	return NULL;
-}
-
-struct gprs_rlcmac_ul_tbf *bts_ul_tbf_by_poll_fn(struct gprs_rlcmac_bts *bts, uint32_t fn, uint8_t trx, uint8_t ts)
-{
-	struct llist_item *pos;
-	struct gprs_rlcmac_tbf *tbf;
-
-	/* only one TBF can poll on specific TS/FN, because scheduler can only
-	 * schedule one downlink control block (with polling) at a FN per TS */
-	llist_for_each_entry(pos, &bts->ul_tbfs, list) {
-		tbf = (struct gprs_rlcmac_tbf *)pos->entry;
-		if (tbf_check(tbf, fn, trx, ts))
-			return as_ul_tbf(tbf);
-	}
-	return NULL;
-}
-
 /* lookup downlink TBF Entity (by TFI) */
 struct gprs_rlcmac_dl_tbf *bts_dl_tbf_by_tfi(struct gprs_rlcmac_bts *bts, uint8_t tfi, uint8_t trx, uint8_t ts)
 {
@@ -1162,13 +1120,16 @@
 void bts_update_tbf_ta(struct gprs_rlcmac_bts *bts, const char *p, uint32_t fn,
 		       uint8_t trx_no, uint8_t ts, int8_t ta, bool is_rach)
 {
-	struct gprs_rlcmac_ul_tbf *tbf =
-		bts_ul_tbf_by_poll_fn(bts, fn, trx_no, ts);
-	if (!tbf)
+	struct gprs_rlcmac_pdch *pdch = &bts->trx[trx_no].pdch[ts];
+	struct pdch_ulc_node *poll = pdch_ulc_get_node(pdch->ulc, fn);
+	struct gprs_rlcmac_ul_tbf *tbf;
+	if (!poll || poll->type !=PDCH_ULC_NODE_TBF_POLL ||
+	    poll->tbf_poll.poll_tbf->direction != GPRS_RLCMAC_UL_TBF)
 		LOGP(DL1IF, LOGL_DEBUG, "[%s] update TA = %u ignored due to "
 		     "unknown UL TBF on TRX = %d, TS = %d, FN = %d\n",
 		     p, ta, trx_no, ts, fn);
 	else {
+		tbf = as_ul_tbf(poll->tbf_poll.poll_tbf);
 		/* we need to distinguish TA information provided by L1
 		 * from PH-DATA-IND and PHY-RA-IND so that we can properly
 		 * update TA for given TBF
diff --git a/src/bts.h b/src/bts.h
index 8070abb..a459c2e 100644
--- a/src/bts.h
+++ b/src/bts.h
@@ -195,7 +195,6 @@
 	bool single_block;
 };
 
-struct PollController;
 struct GprsMsStorage;
 struct pcu_l1_meas;
 
@@ -251,7 +250,6 @@
 	int cur_blk_fn;
 	uint8_t max_cs_dl, max_cs_ul;
 	uint8_t max_mcs_dl, max_mcs_ul;
-	struct PollController *pollController;
 	struct rate_ctr_group *ratectrs;
 	struct osmo_stat_item_group *statg;
 
@@ -272,8 +270,6 @@
 
 uint32_t bts_rfn_to_fn(const struct gprs_rlcmac_bts *bts, int32_t rfn);
 
-struct gprs_rlcmac_dl_tbf *bts_dl_tbf_by_poll_fn(struct gprs_rlcmac_bts *bts, uint32_t fn, uint8_t trx, uint8_t ts);
-struct gprs_rlcmac_ul_tbf *bts_ul_tbf_by_poll_fn(struct gprs_rlcmac_bts *bts, uint32_t fn, uint8_t trx, uint8_t ts);
 struct gprs_rlcmac_dl_tbf *bts_dl_tbf_by_tfi(struct gprs_rlcmac_bts *bts, uint8_t tfi, uint8_t trx, uint8_t ts);
 struct gprs_rlcmac_ul_tbf *bts_ul_tbf_by_tfi(struct gprs_rlcmac_bts *bts, uint8_t tfi, uint8_t trx, uint8_t ts);
 
diff --git a/src/pdch.cpp b/src/pdch.cpp
index 1e54117..727ffc6 100644
--- a/src/pdch.cpp
+++ b/src/pdch.cpp
@@ -306,12 +306,10 @@
 	uint32_t tlli = packet->TLLI;
 	GprsMs *ms = bts_ms_by_tlli(bts(), tlli, GSM_RESERVED_TMSI);
 	gprs_rlcmac_ul_tbf *ul_tbf;
+	struct pdch_ulc_node *poll;
 
-	tbf = bts_ul_tbf_by_poll_fn(bts(), fn, trx_no(), ts_no);
-	if (!tbf)
-		tbf = bts_dl_tbf_by_poll_fn(bts(), fn, trx_no(), ts_no);
-
-	if (!tbf) {
+	poll = pdch_ulc_get_node(ulc, fn);
+	if (!poll || poll->type !=PDCH_ULC_NODE_TBF_POLL || !poll->tbf_poll.poll_tbf) {
 		LOGPDCH(this, DRLCMAC, LOGL_NOTICE, "PACKET CONTROL ACK with "
 			"unknown FN=%u TLLI=0x%08x (TRX %d TS %d)\n",
 			fn, tlli, trx_no(), ts_no);
@@ -326,6 +324,7 @@
 				ms_dl_tbf(ms) ? ms_dl_tbf(ms)->state_name() : "None");
 		return;
 	}
+	tbf = poll->tbf_poll.poll_tbf;
 
 	/* Reset N3101 counter: */
 	tbf->n_reset(N3101);
@@ -334,6 +333,7 @@
 
 	LOGPTBF(tbf, LOGL_DEBUG, "RX: [PCU <- BTS] Packet Control Ack\n");
 	TBF_POLL_SCHED_UNSET(tbf);
+	pdch_ulc_release_fn(ulc, fn);
 
 	/* check if this control ack belongs to packet uplink ack */
 	ul_tbf = as_ul_tbf(tbf);
@@ -426,6 +426,7 @@
 void gprs_rlcmac_pdch::rcv_control_dl_ack_nack(Packet_Downlink_Ack_Nack_t *ack_nack, uint32_t fn, struct pcu_l1_meas *meas)
 {
 	int8_t tfi = 0; /* must be signed */
+	struct pdch_ulc_node *poll;
 	struct gprs_rlcmac_dl_tbf *tbf;
 	int rc;
 	int num_blocks;
@@ -435,13 +436,14 @@
 	char show_bits[RLC_GPRS_WS + 1];
 
 	tfi = ack_nack->DOWNLINK_TFI;
-	tbf = bts_dl_tbf_by_poll_fn(bts(), fn, trx_no(), ts_no);
-	if (!tbf) {
+	poll = pdch_ulc_get_node(ulc, fn);
+	if (!poll || poll->type != PDCH_ULC_NODE_TBF_POLL) {
 		LOGPDCH(this, DRLCMAC, LOGL_NOTICE, "PACKET DOWNLINK ACK with "
 			"unknown FN=%u TFI=%d (TRX %d TS %d)\n",
 			fn, tfi, trx_no(), ts_no);
 		return;
 	}
+	tbf = as_dl_tbf(poll->tbf_poll.poll_tbf);
 	if (tbf->tfi() != tfi) {
 		LOGPTBFDL(tbf, LOGL_NOTICE,
 			  "PACKET DOWNLINK ACK with wrong TFI=%d, ignoring!\n", tfi);
@@ -453,6 +455,7 @@
 
 	if (tbf->handle_ack_nack())
 		LOGPTBF(tbf, LOGL_NOTICE, "Recovered downlink ack\n");
+	pdch_ulc_release_fn(ulc, fn);
 
 	LOGPTBF(tbf, LOGL_DEBUG, "RX: [PCU <- BTS] Packet Downlink Ack/Nack\n");
 
@@ -493,6 +496,7 @@
 {
 	int8_t tfi = 0; /* must be signed */
 	struct gprs_rlcmac_dl_tbf *tbf;
+	struct pdch_ulc_node *poll;
 	gprs_rlc_dl_window *window;
 	int rc;
 	int num_blocks;
@@ -502,13 +506,14 @@
 	int bsn_begin, bsn_end;
 
 	tfi = ack_nack->DOWNLINK_TFI;
-	tbf = bts_dl_tbf_by_poll_fn(bts(), fn, trx_no(), ts_no);
-	if (!tbf) {
+	poll = pdch_ulc_get_node(ulc, fn);
+	if (!poll || poll->type !=PDCH_ULC_NODE_TBF_POLL) {
 		LOGPDCH(this, DRLCMAC, LOGL_NOTICE, "EGPRS PACKET DOWNLINK ACK with "
 			"unknown FN=%u TFI=%d (TRX %d TS %d)\n",
 			fn, tfi, trx_no(), ts_no);
 		return;
 	}
+	tbf = as_dl_tbf(poll->tbf_poll.poll_tbf);
 	if (tbf->tfi() != tfi) {
 		LOGPDCH(this, DRLCMAC, LOGL_NOTICE, "EGPRS PACKET DOWNLINK ACK with "
 			"wrong TFI=%d, ignoring!\n", tfi);
@@ -520,6 +525,7 @@
 
 	if (tbf->handle_ack_nack())
 		LOGPTBF(tbf, LOGL_NOTICE, "Recovered EGPRS downlink ack\n");
+	pdch_ulc_release_fn(ulc, fn);
 
 	LOGPTBF(tbf, LOGL_DEBUG,
 		"RX: [PCU <- BTS] EGPRS Packet Downlink Ack/Nack\n");
@@ -694,6 +700,7 @@
 void gprs_rlcmac_pdch::rcv_measurement_report(Packet_Measurement_Report_t *report, uint32_t fn)
 {
 	struct gprs_rlcmac_sba *sba;
+	struct pdch_ulc_node *poll;
 	GprsMs *ms;
 
 	ms = bts_ms_by_tlli(bts(), report->TLLI, GSM_RESERVED_TMSI);
@@ -703,9 +710,23 @@
 		ms = bts_alloc_ms(bts(), 0, 0);
 		ms_set_tlli(ms, report->TLLI);
 	}
-	if ((sba = pdch_ulc_get_sba(this->ulc, fn))) {
-		ms_set_ta(ms, sba->ta);
-		sba_free(sba);
+	if ((poll = pdch_ulc_get_node(ulc, fn))) {
+		switch (poll->type) {
+		case PDCH_ULC_NODE_TBF_USF:
+			break;
+		case PDCH_ULC_NODE_TBF_POLL:
+			LOGPDCH(this, DRLCMAC, LOGL_INFO, "FN=%" PRIu32 " Rx Meas Report "
+				"on RRBP POLL, this probably means a DL/CTRL ACK/NACk will "
+				"need to be polled again later\n", fn);
+			TBF_POLL_SCHED_UNSET(poll->tbf_poll.poll_tbf);
+			pdch_ulc_release_fn(ulc, fn);
+			break;
+		case PDCH_ULC_NODE_SBA:
+			sba = poll->sba.sba;
+			ms_set_ta(ms, sba->ta);
+			sba_free(sba);
+			break;
+		}
 	}
 	gprs_rlcmac_meas_rep(ms, report);
 }
@@ -795,6 +816,7 @@
 			"FN=%u RX: [PCU <- BTS] unknown control block(%d) received\n",
 			fn, ul_control_block->u.MESSAGE_TYPE);
 	}
+
 free_ret:
 	talloc_free(ul_control_block);
 	bitvec_free(rlc_block);
@@ -809,13 +831,8 @@
 	bts_set_current_frame_number(trx->bts, fn);
 
 	/* No successfully decoded UL block was received during this FN: */
-	if (len == 0) {
-		/* TODO: Here, in the future, it can be checked whether a UL block was expected for:
-		 * - UL/DL TBFs: RRBP poll pending (bts->pollController->expireTimedout)
-		 * - UL TBFs: USF poll pending (we don't store this info in ul_tbf yet) -> inc N3101 (OS#5033)
-		 */
+	if (len == 0)
 		return 0;
-	}
 
 	enum CodingScheme cs = mcs_get_by_size_ul(len);
 	if (!cs) {
@@ -1007,6 +1024,8 @@
 	m_assigned_tfi[tbf->direction] &= ~(1UL << tbf->tfi());
 	m_tbfs[tbf->direction][tbf->tfi()] = NULL;
 
+	pdch_ulc_release_tbf(ulc, tbf);
+
 	LOGPDCH(this, DRLCMAC, LOGL_INFO, "Detaching %s, %d TBFs, "
 		"USFs = %02x, TFIs = %08x.\n",
 		tbf->name(), m_num_tbfs[tbf->direction],
diff --git a/src/pdch_ul_controller.c b/src/pdch_ul_controller.c
index ecd3691..38e6893 100644
--- a/src/pdch_ul_controller.c
+++ b/src/pdch_ul_controller.c
@@ -25,6 +25,7 @@
 #include "bts.h"
 #include "sba.h"
 #include "pdch.h"
+#include "pcu_utils.h"
 
 /* TS 44.060 Table 10.4.5.1 states maximum RRBP is N + 26. Give extra space for time diff between Tx and Rx? */
 #define MAX_FN_RESERVED (27 + 50)
@@ -83,6 +84,22 @@
 	return !pdch_ulc_get_node(ulc, fn);
 }
 
+int pdch_ulc_get_next_free_rrbp_fn(struct pdch_ulc *ulc, uint32_t fn, uint32_t *poll_fn, unsigned int *rrbp)
+{
+	/* TODO: support other RRBP offsets, see TS 44.060 able 10.4.5.1 */
+	uint32_t new_poll_fn = next_fn(fn, 13);
+	if (!pdch_ulc_fn_is_free(ulc, new_poll_fn)) {
+		LOGPDCH(ulc->pdch, DRLCMAC, LOGL_ERROR, "Polling is already scheduled "
+			"for single block allocation at FN=%u\n", fn);
+		return -EBUSY;
+	}
+
+	*poll_fn = new_poll_fn;
+	*rrbp = 0;
+
+	return 0;
+}
+
 static struct pdch_ulc_node *_alloc_node(struct pdch_ulc *ulc, uint32_t fn)
 {
 	struct pdch_ulc_node *node;
@@ -126,7 +143,10 @@
 
 int pdch_ulc_reserve_tbf_poll(struct pdch_ulc *ulc, uint32_t fn, struct gprs_rlcmac_tbf *tbf)
 {
-	return 0; /* TODO: implement */
+	struct pdch_ulc_node *item = _alloc_node(ulc, fn);
+	item->type = PDCH_ULC_NODE_TBF_POLL;
+	item->tbf_poll.poll_tbf = tbf;
+	return pdch_ulc_add_node(ulc, item);
 }
 
 int pdch_ulc_reserve_sba(struct pdch_ulc *ulc, struct gprs_rlcmac_sba *sba)
@@ -147,6 +167,40 @@
 	return 0;
 }
 
+void pdch_ulc_release_tbf(struct pdch_ulc *ulc, const struct gprs_rlcmac_tbf *tbf)
+{
+	bool tree_modified;
+	do {
+		struct rb_node *node;
+		struct pdch_ulc_node *item;
+		const struct gprs_rlcmac_tbf *item_tbf;
+
+		tree_modified = false;
+		for (node = rb_first(&ulc->tree_root); node; node = rb_next(node)) {
+			item = container_of(node, struct pdch_ulc_node, node);
+			switch (item->type) {
+			case PDCH_ULC_NODE_SBA:
+				continue;
+			case PDCH_ULC_NODE_TBF_POLL:
+				item_tbf = item->tbf_poll.poll_tbf;
+				break;
+			case PDCH_ULC_NODE_TBF_USF:
+				item_tbf = (struct gprs_rlcmac_tbf *)item->tbf_usf.ul_tbf;
+				break;
+			}
+			if (item_tbf != tbf)
+				continue;
+			/* One entry found, remove it from tree and restart
+			 * search from start (to avoid traverse continue from
+			 * no-more existant node */
+			tree_modified = true;
+			rb_erase(&item->node, &ulc->tree_root);
+			talloc_free(item);
+			break;
+		}
+	} while (tree_modified);
+}
+
 void pdch_ulc_expire_fn(struct pdch_ulc *ulc, uint32_t fn)
 {
 	struct gprs_rlcmac_sba *sba;
@@ -170,7 +224,10 @@
 			/* TODO: increase N3...*/
 			break;
 		case PDCH_ULC_NODE_TBF_POLL:
-			/* TODO: increase N3...*/
+			LOGPDCH(ulc->pdch, DRLCMAC, LOGL_NOTICE,
+				"Timeout for registered POLL (FN=%u): %s\n",
+				item->fn, tbf_name(item->tbf_poll.poll_tbf));
+			tbf_poll_timeout(item->tbf_poll.poll_tbf);
 			break;
 		case PDCH_ULC_NODE_SBA:
 			sba = item->sba.sba;
diff --git a/src/pdch_ul_controller.h b/src/pdch_ul_controller.h
index e86dbf5..53604a9 100644
--- a/src/pdch_ul_controller.h
+++ b/src/pdch_ul_controller.h
@@ -70,10 +70,14 @@
 
 bool pdch_ulc_fn_is_free(struct pdch_ulc *ulc, uint32_t fn);
 
+int pdch_ulc_get_next_free_rrbp_fn(struct pdch_ulc *ulc, uint32_t fn, uint32_t *poll_fn, unsigned int *rrbp);
+
 struct pdch_ulc_node *pdch_ulc_get_node(struct pdch_ulc *ulc, uint32_t fn);
 struct pdch_ulc_node *pdch_ulc_pop_node(struct pdch_ulc *ulc, uint32_t fn);
 struct gprs_rlcmac_sba *pdch_ulc_get_sba(struct pdch_ulc *ulc, uint32_t fn);
 
+void pdch_ulc_release_tbf(struct pdch_ulc *ulc, const struct gprs_rlcmac_tbf *tbf);
+
 int pdch_ulc_release_fn(struct pdch_ulc *ulc, uint32_t fn);
 
 void pdch_ulc_expire_fn(struct pdch_ulc *ulc, uint32_t fn);
diff --git a/src/poll_controller.cpp b/src/poll_controller.cpp
deleted file mode 100644
index fe10171..0000000
--- a/src/poll_controller.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-/* poll_controller.h
- *
- * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
- * Copyright (C) 2013 by Holger Hans Peter Freyther
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#include <poll_controller.h>
-#include <bts.h>
-#include <tbf.h>
-#include <tbf_ul.h>
-#include <tbf_dl.h>
-#include <cxx_linuxlist.h>
-#include <sba.h>
-
-extern "C" {
-#include <osmocom/core/linuxlist.h>
-#include <osmocom/gsm/gsm_utils.h>
-}
-
-PollController::PollController(struct gprs_rlcmac_bts& bts)
-	: m_bts(bts)
-{}
-
-static inline bool elapsed_fn_check(unsigned max_delay, int frame_number, uint32_t from)
-{
-	uint32_t elapsed = (frame_number + GSM_MAX_FN - from) % GSM_MAX_FN;
-
-	if (elapsed > max_delay && elapsed < 2715400)
-		return true;
-
-	return false;
-}
-
-void PollController::expireTimedout(int frame_number, unsigned max_delay)
-{
-	struct gprs_rlcmac_dl_tbf *dl_tbf;
-	struct gprs_rlcmac_ul_tbf *ul_tbf;
-	struct llist_item *pos;
-
-	llist_for_each_entry(pos, &m_bts.ul_tbfs, list) {
-		ul_tbf = as_ul_tbf((struct gprs_rlcmac_tbf *)pos->entry);
-		if (ul_tbf->poll_scheduled()) {
-			if (elapsed_fn_check(max_delay, frame_number, ul_tbf->poll_fn))
-				ul_tbf->poll_timeout();
-		}
-	}
-	llist_for_each_entry(pos, &m_bts.dl_tbfs, list) {
-		dl_tbf = as_dl_tbf((struct gprs_rlcmac_tbf *)pos->entry);
-		if (dl_tbf->poll_scheduled()) {
-			if (elapsed_fn_check(max_delay, frame_number, dl_tbf->poll_fn))
-				dl_tbf->poll_timeout();
-		}
-	}
-}
diff --git a/src/poll_controller.h b/src/poll_controller.h
deleted file mode 100644
index 8e709a3..0000000
--- a/src/poll_controller.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/* poll_controller.h
- *
- * Copyright (C) 2013 by Holger Hans Peter Freyther
- *
- * All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- */
-
-#pragma once
-
-struct gprs_rlcmac_bts;
-
-/**
- * I belong to a BTS and I am responsible for finding TBFs and
- * SBAs that should have been polled and execute the timeout
- * action on them.
- */
-struct PollController {
-public:
-	PollController(struct gprs_rlcmac_bts& bts);
-
-	/* check for poll timeout */
-	void expireTimedout(int frame_number, unsigned max_delay);
-
-private:
-	struct gprs_rlcmac_bts& m_bts;
-
-private:
-	/* disable copying to avoid slicing */
-	PollController(const PollController&);
-	PollController& operator=(const PollController&);
-};
diff --git a/src/tbf.cpp b/src/tbf.cpp
index 87ca5bd..7237f58 100644
--- a/src/tbf.cpp
+++ b/src/tbf.cpp
@@ -550,8 +550,7 @@
 int gprs_rlcmac_tbf::check_polling(uint32_t fn, uint8_t ts,
 	uint32_t *poll_fn_, unsigned int *rrbp_) const
 {
-	uint32_t new_poll_fn = next_fn(fn, 13);
-
+	int rc;
 	if (!is_control_ts(ts)) {
 		LOGPTBF(this, LOGL_DEBUG, "Polling cannot be "
 			"scheduled in this TS %d (first control TS %d)\n",
@@ -562,15 +561,13 @@
 		LOGPTBF(this, LOGL_DEBUG, "Polling is already scheduled\n");
 		return -EBUSY;
 	}
-	if (!pdch_ulc_fn_is_free(trx->pdch[ts].ulc, new_poll_fn)) {
-		LOGPTBF(this, LOGL_DEBUG, "Polling is already scheduled "
-			"for single block allocation at FN %d TS %d ...\n",
-			new_poll_fn, ts);
-		return -EBUSY;
-	}
 
-	*poll_fn_ = new_poll_fn;
-	*rrbp_ = 0;
+	if ((rc = pdch_ulc_get_next_free_rrbp_fn(trx->pdch[ts].ulc, fn, poll_fn_, rrbp_)) < 0) {
+		LOGPTBF(this, LOGL_DEBUG,
+			"(bts=%u,trx=%u,ts=%u) FN=%u No suitable free RRBP offset found!\n",
+			trx->bts->nr, trx->trx_no, ts, fn);
+		return rc;
+	}
 
 	return 0;
 }
@@ -591,6 +588,11 @@
 			  chan, new_poll_fn, ts);
 
 	/* schedule polling */
+	if (pdch_ulc_reserve_tbf_poll(trx->pdch[ts].ulc, new_poll_fn, this) < 0) {
+		LOGPTBFDL(this, LOGL_ERROR, "Failed scheduling poll on %s (FN=%d, TS=%d)\n",
+			  chan, poll_fn, ts);
+		return;
+	}
 	poll_state = GPRS_RLCMAC_POLL_SCHED;
 	poll_fn = new_poll_fn;
 	poll_ts = ts;
@@ -1230,3 +1232,8 @@
 {
 	return tbf->set_polling(new_poll_fn, ts, t);
 }
+
+void tbf_poll_timeout(struct gprs_rlcmac_tbf *tbf)
+{
+	tbf->poll_timeout();
+}
diff --git a/src/tbf.h b/src/tbf.h
index 989ec08..779bc26 100644
--- a/src/tbf.h
+++ b/src/tbf.h
@@ -209,6 +209,7 @@
 int tbf_assign_control_ts(struct gprs_rlcmac_tbf *tbf);
 int tbf_check_polling(const struct gprs_rlcmac_tbf *tbf, uint32_t fn, uint8_t ts, uint32_t *poll_fn, unsigned int *rrbp);
 void tbf_set_polling(struct gprs_rlcmac_tbf *tbf, uint32_t new_poll_fn, uint8_t ts, enum gprs_rlcmac_tbf_poll_type t);
+void tbf_poll_timeout(struct gprs_rlcmac_tbf *tbf);
 #ifdef __cplusplus
 }
 #endif