llc: Separate LLC queue handling from gprs_llc

Currently the gprs_llc class handles both LLC queueing and the
partition into smaller pieces for RLC/MAC encapsulation. This hinders
the separation of TBF and MS related data, since LLC queueing belongs
to the MS related code while the RLC/MAC encoding/decoding belongs to
the TBF layer.

This commits takes the LLC queueing related methods and members and
puts them into a new class gprs_llc_queue. It puts the queueing
object into gprs_rlcmac_tbf and adds accessor functions. The
implementation in tbf.cpp and tbf_dl.cpp is adapted accordingly.

Ticket: #1674
Sponsored-by: On-Waves ehf
diff --git a/src/llc.cpp b/src/llc.cpp
index 9c5581f..09242a5 100644
--- a/src/llc.cpp
+++ b/src/llc.cpp
@@ -42,12 +42,6 @@
 	m_index = 0;
 }
 
-void gprs_llc::enqueue(struct msgb *llc_msg)
-{
-	m_queue_size += 1;
-	msgb_enqueue(&queue, llc_msg);
-}
-
 /* Put an Unconfirmed Information (UI) Dummy command, see GSM 44.064, 6.4.2.2 */
 void gprs_llc::put_dummy_frame(size_t req_len)
 {
@@ -82,36 +76,62 @@
 	m_length += len;
 }
 
