diff options
Diffstat (limited to 'src/tbf_dl.cpp')
-rw-r--r-- | src/tbf_dl.cpp | 640 |
1 files changed, 398 insertions, 242 deletions
diff --git a/src/tbf_dl.cpp b/src/tbf_dl.cpp index 70194f7..7540d1b 100644 --- a/src/tbf_dl.cpp +++ b/src/tbf_dl.cpp @@ -27,6 +27,7 @@ #include <gprs_bssgp_pcu.h> #include <gprs_codel.h> #include <decoding.h> +#include <encoding.h> #include "pcu_utils.h" @@ -180,7 +181,7 @@ static int tbf_new_dl_assignment(struct gprs_rlcmac_bts *bts, */ int gprs_rlcmac_dl_tbf::handle(struct gprs_rlcmac_bts *bts, const uint32_t tlli, const uint32_t tlli_old, const char *imsi, - const uint8_t ms_class, const uint8_t egprs_ms_class, + uint8_t ms_class, uint8_t egprs_ms_class, const uint16_t delay_csec, const uint8_t *data, const uint16_t len) { @@ -190,9 +191,16 @@ int gprs_rlcmac_dl_tbf::handle(struct gprs_rlcmac_bts *bts, /* check for existing TBF */ ms = bts->bts->ms_store().get_ms(tlli, tlli_old, imsi); - if (ms) + if (ms) { dl_tbf = ms->dl_tbf(); + /* If we known the GPRS/EGPRS MS class, use it */ + if (ms->ms_class() || ms->egprs_ms_class()) { + ms_class = ms->ms_class(); + egprs_ms_class = ms->egprs_ms_class(); + } + } + if (ms && strlen(ms->imsi()) == 0) { ms_old = bts->bts->ms_store().get_ms(0, 0, imsi); if (ms_old && ms_old != ms) { @@ -318,81 +326,139 @@ drop_frame: return msg; } -/* - * Create DL data block - * The messages are fragmented and forwarded as data blocks. - */ -struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(uint32_t fn, uint8_t ts) +bool gprs_rlcmac_dl_tbf::restart_bsn_cycle() { - LOGP(DRLCMACDL, LOGL_DEBUG, "%s downlink (V(A)==%d .. " - "V(S)==%d)\n", tbf_name(this), - m_window.v_a(), m_window.v_s()); + /* 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 (m_window.window_empty()) { + LOGP(DRLCMACDL, LOGL_DEBUG, "- MS acked all blocks\n"); + return false; + } -do_resend: - /* check if there is a block with negative acknowledgement */ - int resend_bsn = m_window.resend_needed(); - if (resend_bsn >= 0) { - LOGP(DRLCMACDL, LOGL_DEBUG, "- Resending BSN %d\n", resend_bsn); - /* re-send block with negative aknowlegement */ - m_window.m_v_b.mark_unacked(resend_bsn); - bts->rlc_resent(); - return create_dl_acked_block(fn, ts, resend_bsn); + /* cycle through all unacked blocks */ + int resend = m_window.mark_for_resend(); + + /* At this point there should be at least 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"); + return false; } - /* if the window has stalled, or transfer is complete, - * send an unacknowledged block */ - if (state_is(GPRS_RLCMAC_FINISHED)) { + return true; +} + +int gprs_rlcmac_dl_tbf::take_next_bsn(uint32_t fn, + int previous_bsn, bool *may_combine) +{ + int bsn; + int data_len2, force_data_len = -1; + GprsCodingScheme cs2; + GprsCodingScheme force_cs; + + bsn = m_window.resend_needed(); + + if (previous_bsn >= 0) { + force_cs = m_rlc.block(previous_bsn)->cs; + if (!force_cs.isEgprs()) + return -1; + force_data_len = m_rlc.block(previous_bsn)->len; + } + + if (bsn >= 0) { + if (previous_bsn == bsn) + return -1; + + if (previous_bsn >= 0 && + m_window.mod_sns(bsn - previous_bsn) > RLC_EGPRS_MAX_BSN_DELTA) + return -1; + + cs2 = m_rlc.block(bsn)->cs; + data_len2 = m_rlc.block(bsn)->len; + if (force_data_len > 0 && force_data_len != data_len2) + return -1; + LOGP(DRLCMACDL, LOGL_DEBUG, "- Resending BSN %d\n", bsn); + /* re-send block with negative aknowlegement */ + m_window.m_v_b.mark_unacked(bsn); + bts->rlc_resent(); + } else if (state_is(GPRS_RLCMAC_FINISHED)) { LOGP(DRLCMACDL, LOGL_DEBUG, "- Restarting at BSN %d, " "because all blocks have been transmitted.\n", m_window.v_a()); bts->rlc_restarted(); + if (restart_bsn_cycle()) + return take_next_bsn(fn, previous_bsn, may_combine); } else if (dl_window_stalled()) { LOGP(DRLCMACDL, LOGL_NOTICE, "- Restarting at BSN %d, " - "because all window is stalled.\n", + "because the window is stalled.\n", m_window.v_a()); bts->rlc_stalled(); + if (restart_bsn_cycle()) + return take_next_bsn(fn, previous_bsn, may_combine); } else if (have_data()) { /* New blocks may be send */ - return create_new_bsn(fn, ts); + cs2 = force_cs ? force_cs : current_cs(); + LOGP(DRLCMACDL, LOGL_DEBUG, + "- Sending new block at BSN %d, CS=%s\n", + m_window.v_s(), cs2.name()); + + bsn = create_new_bsn(fn, cs2); } else if (!m_window.window_empty()) { LOGP(DRLCMACDL, LOGL_DEBUG, "- Restarting at BSN %d, " "because all blocks have been transmitted (FLOW).\n", m_window.v_a()); bts->rlc_restarted(); + if (restart_bsn_cycle()) + return take_next_bsn(fn, previous_bsn, may_combine); } else { /* Nothing left to send, create dummy LLC commands */ - return create_new_bsn(fn, ts); + LOGP(DRLCMACDL, LOGL_DEBUG, + "- Sending new dummy block at BSN %d, CS=%s\n", + m_window.v_s(), current_cs().name()); + bsn = create_new_bsn(fn, current_cs()); + /* Don't send a second block, so don't set cs2 */ } - /* 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 (m_window.window_empty()) { - LOGP(DRLCMACDL, LOGL_DEBUG, "- MS acked all blocks, " - "so we re-transmit final block!\n"); + if (bsn < 0) { /* we just send final block again */ - int16_t index = m_window.v_s_mod(-1); + LOGP(DRLCMACDL, LOGL_DEBUG, + "- Nothing else to send, Re-transmit final block!\n"); + bsn = m_window.v_s_mod(-1); bts->rlc_resent(); - return create_dl_acked_block(fn, ts, index); } - /* cycle through all unacked blocks */ - int resend = m_window.mark_for_resend(); + *may_combine = cs2.numDataBlocks() > 1; - /* At this point there should be at least 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 */ - int16_t index = m_window.v_s_mod(-1); - return create_dl_acked_block(fn, ts, index); - } - goto do_resend; + return bsn; +} + +/* + * Create DL data block + * The messages are fragmented and forwarded as data blocks. + */ +struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(uint32_t fn, uint8_t ts) +{ + int bsn, bsn2 = -1; + bool may_combine; + + LOGP(DRLCMACDL, LOGL_DEBUG, "%s downlink (V(A)==%d .. " + "V(S)==%d)\n", tbf_name(this), + m_window.v_a(), m_window.v_s()); + + bsn = take_next_bsn(fn, -1, &may_combine); + if (bsn < 0) + return NULL; + + if (may_combine) + bsn2 = take_next_bsn(fn, bsn, &may_combine); + + return create_dl_acked_block(fn, ts, bsn, bsn2); } void gprs_rlcmac_dl_tbf::schedule_next_frame() @@ -417,55 +483,43 @@ void gprs_rlcmac_dl_tbf::schedule_next_frame() m_last_dl_drained_fn = -1; } -struct msgb *gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, const uint8_t ts) +int gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, GprsCodingScheme cs) { - struct rlc_dl_header *rh; - struct rlc_li_field *li; - uint8_t *delimiter, *data, *e_pointer; - uint16_t space, chunk; + uint8_t *data; gprs_rlc_data *rlc_data; const uint16_t bsn = m_window.v_s(); - uint8_t cs_n = 1; + gprs_rlc_data_block_info *rdbi; + int num_chunks = 0; + int write_offset = 0; + Encoding::AppendResult ar; if (m_llc.frame_length() == 0) schedule_next_frame(); - cs_n = current_cs(); - - LOGP(DRLCMACDL, LOGL_DEBUG, "- Sending new block at BSN %d, CS=%d\n", - m_window.v_s(), cs_n); + OSMO_ASSERT(cs.isValid()); - OSMO_ASSERT(cs_n >= 1); - OSMO_ASSERT(cs_n <= 4); - - /* TODO: Use GprsCodingScheme everywhere and remove cast */ - GprsCodingScheme cs((GprsCodingScheme::Scheme)cs_n); - - /* total length of block, including spare bits */ - const uint8_t block_length = cs.sizeDL(); - /* length of usable data of block, w/o spare bits, inc. MAC */ - const uint8_t block_data_len = cs.maxBytesDL(); + /* length of usable data block (single data unit w/o header) */ + const uint8_t block_data_len = cs.maxDataBlockBytes(); /* now we still have untransmitted LLC data, so we fill mac block */ rlc_data = m_rlc.block(bsn); data = rlc_data->prepare(block_data_len); rlc_data->cs = cs; + rlc_data->len = block_data_len; + + rdbi = &(rlc_data->block_info); + memset(rdbi, 0, sizeof(*rdbi)); + rdbi->data_len = block_data_len; + + rdbi->cv = 15; /* Final Block Indicator, set late, if true */ + rdbi->bsn = bsn; /* Block Sequence Number */ + rdbi->e = 1; /* Extension bit, maybe set later (1: no extension) */ + + do { + bool is_final; - 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 = m_tfi; /* TFI */ - rh->fbi = 0; /* Final Block Indicator, set late, if true */ - rh->bsn = bsn; /* Block Sequence Number */ - rh->e = 0; /* Extension bit, maybe set later */ - e_pointer = data + 2; /* points to E of current chunk */ - data += sizeof(*rh); - delimiter = data; /* where next length header would be stored */ - space = block_data_len - sizeof(*rh); - while (1) { if (m_llc.frame_length() == 0) { + int space = block_data_len - write_offset; /* A header will need to by added, so we just need * space-1 octets */ m_llc.put_dummy_frame(space - 1); @@ -485,141 +539,145 @@ struct msgb *gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, const uint8_t m_llc.frame_length(), frames_since_last_drain(fn)); } - chunk = m_llc.chunk_size(); - - /* 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 */ - m_llc.consume(data, space); - /* return data block as message */ - break; - } - /* if FINAL chunk would fit precisely in space left */ - 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 " - "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 " - "%s that fits precisely in last block: " - "len=%d\n", tbf_name(this), m_llc.frame_length()); - gprs_rlcmac_dl_bw(this, m_llc.frame_length()); - /* block is filled, so there is no extension */ - *e_pointer |= 0x01; - /* fill space */ - m_llc.consume(data, space); - m_llc.reset(); - /* final block */ - rh->fbi = 1; /* we indicate final block */ - request_dl_ack(); - set_state(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) - memmove(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 */ - m_llc.consume(data, space); - /* return data block as message */ + is_final = llc_queue()->size() == 0 && !keep_open(fn); + + ar = Encoding::rlc_data_to_dl_append(rdbi, cs, + &m_llc, &write_offset, &num_chunks, data, is_final); + + if (ar == Encoding::AR_NEED_MORE_BLOCKS) 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) - memmove(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 and reset later */ - m_llc.consume(data, chunk); - data += chunk; - space -= chunk; LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for %s" "len=%d\n", tbf_name(this), m_llc.frame_length()); gprs_rlcmac_dl_bw(this, m_llc.frame_length()); m_llc.reset(); - /* dequeue next LLC frame, if any */ - schedule_next_frame(); - /* if we have more data and we have space left */ - if (space > 0 && (m_llc.frame_length() || keep_open(fn))) { - li->m = 1; /* we indicate more frames to follow */ - continue; - } - /* if we don't have more LLC frames */ - if (!m_llc.frame_length() && !keep_open(fn)) { - LOGP(DRLCMACDL, LOGL_DEBUG, "-- Final block, so we " - "done.\n"); - li->e = 1; /* we cannot extend */ - rh->fbi = 1; /* we indicate final block */ + if (is_final) { request_dl_ack(); set_state(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_data->block, block_length)); -#warning "move this up?" - rlc_data->len = block_length; + + /* dequeue next LLC frame, if any */ + schedule_next_frame(); + } while (ar == Encoding::AR_COMPLETED_SPACE_LEFT); + + LOGP(DRLCMACDL, LOGL_DEBUG, "data block (BSN %d, %s): %s\n", + bsn, rlc_data->cs.name(), + osmo_hexdump(rlc_data->block, block_data_len)); /* raise send state and set ack state array */ m_window.m_v_b.mark_unacked(bsn); m_window.increment_send(); - return create_dl_acked_block(fn, ts, bsn); + return bsn; } 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 *data; - struct rlc_dl_header *rh; + uint8_t *msg_data; struct msgb *dl_msg; - uint8_t len; + unsigned msg_len; bool need_poll; + /* TODO: support MCS-7 - MCS-9, where data_block_idx can be 1 */ + unsigned int data_block_idx = 0; + unsigned int rrbp; + uint32_t new_poll_fn; + int rc; + bool is_final = false; + gprs_rlc_data_info rlc; + GprsCodingScheme cs; + int bsns[ARRAY_SIZE(rlc.block_info)]; + unsigned num_bsns; + int punct[ARRAY_SIZE(rlc.block_info)]; + bool need_padding = false; + + /* + * 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; + + if (index2 >= 0) { + bsns[num_bsns] = index2; + num_bsns += 1; + } + + if (num_bsns == 1) { + /* TODO: remove the conditional when MCS-6 padding isn't + * failing to be decoded by MEs anymore */ + if (cs != GprsCodingScheme(GprsCodingScheme::MCS8)) + cs.decToSingleBlock(&need_padding); + } - /* get data and header from current block */ - data = m_rlc.block(index)->block; - len = m_rlc.block(index)->len; - rh = (struct rlc_dl_header *)data; + 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 */ + + /* 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 */ @@ -627,9 +685,7 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block( m_last_dl_poll_fn = fn; need_poll = state_flags & (1 << GPRS_RLCMAC_FLAG_TO_DL_ACK); - /* 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 (m_tx_counter >= POLL_ACK_AFTER_FRAMES || m_dl_ack_requested || @@ -646,31 +702,18 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block( "polling, because %d blocks sent.\n", POLL_ACK_AFTER_FRAMES); } - /* scheduling not possible, because: */ - if (poll_state != GPRS_RLCMAC_POLL_NONE) - LOGP(DRLCMACDL, LOGL_DEBUG, "Polling is already " - "sheduled for %s, so we must wait for " - "requesting downlink ack\n", tbf_name(this)); - else if (control_ts != ts) - LOGP(DRLCMACDL, LOGL_DEBUG, "Polling cannot be " - "sheduled in this TS %d, waiting for " - "TS %d\n", ts, control_ts); - else if (bts->sba()->find(trx->trx_no, ts, (fn + 13) % 2715648)) - LOGP(DRLCMACDL, LOGL_DEBUG, "Polling cannot be " - "sheduled, because single block alllocation " - "already exists\n"); - else { - LOGP(DRLCMACDL, LOGL_DEBUG, "Polling sheduled in this " + + rc = check_polling(fn, ts, &new_poll_fn, &rrbp); + if (rc >= 0) { + set_polling(new_poll_fn, ts); + + LOGP(DRLCMACDL, LOGL_DEBUG, "Polling scheduled in this " "TS %d\n", ts); m_tx_counter = 0; /* start timer whenever we send the final block */ - if (rh->fbi == 1) + if (is_final) tbf_timer_start(this, 3191, bts_data()->t3191, 0); - /* schedule polling */ - poll_state = GPRS_RLCMAC_POLL_SCHED; - poll_fn = (fn + 13) % 2715648; - /* Clear poll timeout flag */ state_flags &= ~(1 << GPRS_RLCMAC_FLAG_TO_DL_ACK); @@ -678,34 +721,35 @@ struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block( m_dl_ack_requested = false; /* set polling in header */ - rh->rrbp = 0; /* N+13 */ - rh->s_p = 1; /* Polling */ + rlc.rrbp = rrbp; + rlc.es_p = 1; /* Polling */ m_last_dl_poll_fn = poll_fn; LOGP(DRLCMACDL, LOGL_INFO, "%s Scheduled Ack/Nack polling on FN=%d, TS=%d\n", - name(), poll_fn, ts); + name(), poll_fn, poll_ts); } } - /* return data block as message */ - dl_msg = msgb_alloc(len, "rlcmac_dl_data"); - if (!dl_msg) - return NULL; + Encoding::rlc_write_dl_data_header(&rlc, msg_data); + + 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 */ m_tx_counter++; - memcpy(msgb_put(dl_msg, len), data, len); bts->rlc_sent(); return dl_msg; } -static uint16_t bitnum_to_bsn(int bitnum, uint16_t ssn, uint16_t mod_sns) +static uint16_t bitnum_to_bsn(int bitnum, uint16_t ssn) { - return (ssn - 1 - bitnum) & mod_sns; + return ssn - 1 - bitnum; } int gprs_rlcmac_dl_tbf::analyse_errors(char *show_rbb, uint8_t ssn, @@ -713,20 +757,24 @@ int gprs_rlcmac_dl_tbf::analyse_errors(char *show_rbb, uint8_t ssn, { gprs_rlc_data *rlc_data; uint16_t lost = 0, received = 0, skipped = 0; - char info[65]; - memset(info, '.', sizeof(info)); - info[64] = 0; + char info[RLC_MAX_WS + 1]; + memset(info, '.', m_window.ws()); + info[m_window.ws()] = 0; uint16_t bsn = 0; unsigned received_bytes = 0, lost_bytes = 0; unsigned received_packets = 0, lost_packets = 0; + unsigned num_blocks = strlen(show_rbb); /* SSN - 1 is in range V(A)..V(S)-1 */ - for (int bitpos = 0; bitpos < m_window.ws(); bitpos++) { - bool is_received = show_rbb[m_window.ws() - 1 - bitpos] == 'R'; + for (unsigned int bitpos = 0; bitpos < num_blocks; bitpos++) { + bool is_received; + int index = num_blocks - 1 - bitpos; - bsn = bitnum_to_bsn(bitpos, ssn, m_window.mod_sns()); + is_received = (index >= 0 && show_rbb[index] == 'R'); - if (bsn == ((m_window.v_a() - 1) & m_window.mod_sns())) { + bsn = m_window.mod_sns(bitnum_to_bsn(bitpos, ssn)); + + if (bsn == m_window.mod_sns(m_window.v_a() - 1)) { info[bitpos] = '$'; break; } @@ -748,8 +796,7 @@ int gprs_rlcmac_dl_tbf::analyse_errors(char *show_rbb, uint8_t ssn, /* Get statistics for current CS */ - /* TODO: Use GprsCodingScheme everywhere and remove cast */ - if (rlc_data->cs != (GprsCodingScheme::Scheme)current_cs()) { + if (rlc_data->cs != current_cs()) { /* This block has already been encoded with a different * CS, so it doesn't help us to decide, whether the * current CS is ok. Ignore it. */ @@ -787,6 +834,69 @@ int gprs_rlcmac_dl_tbf::analyse_errors(char *show_rbb, uint8_t ssn, return lost * 100 / (lost + received); } +int gprs_rlcmac_dl_tbf::update_window(unsigned first_bsn, + const struct bitvec *rbb) +{ + int16_t dist; /* must be signed */ + uint16_t lost = 0, received = 0; + char show_v_b[RLC_MAX_SNS + 1]; + char show_rbb[RLC_MAX_SNS + 1]; + int error_rate; + struct ana_result ana_res; + unsigned num_blocks = rbb->cur_bit; + unsigned behind_last_bsn = m_window.mod_sns(first_bsn + num_blocks); + + Decoding::extract_rbb(rbb, show_rbb); + /* show received array in debug */ + LOGP(DRLCMACDL, LOGL_DEBUG, "- ack: (BSN=%d)\"%s\"" + "(BSN=%d) R=ACK I=NACK\n", first_bsn, + show_rbb, m_window.mod_sns(behind_last_bsn - 1)); + + /* apply received array to receive state (first_bsn..behind_last_bsn-1) */ + if (num_blocks > 0) { + /* calculate distance of ssn from V(S) */ + dist = m_window.mod_sns(m_window.v_s() - behind_last_bsn); + /* check if distance is less than distance V(A)..V(S) */ + if (dist >= m_window.distance()) { + /* this might happpen, if the downlink assignment + * was not received by ms and the ack refers + * to previous TBF + * FIXME: we should implement polling for + * control ack! + * TODO: check whether this FIXME still makes sense + */ + LOGP(DRLCMACDL, LOGL_NOTICE, "- ack range is out of " + "V(A)..V(S) range %s Free TBF!\n", tbf_name(this)); + return 1; /* indicate to free TBF */ + } + } + + error_rate = analyse_errors(show_rbb, behind_last_bsn, &ana_res); + + if (bts_data()->cs_adj_enabled && ms()) + ms()->update_error_rate(this, error_rate); + + m_window.update(bts, rbb, first_bsn, &lost, &received); + + /* report lost and received packets */ + gprs_rlcmac_received_lost(this, received, lost); + + /* Used to measure the leak rate */ + gprs_bssgp_update_bytes_received(ana_res.received_bytes, + ana_res.received_packets + ana_res.lost_packets); + + /* raise V(A), if possible */ + m_window.raise(m_window.move_window()); + + /* show receive state array in debug (V(A)..V(S)-1) */ + m_window.show_state(show_v_b); + LOGP(DRLCMACDL, LOGL_DEBUG, "- V(B): (V(A)=%d)\"%s\"" + "(V(S)-1=%d) A=Acked N=Nacked U=Unacked " + "X=Resend-Unacked I=Invalid\n", + m_window.v_a(), show_v_b, + m_window.v_s_mod(-1)); + return 0; +} int gprs_rlcmac_dl_tbf::update_window(const uint8_t ssn, const uint8_t *rbb) { @@ -794,19 +904,18 @@ int gprs_rlcmac_dl_tbf::update_window(const uint8_t ssn, const uint8_t *rbb) uint16_t lost = 0, received = 0; char show_rbb[65]; char show_v_b[RLC_MAX_SNS + 1]; - const uint16_t mod_sns = m_window.mod_sns(); int error_rate; struct ana_result ana_res; Decoding::extract_rbb(rbb, show_rbb); /* show received array in debug (bit 64..1) */ LOGP(DRLCMACDL, LOGL_DEBUG, "- ack: (BSN=%d)\"%s\"" - "(BSN=%d) R=ACK I=NACK\n", (ssn - 64) & mod_sns, - show_rbb, (ssn - 1) & mod_sns); + "(BSN=%d) R=ACK I=NACK\n", m_window.mod_sns(ssn - 64), + show_rbb, m_window.mod_sns(ssn - 1)); /* apply received array to receive state (SSN-64..SSN-1) */ /* calculate distance of ssn from V(S) */ - dist = (m_window.v_s() - ssn) & mod_sns; + dist = m_window.mod_sns(m_window.v_s() - ssn); /* check if distance is less than distance V(A)..V(S) */ if (dist >= m_window.distance()) { /* this might happpen, if the downlink assignment @@ -895,6 +1004,53 @@ int gprs_rlcmac_dl_tbf::release() return 0; } +int gprs_rlcmac_dl_tbf::abort() +{ + uint16_t lost; + + if (state_is(GPRS_RLCMAC_FLOW)) { + /* range V(A)..V(S)-1 */ + lost = m_window.count_unacked(); + + /* report all outstanding packets as lost */ + gprs_rlcmac_received_lost(this, 0, lost); + gprs_rlcmac_lost_rep(this); + + /* TODO: Reschedule all LLC frames starting with the one that is + * (partly) encoded in chunk 1 of block V(A). (optional) */ + } + + set_state(GPRS_RLCMAC_RELEASING); + + /* reset rlc states */ + m_window.reset(); + + /* keep to flags */ + state_flags &= GPRS_RLCMAC_FLAG_TO_MASK; + state_flags &= ~(1 << GPRS_RLCMAC_FLAG_CCCH); + + return 0; +} + +int gprs_rlcmac_dl_tbf::rcvd_dl_ack(uint8_t final_ack, unsigned first_bsn, + struct bitvec *rbb) +{ + int rc; + LOGP(DRLCMACDL, LOGL_DEBUG, "%s downlink acknowledge\n", tbf_name(this)); + + rc = update_window(first_bsn, rbb); + + if (final_ack) { + LOGP(DRLCMACDL, LOGL_DEBUG, "- Final ACK received.\n"); + rc = maybe_start_new_window(); + } else if (state_is(GPRS_RLCMAC_FINISHED) && m_window.window_empty()) { + LOGP(DRLCMACDL, LOGL_NOTICE, "Received acknowledge of " + "all blocks, but without final ack " + "indication (don't worry)\n"); + } + + return rc; +} int gprs_rlcmac_dl_tbf::rcvd_dl_ack(uint8_t final_ack, uint8_t ssn, uint8_t *rbb) { |