aboutsummaryrefslogtreecommitdiffstats
path: root/src/bts.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/bts.cpp')
-rw-r--r--src/bts.cpp235
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);