diff options
-rw-r--r-- | src/Makefile.am | 7 | ||||
-rw-r--r-- | src/bts.cpp | 235 | ||||
-rw-r--r-- | src/bts.h | 8 | ||||
-rw-r--r-- | src/decoding.cpp | 70 | ||||
-rw-r--r-- | src/decoding.h | 28 | ||||
-rw-r--r-- | src/gprs_rlcmac.h | 4 | ||||
-rw-r--r-- | src/gprs_rlcmac_data.cpp | 465 | ||||
-rw-r--r-- | src/rlc.h | 56 | ||||
-rw-r--r-- | src/tbf.cpp | 167 | ||||
-rw-r--r-- | src/tbf.h | 3 |
10 files changed, 571 insertions, 472 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index d1cbd4b4..d2f1fce3 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 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); @@ -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 00000000..596b66eb --- /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 00000000..47983a2a --- /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 081271d8..af15e77a 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 324e2b96..f2887b2a 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 00000000..df10ec37 --- /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 683b33ac..259e4614 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++) { @@ -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; |