tbf: Move gprs_rlcmac_send_data_block_acknowledged into tbf
We can now remove the gprs_rlcmac_data as the code has been
moved into the tbf, pdch and bts.
diff --git a/src/tbf.cpp b/src/tbf.cpp
index 08f6cf3..eb76ab8 100644
--- a/src/tbf.cpp
+++ b/src/tbf.cpp
@@ -35,10 +35,22 @@
#include <errno.h>
#include <string.h>
+/* After sending these frames, we poll for ack/nack. */
+#define POLL_ACK_AFTER_FRAMES 20
+
/* If acknowledgement to downlink assignment should be polled */
#define POLLING_ASSIGNMENT_DL 1
#define POLLING_ASSIGNMENT_UL 1
+static const struct gprs_rlcmac_cs gprs_rlcmac_cs[] = {
+/* frame length data block max payload */
+ { 0, 0, 0 },
+ { 23, 23, 20 }, /* CS-1 */
+ { 34, 33, 30 }, /* CS-2 */
+ { 40, 39, 36 }, /* CS-3 */
+ { 54, 53, 50 }, /* CS-4 */
+};
+
extern "C" {
int bssgp_tx_llc_discarded(struct bssgp_bvc_ctx *bctx, uint32_t tlli,
uint8_t num_frames, uint32_t num_octets);
@@ -885,6 +897,325 @@
return 0;
}
+/*
+ * Create DL data block
+ * The messages are fragmented and forwarded as data blocks.
+ */
+struct msgb *gprs_rlcmac_tbf::create_dl_acked_block(uint32_t fn, uint8_t ts)
+{
+ struct rlc_dl_header *rh;
+ struct rlc_li_field *li;
+ uint8_t block_length; /* total length of block, including spare bits */
+ uint8_t block_data; /* usable data of block, w/o spare bits, inc. MAC */
+ struct msgb *msg, *dl_msg;
+ uint8_t bsn;
+ uint16_t mod_sns = sns - 1;
+ uint16_t mod_sns_half = (sns >> 1) - 1;
+ uint16_t index;
+ uint8_t *delimiter, *data, *e_pointer;
+ uint8_t len;
+ uint16_t space, chunk;
+ int first_fin_ack = 0;
+
+ LOGP(DRLCMACDL, LOGL_DEBUG, "DL DATA TBF=%d downlink (V(A)==%d .. "
+ "V(S)==%d)\n", tfi, dir.dl.v_a, dir.dl.v_s);
+
+do_resend:
+ /* check if there is a block with negative acknowledgement */
+ for (bsn = dir.dl.v_a; bsn != dir.dl.v_s;
+ bsn = (bsn + 1) & mod_sns) {
+ index = (bsn & mod_sns_half);
+ if (dir.dl.v_b[index] == 'N'
+ || dir.dl.v_b[index] == 'X') {
+ LOGP(DRLCMACDL, LOGL_DEBUG, "- Resending BSN %d\n",
+ bsn);
+ /* re-send block with negative aknowlegement */
+ dir.dl.v_b[index] = 'U'; /* unacked */
+ goto tx_block;
+ }
+ }
+
+ /* if the window has stalled, or transfer is complete,
+ * send an unacknowledged block */
+ if (state_is(GPRS_RLCMAC_FINISHED)
+ || ((dir.dl.v_s - dir.dl.v_a) & mod_sns) == ws) {
+ int resend = 0;
+
+ if (state_is(GPRS_RLCMAC_FINISHED))
+ LOGP(DRLCMACDL, LOGL_DEBUG, "- Restarting at BSN %d, "
+ "because all blocks have been transmitted.\n",
+ dir.dl.v_a);
+ else
+ LOGP(DRLCMACDL, LOGL_NOTICE, "- Restarting at BSN %d, "
+ "because all window is stalled.\n",
+ dir.dl.v_a);
+ /* If V(S) == V(A) and finished state, we would have received
+ * acknowledgement of all transmitted block. In this case we
+ * would have transmitted the final block, and received ack
+ * from MS. But in this case we did not receive the final ack
+ * indication from MS. This should never happen if MS works
+ * correctly. */
+ if (dir.dl.v_s == dir.dl.v_a) {
+ LOGP(DRLCMACDL, LOGL_DEBUG, "- MS acked all blocks, "
+ "so we re-transmit final block!\n");
+ /* we just send final block again */
+ index = ((dir.dl.v_s - 1) & mod_sns_half);
+ goto tx_block;
+ }
+
+ /* cycle through all unacked blocks */
+ for (bsn = dir.dl.v_a; bsn != dir.dl.v_s;
+ bsn = (bsn + 1) & mod_sns) {
+ index = (bsn & mod_sns_half);
+ if (dir.dl.v_b[index] == 'U') {
+ /* mark to be re-send */
+ dir.dl.v_b[index] = 'X';
+ resend++;
+ }
+ }
+ /* At this point there should be at leasst one unacked block
+ * to be resent. If not, this is an software error. */
+ if (resend == 0) {
+ LOGP(DRLCMACDL, LOGL_ERROR, "Software error: "
+ "There are no unacknowledged blocks, but V(A) "
+ " != V(S). PLEASE FIX!\n");
+ /* we just send final block again */
+ index = ((dir.dl.v_s - 1) & mod_sns_half);
+ goto tx_block;
+ }
+ goto do_resend;
+ }
+
+ LOGP(DRLCMACDL, LOGL_DEBUG, "- Sending new block at BSN %d\n",
+ dir.dl.v_s);
+
+ /* now we still have untransmitted LLC data, so we fill mac block */
+ index = dir.dl.v_s & mod_sns_half;
+ data = rlc_block[index];
+#warning "Selection of the CS doesn't belong here"
+ if (cs == 0) {
+ cs = bts_data()->initial_cs_dl;
+ if (cs < 1 || cs > 4)
+ cs = 1;
+ }
+ block_length = gprs_rlcmac_cs[cs].block_length;
+ block_data = gprs_rlcmac_cs[cs].block_data;
+ memset(data, 0x2b, block_data); /* spare bits will be left 0 */
+ rh = (struct rlc_dl_header *)data;
+ rh->pt = 0; /* Data Block */
+ rh->rrbp = rh->s_p = 0; /* Polling, set later, if required */
+ rh->usf = 7; /* will be set at scheduler */
+ rh->pr = 0; /* FIXME: power reduction */
+ rh->tfi = tfi; /* TFI */
+ rh->fbi = 0; /* Final Block Indicator, set late, if true */
+ rh->bsn = dir.dl.v_s; /* Block Sequence Number */
+ rh->e = 0; /* Extension bit, maybe set later */
+ e_pointer = data + 2; /* points to E of current chunk */
+ data += 3;
+ delimiter = data; /* where next length header would be stored */
+ space = block_data - 3;
+ while (1) {
+ chunk = llc_length - llc_index;
+ /* if chunk will exceed block limit */
+ if (chunk > space) {
+ LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d "
+ "larger than space (%d) left in block: copy "
+ "only remaining space, and we are done\n",
+ chunk, space);
+ /* block is filled, so there is no extension */
+ *e_pointer |= 0x01;
+ /* fill only space */
+ memcpy(data, llc_frame + llc_index, space);
+ /* incement index */
+ llc_index += space;
+ /* return data block as message */
+ break;
+ }
+ /* if FINAL chunk would fit precisely in space left */
+ if (chunk == space && llist_empty(&llc_queue)) {
+ LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d "
+ "would exactly fit into space (%d): because "
+ "this is a final block, we don't add length "
+ "header, and we are done\n", chunk, space);
+ LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for "
+ "TBF=%d that fits precisely in last block: "
+ "len=%d\n", tfi, llc_length);
+ gprs_rlcmac_dl_bw(this, llc_length);
+ /* block is filled, so there is no extension */
+ *e_pointer |= 0x01;
+ /* fill space */
+ memcpy(data, llc_frame + llc_index, space);
+ /* reset LLC frame */
+ llc_index = llc_length = 0;
+ /* final block */
+ rh->fbi = 1; /* we indicate final block */
+ tbf_new_state(this, GPRS_RLCMAC_FINISHED);
+ /* return data block as message */
+ break;
+ }
+ /* if chunk would fit exactly in space left */
+ if (chunk == space) {
+ LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d "
+ "would exactly fit into space (%d): add length "
+ "header with LI=0, to make frame extend to "
+ "next block, and we are done\n", chunk, space);
+ /* make space for delimiter */
+ if (delimiter != data)
+ memcpy(delimiter + 1, delimiter,
+ data - delimiter);
+ data++;
+ space--;
+ /* add LI with 0 length */
+ li = (struct rlc_li_field *)delimiter;
+ li->e = 1; /* not more extension */
+ li->m = 0; /* shall be set to 0, in case of li = 0 */
+ li->li = 0; /* chunk fills the complete space */
+ // no need to set e_pointer nor increase delimiter
+ /* fill only space, which is 1 octet less than chunk */
+ memcpy(data, llc_frame + llc_index, space);
+ /* incement index */
+ llc_index += space;
+ /* return data block as message */
+ break;
+ }
+ LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d is less "
+ "than remaining space (%d): add length header to "
+ "to delimit LLC frame\n", chunk, space);
+ /* the LLC frame chunk ends in this block */
+ /* make space for delimiter */
+ if (delimiter != data)
+ memcpy(delimiter + 1, delimiter, data - delimiter);
+ data++;
+ space--;
+ /* add LI to delimit frame */
+ li = (struct rlc_li_field *)delimiter;
+ li->e = 0; /* Extension bit, maybe set later */
+ li->m = 0; /* will be set later, if there is more LLC data */
+ li->li = chunk; /* length of chunk */
+ e_pointer = delimiter; /* points to E of current delimiter */
+ delimiter++;
+ /* copy (rest of) LLC frame to space */
+ memcpy(data, llc_frame + llc_index, chunk);
+ data += chunk;
+ space -= chunk;
+ LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for TBF=%d: "
+ "len=%d\n", tfi, llc_length);
+ gprs_rlcmac_dl_bw(this, llc_length);
+ /* reset LLC frame */
+ llc_index = llc_length = 0;
+ /* dequeue next LLC frame, if any */
+ msg = llc_dequeue(gprs_bssgp_pcu_current_bctx());
+ if (msg) {
+ LOGP(DRLCMACDL, LOGL_INFO, "- Dequeue next LLC for "
+ "TBF=%d (len=%d)\n", tfi, msg->len);
+ update_llc_frame(msg);
+ msgb_free(msg);
+ }
+ /* if we have more data and we have space left */
+ if (space > 0 && llc_length) {
+ li->m = 1; /* we indicate more frames to follow */
+ continue;
+ }
+ /* if we don't have more LLC frames */
+ if (!llc_length) {
+ LOGP(DRLCMACDL, LOGL_DEBUG, "-- Final block, so we "
+ "done.\n");
+ li->e = 1; /* we cannot extend */
+ rh->fbi = 1; /* we indicate final block */
+ first_fin_ack = 1;
+ /* + 1 indicates: first final ack */
+ tbf_new_state(this, GPRS_RLCMAC_FINISHED);
+ break;
+ }
+ /* we have no space left */
+ LOGP(DRLCMACDL, LOGL_DEBUG, "-- No space left, so we are "
+ "done.\n");
+ li->e = 1; /* we cannot extend */
+ break;
+ }
+ LOGP(DRLCMACDL, LOGL_DEBUG, "data block: %s\n",
+ osmo_hexdump(rlc_block[index], block_length));
+ rlc_block_len[index] = block_length;
+ /* raise send state and set ack state array */
+ dir.dl.v_b[index] = 'U'; /* unacked */
+ dir.dl.v_s = (dir.dl.v_s + 1) & mod_sns; /* inc send state */
+
+tx_block:
+ /* from this point on, new block is sent or old block is resent */
+
+ /* get data and header from current block */
+ data = rlc_block[index];
+ len = rlc_block_len[index];
+ rh = (struct rlc_dl_header *)data;
+
+ /* Clear Polling, if still set in history buffer */
+ rh->s_p = 0;
+
+ /* poll after POLL_ACK_AFTER_FRAMES frames, or when final block is tx.
+ */
+ if (dir.dl.tx_counter >= POLL_ACK_AFTER_FRAMES || first_fin_ack) {
+ if (first_fin_ack) {
+ LOGP(DRLCMACDL, LOGL_DEBUG, "- Scheduling Ack/Nack "
+ "polling, because first final block sent.\n");
+ } else {
+ LOGP(DRLCMACDL, LOGL_DEBUG, "- Scheduling Ack/Nack "
+ "polling, because %d blocks sent.\n",
+ POLL_ACK_AFTER_FRAMES);
+ }
+ /* scheduling not possible, because: */
+ if (poll_state != GPRS_RLCMAC_POLL_NONE)
+ LOGP(DRLCMAC, LOGL_DEBUG, "Polling is already "
+ "sheduled for TBF=%d, so we must wait for "
+ "requesting downlink ack\n", tfi);
+ else if (control_ts != ts)
+ LOGP(DRLCMAC, LOGL_DEBUG, "Polling cannot be "
+ "sheduled in this TS %d, waiting for "
+ "TS %d\n", ts, control_ts);
+ else if (bts->sba()->find(trx_no, ts, (fn + 13) % 2715648))
+ LOGP(DRLCMAC, LOGL_DEBUG, "Polling cannot be "
+ "sheduled, because single block alllocation "
+ "already exists\n");
+ else {
+ LOGP(DRLCMAC, LOGL_DEBUG, "Polling sheduled in this "
+ "TS %d\n", ts);
+ dir.dl.tx_counter = 0;
+ /* start timer whenever we send the final block */
+ if (rh->fbi == 1)
+ tbf_timer_start(this, 3191, bts_data()->t3191, 0);
+
+ /* schedule polling */
+ poll_state = GPRS_RLCMAC_POLL_SCHED;
+ poll_fn = (fn + 13) % 2715648;
+
+#ifdef DEBUG_DIAGRAM
+ debug_diagram(bts, diag, "poll DL-ACK");
+ if (first_fin_ack)
+ debug_diagram(bts, diag, "(is first FINAL)");
+ if (rh->fbi)
+ debug_diagram(bts, diag, "(FBI is set)");
+#endif
+
+ /* set polling in header */
+ rh->rrbp = 0; /* N+13 */
+ rh->s_p = 1; /* Polling */
+
+ /* Increment TX-counter */
+ dir.dl.tx_counter++;
+ }
+ } else {
+ /* Increment TX-counter */
+ dir.dl.tx_counter++;
+ }
+
+ /* return data block as message */
+ dl_msg = msgb_alloc(len, "rlcmac_dl_data");
+ if (!dl_msg)
+ return NULL;
+ memcpy(msgb_put(dl_msg, len), data, len);
+
+ return dl_msg;
+}
+
struct msgb *gprs_rlcmac_tbf::create_dl_ass(uint32_t fn)
{
struct msgb *msg;