aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/Makefile.am1
-rw-r--r--src/gprs_rlcmac.h3
-rw-r--r--src/gprs_rlcmac_data.cpp370
-rw-r--r--src/gprs_rlcmac_sched.cpp2
-rw-r--r--src/tbf.cpp331
-rw-r--r--src/tbf.h1
6 files changed, 333 insertions, 375 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index d2f1fce3..9bc41833 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -34,7 +34,6 @@ libgprs_la_SOURCES = \
gsm_rlcmac.cpp \
gprs_bssgp_pcu.cpp \
gprs_rlcmac.cpp \
- gprs_rlcmac_data.cpp \
gprs_rlcmac_sched.cpp \
gprs_rlcmac_meas.cpp \
gprs_rlcmac_ts_alloc.cpp \
diff --git a/src/gprs_rlcmac.h b/src/gprs_rlcmac.h
index 0f05b167..0f699854 100644
--- a/src/gprs_rlcmac.h
+++ b/src/gprs_rlcmac.h
@@ -97,9 +97,6 @@ int gprs_rlcmac_tx_ul_ud(gprs_rlcmac_tbf *tbf);
int gprs_rlcmac_paging_request(uint8_t *ptmsi, uint16_t ptmsi_len,
const char *imsi);
-struct msgb *gprs_rlcmac_send_data_block_acknowledged(
- struct gprs_rlcmac_tbf *tbf, uint32_t fn, uint8_t ts);
-
int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
uint8_t trx, uint8_t ts, uint16_t arfcn,
uint32_t fn, uint8_t block_nr);
diff --git a/src/gprs_rlcmac_data.cpp b/src/gprs_rlcmac_data.cpp
deleted file mode 100644
index 18a037ad..00000000
--- a/src/gprs_rlcmac_data.cpp
+++ /dev/null
@@ -1,370 +0,0 @@
-/* Data block transfer
- *
- * Copyright (C) 2012 Ivan Klyuchnikov
- * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * 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 General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#include <gprs_bssgp_pcu.h>
-#include <gprs_rlcmac.h>
-#include <pcu_l1_if.h>
-#include <bts.h>
-#include <encoding.h>
-#include <tbf.h>
-#include <rlc.h>
-
-static 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 void *tall_pcu_ctx;
-
-/* After sending these frames, we poll for ack/nack. */
-#define POLL_ACK_AFTER_FRAMES 20
-
-
-
-/*
- * UL data block flow
- */
-
-/* send DL data block
- *
- * The messages are fragmented and forwarded as data blocks.
- */
-struct msgb *gprs_rlcmac_send_data_block_acknowledged(
- struct gprs_rlcmac_tbf *tbf, 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 = tbf->sns - 1;
- uint16_t mod_sns_half = (tbf->sns >> 1) - 1;
- uint16_t index;
- uint8_t *delimiter, *data, *e_pointer;
- uint8_t len;
- uint16_t space, chunk;
- int first_fin_ack = 0;
- gprs_rlcmac_bts *bts = tbf->bts->bts_data();
-
- LOGP(DRLCMACDL, LOGL_DEBUG, "DL DATA TBF=%d downlink (V(A)==%d .. "
- "V(S)==%d)\n", tbf->tfi, tbf->dir.dl.v_a, tbf->dir.dl.v_s);
-
-do_resend:
- /* check if there is a block with negative acknowledgement */
- for (bsn = tbf->dir.dl.v_a; bsn != tbf->dir.dl.v_s;
- bsn = (bsn + 1) & mod_sns) {
- index = (bsn & mod_sns_half);
- if (tbf->dir.dl.v_b[index] == 'N'
- || tbf->dir.dl.v_b[index] == 'X') {
- LOGP(DRLCMACDL, LOGL_DEBUG, "- Resending BSN %d\n",
- bsn);
- /* re-send block with negative aknowlegement */
- tbf->dir.dl.v_b[index] = 'U'; /* unacked */
- goto tx_block;
- }
- }
-
- /* if the window has stalled, or transfer is complete,
- * send an unacknowledged block */
- if (tbf->state_is(GPRS_RLCMAC_FINISHED)
- || ((tbf->dir.dl.v_s - tbf->dir.dl.v_a) & mod_sns) == tbf->ws) {
- int resend = 0;
-
- if (tbf->state_is(GPRS_RLCMAC_FINISHED))
- LOGP(DRLCMACDL, LOGL_DEBUG, "- Restarting at BSN %d, "
- "because all blocks have been transmitted.\n",
- tbf->dir.dl.v_a);
- else
- LOGP(DRLCMACDL, LOGL_NOTICE, "- Restarting at BSN %d, "
- "because all window is stalled.\n",
- tbf->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 (tbf->dir.dl.v_s == tbf->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 = ((tbf->dir.dl.v_s - 1) & mod_sns_half);
- goto tx_block;
- }
-
- /* cycle through all unacked blocks */
- for (bsn = tbf->dir.dl.v_a; bsn != tbf->dir.dl.v_s;
- bsn = (bsn + 1) & mod_sns) {
- index = (bsn & mod_sns_half);
- if (tbf->dir.dl.v_b[index] == 'U') {
- /* mark to be re-send */
- tbf->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 = ((tbf->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",
- tbf->dir.dl.v_s);
-
- /* now we still have untransmitted LLC data, so we fill mac block */
- index = tbf->dir.dl.v_s & mod_sns_half;
- data = tbf->rlc_block[index];
-#warning "Selection of the CS doesn't belong here"
- if (tbf->cs == 0) {
- tbf->cs = bts->initial_cs_dl;
- if (tbf->cs < 1 || tbf->cs > 4)
- tbf->cs = 1;
- }
- block_length = gprs_rlcmac_cs[tbf->cs].block_length;
- block_data = gprs_rlcmac_cs[tbf->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 = tbf->tfi; /* TFI */
- rh->fbi = 0; /* Final Block Indicator, set late, if true */
- rh->bsn = tbf->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 = tbf->llc_length - tbf->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, tbf->llc_frame + tbf->llc_index, space);
- /* incement index */
- tbf->llc_index += space;
- /* return data block as message */
- break;
- }
- /* if FINAL chunk would fit precisely in space left */
- if (chunk == space && llist_empty(&tbf->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", tbf->tfi, tbf->llc_length);
- gprs_rlcmac_dl_bw(tbf, tbf->llc_length);
- /* block is filled, so there is no extension */
- *e_pointer |= 0x01;
- /* fill space */
- memcpy(data, tbf->llc_frame + tbf->llc_index, space);
- /* reset LLC frame */
- tbf->llc_index = tbf->llc_length = 0;
- /* final block */
- rh->fbi = 1; /* we indicate final block */
- tbf_new_state(tbf, 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, tbf->llc_frame + tbf->llc_index, space);
- /* incement index */
- tbf->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, tbf->llc_frame + tbf->llc_index, chunk);
- data += chunk;
- space -= chunk;
- LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for TBF=%d: "
- "len=%d\n", tbf->tfi, tbf->llc_length);
- gprs_rlcmac_dl_bw(tbf, tbf->llc_length);
- /* reset LLC frame */
- tbf->llc_index = tbf->llc_length = 0;
- /* dequeue next LLC frame, if any */
- msg = tbf->llc_dequeue(gprs_bssgp_pcu_current_bctx());
- if (msg) {
- LOGP(DRLCMACDL, LOGL_INFO, "- Dequeue next LLC for "
- "TBF=%d (len=%d)\n", tbf->tfi, msg->len);
- tbf->update_llc_frame(msg);
- msgb_free(msg);
- }
- /* if we have more data and we have space left */
- if (space > 0 && tbf->llc_length) {
- li->m = 1; /* we indicate more frames to follow */
- continue;
- }
- /* if we don't have more LLC frames */
- if (!tbf->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(tbf, 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(tbf->rlc_block[index], block_length));
- tbf->rlc_block_len[index] = block_length;
- /* raise send state and set ack state array */
- tbf->dir.dl.v_b[index] = 'U'; /* unacked */
- tbf->dir.dl.v_s = (tbf->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 = tbf->rlc_block[index];
- len = tbf->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 (tbf->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 (tbf->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", tbf->tfi);
- else if (tbf->control_ts != ts)
- LOGP(DRLCMAC, LOGL_DEBUG, "Polling cannot be "
- "sheduled in this TS %d, waiting for "
- "TS %d\n", ts, tbf->control_ts);
- else if (tbf->bts->sba()->find(tbf->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);
- tbf->dir.dl.tx_counter = 0;
- /* start timer whenever we send the final block */
- if (rh->fbi == 1)
- tbf_timer_start(tbf, 3191, bts->t3191, 0);
-
- /* schedule polling */
- tbf->poll_state = GPRS_RLCMAC_POLL_SCHED;
- tbf->poll_fn = (fn + 13) % 2715648;
-
-#ifdef DEBUG_DIAGRAM
- debug_diagram(bts->bts, tbf->diag, "poll DL-ACK");
- if (first_fin_ack)
- debug_diagram(bts->bts, tbf->diag, "(is first FINAL)");
- if (rh->fbi)
- debug_diagram(bts->bts, tbf->diag, "(FBI is set)");
-#endif
-
- /* set polling in header */
- rh->rrbp = 0; /* N+13 */
- rh->s_p = 1; /* Polling */
-
- /* Increment TX-counter */
- tbf->dir.dl.tx_counter++;
- }
- } else {
- /* Increment TX-counter */
- tbf->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;
-}
-
diff --git a/src/gprs_rlcmac_sched.cpp b/src/gprs_rlcmac_sched.cpp
index 11a9630d..b411e2ff 100644
--- a/src/gprs_rlcmac_sched.cpp
+++ b/src/gprs_rlcmac_sched.cpp
@@ -182,7 +182,7 @@ static struct msgb *sched_select_downlink(struct gprs_rlcmac_bts *bts,
/* next TBF to handle ressource is the next one */
pdch->next_dl_tfi = (tfi + 1) & 31;
/* generate DL data block */
- msg = gprs_rlcmac_send_data_block_acknowledged(tbf, fn, ts);
+ msg = tbf->create_dl_acked_block(fn, ts);
break;
}
diff --git a/src/tbf.cpp b/src/tbf.cpp
index 08f6cf38..eb76ab86 100644
--- a/src/tbf.cpp
+++ b/src/tbf.cpp
@@ -35,10 +35,22 @@ extern "C" {
#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 @@ int gprs_rlcmac_tbf::assemble_forward_llc(uint8_t *data, uint8_t len)
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;
diff --git a/src/tbf.h b/src/tbf.h
index a1a7986d..ea184442 100644
--- a/src/tbf.h
+++ b/src/tbf.h
@@ -99,6 +99,7 @@ struct gprs_rlcmac_tbf {
/* TODO: extract LLC class? */
int assemble_forward_llc(uint8_t *data, uint8_t len);
+ struct msgb *create_dl_acked_block(uint32_t fn, uint8_t ts);
struct msgb *create_dl_ass(uint32_t fn);
struct msgb *create_ul_ass(uint32_t fn);
struct msgb *create_ul_ack(uint32_t fn);