edge: Refactor create_dl_acked_block for multi-block support

Currently the method is hard-coded to support a single BSN only.
MCS-7 to MCS-9 encode 2 data blocks into a single RLC data message.

This commit refactors create_dl_acked_block to process any number
of blocks internally.

Note that this does not extend the parameter list accordingly and
just duplicates the BSN if these MCS are being used.

Sponsored-by: On-Waves ehf
diff --git a/src/tbf_dl.cpp b/src/tbf_dl.cpp
index 6208b60..21b2ba1 100644
--- a/src/tbf_dl.cpp
+++ b/src/tbf_dl.cpp
@@ -519,9 +519,9 @@
 
 struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
 				const uint32_t fn, const uint8_t ts,
-				const int index)
+				int index, int index2)
 {
-	uint8_t *block_data, *msg_data;
+	uint8_t *msg_data;
 	struct msgb *dl_msg;
 	unsigned msg_len;
 	bool need_poll;
@@ -530,26 +530,96 @@
 	unsigned int rrbp;
 	uint32_t new_poll_fn;
 	int rc;
-
+	bool is_final = false;
 	gprs_rlc_data_info rlc;
 	GprsCodingScheme cs;
-	gprs_rlc_data_block_info *rdbi;
+	int bsns[ARRAY_SIZE(rlc.block_info)];
+	unsigned num_bsns;
+	int punct[ARRAY_SIZE(rlc.block_info)];
+	bool need_padding = false;
 
-	/* get data and header from current block */
-	block_data = m_rlc.block(index)->block;
-
+	/*
+	 * TODO: This is an experimental work-around to put 2 BSN into
+	 * MSC-7 to MCS-9 encoded messages. It just sends the same BSN
+	 * twice in the block. The cs should be derived from the TBF's
+	 * current CS such that both BSNs (that must be compatible) can
+	 * be put into the data area, even if the resulting CS is higher than
+	 * the current limit.
+	 */
 	cs = m_rlc.block(index)->cs;
+	bsns[0] = index;
+	num_bsns = 1;
 
-	gprs_rlc_data_info_init_dl(&rlc, cs, false);
+	if (index2 >= 0) {
+		bsns[num_bsns] = index2;
+		num_bsns += 1;
+	}
+
+	if (num_bsns == 1)
+		cs.decToSingleBlock(&need_padding);
+
+	gprs_rlc_data_info_init_dl(&rlc, cs, need_padding);
 
 	rlc.usf = 7; /* will be set at scheduler */
 	rlc.pr = 0; /* FIXME: power reduction */
 	rlc.tfi = m_tfi; /* TFI */
-	/* TODO: Use real puncturing values */
-	rlc.cps = gprs_rlc_mcs_cps(cs, 0, 0, 0);
 
-	rlc.block_info[data_block_idx] = m_rlc.block(index)->block_info;
-	rdbi = &rlc.block_info[data_block_idx];
+	/* return data block(s) as message */
+	msg_len = cs.sizeDL();
+	dl_msg = msgb_alloc(msg_len, "rlcmac_dl_data");
+	if (!dl_msg)
+		return NULL;
+
+	msg_data = msgb_put(dl_msg, msg_len);
+
+	/* Copy block(s) to RLC message */
+	for (data_block_idx = 0; data_block_idx < rlc.num_data_blocks;
+		data_block_idx++)
+	{
+		int bsn;
+		GprsCodingScheme cs_enc;
+		uint8_t *block_data;
+		gprs_rlc_data_block_info *rdbi, *block_info;
+
+		/* Check if there are more blocks than BSNs */
+		if (data_block_idx < num_bsns)
+			bsn = bsns[data_block_idx];
+		else
+			bsn = bsns[0];
+
+		cs_enc = m_rlc.block(bsn)->cs;
+
+		/* get data and header from current block */
+		block_data = m_rlc.block(bsn)->block;
+
+		/* TODO: Use real puncturing values */
+		punct[data_block_idx] = data_block_idx;
+
+		rdbi = &rlc.block_info[data_block_idx];
+		block_info = &m_rlc.block(bsn)->block_info;
+
+		if(rdbi->data_len != m_rlc.block(bsn)->len) {
+			LOGP(DRLCMACDL, LOGL_ERROR,
+				"ERROR: Expected len = %d for %s instead of "
+				"%d in data unit %d (BSN %d, %s)\n",
+				rdbi->data_len, cs.name(), m_rlc.block(bsn)->len,
+				data_block_idx, bsn, cs_enc.name());
+			OSMO_ASSERT(rdbi->data_len == m_rlc.block(bsn)->len);
+		}
+		rdbi->e   = block_info->e;
+		rdbi->cv  = block_info->cv;
+		rdbi->bsn = bsn;
+		is_final = is_final || rdbi->cv == 0;
+
+		LOGP(DRLCMACDL, LOGL_DEBUG, "- Copying data unit %d (BSN %d)\n",
+			data_block_idx, bsn);
+
+		Encoding::rlc_copy_from_aligned_buffer(&rlc, data_block_idx,
+			msg_data, block_data);
+	}
+
+	OSMO_ASSERT(ARRAY_SIZE(punct) >= 2);
+	rlc.cps = gprs_rlc_mcs_cps(cs, punct[0], punct[1], need_padding);
 
 	/* If the TBF has just started, relate frames_since_last_poll to the
 	 * current fn */
@@ -583,7 +653,7 @@
 				"TS %d\n", ts);
 			m_tx_counter = 0;
 			/* start timer whenever we send the final block */
-			if (rdbi->cv == 0)
+			if (is_final)
 				tbf_timer_start(this, 3191, bts_data()->t3191, 0);
 
 			/* Clear poll timeout flag */
@@ -604,20 +674,11 @@
 		}
 	}
 
-	msg_len = cs.sizeDL();
-
-	/* return data block as message */
-	dl_msg = msgb_alloc(msg_len, "rlcmac_dl_data");
-	if (!dl_msg)
-		return NULL;
-
-	msg_data = msgb_put(dl_msg, msg_len);
 	Encoding::rlc_write_dl_data_header(&rlc, msg_data);
-	Encoding::rlc_copy_from_aligned_buffer(&rlc, data_block_idx, msg_data,
-		block_data);
 
-	LOGP(DRLCMACDL, LOGL_DEBUG, "msg block (BSN %d, %s): %s\n",
+	LOGP(DRLCMACDL, LOGL_DEBUG, "msg block (BSN %d, %s%s): %s\n",
 		index, cs.name(),
+		need_padding ? ", padded" : "",
 		msgb_hexdump(dl_msg));
 
 	/* Increment TX-counter */