diff options
author | Holger Hans Peter Freyther <holger@moiji-mobile.com> | 2013-10-26 21:48:38 +0200 |
---|---|---|
committer | Holger Hans Peter Freyther <holger@moiji-mobile.com> | 2013-10-30 21:24:12 +0100 |
commit | 61a0a04d2651211715dc5e379021da334606c229 (patch) | |
tree | e53f487bc8f59b4393f7eb2e58fd60aeac2f6767 /src | |
parent | 3dc56a3b341ffd22a7a5951a5ec6f78163fdc800 (diff) |
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.
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/gprs_rlcmac.h | 3 | ||||
-rw-r--r-- | src/gprs_rlcmac_data.cpp | 370 | ||||
-rw-r--r-- | src/gprs_rlcmac_sched.cpp | 2 | ||||
-rw-r--r-- | src/tbf.cpp | 331 | ||||
-rw-r--r-- | src/tbf.h | 1 |
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; @@ -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); |