diff options
Diffstat (limited to 'src/bts.cpp')
-rw-r--r-- | src/bts.cpp | 235 |
1 files changed, 233 insertions, 2 deletions
diff --git a/src/bts.cpp b/src/bts.cpp index af1a8194..95672143 100644 --- a/src/bts.cpp +++ b/src/bts.cpp @@ -22,6 +22,8 @@ #include <poll_controller.h> #include <tbf.h> #include <encoding.h> +#include <decoding.h> +#include <rlc.h> #include <gprs_rlcmac.h> #include <gprs_debug.h> @@ -212,6 +214,14 @@ gprs_rlcmac_tbf *BTS::tbf_by_poll_fn(uint32_t fn, uint8_t trx, uint8_t ts) return NULL; } + +/* + * PDCH code below. TODO: move to a separate file + */ + +/* After receiving these frames, we send ack/nack. */ +#define SEND_ACK_AFTER_FRAMES 20 + void gprs_rlcmac_pdch::enable() { /* TODO: Check if there are still allocated resources.. */ @@ -340,6 +350,228 @@ void gprs_rlcmac_pdch::add_paging(struct gprs_rlcmac_paging *pag) llist_add(&pag->list, &paging_list); } +/* receive UL data block + * + * The blocks are defragmented and forwarded as LLC frames, if complete. + */ +int gprs_rlcmac_pdch::rcv_data_block_acknowledged(struct gprs_rlcmac_bts *bts, + uint8_t trx, uint8_t ts, + uint8_t *data, uint8_t len, int8_t rssi) +{ + struct gprs_rlcmac_tbf *tbf; + struct rlc_ul_header *rh = (struct rlc_ul_header *)data; + uint16_t mod_sns, mod_sns_half, offset_v_q, offset_v_r, index; + int rc; + + switch (len) { + case 54: + /* omitting spare bits */ + len = 53; + break; + case 40: + /* omitting spare bits */ + len = 39; + break; + case 34: + /* omitting spare bits */ + len = 33; + break; + case 23: + break; + default: + LOGP(DRLCMACUL, LOGL_ERROR, "Dropping data block with invalid" + "length: %d)\n", len); + return -EINVAL; + } + + /* find TBF inst from given TFI */ + tbf = tbf_by_tfi(bts, rh->tfi, trx, GPRS_RLCMAC_UL_TBF); + if (!tbf) { + LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA unknown TBF=%d\n", + rh->tfi); + return 0; + } + tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_UL_DATA); + + LOGP(DRLCMACUL, LOGL_DEBUG, "UL DATA TBF=%d received (V(Q)=%d .. " + "V(R)=%d)\n", rh->tfi, tbf->dir.ul.v_q, tbf->dir.ul.v_r); + + /* process RSSI */ + gprs_rlcmac_rssi(tbf, rssi); + + /* get TLLI */ + if (!tbf->tlli_valid) { + struct gprs_rlcmac_tbf *dl_tbf, *ul_tbf; + + /* no TLLI yet */ + if (!rh->ti) { + LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA TBF=%d without " + "TLLI, but no TLLI received yet\n", rh->tfi); + return 0; + } + rc = Decoding::tlli_from_ul_data(data, len, &tbf->tlli); + if (rc) { + LOGP(DRLCMACUL, LOGL_NOTICE, "Failed to decode TLLI " + "of UL DATA TBF=%d.\n", rh->tfi); + return 0; + } + LOGP(DRLCMACUL, LOGL_INFO, "Decoded premier TLLI=0x%08x of " + "UL DATA TBF=%d.\n", tbf->tlli, rh->tfi); + if ((dl_tbf = bts->bts->tbf_by_tlli(tbf->tlli, GPRS_RLCMAC_DL_TBF))) { + LOGP(DRLCMACUL, LOGL_NOTICE, "Got RACH from " + "TLLI=0x%08x while DL TBF=%d still exists. " + "Killing pending DL TBF\n", tbf->tlli, + dl_tbf->tfi); + tbf_free(dl_tbf); + } + /* tbf_by_tlli will not find your TLLI, because it is not + * yet marked valid */ + if ((ul_tbf = bts->bts->tbf_by_tlli(tbf->tlli, GPRS_RLCMAC_UL_TBF))) { + LOGP(DRLCMACUL, LOGL_NOTICE, "Got RACH from " + "TLLI=0x%08x while UL TBF=%d still exists. " + "Killing pending UL TBF\n", tbf->tlli, + ul_tbf->tfi); + tbf_free(ul_tbf); + } + /* mark TLLI valid now */ + tbf->tlli_valid = 1; + /* store current timing advance */ + bts->bts->timing_advance()->remember(tbf->tlli, tbf->ta); + /* already have TLLI, but we stille get another one */ + } else if (rh->ti) { + uint32_t tlli; + rc = Decoding::tlli_from_ul_data(data, len, &tlli); + if (rc) { + LOGP(DRLCMACUL, LOGL_NOTICE, "Failed to decode TLLI " + "of UL DATA TBF=%d.\n", rh->tfi); + return 0; + } + if (tlli != tbf->tlli) { + LOGP(DRLCMACUL, LOGL_NOTICE, "TLLI mismatch on UL " + "DATA TBF=%d. (Ignoring due to contention " + "resolution)\n", rh->tfi); + return 0; + } + } + + mod_sns = tbf->sns - 1; + mod_sns_half = (tbf->sns >> 1) - 1; + + /* restart T3169 */ + tbf_timer_start(tbf, 3169, bts->t3169, 0); + + /* Increment RX-counter */ + tbf->dir.ul.rx_counter++; + + /* current block relative to lowest unreceived block */ + offset_v_q = (rh->bsn - tbf->dir.ul.v_q) & mod_sns; + /* If out of window (may happen if blocks below V(Q) are received + * again. */ + if (offset_v_q >= tbf->ws) { + LOGP(DRLCMACUL, LOGL_DEBUG, "- BSN %d out of window " + "%d..%d (it's normal)\n", rh->bsn, tbf->dir.ul.v_q, + (tbf->dir.ul.v_q + tbf->ws - 1) & mod_sns); + return 0; + } + /* Write block to buffer and set receive state array. */ + index = rh->bsn & mod_sns_half; /* memory index of block */ + memcpy(tbf->rlc_block[index], data, len); /* Copy block. */ + tbf->rlc_block_len[index] = len; + tbf->dir.ul.v_n[index] = 'R'; /* Mark received block. */ + LOGP(DRLCMACUL, LOGL_DEBUG, "- BSN %d storing in window (%d..%d)\n", + rh->bsn, tbf->dir.ul.v_q, + (tbf->dir.ul.v_q + tbf->ws - 1) & mod_sns); + /* Raise V(R) to highest received sequence number not received. */ + offset_v_r = (rh->bsn + 1 - tbf->dir.ul.v_r) & mod_sns; + if (offset_v_r < (tbf->sns >> 1)) { /* Positive offset, so raise. */ + while (offset_v_r--) { + if (offset_v_r) /* all except the received block */ + tbf->dir.ul.v_n[tbf->dir.ul.v_r & mod_sns_half] + = 'N'; /* Mark block as not received */ + tbf->dir.ul.v_r = (tbf->dir.ul.v_r + 1) & mod_sns; + /* Inc V(R). */ + } + LOGP(DRLCMACUL, LOGL_DEBUG, "- Raising V(R) to %d\n", + tbf->dir.ul.v_r); + } + + /* Raise V(Q) if possible, and retrieve LLC frames from blocks. + * This is looped until there is a gap (non received block) or + * the window is empty.*/ + while (tbf->dir.ul.v_q != tbf->dir.ul.v_r && tbf->dir.ul.v_n[ + (index = tbf->dir.ul.v_q & mod_sns_half)] == 'R') { + LOGP(DRLCMACUL, LOGL_DEBUG, "- Taking block %d out, raising " + "V(Q) to %d\n", tbf->dir.ul.v_q, + (tbf->dir.ul.v_q + 1) & mod_sns); + /* get LLC data from block */ + tbf->assemble_forward_llc(tbf->rlc_block[index], tbf->rlc_block_len[index]); + /* raise V(Q), because block already received */ + tbf->dir.ul.v_q = (tbf->dir.ul.v_q + 1) & mod_sns; + } + + /* Check CV of last frame in buffer */ + if (tbf->state_is(GPRS_RLCMAC_FLOW) /* still in flow state */ + && tbf->dir.ul.v_q == tbf->dir.ul.v_r) { /* if complete */ + struct rlc_ul_header *last_rh = (struct rlc_ul_header *) + tbf->rlc_block[(tbf->dir.ul.v_r - 1) & mod_sns_half]; + LOGP(DRLCMACUL, LOGL_DEBUG, "- No gaps in received block, " + "last block: BSN=%d CV=%d\n", last_rh->bsn, + last_rh->cv); + if (last_rh->cv == 0) { + LOGP(DRLCMACUL, LOGL_DEBUG, "- Finished with UL " + "TBF\n"); + tbf_new_state(tbf, GPRS_RLCMAC_FINISHED); + /* Reset N3103 counter. */ + tbf->dir.ul.n3103 = 0; + } + } + + /* If TLLI is included or if we received half of the window, we send + * an ack/nack */ + if (rh->si || rh->ti || tbf->state_is(GPRS_RLCMAC_FINISHED) + || (tbf->dir.ul.rx_counter % SEND_ACK_AFTER_FRAMES) == 0) { + if (rh->si) { + LOGP(DRLCMACUL, LOGL_NOTICE, "- Scheduling Ack/Nack, " + "because MS is stalled.\n"); + } + if (rh->ti) { + LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, " + "because TLLI is included.\n"); + } + if (tbf->state_is(GPRS_RLCMAC_FINISHED)) { + LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, " + "because last block has CV==0.\n"); + } + if ((tbf->dir.ul.rx_counter % SEND_ACK_AFTER_FRAMES) == 0) { + LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, " + "because %d frames received.\n", + SEND_ACK_AFTER_FRAMES); + } + if (tbf->ul_ack_state == GPRS_RLCMAC_UL_ACK_NONE) { +#ifdef DEBUG_DIAGRAM + if (rh->si) + debug_diagram(bts->bts, tbf->diag, "sched UL-ACK stall"); + if (rh->ti) + debug_diagram(bts->bts, tbf->diag, "sched UL-ACK TLLI"); + if (tbf->state_is(GPRS_RLCMAC_FINISHED)) + debug_diagram(bts->bts, tbf->diag, "sched UL-ACK CV==0"); + if ((tbf->dir.ul.rx_counter % SEND_ACK_AFTER_FRAMES) == 0) + debug_diagram(bts->bts, tbf->diag, "sched UL-ACK n=%d", + tbf->dir.ul.rx_counter); +#endif + /* trigger sending at next RTS */ + tbf->ul_ack_state = GPRS_RLCMAC_UL_ACK_SEND_ACK; + } else { + /* already triggered */ + LOGP(DRLCMACUL, LOGL_DEBUG, "- Sending Ack/Nack is " + "already triggered, don't schedule!\n"); + } + } + + return 0; +} + + /* received RLC/MAC block from L1 */ int gprs_rlcmac_pdch::rcv_block(uint8_t *data, uint8_t len, uint32_t fn, int8_t rssi) { @@ -351,8 +583,7 @@ int gprs_rlcmac_pdch::rcv_block(uint8_t *data, uint8_t len, uint32_t fn, int8_t switch (payload) { case GPRS_RLCMAC_DATA_BLOCK: - rc = gprs_rlcmac_rcv_data_block_acknowledged(bts, trx_no, ts_no, data, - len, rssi); + rc = rcv_data_block_acknowledged(bts, trx_no, ts_no, data, len, rssi); break; case GPRS_RLCMAC_CONTROL_BLOCK: block = bitvec_alloc(len); |