aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHolger Hans Peter Freyther <holger@moiji-mobile.com>2013-10-26 17:32:04 +0200
committerHolger Hans Peter Freyther <holger@moiji-mobile.com>2013-10-30 21:24:11 +0100
commitd11290b90bb5f32a7d57affbe11bdadb24e42db0 (patch)
treeaad9260528d069fab731ec88b59c49b96e15913d
parent6f9f434463d6ba0d195f0b36cd67b57e24b1c7e9 (diff)
pdch/tbf: Move gprs_rlcmac_rcv_data_block_acknowledged into the pdch
Move the method into the PDCH. Extract the finding of TLLI into a new class called Decoding. Move the assemble and forward LLC frames into the TBF as it is poking in the internals of the TBF.
-rw-r--r--src/Makefile.am7
-rw-r--r--src/bts.cpp235
-rw-r--r--src/bts.h8
-rw-r--r--src/decoding.cpp70
-rw-r--r--src/decoding.h28
-rw-r--r--src/gprs_rlcmac.h4
-rw-r--r--src/gprs_rlcmac_data.cpp465
-rw-r--r--src/rlc.h56
-rw-r--r--src/tbf.cpp167
-rw-r--r--src/tbf.h3
10 files changed, 571 insertions, 472 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index d1cbd4b..d2f1fce 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -47,7 +47,8 @@ libgprs_la_SOURCES = \
poll_controller.cpp \
encoding.cpp \
ta.cpp \
- sba.cpp
+ sba.cpp \
+ decoding.cpp
if ENABLE_SYSMOBTS
libgprs_la_SOURCES += \
@@ -85,7 +86,9 @@ noinst_HEADERS = \
poll_controller.h \
encoding.h \
ta.h \
- sba.h
+ sba.h \
+ rlc.h \
+ decoding.h
osmo_pcu_SOURCES = pcu_main.cpp
diff --git a/src/bts.cpp b/src/bts.cpp
index af1a819..9567214 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);
diff --git a/src/bts.h b/src/bts.h
index 4b9ede4..7eabfbc 100644
--- a/src/bts.h
+++ b/src/bts.h
@@ -70,6 +70,14 @@ struct gprs_rlcmac_pdch {
/* back pointers */
struct gprs_rlcmac_trx *trx;
uint8_t ts_no;
+
+#ifdef __cplusplus
+private:
+ int rcv_data_block_acknowledged(struct gprs_rlcmac_bts *bts,
+ uint8_t trx, uint8_t ts,
+ uint8_t *data, uint8_t len, int8_t rssi);
+
+#endif
};
struct gprs_rlcmac_trx {
diff --git a/src/decoding.cpp b/src/decoding.cpp
new file mode 100644
index 0000000..596b66e
--- /dev/null
+++ b/src/decoding.cpp
@@ -0,0 +1,70 @@
+/* decoding
+ *
+ * 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 <decoding.h>
+#include <rlc.h>
+#include <gprs_debug.h>
+
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <string.h>
+
+
+int Decoding::tlli_from_ul_data(const uint8_t *data, uint8_t len,
+ uint32_t *tlli)
+{
+ struct rlc_ul_header *rh = (struct rlc_ul_header *)data;
+ struct rlc_li_field *li;
+ uint8_t e;
+ uint32_t _tlli;
+
+ if (!rh->ti)
+ return -EINVAL;
+
+ data += 3;
+ len -= 3;
+ e = rh->e;
+ /* if E is not set (LI follows) */
+ while (!e) {
+ if (!len) {
+ LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, "
+ "but no more data\n");
+ return -EINVAL;
+ }
+ /* get new E */
+ li = (struct rlc_li_field *)data;
+ if (li->e == 0) /* if LI==0, E is interpreted as '1' */
+ e = 1;
+ else
+ e = li->e;
+ data++;
+ len--;
+ }
+ if (len < 4) {
+ LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA TLLI out of frame "
+ "border\n");
+ return -EINVAL;
+ }
+ memcpy(&_tlli, data, 4);
+ *tlli = ntohl(_tlli);
+
+ return 0;
+}
+
diff --git a/src/decoding.h b/src/decoding.h
new file mode 100644
index 0000000..47983a2
--- /dev/null
+++ b/src/decoding.h
@@ -0,0 +1,28 @@
+/* decoding
+ *
+ * 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.
+ */
+#pragma once
+
+#include <stdint.h>
+
+class Decoding {
+public:
+ static int tlli_from_ul_data(const uint8_t *data, uint8_t len,
+ uint32_t *tlli);
+};
diff --git a/src/gprs_rlcmac.h b/src/gprs_rlcmac.h
index 081271d..af15e77 100644
--- a/src/gprs_rlcmac.h
+++ b/src/gprs_rlcmac.h
@@ -119,10 +119,6 @@ int gprs_rlcmac_downlink_ack(
int gprs_rlcmac_paging_request(uint8_t *ptmsi, uint16_t ptmsi_len,
const char *imsi);
-int gprs_rlcmac_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 msgb *gprs_rlcmac_send_data_block_acknowledged(
struct gprs_rlcmac_tbf *tbf, uint32_t fn, uint8_t ts);
diff --git a/src/gprs_rlcmac_data.cpp b/src/gprs_rlcmac_data.cpp
index 324e2b9..f2887b2 100644
--- a/src/gprs_rlcmac_data.cpp
+++ b/src/gprs_rlcmac_data.cpp
@@ -24,6 +24,7 @@
#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 */
@@ -37,9 +38,6 @@ static struct gprs_rlcmac_cs gprs_rlcmac_cs[] = {
extern void *tall_pcu_ctx;
-/* After receiving these frames, we send ack/nack. */
-#define SEND_ACK_AFTER_FRAMES 20
-
/* After sending these frames, we poll for ack/nack. */
#define POLL_ACK_AFTER_FRAMES 20
@@ -47,39 +45,6 @@ extern void *tall_pcu_ctx;
#define POLLING_ASSIGNMENT_DL 1
#define POLLING_ASSIGNMENT_UL 1
-extern "C" {
-/* TS 04.60 10.2.2 */
-struct rlc_ul_header {
- uint8_t r:1,
- si:1,
- cv:4,
- pt:2;
- uint8_t ti:1,
- tfi:5,
- pi:1,
- spare:1;
- uint8_t e:1,
- bsn:7;
-} __attribute__ ((packed));
-
-struct rlc_dl_header {
- uint8_t usf:3,
- s_p:1,
- rrbp:2,
- pt:2;
- uint8_t fbi:1,
- tfi:5,
- pr:2;
- uint8_t e:1,
- bsn:7;
-} __attribute__ ((packed));
-
-struct rlc_li_field {
- uint8_t e:1,
- m:1,
- li:6;
-} __attribute__ ((packed));
-}
static void gprs_rlcmac_downlink_assignment(
gprs_rlcmac_tbf *tbf, uint8_t poll,
@@ -469,212 +434,6 @@ int gprs_rlcmac_rcv_control_block(struct gprs_rlcmac_bts *bts,
* UL data block flow
*/
-/* get TLLI from received UL data block */
-static int tlli_from_ul_data(uint8_t *data, uint8_t len, uint32_t *tlli)
-{
- struct rlc_ul_header *rh = (struct rlc_ul_header *)data;
- struct rlc_li_field *li;
- uint8_t e;
- uint32_t _tlli;
-
- if (!rh->ti)
- return -EINVAL;
-
- data += 3;
- len -= 3;
- e = rh->e;
- /* if E is not set (LI follows) */
- while (!e) {
- if (!len) {
- LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, "
- "but no more data\n");
- return -EINVAL;
- }
- /* get new E */
- li = (struct rlc_li_field *)data;
- if (li->e == 0) /* if LI==0, E is interpreted as '1' */
- e = 1;
- else
- e = li->e;
- data++;
- len--;
- }
- if (len < 4) {
- LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA TLLI out of frame "
- "border\n");
- return -EINVAL;
- }
- memcpy(&_tlli, data, 4);
- *tlli = ntohl(_tlli);
-
- return 0;
-}
-
-/* Store received block data in LLC message(s) and forward to SGSN if complete.
- */
-static int gprs_rlcmac_assemble_llc(struct gprs_rlcmac_tbf *tbf, uint8_t *data,
- uint8_t len)
-{
- struct rlc_ul_header *rh = (struct rlc_ul_header *)data;
- uint8_t e, m;
- struct rlc_li_field *li;
- uint8_t frame_offset[16], offset = 0, chunk;
- int i, frames = 0;
-
- LOGP(DRLCMACUL, LOGL_DEBUG, "- Assembling frames: (len=%d)\n", len);
-
- data += 3;
- len -= 3;
- e = rh->e; /* if extended */
- m = 1; /* more frames, that means: the first frame */
-
- /* Parse frame offsets from length indicator(s), if any. */
- while (1) {
- if (frames == (int)sizeof(frame_offset)) {
- LOGP(DRLCMACUL, LOGL_ERROR, "Too many frames in "
- "block\n");
- return -EINVAL;
- }
- frame_offset[frames++] = offset;
- LOGP(DRLCMACUL, LOGL_DEBUG, "-- Frame %d starts at offset "
- "%d\n", frames, offset);
- if (!len)
- break;
- /* M == 0 and E == 0 is not allowed in this version. */
- if (!m && !e) {
- LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA TBF=%d "
- "ignored, because M='0' and E='0'.\n",
- tbf->tfi);
- return 0;
- }
- /* no more frames in this segment */
- if (e) {
- break;
- }
- /* There is a new frame and an LI that delimits it. */
- if (m) {
- li = (struct rlc_li_field *)data;
- LOGP(DRLCMACUL, LOGL_DEBUG, "-- Delimiter len=%d\n",
- li->li);
- /* Special case: LI == 0
- * If the last segment would fit precisely into the
- * rest of the RLC MAC block, there would be no way
- * to delimit that this segment ends and is not
- * continued in the next block.
- * The special LI (0) is used to force the segment to
- * extend into the next block, so it is delimited there.
- * This LI must be skipped. Also it is the last LI.
- */
- if (li->li == 0) {
- data++;
- len--;
- m = 1; /* M is ignored, we know there is more */
- break; /* handle E as '1', so we break! */
- }
- e = li->e;
- m = li->m;
- offset += li->li;
- data++;
- len--;
- continue;
- }
- }
- if (!m) {
- LOGP(DRLCMACUL, LOGL_DEBUG, "- Last frame carries spare "
- "data\n");
- }
-
- LOGP(DRLCMACUL, LOGL_DEBUG, "- Data length after length fields: %d\n",
- len);
- /* TLLI */
- if (rh->ti) {
- if (len < 4) {
- LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA TLLI out of "
- "frame border\n");
- return -EINVAL;
- }
- data += 4;
- len -= 4;
- LOGP(DRLCMACUL, LOGL_DEBUG, "- Length after skipping TLLI: "
- "%d\n", len);
- }
-
- /* PFI */
- if (rh->pi) {
- LOGP(DRLCMACUL, LOGL_ERROR, "ERROR: PFI not supported, "
- "please disable in SYSTEM INFORMATION\n");
- if (len < 1) {
- LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA PFI out of "
- "frame border\n");
- return -EINVAL;
- }
- data++;
- len--;
- LOGP(DRLCMACUL, LOGL_DEBUG, "- Length after skipping PFI: "
- "%d\n", len);
- }
-
- /* Now we have:
- * - a list of frames offsets: frame_offset[]
- * - number of frames: i
- * - m == 0: Last frame carries spare data (end of TBF).
- */
-
- /* Check if last offset would exceed frame. */
- if (offset > len) {
- LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA TBF=%d ignored, "
- "because LI delimits data that exceeds block size.\n",
- tbf->tfi);
- return -EINVAL;
- }
-
- /* create LLC frames */
- for (i = 0; i < frames; i++) {
- /* last frame ? */
- if (i == frames - 1) {
- /* no more data in last frame */
- if (!m)
- break;
- /* data until end of frame */
- chunk = len - frame_offset[i];
- } else {
- /* data until next frame */
- chunk = frame_offset[i + 1] - frame_offset[i];
- }
- LOGP(DRLCMACUL, LOGL_DEBUG, "-- Appending chunk (len=%d) to "
- "frame at %d.\n", chunk, tbf->llc_index);
- if (tbf->llc_index + chunk > LLC_MAX_LEN) {
- LOGP(DRLCMACUL, LOGL_NOTICE, "LLC frame exceeds "
- "maximum size.\n");
- chunk = LLC_MAX_LEN - tbf->llc_index;
- }
- memcpy(tbf->llc_frame + tbf->llc_index, data + frame_offset[i],
- chunk);
- tbf->llc_index += chunk;
- /* not last frame. */
- if (i != frames - 1) {
- /* send frame to SGSN */
- LOGP(DRLCMACUL, LOGL_INFO, "Complete UL frame for "
- "TBF=%d: len=%d\n", tbf->tfi, tbf->llc_index);
- gprs_rlcmac_tx_ul_ud(tbf);
- tbf->llc_index = 0; /* reset frame space */
- /* also check if CV==0, because the frame may fill up the
- * block precisely, then it is also complete. normally the
- * frame would be extended into the next block with a 0-length
- * delimiter added to this block. */
- } else if (rh->cv == 0) {
- /* send frame to SGSN */
- LOGP(DRLCMACUL, LOGL_INFO, "Complete UL frame for "
- "TBF=%d that fits precisely in last block: "
- "len=%d\n", tbf->tfi, tbf->llc_index);
- gprs_rlcmac_tx_ul_ud(tbf);
- tbf->llc_index = 0; /* reset frame space */
- }
- }
-
- return 0;
-}
-
struct msgb *gprs_rlcmac_send_uplink_ack(
struct gprs_rlcmac_tbf *tbf,
uint32_t fn)
@@ -731,228 +490,6 @@ struct msgb *gprs_rlcmac_send_uplink_ack(
return msg;
}
-/* receive UL data block
- *
- * The blocks are defragmented and forwarded as LLC frames, if complete.
- */
-int gprs_rlcmac_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 = 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 = 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 */
- gprs_rlcmac_assemble_llc(tbf, 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;
-}
-
struct msgb *gprs_rlcmac_send_packet_uplink_assignment(
struct gprs_rlcmac_tbf *tbf, uint32_t fn)
{
diff --git a/src/rlc.h b/src/rlc.h
new file mode 100644
index 0000000..df10ec3
--- /dev/null
+++ b/src/rlc.h
@@ -0,0 +1,56 @@
+/* rlc header descriptions
+ *
+ * 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.
+ */
+#pragma once
+
+#include <stdint.h>
+
+extern "C" {
+/* TS 04.60 10.2.2 */
+struct rlc_ul_header {
+ uint8_t r:1,
+ si:1,
+ cv:4,
+ pt:2;
+ uint8_t ti:1,
+ tfi:5,
+ pi:1,
+ spare:1;
+ uint8_t e:1,
+ bsn:7;
+} __attribute__ ((packed));
+
+struct rlc_dl_header {
+ uint8_t usf:3,
+ s_p:1,
+ rrbp:2,
+ pt:2;
+ uint8_t fbi:1,
+ tfi:5,
+ pr:2;
+ uint8_t e:1,
+ bsn:7;
+} __attribute__ ((packed));
+
+struct rlc_li_field {
+ uint8_t e:1,
+ m:1,
+ li:6;
+} __attribute__ ((packed));
+}
diff --git a/src/tbf.cpp b/src/tbf.cpp
index 683b33a..259e461 100644
--- a/src/tbf.cpp
+++ b/src/tbf.cpp
@@ -21,6 +21,7 @@
#include <bts.h>
#include <tbf.h>
+#include <rlc.h>
#include <gprs_rlcmac.h>
#include <gprs_debug.h>
@@ -626,6 +627,172 @@ void gprs_rlcmac_tbf::update_llc_frame(struct msgb *msg)
llc_length = msg->len;
}
+/*
+ * Store received block data in LLC message(s) and forward to SGSN
+ * if complete.
+ */
+int gprs_rlcmac_tbf::assemble_forward_llc(uint8_t *data, uint8_t len)
+{
+ struct rlc_ul_header *rh = (struct rlc_ul_header *)data;
+ uint8_t e, m;
+ struct rlc_li_field *li;
+ uint8_t frame_offset[16], offset = 0, chunk;
+ int i, frames = 0;
+
+ LOGP(DRLCMACUL, LOGL_DEBUG, "- Assembling frames: (len=%d)\n", len);
+
+ data += 3;
+ len -= 3;
+ e = rh->e; /* if extended */
+ m = 1; /* more frames, that means: the first frame */
+
+ /* Parse frame offsets from length indicator(s), if any. */
+ while (1) {
+ if (frames == (int)sizeof(frame_offset)) {
+ LOGP(DRLCMACUL, LOGL_ERROR, "Too many frames in "
+ "block\n");
+ return -EINVAL;
+ }
+ frame_offset[frames++] = offset;
+ LOGP(DRLCMACUL, LOGL_DEBUG, "-- Frame %d starts at offset "
+ "%d\n", frames, offset);
+ if (!len)
+ break;
+ /* M == 0 and E == 0 is not allowed in this version. */
+ if (!m && !e) {
+ LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA TBF=%d "
+ "ignored, because M='0' and E='0'.\n",
+ this->tfi);
+ return 0;
+ }
+ /* no more frames in this segment */
+ if (e) {
+ break;
+ }
+ /* There is a new frame and an LI that delimits it. */
+ if (m) {
+ li = (struct rlc_li_field *)data;
+ LOGP(DRLCMACUL, LOGL_DEBUG, "-- Delimiter len=%d\n",
+ li->li);
+ /* Special case: LI == 0
+ * If the last segment would fit precisely into the
+ * rest of the RLC MAC block, there would be no way
+ * to delimit that this segment ends and is not
+ * continued in the next block.
+ * The special LI (0) is used to force the segment to
+ * extend into the next block, so it is delimited there.
+ * This LI must be skipped. Also it is the last LI.
+ */
+ if (li->li == 0) {
+ data++;
+ len--;
+ m = 1; /* M is ignored, we know there is more */
+ break; /* handle E as '1', so we break! */
+ }
+ e = li->e;
+ m = li->m;
+ offset += li->li;
+ data++;
+ len--;
+ continue;
+ }
+ }
+ if (!m) {
+ LOGP(DRLCMACUL, LOGL_DEBUG, "- Last frame carries spare "
+ "data\n");
+ }
+
+ LOGP(DRLCMACUL, LOGL_DEBUG, "- Data length after length fields: %d\n",
+ len);
+ /* TLLI */
+ if (rh->ti) {
+ if (len < 4) {
+ LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA TLLI out of "
+ "frame border\n");
+ return -EINVAL;
+ }
+ data += 4;
+ len -= 4;
+ LOGP(DRLCMACUL, LOGL_DEBUG, "- Length after skipping TLLI: "
+ "%d\n", len);
+ }
+
+ /* PFI */
+ if (rh->pi) {
+ LOGP(DRLCMACUL, LOGL_ERROR, "ERROR: PFI not supported, "
+ "please disable in SYSTEM INFORMATION\n");
+ if (len < 1) {
+ LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA PFI out of "
+ "frame border\n");
+ return -EINVAL;
+ }
+ data++;
+ len--;
+ LOGP(DRLCMACUL, LOGL_DEBUG, "- Length after skipping PFI: "
+ "%d\n", len);
+ }
+
+ /* Now we have:
+ * - a list of frames offsets: frame_offset[]
+ * - number of frames: i
+ * - m == 0: Last frame carries spare data (end of TBF).
+ */
+
+ /* Check if last offset would exceed frame. */
+ if (offset > len) {
+ LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA TBF=%d ignored, "
+ "because LI delimits data that exceeds block size.\n",
+ this->tfi);
+ return -EINVAL;
+ }
+
+ /* create LLC frames */
+ for (i = 0; i < frames; i++) {
+ /* last frame ? */
+ if (i == frames - 1) {
+ /* no more data in last frame */
+ if (!m)
+ break;
+ /* data until end of frame */
+ chunk = len - frame_offset[i];
+ } else {
+ /* data until next frame */
+ chunk = frame_offset[i + 1] - frame_offset[i];
+ }
+ LOGP(DRLCMACUL, LOGL_DEBUG, "-- Appending chunk (len=%d) to "
+ "frame at %d.\n", chunk, this->llc_index);
+ if (this->llc_index + chunk > LLC_MAX_LEN) {
+ LOGP(DRLCMACUL, LOGL_NOTICE, "LLC frame exceeds "
+ "maximum size.\n");
+ chunk = LLC_MAX_LEN - this->llc_index;
+ }
+ memcpy(this->llc_frame + this->llc_index, data + frame_offset[i],
+ chunk);
+ this->llc_index += chunk;
+ /* not last frame. */
+ if (i != frames - 1) {
+ /* send frame to SGSN */
+ LOGP(DRLCMACUL, LOGL_INFO, "Complete UL frame for "
+ "TBF=%d: len=%d\n", this->tfi, this->llc_index);
+ gprs_rlcmac_tx_ul_ud(this);
+ this->llc_index = 0; /* reset frame space */
+ /* also check if CV==0, because the frame may fill up the
+ * block precisely, then it is also complete. normally the
+ * frame would be extended into the next block with a 0-length
+ * delimiter added to this block. */
+ } else if (rh->cv == 0) {
+ /* send frame to SGSN */
+ LOGP(DRLCMACUL, LOGL_INFO, "Complete UL frame for "
+ "TBF=%d that fits precisely in last block: "
+ "len=%d\n", this->tfi, this->llc_index);
+ gprs_rlcmac_tx_ul_ud(this);
+ this->llc_index = 0; /* reset frame space */
+ }
+ }
+
+ return 0;
+}
+
void gprs_rlcmac_tbf::free_all(struct gprs_rlcmac_trx *trx)
{
for (uint8_t tfi = 0; tfi < 32; tfi++) {
diff --git a/src/tbf.h b/src/tbf.h
index 0441096..10bff14 100644
--- a/src/tbf.h
+++ b/src/tbf.h
@@ -96,6 +96,9 @@ struct gprs_rlcmac_tbf {
struct msgb *llc_dequeue(bssgp_bvc_ctx *bctx);
void update_llc_frame(struct msgb *msg);
+ /* TODO: extract LLC class? */
+ int assemble_forward_llc(uint8_t *data, uint8_t len);
+
int rlcmac_diag();
struct llist_head list;