-void gprs_llc::clear(BTS *bts)
+void gprs_llc::init()
+{
+	reset();
+}
+
+bool gprs_llc::is_user_data_frame(uint8_t *data, size_t len)
+{
+	if (len < 2)
+		return false;
+
+	if ((data[0] & 0x0f) == 1 /* GPRS_SAPI_GMM */)
+		return false;
+
+	if ((data[0] & 0x0e) != 0xc0 /* LLC UI */)
+		/* It is not an LLC UI frame */
+		return false;
+
+	return true;
+}
+
+void gprs_llc_queue::init()
+{
+	INIT_LLIST_HEAD(&m_queue);
+	m_queue_size = 0;
+	m_avg_queue_delay = 0;
+}
+
+void gprs_llc_queue::enqueue(struct msgb *llc_msg)
+{
+	m_queue_size += 1;
+	msgb_enqueue(&m_queue, llc_msg);
+}
+
+void gprs_llc_queue::clear(BTS *bts)
 {
 	struct msgb *msg;
 
-	while ((msg = msgb_dequeue(&queue))) {
-		bts->llc_dropped_frame();
+	while ((msg = msgb_dequeue(&m_queue))) {
+		if (bts)
+			bts->llc_dropped_frame();
 		msgb_free(msg);
 	}
 
 	m_queue_size = 0;
 }
 
-void gprs_llc::init()
-{
-	INIT_LLIST_HEAD(&queue);
-	m_queue_size = 0;
-	m_avg_queue_delay = 0;
-	reset();
-}
-
 #define ALPHA 0.5f
 
-struct msgb *gprs_llc::dequeue()
+struct msgb *gprs_llc_queue::dequeue()
 {
 	struct msgb *msg;
 	struct timeval *tv, tv_now, tv_result;
 	uint32_t lifetime;
 
 
-	msg = msgb_dequeue(&queue);
+	msg = msgb_dequeue(&m_queue);
 	if (!msg)
 		return NULL;
 
@@ -128,8 +148,7 @@
 	return msg;
 }
 
-
-void gprs_llc::calc_pdu_lifetime(BTS *bts, const uint16_t pdu_delay_csec, struct timeval *tv)
+void gprs_llc_queue::calc_pdu_lifetime(BTS *bts, const uint16_t pdu_delay_csec, struct timeval *tv)
 {
 	uint16_t delay_csec;
 	if (bts->bts_data()->force_llc_lifetime)
@@ -152,7 +171,7 @@
 	timeradd(&now, &csec, tv);
 }
 
-bool gprs_llc::is_frame_expired(struct timeval *tv_now, struct timeval *tv)
+bool gprs_llc_queue::is_frame_expired(struct timeval *tv_now, struct timeval *tv)
 {
 	/* Timeout is infinite */
 	if (tv->tv_sec == 0 && tv->tv_usec == 0)
@@ -160,18 +179,3 @@
 
 	return timercmp(tv_now, tv, >);
 }
-
-bool gprs_llc::is_user_data_frame(uint8_t *data, size_t len)
-{
-	if (len < 2)
-		return false;
-
-	if ((data[0] & 0x0f) == 1 /* GPRS_SAPI_GMM */)
-		return false;
-
-	if ((data[0] & 0x0e) != 0xc0 /* LLC UI */)
-		/* It is not an LLC UI frame */
-		return false;
-
-	return true;
-}
diff --git a/src/llc.h b/src/llc.h
index 11a0c7e..251712a 100644
--- a/src/llc.h
+++ b/src/llc.h
@@ -27,24 +27,18 @@
  * I represent the LLC data to a MS
  */
 struct gprs_llc {
-	static void calc_pdu_lifetime(BTS *bts, const uint16_t pdu_delay_csec, struct timeval *tv);
-	static bool is_frame_expired(struct timeval *now, struct timeval *tv);
 	static bool is_user_data_frame(uint8_t *data, size_t len);
 
 	void init();
 	void reset();
 	void reset_frame_space();
 
-	void enqueue(struct msgb *llc_msg);
-	struct msgb *dequeue();
-
 	void put_frame(const uint8_t *data, size_t len);
 	void put_dummy_frame(size_t req_len);
 	void append_frame(const uint8_t *data, size_t len);
 
 	void consume(size_t len);
 	void consume(uint8_t *data, size_t len);
-	void clear(BTS *bts);
 
 	uint16_t chunk_size() const;
 	uint16_t remaining_space() const;
@@ -55,12 +49,31 @@
 	uint8_t frame[LLC_MAX_LEN]; /* current DL or UL frame */
 	uint16_t m_index; /* current write/read position of frame */
 	uint16_t m_length; /* len of current DL LLC_frame, 0 == no frame */
-	struct llist_head queue; /* queued LLC DL data */
+};
 
+/**
+ * I store the LLC frames that come from the SGSN.
+ */
+struct gprs_llc_queue {
+	static void calc_pdu_lifetime(BTS *bts, const uint16_t pdu_delay_csec, struct timeval *tv);
+	static bool is_frame_expired(struct timeval *now, struct timeval *tv);
+	static bool is_user_data_frame(uint8_t *data, size_t len);
+
+	void init();
+
+	void enqueue(struct msgb *llc_msg);
+	struct msgb *dequeue();
+	void clear(BTS *bts);
+	size_t size() const;
+
+private:
 	uint32_t m_avg_queue_delay; /* Average delay of data going through the queue */
 	size_t m_queue_size;
+	struct llist_head m_queue; /* queued LLC DL data */
+
 };
 
+
 inline uint16_t gprs_llc::chunk_size() const
 {
 	return m_length - m_index;
@@ -92,3 +105,8 @@
 {
 	return m_length + chunk_size <= LLC_MAX_LEN;
 }
+
+inline size_t gprs_llc_queue::size() const
+{
+	return this ? m_queue_size : 0;
+}
diff --git a/src/tbf.cpp b/src/tbf.cpp
index 8501b16..dc26cc7 100644
--- a/src/tbf.cpp
+++ b/src/tbf.cpp
@@ -219,7 +219,7 @@
 			tbf_name(tbf));
 	tbf->stop_timer();
 	#warning "TODO: Could/Should generate  bssgp_tx_llc_discarded"
-	tbf->m_llc.clear(tbf->bts);
+	tbf->llc_queue()->clear(tbf->bts);
 	tbf_unlink_pdch(tbf);
 	llist_del(&tbf->list.list);
 
@@ -451,6 +451,7 @@
 	gettimeofday(&tbf->meas.rssi_tv, NULL);
 
 	tbf->m_llc.init();
+	tbf->llc_queue()->init();
 	return 0;
 }
 
diff --git a/src/tbf.h b/src/tbf.h
index d288669..2a403c6 100644
--- a/src/tbf.h
+++ b/src/tbf.h
@@ -151,6 +151,8 @@
 	void assign_imsi(const char *imsi);
 	uint8_t ta() const;
 	void set_ta(uint8_t);
+	gprs_llc_queue *llc_queue();
+	const gprs_llc_queue *llc_queue() const;
 
 	time_t created_ts() const;
 
@@ -229,6 +231,8 @@
 
 	/* Field to take the TA value if no MS is associated */
 	uint8_t m_ta;
+
+	gprs_llc_queue m_llc_queue;
 private:
 	mutable char m_name_buf[60];
 };
@@ -285,6 +289,16 @@
 	return m_ms;
 }
 
+inline gprs_llc_queue *gprs_rlcmac_tbf::llc_queue()
+{
+	return &m_llc_queue;
+}
+
+inline const gprs_llc_queue *gprs_rlcmac_tbf::llc_queue() const
+{
+	return &m_llc_queue;
+}
+
 inline bool gprs_rlcmac_tbf::is_tlli_valid() const
 {
 	return tlli() != 0;
diff --git a/src/tbf_dl.cpp b/src/tbf_dl.cpp
index 2289e3f..5b9c06c 100644
--- a/src/tbf_dl.cpp
+++ b/src/tbf_dl.cpp
@@ -122,11 +122,11 @@
 		if (!llc_msg)
 			return -ENOMEM;
 		tv = (struct timeval *)msgb_put(llc_msg, sizeof(*tv));
-		gprs_llc::calc_pdu_lifetime(bts, pdu_delay_csec, tv);
+		gprs_llc_queue::calc_pdu_lifetime(bts, pdu_delay_csec, tv);
 		tv = (struct timeval *)msgb_put(llc_msg, sizeof(*tv));
 		gettimeofday(tv, NULL);
 		memcpy(msgb_put(llc_msg, len), data, len);
-		m_llc.enqueue(llc_msg);
+		llc_queue()->enqueue(llc_msg);
 		tbf_update_ms_class(this, ms_class);
 		start_llc_timer();
 	}
@@ -253,7 +253,7 @@
 	gettimeofday(&tv_now, NULL);
 	timeradd(&tv_now, &hyst_delta, &tv_now2);
 
-	while ((msg = m_llc.dequeue())) {
+	while ((msg = llc_queue()->dequeue())) {
 		tv_disc = (struct timeval *)msg->data;
 		msgb_pull(msg, sizeof(*tv_disc));
 		tv_recv = (struct timeval *)msg->data;
@@ -262,11 +262,11 @@
 		gprs_bssgp_update_queue_delay(tv_recv, &tv_now);
 
 		/* Is the age below the low water mark? */
-		if (!gprs_llc::is_frame_expired(&tv_now2, tv_disc))
+		if (!gprs_llc_queue::is_frame_expired(&tv_now2, tv_disc))
 			break;
 
 		/* Is the age below the high water mark */
-		if (!gprs_llc::is_frame_expired(&tv_now, tv_disc)) {
+		if (!gprs_llc_queue::is_frame_expired(&tv_now, tv_disc)) {
 			/* Has the previous message not been dropped? */
 			if (frames == 0)
 				break;
@@ -297,7 +297,7 @@
 		LOGP(DRLCMACDL, LOGL_NOTICE, "%s Discarding LLC PDU "
 			"because lifetime limit reached, "
 			"count=%u new_queue_size=%zu\n",
-			tbf_name(this), frames, m_llc.m_queue_size);
+			tbf_name(this), frames, llc_queue()->size());
 		if (frames > 0xff)
 			frames = 0xff;
 		if (octets > 0xffffff)
@@ -463,7 +463,7 @@
 			break;
 		}
 		/* if FINAL chunk would fit precisely in space left */
-		if (chunk == space && llist_empty(&m_llc.queue) && !keep_open(fn))
+		if (chunk == space && llc_queue()->size() == 0 && !keep_open(fn))
 		{
 			LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d "
 				"would exactly fit into space (%d): because "
@@ -794,8 +794,8 @@
 	new_tbf->m_llc.put_frame(data, len);
 	bts->llc_frame_sched();
 
-	while ((msg = m_llc.dequeue()))
-		new_tbf->m_llc.enqueue(msg);
+	while ((msg = llc_queue()->dequeue()))
+		new_tbf->llc_queue()->enqueue(msg);
 
 	/* reset rlc states */
 	m_tx_counter = 0;
@@ -836,7 +836,7 @@
 
 bool gprs_rlcmac_dl_tbf::have_data() const
 {
-	return m_llc.chunk_size() > 0 || !llist_empty(&m_llc.queue);
+	return m_llc.chunk_size() > 0 || llc_queue()->size() > 0;
 }
 
 int gprs_rlcmac_dl_tbf::frames_since_last_poll(unsigned fn) const