Merge remote-tracking branch 'origin/sysmocom/master'

This commit is contained in:
Holger Hans Peter Freyther 2014-05-15 12:24:12 +02:00
commit 8f3520579a
51 changed files with 15395 additions and 4041 deletions

3
.gitignore vendored
View File

@ -32,7 +32,10 @@ src/osmo-pcu-remote
.dirstamp
tests/atconfig
tests/package.m4
tests/alloc/AllocTest
tests/rlcmac/RLCMACTest
tests/tbf/TbfTest
tests/types/TypesTest
tests/emu/pcu_emu
tests/testsuite
tests/testsuite.log

42
TODO Normal file
View File

@ -0,0 +1,42 @@
* Make the TBF ul/dl list per BTS
* Make the SBA per BTS
* Make the tbf point to the BTS so we can kill plenty of parameters
* Group more into in classes.. remove bts pointers.
* Change functions with 100 parameters to get a struct as param
* Move move into the TBF class
* Replace trx/ts with pointers. E.g. a PDCH should know the trx
it is on... then we can omit trx, ts and parameters and just pass
the pdch.
* On global free/reset... also flush the timing advance..
* tbf/llc window code appears to be duplicated and nested in other
methods. This needs to be cleaned.
* Possible race condition:
When scheduling a Downlink Assignment on the UL-TBF we need to make
sure that the assignment is sent _before_ the final ack. With my fairness
changes it gets more likely that this event is trigerred.
* Optimize:
After receiving an ACK/NACK.. schedule another one if the window
is kind of stalled anyway. This way we avoid resending frames that
might have already arrived. It could increase the throughput..
Do not re-transmit after we got ack/nacked and where in the resending
mode... and increase the window.
<0004> tbf.cpp:907 - Sending new block at BSN 111
...
tbf.cpp:858 - Restarting at BSN 48, because all window is stalled.
...
tbf.cpp:1383 - V(B): (V(A)=59)"NNAAAAAAANAAAAANNAAAAAAAAAAAAAAAAAAAXXXXXXXXXXXXXXXXX"(V(S)-1=111) A=Acked N=Nacked U=Unacked X=Resend-Unacked I=Invalid
.. retransmitting the nacked.. and then the ones that migh have
already arrived
<0004> tbf.cpp:834 TBF(TFI=0 TLLI=0xd7b78810 DIR=DL) downlink (V(A)==59 .. V(S)==112)
<0004> tbf.cpp:840 - Resending BSN 111
Figure out scheduling issue. Why do we reach the 20 re-transmits and
stil haven't received the ACK/NACK? was it scheduled? The whole
scheduler could be re-worked to be more determestic.. and answer
questions like if it has been sent or not

16
contrib/sysmopcu.service Normal file
View File

@ -0,0 +1,16 @@
[Unit]
Description=sysmocom sysmoPCU
[Service]
Type=simple
ExecStart=/usr/bin/osmo-pcu -c /etc/osmocom/osmo-pcu.cfg -e
Restart=always
RestartSec=2
RestartPreventExitStatus=1
# The msg queues must be read fast enough
CPUSchedulingPolicy=rr
CPUSchedulingPriority=1
[Install]
WantedBy=multi-user.target

View File

@ -34,14 +34,22 @@ libgprs_la_SOURCES = \
gsm_rlcmac.cpp \
gprs_bssgp_pcu.cpp \
gprs_rlcmac.cpp \
gprs_rlcmac_data.cpp \
gprs_rlcmac_sched.cpp \
gprs_rlcmac_meas.cpp \
gprs_rlcmac_ts_alloc.cpp \
gsm_timer.cpp \
bitvector.cpp \
pcu_l1_if.cpp \
pcu_vty.c \
tbf.cpp
tbf.cpp \
bts.cpp \
poll_controller.cpp \
encoding.cpp \
ta.cpp \
sba.cpp \
decoding.cpp \
llc.cpp \
rlc.cpp
if ENABLE_SYSMOBTS
libgprs_la_SOURCES += \
@ -74,7 +82,15 @@ noinst_HEADERS = \
pcu_vty.h \
sysmo_l1_if.h \
femtobts.h \
tbf.h
tbf.h \
bts.h \
poll_controller.h \
encoding.h \
ta.h \
sba.h \
rlc.h \
decoding.h \
llc.h
osmo_pcu_SOURCES = pcu_main.cpp

990
src/bts.cpp Normal file
View File

@ -0,0 +1,990 @@
/*
* Copyright (C) 2013 by Holger Hans Peter Freyther
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <bts.h>
#include <poll_controller.h>
#include <tbf.h>
#include <encoding.h>
#include <decoding.h>
#include <rlc.h>
#include <pcu_l1_if.h>
#include <gprs_rlcmac.h>
#include <gprs_debug.h>
extern "C" {
#include <osmocom/core/talloc.h>
#include <osmocom/core/msgb.h>
}
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
extern void *tall_pcu_ctx;
static BTS s_bts;
/**
* For gcc-4.4 compat do not use extended initializer list but keep the
* order from the enum here. Once we support GCC4.7 and up we can change
* the code below.
*/
static const struct rate_ctr_desc bts_ctr_description[] = {
{ "tbf.dl.alloc", "TBF DL Allocated "},
{ "tbf.dl.freed", "TBF DL Freed "},
{ "tbf.ul.alloc", "TBF UL Allocated "},
{ "tbf.ul.freed", "TBF UL Freed "},
{ "tbf.reused", "TBF Reused "},
{ "rlc.sent", "RLC Sent "},
{ "rlc.resent", "RLC Resent "},
{ "rlc.restarted", "RLC Restarted "},
{ "rlc.stalled", "RLC Stalled "},
{ "rlc.nacked", "RLC Nacked "},
{ "decode.errors", "Decode Errors "},
{ "sba.allocated", "SBA Allocated "},
{ "sba.freed", "SBA Freed "},
{ "sba.timedout", "SBA Timeout "},
{ "llc.timeout", "Timedout Frames "},
{ "llc.dropped", "Dropped Frames "},
{ "llc.scheduled", "Scheduled Frames "},
{ "rach.requests", "RACH requests "},
};
static const struct rate_ctr_group_desc bts_ctrg_desc = {
"bts",
"BTS Statistics",
ARRAY_SIZE(bts_ctr_description),
bts_ctr_description,
};
BTS* BTS::main_bts()
{
return &s_bts;
}
struct gprs_rlcmac_bts *BTS::bts_data()
{
return &m_bts;
}
struct gprs_rlcmac_bts *bts_main_data()
{
return BTS::main_bts()->bts_data();
}
struct rate_ctr_group *bts_main_data_stats()
{
return BTS::main_bts()->rate_counters();
}
BTS::BTS()
: m_cur_fn(0)
, m_pollController(*this)
, m_sba(*this)
{
memset(&m_bts, 0, sizeof(m_bts));
INIT_LLIST_HEAD(&m_bts.ul_tbfs);
INIT_LLIST_HEAD(&m_bts.dl_tbfs);
m_bts.bts = this;
/* initialize back pointers */
for (size_t trx_no = 0; trx_no < ARRAY_SIZE(m_bts.trx); ++trx_no) {
struct gprs_rlcmac_trx *trx = &m_bts.trx[trx_no];
trx->trx_no = trx_no;
trx->bts = this;
for (size_t ts_no = 0; ts_no < ARRAY_SIZE(trx->pdch); ++ts_no) {
struct gprs_rlcmac_pdch *pdch = &trx->pdch[ts_no];
pdch->ts_no = ts_no;
pdch->trx = trx;
}
}
m_ratectrs = rate_ctr_group_alloc(tall_pcu_ctx, &bts_ctrg_desc, 0);
}
BTS::~BTS()
{
rate_ctr_group_free(m_ratectrs);
}
void BTS::set_current_frame_number(int fn)
{
m_cur_fn = fn;
m_pollController.expireTimedout(m_cur_fn);
}
int BTS::add_paging(uint8_t chan_needed, uint8_t *identity_lv)
{
uint8_t l, trx, ts, any_tbf = 0;
struct gprs_rlcmac_tbf *tbf;
struct gprs_rlcmac_paging *pag;
uint8_t slot_mask[8];
int8_t first_ts; /* must be signed */
llist_head *tbfs_lists[] = {
&m_bts.ul_tbfs,
&m_bts.dl_tbfs,
NULL
};
LOGP(DRLCMAC, LOGL_INFO, "Add RR paging: chan-needed=%d MI=%s\n",
chan_needed, osmo_hexdump(identity_lv + 1, identity_lv[0]));
/* collect slots to page
* Mark slots for every TBF, but only mark one of it.
* Mark only the first slot found.
* Don't mark, if TBF uses a different slot that is already marked. */
memset(slot_mask, 0, sizeof(slot_mask));
for (l = 0; tbfs_lists[l]; l++) {
llist_for_each_entry(tbf, tbfs_lists[l], list) {
first_ts = -1;
for (ts = 0; ts < 8; ts++) {
if (tbf->pdch[ts]) {
/* remember the first slot found */
if (first_ts < 0)
first_ts = ts;
/* break, if we already marked a slot */
if ((slot_mask[tbf->trx->trx_no] & (1 << ts)))
break;
}
}
/* mark first slot found, if none is marked already */
if (ts == 8 && first_ts >= 0) {
LOGP(DRLCMAC, LOGL_DEBUG, "- %s uses "
"TRX=%d TS=%d, so we mark\n",
tbf_name(tbf),
tbf->trx->trx_no, first_ts);
slot_mask[tbf->trx->trx_no] |= (1 << first_ts);
} else
LOGP(DRLCMAC, LOGL_DEBUG, "- %s uses "
"already marked TRX=%d TS=%d\n",
tbf_name(tbf),
tbf->trx->trx_no, ts);
}
}
/* Now we have a list of marked slots. Every TBF uses at least one
* of these slots. */
/* schedule paging to all marked slots */
for (trx = 0; trx < 8; trx++) {
if (slot_mask[trx] == 0)
continue;
for (ts = 0; ts < 8; ts++) {
if ((slot_mask[trx] & (1 << ts))) {
/* schedule */
pag = talloc_zero(tall_pcu_ctx,
struct gprs_rlcmac_paging);
if (!pag)
return -ENOMEM;
pag->chan_needed = chan_needed;
memcpy(pag->identity_lv, identity_lv,
identity_lv[0] + 1);
m_bts.trx[trx].pdch[ts].add_paging(pag);
LOGP(DRLCMAC, LOGL_INFO, "Paging on PACCH of "
"TRX=%d TS=%d\n", trx, ts);
any_tbf = 1;
}
}
}
if (!any_tbf)
LOGP(DRLCMAC, LOGL_INFO, "No paging, because no TBF\n");
return 0;
}
/* search for active downlink or uplink tbf */
gprs_rlcmac_tbf *BTS::tbf_by_tlli(uint32_t tlli, enum gprs_rlcmac_tbf_direction dir)
{
struct gprs_rlcmac_tbf *tbf;
if (dir == GPRS_RLCMAC_UL_TBF) {
llist_for_each_entry(tbf, &m_bts.ul_tbfs, list) {
if (tbf->state_is_not(GPRS_RLCMAC_RELEASING)
&& tbf->tlli() == tlli && tbf->is_tlli_valid())
return tbf;
}
} else {
llist_for_each_entry(tbf, &m_bts.dl_tbfs, list) {
if (tbf->state_is_not(GPRS_RLCMAC_RELEASING)
&& tbf->tlli() == tlli)
return tbf;
}
}
return NULL;
}
gprs_rlcmac_tbf *BTS::tbf_by_poll_fn(uint32_t fn, uint8_t trx, uint8_t ts)
{
struct gprs_rlcmac_tbf *tbf;
/* only one TBF can poll on specific TS/FN, because scheduler can only
* schedule one downlink control block (with polling) at a FN per TS */
llist_for_each_entry(tbf, &m_bts.ul_tbfs, list) {
if (tbf->state_is_not(GPRS_RLCMAC_RELEASING)
&& tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
&& tbf->poll_fn == fn && tbf->trx->trx_no == trx
&& tbf->control_ts == ts)
return tbf;
}
llist_for_each_entry(tbf, &m_bts.dl_tbfs, list) {
if (tbf->state_is_not(GPRS_RLCMAC_RELEASING)
&& tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
&& tbf->poll_fn == fn && tbf->trx->trx_no == trx
&& tbf->control_ts == ts)
return tbf;
}
return NULL;
}
/* lookup TBF Entity (by TFI) */
gprs_rlcmac_tbf *BTS::tbf_by_tfi(uint8_t tfi, uint8_t trx,
enum gprs_rlcmac_tbf_direction dir)
{
struct gprs_rlcmac_tbf *tbf;
if (tfi >= 32 || trx >= 8)
return NULL;
if (dir == GPRS_RLCMAC_UL_TBF)
tbf = m_bts.trx[trx].ul_tbf[tfi];
else
tbf = m_bts.trx[trx].dl_tbf[tfi];
if (!tbf)
return NULL;
if (tbf->state_is_not(GPRS_RLCMAC_RELEASING))
return tbf;
return NULL;
}
/* FIXME: spread resources over multiple TRX. Also add option to use same
* TRX in case of existing TBF for TLLI in the other direction. */
/* search for free TFI and return TFI, TRX */
int BTS::tfi_find_free(enum gprs_rlcmac_tbf_direction dir,
uint8_t *_trx, int8_t use_trx)
{
struct gprs_rlcmac_pdch *pdch;
struct gprs_rlcmac_tbf **tbfp;
uint8_t trx_from, trx_to, trx, ts, tfi;
if (use_trx >= 0 && use_trx < 8)
trx_from = trx_to = use_trx;
else {
trx_from = 0;
trx_to = 7;
}
/* on TRX find first enabled TS */
for (trx = trx_from; trx <= trx_to; trx++) {
for (ts = 0; ts < 8; ts++) {
pdch = &m_bts.trx[trx].pdch[ts];
if (!pdch->is_enabled())
continue;
break;
}
if (ts < 8)
break;
}
if (trx > trx_to) {
LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH available.\n");
return -EINVAL;
}
LOGP(DRLCMAC, LOGL_DEBUG, "Searching for first unallocated TFI: "
"TRX=%d first TS=%d\n", trx, ts);
if (dir == GPRS_RLCMAC_UL_TBF)
tbfp = m_bts.trx[trx].ul_tbf;
else
tbfp = m_bts.trx[trx].dl_tbf;
for (tfi = 0; tfi < 32; tfi++) {
if (!tbfp[tfi])
break;
}
if (tfi < 32) {
LOGP(DRLCMAC, LOGL_DEBUG, " Found TFI=%d.\n", tfi);
*_trx = trx;
return tfi;
}
LOGP(DRLCMAC, LOGL_NOTICE, "No TFI available.\n");
return -1;
}
int BTS::rcv_imm_ass_cnf(const uint8_t *data, uint32_t fn)
{
struct gprs_rlcmac_tbf *tbf;
uint8_t plen;
uint32_t tlli;
/* move to IA Rest Octets */
plen = data[0] >> 2;
data += 1 + plen;
if ((*data & 0xf0) != 0xd0) {
LOGP(DRLCMAC, LOGL_ERROR, "Got IMM.ASS confirm, but rest "
"octets do not start with bit sequence 'HH01' "
"(Packet Downlink Assignment)\n");
return -EINVAL;
}
/* get TLLI from downlink assignment */
tlli = (*data++) << 28;
tlli |= (*data++) << 20;
tlli |= (*data++) << 12;
tlli |= (*data++) << 4;
tlli |= (*data++) >> 4;
tbf = tbf_by_tlli(tlli, GPRS_RLCMAC_DL_TBF);
if (!tbf) {
LOGP(DRLCMAC, LOGL_ERROR, "Got IMM.ASS confirm, but TLLI=%08x "
"does not exit\n", tlli);
return -EINVAL;
}
LOGP(DRLCMAC, LOGL_DEBUG, "Got IMM.ASS confirm for TLLI=%08x\n", tlli);
if (tbf->dir.dl.wait_confirm)
tbf_timer_start(tbf, 0, Tassign_agch);
return 0;
}
int BTS::rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta)
{
struct gprs_rlcmac_tbf *tbf;
uint8_t trx_no, ts_no = 0;
int8_t tfi; /* must be signed */
uint8_t sb = 0;
uint32_t sb_fn = 0;
int rc;
uint8_t plen;
rach_frame();
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF on RACH, so we provide "
"one:\n");
if ((ra & 0xf8) == 0x70) {
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single block "
"allocation\n");
sb = 1;
} else if (m_bts.force_two_phase) {
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single phase access, "
"but we force two phase access\n");
sb = 1;
}
if (qta < 0)
qta = 0;
if (qta > 252)
qta = 252;
if (sb) {
rc = sba()->alloc(&trx_no, &ts_no, &sb_fn, qta >> 2);
if (rc < 0)
return rc;
LOGP(DRLCMAC, LOGL_DEBUG, "RX: [PCU <- BTS] RACH qbit-ta=%d "
"ra=0x%02x, Fn=%d (%d,%d,%d)\n", qta, ra, Fn,
(Fn / (26 * 51)) % 32, Fn % 51, Fn % 26);
LOGP(DRLCMAC, LOGL_INFO, "TX: Immediate Assignment Uplink "
"(AGCH)\n");
} else {
// Create new TBF
#warning "Copy and pate with other routines.."
tfi = tfi_find_free(GPRS_RLCMAC_UL_TBF, &trx_no, -1);
if (tfi < 0) {
LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource\n");
/* FIXME: send reject */
return -EBUSY;
}
/* set class to 0, since we don't know the multislot class yet */
tbf = tbf_alloc(&m_bts, NULL, GPRS_RLCMAC_UL_TBF, tfi, trx_no, 0, 1);
if (!tbf) {
LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource\n");
/* FIXME: send reject */
return -EBUSY;
}
tbf->ta = qta >> 2;
tbf_new_state(tbf, GPRS_RLCMAC_FLOW);
tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_CCCH);
tbf_timer_start(tbf, 3169, m_bts.t3169, 0);
LOGP(DRLCMAC, LOGL_DEBUG, "%s [UPLINK] START\n",
tbf_name(tbf));
LOGP(DRLCMAC, LOGL_DEBUG, "%s RX: [PCU <- BTS] RACH "
"qbit-ta=%d ra=0x%02x, Fn=%d (%d,%d,%d)\n",
tbf_name(tbf),
qta, ra, Fn, (Fn / (26 * 51)) % 32, Fn % 51, Fn % 26);
LOGP(DRLCMAC, LOGL_INFO, "%s TX: START Immediate "
"Assignment Uplink (AGCH)\n", tbf_name(tbf));
}
bitvec *immediate_assignment = bitvec_alloc(22) /* without plen */;
bitvec_unhex(immediate_assignment,
"2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
if (sb)
plen = Encoding::write_immediate_assignment(&m_bts, immediate_assignment, 0, ra,
Fn, qta >> 2, m_bts.trx[trx_no].arfcn, ts_no,
m_bts.trx[trx_no].pdch[ts_no].tsc, 0, 0, 0, 0, sb_fn, 1,
m_bts.alpha, m_bts.gamma, -1);
else
plen = Encoding::write_immediate_assignment(&m_bts, immediate_assignment, 0, ra,
Fn, tbf->ta, tbf->trx->arfcn, tbf->first_ts, tbf->tsc(),
tbf->tfi(), tbf->dir.ul.usf[tbf->first_ts], 0, 0, 0, 0,
m_bts.alpha, m_bts.gamma, -1);
pcu_l1if_tx_agch(immediate_assignment, plen);
bitvec_free(immediate_assignment);
return 0;
}
/* depending on the current TBF, we assign on PACCH or AGCH */
void BTS::trigger_dl_ass(
struct gprs_rlcmac_tbf *tbf,
struct gprs_rlcmac_tbf *old_tbf, const char *imsi)
{
/* stop pending timer */
tbf->stop_timer();
/* check for downlink tbf: */
if (old_tbf) {
LOGP(DRLCMAC, LOGL_DEBUG, "Send dowlink assignment on "
"PACCH, because %s exists\n", tbf_name(old_tbf));
old_tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_SEND_ASS;
/* use TA from old TBF */
tbf->ta = old_tbf->ta;
/* change state */
tbf_new_state(tbf, GPRS_RLCMAC_ASSIGN);
tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_PACCH);
/* start timer */
tbf_timer_start(tbf, 0, Tassign_pacch);
} else {
LOGP(DRLCMAC, LOGL_DEBUG, "Send dowlink assignment for %s on PCH, no TBF exist (IMSI=%s)\n", tbf_name(tbf), imsi);
if (!imsi || strlen(imsi) < 3) {
LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI!\n");
return;
}
/* change state */
tbf_new_state(tbf, GPRS_RLCMAC_ASSIGN);
tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_CCCH);
tbf->assign_imsi(imsi);
/* send immediate assignment */
tbf->bts->snd_dl_ass(tbf, 0, imsi);
tbf->dir.dl.wait_confirm = 1;
}
}
void BTS::snd_dl_ass(gprs_rlcmac_tbf *tbf, uint8_t poll, const char *imsi)
{
int plen;
LOGP(DRLCMAC, LOGL_INFO, "TX: START %s Immediate Assignment Downlink (PCH)\n", tbf_name(tbf));
bitvec *immediate_assignment = bitvec_alloc(22); /* without plen */
bitvec_unhex(immediate_assignment, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
/* use request reference that has maximum distance to current time,
* so the assignment will not conflict with possible RACH requests. */
plen = Encoding::write_immediate_assignment(&m_bts, immediate_assignment, 1, 125,
(tbf->pdch[tbf->first_ts]->last_rts_fn + 21216) % 2715648, tbf->ta,
tbf->trx->arfcn, tbf->first_ts, tbf->tsc(), tbf->tfi(), 0, tbf->tlli(), poll,
tbf->poll_fn, 0, m_bts.alpha, m_bts.gamma, -1);
pcu_l1if_tx_pch(immediate_assignment, plen, imsi);
bitvec_free(immediate_assignment);
}
/*
* PDCH code below. TODO: move to a separate file
*/
void gprs_rlcmac_pdch::enable()
{
/* TODO: Check if there are still allocated resources.. */
INIT_LLIST_HEAD(&paging_list);
m_is_enabled = 1;
}
void gprs_rlcmac_pdch::disable()
{
/* TODO.. kick free_resources once we know the TRX/TS we are on */
m_is_enabled = 0;
}
/* TODO: kill the parameter and make a pdch belong to a trx.. to a bts.. */
void gprs_rlcmac_pdch::free_resources()
{
struct gprs_rlcmac_paging *pag;
/* we are not enabled. there should be no resources */
if (!is_enabled())
return;
/* kick all TBF on slot */
gprs_rlcmac_tbf::free_all(this);
/* flush all pending paging messages */
while ((pag = dequeue_paging()))
talloc_free(pag);
trx->bts->sba()->free_resources(this);
}
struct gprs_rlcmac_paging *gprs_rlcmac_pdch::dequeue_paging()
{
struct gprs_rlcmac_paging *pag;
if (llist_empty(&paging_list))
return NULL;
pag = llist_entry(paging_list.next, struct gprs_rlcmac_paging, list);
llist_del(&pag->list);
return pag;
}
struct msgb *gprs_rlcmac_pdch::packet_paging_request()
{
struct gprs_rlcmac_paging *pag;
struct msgb *msg;
unsigned wp = 0, len;
/* no paging, no message */
pag = dequeue_paging();
if (!pag)
return NULL;
LOGP(DRLCMAC, LOGL_DEBUG, "Scheduling paging\n");
/* alloc message */
msg = msgb_alloc(23, "pag ctrl block");
if (!msg) {
talloc_free(pag);
return NULL;
}
bitvec *pag_vec = bitvec_alloc(23);
if (!pag_vec) {
msgb_free(msg);
talloc_free(pag);
return NULL;
}
wp = Encoding::write_packet_paging_request(pag_vec);
/* loop until message is full */
while (pag) {
/* try to add paging */
if ((pag->identity_lv[1] & 0x07) == 4) {
/* TMSI */
LOGP(DRLCMAC, LOGL_DEBUG, "- TMSI=0x%08x\n",
ntohl(*((uint32_t *)(pag->identity_lv + 1))));
len = 1 + 1 + 1 + 32 + 2 + 1;
if (pag->identity_lv[0] != 5) {
LOGP(DRLCMAC, LOGL_ERROR, "TMSI paging with "
"MI != 5 octets!\n");
goto continue_next;
}
} else {
/* MI */
LOGP(DRLCMAC, LOGL_DEBUG, "- MI=%s\n",
osmo_hexdump(pag->identity_lv + 1,
pag->identity_lv[0]));
len = 1 + 1 + 1 + 4 + (pag->identity_lv[0]<<3) + 2 + 1;
if (pag->identity_lv[0] > 8) {
LOGP(DRLCMAC, LOGL_ERROR, "Paging with "
"MI > 8 octets!\n");
goto continue_next;
}
}
if (wp + len > 184) {
LOGP(DRLCMAC, LOGL_DEBUG, "- Does not fit, so schedule "
"next time\n");
/* put back paging record, because does not fit */
llist_add_tail(&pag->list, &paging_list);
break;
}
Encoding::write_repeated_page_info(pag_vec, wp, pag->identity_lv[0],
pag->identity_lv + 1, pag->chan_needed);
continue_next:
talloc_free(pag);
pag = dequeue_paging();
}
bitvec_pack(pag_vec, msgb_put(msg, 23));
RlcMacDownlink_t * mac_control_block = (RlcMacDownlink_t *)talloc_zero(tall_pcu_ctx, RlcMacDownlink_t);
LOGP(DRLCMAC, LOGL_DEBUG, "+++++++++++++++++++++++++ TX : Packet Paging Request +++++++++++++++++++++++++\n");
decode_gsm_rlcmac_downlink(pag_vec, mac_control_block);
LOGPC(DCSN1, LOGL_NOTICE, "\n");
LOGP(DRLCMAC, LOGL_DEBUG, "------------------------- TX : Packet Paging Request -------------------------\n");
bitvec_free(pag_vec);
talloc_free(mac_control_block);
return msg;
}
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(uint8_t *data, uint8_t len, int8_t rssi)
{
struct gprs_rlcmac_tbf *tbf;
struct rlc_ul_header *rh = (struct rlc_ul_header *)data;
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:
bts()->decode_error();
LOGP(DRLCMACUL, LOGL_ERROR, "Dropping data block with invalid"
"length: %d)\n", len);
return -EINVAL;
}
/* find TBF inst from given TFI */
tbf = bts()->tbf_by_tfi(rh->tfi, trx_no(), GPRS_RLCMAC_UL_TBF);
if (!tbf) {
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA unknown TFI=%d\n",
rh->tfi);
return 0;
}
return tbf->rcv_data_block_acknowledged(data, len, rssi);
}
void gprs_rlcmac_pdch::rcv_control_ack(Packet_Control_Acknowledgement_t *packet, uint32_t fn)
{
struct gprs_rlcmac_tbf *tbf;
uint32_t tlli = 0;
tlli = packet->TLLI;
tbf = bts()->tbf_by_poll_fn(fn, trx_no(), ts_no);
if (!tbf) {
LOGP(DRLCMAC, LOGL_NOTICE, "PACKET CONTROL ACK with "
"unknown FN=%u TLLI=0x%08x (TRX %d TS %d)\n",
fn, tlli, trx_no(), ts_no);
return;
}
tbf->update_tlli(tlli);
LOGP(DRLCMAC, LOGL_DEBUG, "RX: [PCU <- BTS] %s Packet Control Ack\n", tbf_name(tbf));
tbf->poll_state = GPRS_RLCMAC_POLL_NONE;
/* check if this control ack belongs to packet uplink ack */
if (tbf->ul_ack_state == GPRS_RLCMAC_UL_ACK_WAIT_ACK) {
LOGP(DRLCMAC, LOGL_DEBUG, "TBF: [UPLINK] END %s\n", tbf_name(tbf));
tbf->ul_ack_state = GPRS_RLCMAC_UL_ACK_NONE;
if ((tbf->state_flags &
(1 << GPRS_RLCMAC_FLAG_TO_UL_ACK))) {
tbf->state_flags &=
~(1 << GPRS_RLCMAC_FLAG_TO_UL_ACK);
LOGP(DRLCMAC, LOGL_NOTICE, "Recovered uplink "
"ack for UL %s\n", tbf_name(tbf));
}
tbf_free(tbf);
return;
}
if (tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_WAIT_ACK) {
LOGP(DRLCMAC, LOGL_DEBUG, "TBF: [UPLINK] DOWNLINK ASSIGNED %s\n", tbf_name(tbf));
/* reset N3105 */
tbf->n3105 = 0;
tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_NONE;
if (tbf->direction == GPRS_RLCMAC_UL_TBF)
tbf = bts()->tbf_by_tlli(tbf->tlli(),
GPRS_RLCMAC_DL_TBF);
#warning "TBF is changing on the way... *sigh*"
if (!tbf) {
LOGP(DRLCMAC, LOGL_ERROR, "Got ACK, but DL "
"TBF is gone TLLI=0x%08x\n", tlli);
return;
}
tbf_new_state(tbf, GPRS_RLCMAC_FLOW);
/* stop pending assignment timer */
tbf->stop_timer();
if ((tbf->state_flags &
(1 << GPRS_RLCMAC_FLAG_TO_DL_ASS))) {
tbf->state_flags &=
~(1 << GPRS_RLCMAC_FLAG_TO_DL_ASS);
LOGP(DRLCMAC, LOGL_NOTICE, "Recovered downlink "
"assignment for %s\n", tbf_name(tbf));
}
tbf_assign_control_ts(tbf);
return;
}
if (tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_WAIT_ACK) {
LOGP(DRLCMAC, LOGL_DEBUG, "TBF: [DOWNLINK] UPLINK ASSIGNED %s\n", tbf_name(tbf));
/* reset N3105 */
tbf->n3105 = 0;
tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_NONE;
#warning "TBF is changing on the way... *sigh*"
if (tbf->direction == GPRS_RLCMAC_DL_TBF)
tbf = bts()->tbf_by_tlli(tbf->tlli(),
GPRS_RLCMAC_UL_TBF);
if (!tbf) {
LOGP(DRLCMAC, LOGL_ERROR, "Got ACK, but UL "
"TBF is gone TLLI=0x%08x\n", tlli);
return;
}
tbf_new_state(tbf, GPRS_RLCMAC_FLOW);
if ((tbf->state_flags &
(1 << GPRS_RLCMAC_FLAG_TO_UL_ASS))) {
tbf->state_flags &=
~(1 << GPRS_RLCMAC_FLAG_TO_UL_ASS);
LOGP(DRLCMAC, LOGL_NOTICE, "Recovered uplink "
"assignment for UL %s\n", tbf_name(tbf));
}
tbf_assign_control_ts(tbf);
return;
}
LOGP(DRLCMAC, LOGL_ERROR, "Error: received PACET CONTROL ACK "
"at no request\n");
}
void gprs_rlcmac_pdch::rcv_control_dl_ack_nack(Packet_Downlink_Ack_Nack_t *ack_nack, uint32_t fn)
{
int8_t tfi = 0; /* must be signed */
struct gprs_rlcmac_tbf *tbf;
int rc;
tfi = ack_nack->DOWNLINK_TFI;
tbf = bts()->tbf_by_poll_fn(fn, trx_no(), ts_no);
if (!tbf) {
LOGP(DRLCMAC, LOGL_NOTICE, "PACKET DOWNLINK ACK with "
"unknown FN=%u TFI=%d (TRX %d TS %d)\n",
fn, tfi, trx_no(), ts_no);
return;
}
if (tbf->tfi() != tfi) {
LOGP(DRLCMAC, LOGL_NOTICE, "PACKET DOWNLINK ACK with "
"wrong TFI=%d, ignoring!\n", tfi);
return;
}
tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_DL_ACK);
if ((tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_TO_DL_ACK))) {
tbf->state_flags &= ~(1 << GPRS_RLCMAC_FLAG_TO_DL_ACK);
LOGP(DRLCMAC, LOGL_NOTICE, "Recovered downlink ack "
"for %s\n", tbf_name(tbf));
}
/* reset N3105 */
tbf->n3105 = 0;
tbf->stop_t3191();
LOGP(DRLCMAC, LOGL_DEBUG, "RX: [PCU <- BTS] %s Packet Downlink Ack/Nack\n", tbf_name(tbf));
tbf->poll_state = GPRS_RLCMAC_POLL_NONE;
rc = tbf->snd_dl_ack(
ack_nack->Ack_Nack_Description.FINAL_ACK_INDICATION,
ack_nack->Ack_Nack_Description.STARTING_SEQUENCE_NUMBER,
ack_nack->Ack_Nack_Description.RECEIVED_BLOCK_BITMAP);
if (rc == 1) {
tbf_free(tbf);
return;
}
/* check for channel request */
if (ack_nack->Exist_Channel_Request_Description) {
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF in ack "
"message, so we provide one:\n");
tbf_alloc_ul(bts_data(), tbf->trx->trx_no, tbf->ms_class, tbf->tlli(), tbf->ta, tbf);
/* schedule uplink assignment */
tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_SEND_ASS;
}
}
void gprs_rlcmac_pdch::rcv_resource_request(Packet_Resource_Request_t *request, uint32_t fn)
{
struct gprs_rlcmac_tbf *tbf;
struct gprs_rlcmac_sba *sba;
int rc;
if (request->ID.UnionType) {
uint32_t tlli = request->ID.u.TLLI;
uint8_t ms_class = 0;
struct gprs_rlcmac_tbf *dl_tbf;
uint8_t ta;
tbf = bts()->tbf_by_tlli(tlli, GPRS_RLCMAC_UL_TBF);
if (tbf) {
LOGP(DRLCMACUL, LOGL_NOTICE, "Got RACH from "
"TLLI=0x%08x while %s still "
"exists. Killing pending DL TBF\n",
tlli, tbf_name(tbf));
tbf_free(tbf);
tbf = NULL;
}
if ((dl_tbf = bts()->tbf_by_tlli(tlli, GPRS_RLCMAC_DL_TBF))) {
LOGP(DRLCMACUL, LOGL_NOTICE, "Got RACH from "
"TLLI=0x%08x while %s still exists. "
"Killing pending DL TBF\n", tlli,
tbf_name(dl_tbf));
tbf_free(dl_tbf);
dl_tbf = NULL;
}
LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF "
"in packet resource request of single "
"block, so we provide one:\n");
sba = bts()->sba()->find(this, fn);
if (!sba) {
LOGP(DRLCMAC, LOGL_NOTICE, "MS requests UL TBF "
"in packet resource request of single "
"block, but there is no resource request "
"scheduled!\n");
rc = bts()->timing_advance()->recall(tlli);
if (rc >= 0)
ta = rc;
else
ta = 0;
} else {
ta = sba->ta;
bts()->timing_advance()->remember(tlli, ta);
bts()->sba()->free_sba(sba);
}
if (request->Exist_MS_Radio_Access_capability)
ms_class = Decoding::get_ms_class_by_capability(&request->MS_Radio_Access_capability);
if (!ms_class)
LOGP(DRLCMAC, LOGL_NOTICE, "MS does not give us a class.\n");
tbf = tbf_alloc_ul(bts_data(), trx_no(), ms_class, tlli, ta, NULL);
if (!tbf)
return;
/* set control ts to current MS's TS, until assignment complete */
LOGP(DRLCMAC, LOGL_DEBUG, "Change control TS to %d until assinment is complete.\n", ts_no);
tbf->control_ts = ts_no;
/* schedule uplink assignment */
tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_SEND_ASS;
return;
}
if (request->ID.u.Global_TFI.UnionType) {
int8_t tfi = request->ID.u.Global_TFI.u.DOWNLINK_TFI;
tbf = bts()->tbf_by_tfi(tfi, trx_no(), GPRS_RLCMAC_DL_TBF);
if (!tbf) {
LOGP(DRLCMAC, LOGL_NOTICE, "PACKET RESSOURCE REQ unknown downlink TFI=%d\n", tfi);
return;
}
} else {
int8_t tfi = request->ID.u.Global_TFI.u.UPLINK_TFI;
tbf = bts()->tbf_by_tfi(tfi, trx_no(), GPRS_RLCMAC_UL_TBF);
if (!tbf) {
LOGP(DRLCMAC, LOGL_NOTICE, "PACKET RESSOURCE REQ unknown uplink TFI=%d\n", tfi);
return;
}
}
LOGP(DRLCMAC, LOGL_ERROR, "RX: [PCU <- BTS] %s FIXME: Packet resource request\n", tbf_name(tbf));
}
void gprs_rlcmac_pdch::rcv_measurement_report(Packet_Measurement_Report_t *report, uint32_t fn)
{
struct gprs_rlcmac_sba *sba;
sba = bts()->sba()->find(this, fn);
if (!sba) {
LOGP(DRLCMAC, LOGL_NOTICE, "MS send measurement "
"in packet resource request of single "
"block, but there is no resource request "
"scheduled! TLLI=0x%08x\n", report->TLLI);
} else {
bts()->timing_advance()->remember(report->TLLI, sba->ta);
bts()->sba()->free_sba(sba);
}
gprs_rlcmac_meas_rep(report);
}
/* Received Uplink RLC control block. */
int gprs_rlcmac_pdch::rcv_control_block(
bitvec *rlc_block, uint32_t fn)
{
RlcMacUplink_t * ul_control_block = (RlcMacUplink_t *)talloc_zero(tall_pcu_ctx, RlcMacUplink_t);
LOGP(DRLCMAC, LOGL_DEBUG, "+++++++++++++++++++++++++ RX : Uplink Control Block +++++++++++++++++++++++++\n");
decode_gsm_rlcmac_uplink(rlc_block, ul_control_block);
LOGPC(DCSN1, LOGL_NOTICE, "\n");
LOGP(DRLCMAC, LOGL_DEBUG, "------------------------- RX : Uplink Control Block -------------------------\n");
switch (ul_control_block->u.MESSAGE_TYPE) {
case MT_PACKET_CONTROL_ACK:
rcv_control_ack(&ul_control_block->u.Packet_Control_Acknowledgement, fn);
break;
case MT_PACKET_DOWNLINK_ACK_NACK:
rcv_control_dl_ack_nack(&ul_control_block->u.Packet_Downlink_Ack_Nack, fn);
break;
case MT_PACKET_RESOURCE_REQUEST:
rcv_resource_request(&ul_control_block->u.Packet_Resource_Request, fn);
break;
case MT_PACKET_MEASUREMENT_REPORT:
rcv_measurement_report(&ul_control_block->u.Packet_Measurement_Report, fn);
break;
case MT_PACKET_UPLINK_DUMMY_CONTROL_BLOCK:
/* ignoring it. change the SI to not force sending these? */
break;
default:
bts()->decode_error();
LOGP(DRLCMAC, LOGL_NOTICE,
"RX: [PCU <- BTS] unknown control block(%d) received\n",
ul_control_block->u.MESSAGE_TYPE);
}
talloc_free(ul_control_block);
return 1;
}
/* received RLC/MAC block from L1 */
int gprs_rlcmac_pdch::rcv_block(uint8_t *data, uint8_t len, uint32_t fn, int8_t rssi)
{
unsigned payload = data[0] >> 6;
bitvec *block;
int rc = 0;
switch (payload) {
case GPRS_RLCMAC_DATA_BLOCK:
rc = rcv_data_block_acknowledged(data, len, rssi);
break;
case GPRS_RLCMAC_CONTROL_BLOCK:
block = bitvec_alloc(len);
if (!block)
return -ENOMEM;
bitvec_unpack(block, data);
rc = rcv_control_block(block, fn);
bitvec_free(block);
break;
case GPRS_RLCMAC_CONTROL_BLOCK_OPT:
LOGP(DRLCMAC, LOGL_NOTICE, "GPRS_RLCMAC_CONTROL_BLOCK_OPT block payload is not supported.\n");
break;
default:
LOGP(DRLCMAC, LOGL_NOTICE, "Unknown RLCMAC block payload(%u).\n", payload);
rc = -EINVAL;
}
return rc;
}

321
src/bts.h Normal file
View File

@ -0,0 +1,321 @@
/* bts.h
*
* Copyright (C) 2012 Ivan Klyuchnikov
* Copyright (C) 2013 by Holger Hans Peter Freyther
*
* 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
#ifdef __cplusplus
extern "C" {
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/rate_ctr.h>
#include <osmocom/core/timer.h>
}
#include "poll_controller.h"
#include "sba.h"
#include "ta.h"
#include "tbf.h"
#endif
#include <stdint.h>
struct BTS;
/*
* PDCH instance
*/
struct gprs_rlcmac_pdch {
#ifdef __cplusplus
struct gprs_rlcmac_paging *dequeue_paging();
struct msgb *packet_paging_request();
void add_paging(struct gprs_rlcmac_paging *pag);
void free_resources();
bool is_enabled() const;
void enable();
void disable();
/* dispatching of messages */
int rcv_block(uint8_t *data, uint8_t len, uint32_t fn, int8_t rssi);
gprs_rlcmac_bts *bts_data() const;
BTS *bts() const;
uint8_t trx_no() const;
#endif
uint8_t m_is_enabled; /* TS is enabled */
uint8_t tsc; /* TSC of this slot */
uint8_t next_ul_tfi; /* next uplink TBF/TFI to schedule (0..31) */
uint8_t next_dl_tfi; /* next downlink TBF/TFI to schedule (0..31) */
uint8_t next_ctrl_prio; /* next kind of ctrl message to schedule */
struct gprs_rlcmac_tbf *ul_tbf[32]; /* array of UL TBF, by UL TFI */
struct gprs_rlcmac_tbf *dl_tbf[32]; /* array of DL TBF, by DL TFI */
struct llist_head paging_list; /* list of paging messages */
uint32_t last_rts_fn; /* store last frame number of RTS */
/* back pointers */
struct gprs_rlcmac_trx *trx;
uint8_t ts_no;
#ifdef __cplusplus
private:
int rcv_data_block_acknowledged(uint8_t *data, uint8_t len, int8_t rssi);
int rcv_control_block(bitvec *rlc_block, uint32_t fn);
void rcv_control_ack(Packet_Control_Acknowledgement_t *, uint32_t fn);
void rcv_control_dl_ack_nack(Packet_Downlink_Ack_Nack_t *, uint32_t fn);
void rcv_resource_request(Packet_Resource_Request_t *t, uint32_t fn);
void rcv_measurement_report(Packet_Measurement_Report_t *t, uint32_t fn);
#endif
};
struct gprs_rlcmac_trx {
void *fl1h;
uint16_t arfcn;
struct gprs_rlcmac_pdch pdch[8];
struct gprs_rlcmac_tbf *ul_tbf[32]; /* array of UL TBF, by UL TFI */
struct gprs_rlcmac_tbf *dl_tbf[32]; /* array of DL TBF, by DL TFI */
/* back pointers */
struct BTS *bts;
uint8_t trx_no;
};
/**
* This is the data from C. As soon as our minimal compiler is gcc 4.7
* we can start to compile pcu_vty.c with c++ and remove the split.
*/
struct gprs_rlcmac_bts {
uint8_t bsic;
uint8_t fc_interval;
uint8_t cs1;
uint8_t cs2;
uint8_t cs3;
uint8_t cs4;
uint8_t initial_cs_dl, initial_cs_ul;
uint8_t force_cs; /* 0=use from BTS 1=use from VTY */
uint16_t force_llc_lifetime; /* overrides lifetime from SGSN */
uint8_t t3142;
uint8_t t3169;
uint8_t t3191;
uint16_t t3193_msec;
uint8_t t3195;
uint8_t n3101;
uint8_t n3103;
uint8_t n3105;
struct gprs_rlcmac_trx trx[8];
int (*alloc_algorithm)(struct gprs_rlcmac_bts *bts,
struct gprs_rlcmac_tbf *old_tbf,
struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single);
uint32_t alloc_algorithm_curst; /* options to customize algorithm */
uint8_t force_two_phase;
uint8_t alpha, gamma;
/* TBF handling, make private or move into TBFController */
/* list of uplink TBFs */
struct llist_head ul_tbfs;
/* list of downlink TBFs */
struct llist_head dl_tbfs;
/**
* Point back to the C++ object. This is used during the transition
* period.
*/
struct BTS *bts;
};
#ifdef __cplusplus
/**
* I represent a GSM BTS. I have one or more TRX, I know the current
* GSM time and I have controllers that help with allocating resources
* on my TRXs.
*/
struct BTS {
public:
enum {
CTR_TBF_DL_ALLOCATED,
CTR_TBF_DL_FREED,
CTR_TBF_UL_ALLOCATED,
CTR_TBF_UL_FREED,
CTR_TBF_REUSED,
CTR_RLC_SENT,
CTR_RLC_RESENT,
CTR_RLC_RESTARTED,
CTR_RLC_STALLED,
CTR_RLC_NACKED,
CTR_DECODE_ERRORS,
CTR_SBA_ALLOCATED,
CTR_SBA_FREED,
CTR_SBA_TIMEDOUT,
CTR_LLC_FRAME_TIMEDOUT,
CTR_LLC_FRAME_DROPPED,
CTR_LLC_FRAME_SCHED,
CTR_RACH_REQUESTS,
};
BTS();
~BTS();
static BTS* main_bts();
struct gprs_rlcmac_bts *bts_data();
SBAController *sba();
TimingAdvance *timing_advance();
/** TODO: change the number to unsigned */
void set_current_frame_number(int frame_number);
int current_frame_number() const;
/** add paging to paging queue(s) */
int add_paging(uint8_t chan_needed, uint8_t *identity_lv);
gprs_rlcmac_tbf *tbf_by_tlli(uint32_t tlli, enum gprs_rlcmac_tbf_direction dir);
gprs_rlcmac_tbf *tbf_by_poll_fn(uint32_t fn, uint8_t trx, uint8_t ts);
gprs_rlcmac_tbf *tbf_by_tfi(uint8_t tfi, uint8_t trx, enum gprs_rlcmac_tbf_direction dir);
int tfi_find_free(enum gprs_rlcmac_tbf_direction dir, uint8_t *_trx, int8_t use_trx);
int rcv_imm_ass_cnf(const uint8_t *data, uint32_t fn);
int rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta);
void trigger_dl_ass(gprs_rlcmac_tbf *tbf, gprs_rlcmac_tbf *old_tbf, const char *imsi);
void snd_dl_ass(gprs_rlcmac_tbf *tbf, uint8_t poll, const char *imsi);
/*
* Statistics
*/
void tbf_dl_created();
void tbf_dl_freed();
void tbf_ul_created();
void tbf_ul_freed();
void tbf_reused();
void rlc_sent();
void rlc_resent();
void rlc_restarted();
void rlc_stalled();
void rlc_nacked();
void decode_error();
void sba_allocated();
void sba_freed();
void sba_timedout();
void llc_timedout_frame();
void llc_dropped_frame();
void llc_frame_sched();
void rach_frame();
/*
* Below for C interface for the VTY
*/
struct rate_ctr_group *rate_counters() const;
private:
int m_cur_fn;
struct gprs_rlcmac_bts m_bts;
PollController m_pollController;
SBAController m_sba;
TimingAdvance m_ta;
struct rate_ctr_group *m_ratectrs;
private:
/* disable copying to avoid slicing */
BTS(const BTS&);
BTS& operator=(const BTS&);
};
inline int BTS::current_frame_number() const
{
return m_cur_fn;
}
inline TimingAdvance *BTS::timing_advance()
{
return &m_ta;
}
inline SBAController *BTS::sba()
{
return &m_sba;
}
inline BTS *gprs_rlcmac_pdch::bts() const
{
return trx->bts;
}
inline struct rate_ctr_group *BTS::rate_counters() const
{
return m_ratectrs;
}
#define CREATE_COUNT_INLINE(func_name, ctr_name) \
inline void BTS::func_name() {\
rate_ctr_inc(&m_ratectrs->ctr[ctr_name]); \
}
CREATE_COUNT_INLINE(tbf_dl_created, CTR_TBF_DL_ALLOCATED)
CREATE_COUNT_INLINE(tbf_dl_freed, CTR_TBF_DL_FREED)
CREATE_COUNT_INLINE(tbf_ul_created, CTR_TBF_UL_ALLOCATED)
CREATE_COUNT_INLINE(tbf_ul_freed, CTR_TBF_UL_FREED)
CREATE_COUNT_INLINE(tbf_reused, CTR_TBF_REUSED)
CREATE_COUNT_INLINE(rlc_sent, CTR_RLC_SENT)
CREATE_COUNT_INLINE(rlc_resent, CTR_RLC_RESENT)
CREATE_COUNT_INLINE(rlc_restarted, CTR_RLC_RESTARTED)
CREATE_COUNT_INLINE(rlc_stalled, CTR_RLC_STALLED)
CREATE_COUNT_INLINE(rlc_nacked, CTR_RLC_NACKED)
CREATE_COUNT_INLINE(decode_error, CTR_DECODE_ERRORS)
CREATE_COUNT_INLINE(sba_allocated, CTR_SBA_ALLOCATED)
CREATE_COUNT_INLINE(sba_freed, CTR_SBA_FREED)
CREATE_COUNT_INLINE(sba_timedout, CTR_SBA_TIMEDOUT)
CREATE_COUNT_INLINE(llc_timedout_frame, CTR_LLC_FRAME_TIMEDOUT);
CREATE_COUNT_INLINE(llc_dropped_frame, CTR_LLC_FRAME_DROPPED);
CREATE_COUNT_INLINE(llc_frame_sched, CTR_LLC_FRAME_SCHED);
CREATE_COUNT_INLINE(rach_frame, CTR_RACH_REQUESTS);
#undef CREATE_COUNT_INLINE
inline gprs_rlcmac_bts *gprs_rlcmac_pdch::bts_data() const
{
return trx->bts->bts_data();
}
inline uint8_t gprs_rlcmac_pdch::trx_no() const
{
return trx->trx_no;
}
#endif
#ifdef __cplusplus
extern "C" {
#endif
struct gprs_rlcmac_bts *bts_main_data();
struct rate_ctr_group *bts_main_data_stats();
#ifdef __cplusplus
}
inline bool gprs_rlcmac_pdch::is_enabled() const
{
return m_is_enabled;
}
#endif

101
src/decoding.cpp Normal file
View File

@ -0,0 +1,101 @@
/* 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;
}
uint8_t Decoding::get_ms_class_by_capability(MS_Radio_Access_capability_t *cap)
{
int i;
for (i = 0; i < cap->Count_MS_RA_capability_value; i++) {
if (!cap->MS_RA_capability_value[i].u.Content.Exist_Multislot_capability)
continue;
if (!cap->MS_RA_capability_value[i].u.Content.Multislot_capability.Exist_GPRS_multislot_class)
continue;
return cap->MS_RA_capability_value[i].u.Content.Multislot_capability.GPRS_multislot_class;
}
return 0;
}
/**
* show_rbb needs to be an array with 65 elements
* The index of the array is the bit position in the rbb
* (show_rbb[63] relates to BSN ssn-1)
*/
void Decoding::extract_rbb(const uint8_t *rbb, char *show_rbb)
{
for (int i = 0; i < 64; i++) {
uint8_t bit;
bit = !!(rbb[i/8] & (1<<(7-i%8)));
show_rbb[i] = bit ? 'R' : 'I';
}
show_rbb[64] = '\0';
}

33
src/decoding.h Normal file
View File

@ -0,0 +1,33 @@
/* 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 <gsm_rlcmac.h>
#include <stdint.h>
class Decoding {
public:
static int tlli_from_ul_data(const uint8_t *data, uint8_t len,
uint32_t *tlli);
static uint8_t get_ms_class_by_capability(MS_Radio_Access_capability_t *cap);
static void extract_rbb(const uint8_t *rbb, char *extracted_rbb);
};

463
src/encoding.cpp Normal file
View File

@ -0,0 +1,463 @@
/* encoding.cpp
*
* Copyright (C) 2012 Ivan Klyuchnikov
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
* Copyright (C) 2013 by Holger Hans Peter Freyther
*
* 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 <encoding.h>
#include <gprs_rlcmac.h>
#include <bts.h>
#include <tbf.h>
#include <gprs_debug.h>
// GSM 04.08 9.1.18 Immediate assignment
int Encoding::write_immediate_assignment(
struct gprs_rlcmac_bts *bts,
bitvec * dest, uint8_t downlink, uint8_t ra,
uint32_t ref_fn, uint8_t ta, uint16_t arfcn, uint8_t ts, uint8_t tsc,
uint8_t tfi, uint8_t usf, uint32_t tlli,
uint8_t polling, uint32_t fn, uint8_t single_block, uint8_t alpha,
uint8_t gamma, int8_t ta_idx)
{
unsigned wp = 0;
uint8_t plen;
bitvec_write_field(dest, wp,0x0,4); // Skip Indicator
bitvec_write_field(dest, wp,0x6,4); // Protocol Discriminator
bitvec_write_field(dest, wp,0x3F,8); // Immediate Assignment Message Type
// 10.5.2.25b Dedicated mode or TBF
bitvec_write_field(dest, wp,0x0,1); // spare
bitvec_write_field(dest, wp,0x0,1); // TMA : Two-message assignment: No meaning
bitvec_write_field(dest, wp,downlink,1); // Downlink : Downlink assignment to mobile in packet idle mode
bitvec_write_field(dest, wp,0x1,1); // T/D : TBF or dedicated mode: this message assigns a Temporary Block Flow (TBF).
bitvec_write_field(dest, wp,0x0,4); // Page Mode
// GSM 04.08 10.5.2.25a Packet Channel Description
bitvec_write_field(dest, wp,0x1,5); // Channel type
bitvec_write_field(dest, wp,ts,3); // TN
bitvec_write_field(dest, wp,tsc,3); // TSC
bitvec_write_field(dest, wp,0x0,3); // non-hopping RF channel configuraion
bitvec_write_field(dest, wp,arfcn,10); // ARFCN
//10.5.2.30 Request Reference
bitvec_write_field(dest, wp,ra,8); // RA
bitvec_write_field(dest, wp,(ref_fn / (26 * 51)) % 32,5); // T1'
bitvec_write_field(dest, wp,ref_fn % 51,6); // T3
bitvec_write_field(dest, wp,ref_fn % 26,5); // T2
// 10.5.2.40 Timing Advance
bitvec_write_field(dest, wp,0x0,2); // spare
bitvec_write_field(dest, wp,ta,6); // Timing Advance value
// No mobile allocation in non-hopping systems.
// A zero-length LV. Just write L=0.
bitvec_write_field(dest, wp,0,8);
if ((wp % 8)) {
LOGP(DRLCMACUL, LOGL_ERROR, "Length of IMM.ASS without rest "
"octets is not multiple of 8 bits, PLEASE FIX!\n");
exit (0);
}
plen = wp / 8;
if (downlink)
{
// GSM 04.08 10.5.2.16 IA Rest Octets
bitvec_write_field(dest, wp, 3, 2); // "HH"
bitvec_write_field(dest, wp, 1, 2); // "01" Packet Downlink Assignment
bitvec_write_field(dest, wp,tlli,32); // TLLI
bitvec_write_field(dest, wp,0x1,1); // switch TFI : on
bitvec_write_field(dest, wp,tfi,5); // TFI
bitvec_write_field(dest, wp,0x0,1); // RLC acknowledged mode
if (alpha) {
bitvec_write_field(dest, wp,0x1,1); // ALPHA = present
bitvec_write_field(dest, wp,alpha,4); // ALPHA
} else {
bitvec_write_field(dest, wp,0x0,1); // ALPHA = not present
}
bitvec_write_field(dest, wp,gamma,5); // GAMMA power control parameter
bitvec_write_field(dest, wp,polling,1); // Polling Bit
bitvec_write_field(dest, wp,!polling,1); // TA_VALID ???
if (ta_idx < 0) {
bitvec_write_field(dest, wp,0x0,1); // switch TIMING_ADVANCE_INDEX = off
} else {
bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_INDEX = on
bitvec_write_field(dest, wp,ta_idx,4); // TIMING_ADVANCE_INDEX
}
if (polling) {
bitvec_write_field(dest, wp,0x1,1); // TBF Starting TIME present
bitvec_write_field(dest, wp,(fn / (26 * 51)) % 32,5); // T1'
bitvec_write_field(dest, wp,fn % 51,6); // T3
bitvec_write_field(dest, wp,fn % 26,5); // T2
} else {
bitvec_write_field(dest, wp,0x0,1); // TBF Starting TIME present
}
bitvec_write_field(dest, wp,0x0,1); // P0 not present
// bitvec_write_field(dest, wp,0x1,1); // P0 not present
// bitvec_write_field(dest, wp,0xb,4);
}
else
{
// GMS 04.08 10.5.2.37b 10.5.2.16
bitvec_write_field(dest, wp, 3, 2); // "HH"
bitvec_write_field(dest, wp, 0, 2); // "0" Packet Uplink Assignment
if (single_block) {
bitvec_write_field(dest, wp, 0, 1); // Block Allocation : Single Block Allocation
if (alpha) {
bitvec_write_field(dest, wp,0x1,1); // ALPHA = present
bitvec_write_field(dest, wp,alpha,4); // ALPHA = present
} else
bitvec_write_field(dest, wp,0x0,1); // ALPHA = not present
bitvec_write_field(dest, wp,gamma,5); // GAMMA power control parameter
if (ta_idx < 0) {
bitvec_write_field(dest, wp,0x0,1); // switch TIMING_ADVANCE_INDEX = off
} else {
bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_INDEX = on
bitvec_write_field(dest, wp,ta_idx,4); // TIMING_ADVANCE_INDEX
}
bitvec_write_field(dest, wp, 1, 1); // TBF_STARTING_TIME_FLAG
bitvec_write_field(dest, wp,(fn / (26 * 51)) % 32,5); // T1'
bitvec_write_field(dest, wp,fn % 51,6); // T3
bitvec_write_field(dest, wp,fn % 26,5); // T2
} else {
bitvec_write_field(dest, wp, 1, 1); // Block Allocation : Not Single Block Allocation
bitvec_write_field(dest, wp, tfi, 5); // TFI_ASSIGNMENT Temporary Flow Identity
bitvec_write_field(dest, wp, 0, 1); // POLLING
bitvec_write_field(dest, wp, 0, 1); // ALLOCATION_TYPE: dynamic
bitvec_write_field(dest, wp, usf, 3); // USF
bitvec_write_field(dest, wp, 0, 1); // USF_GRANULARITY
bitvec_write_field(dest, wp, 0, 1); // "0" power control: Not Present
bitvec_write_field(dest, wp, bts->initial_cs_ul-1, 2); // CHANNEL_CODING_COMMAND
bitvec_write_field(dest, wp, 1, 1); // TLLI_BLOCK_CHANNEL_CODING
if (alpha) {
bitvec_write_field(dest, wp,0x1,1); // ALPHA = present
bitvec_write_field(dest, wp,alpha,4); // ALPHA
} else
bitvec_write_field(dest, wp,0x0,1); // ALPHA = not present
bitvec_write_field(dest, wp,gamma,5); // GAMMA power control parameter
/* note: there is no choise for TAI and no starting time */
bitvec_write_field(dest, wp, 0, 1); // switch TIMING_ADVANCE_INDEX = off
bitvec_write_field(dest, wp, 0, 1); // TBF_STARTING_TIME_FLAG
}
}
return plen;
}
/* generate uplink assignment */
void Encoding::write_packet_uplink_assignment(
struct gprs_rlcmac_bts *bts,
bitvec * dest, uint8_t old_tfi,
uint8_t old_downlink, uint32_t tlli, uint8_t use_tlli,
struct gprs_rlcmac_tbf *tbf, uint8_t poll, uint8_t alpha,
uint8_t gamma, int8_t ta_idx)
{
// TODO We should use our implementation of encode RLC/MAC Control messages.
unsigned wp = 0;
uint8_t ts;
bitvec_write_field(dest, wp,0x1,2); // Payload Type
bitvec_write_field(dest, wp,0x0,2); // Uplink block with TDMA framenumber (N+13)
bitvec_write_field(dest, wp,poll,1); // Suppl/Polling Bit
bitvec_write_field(dest, wp,0x0,3); // Uplink state flag
bitvec_write_field(dest, wp,0xa,6); // MESSAGE TYPE
bitvec_write_field(dest, wp,0x0,2); // Page Mode
bitvec_write_field(dest, wp,0x0,1); // switch PERSIST_LEVEL: off
if (use_tlli) {
bitvec_write_field(dest, wp,0x2,2); // switch TLLI : on
bitvec_write_field(dest, wp,tlli,32); // TLLI
} else {
bitvec_write_field(dest, wp,0x0,1); // switch TFI : on
bitvec_write_field(dest, wp,old_downlink,1); // 0=UPLINK TFI, 1=DL TFI
bitvec_write_field(dest, wp,old_tfi,5); // TFI
}
bitvec_write_field(dest, wp,0x0,1); // Message escape
bitvec_write_field(dest, wp,bts->initial_cs_ul-1, 2); // CHANNEL_CODING_COMMAND
bitvec_write_field(dest, wp,0x1,1); // TLLI_BLOCK_CHANNEL_CODING
bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_VALUE = on
bitvec_write_field(dest, wp,tbf->ta,6); // TIMING_ADVANCE_VALUE
if (ta_idx < 0) {
bitvec_write_field(dest, wp,0x0,1); // switch TIMING_ADVANCE_INDEX = off
} else {
bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_INDEX = on
bitvec_write_field(dest, wp,ta_idx,4); // TIMING_ADVANCE_INDEX
}
#if 1
bitvec_write_field(dest, wp,0x1,1); // Frequency Parameters information elements = present
bitvec_write_field(dest, wp,tbf->tsc(),3); // Training Sequence Code (TSC)
bitvec_write_field(dest, wp,0x0,2); // ARFCN = present
bitvec_write_field(dest, wp,tbf->trx->arfcn,10); // ARFCN
#else
bitvec_write_field(dest, wp,0x0,1); // Frequency Parameters = off
#endif
bitvec_write_field(dest, wp,0x1,2); // Dynamic Allocation
bitvec_write_field(dest, wp,0x0,1); // Extended Dynamic Allocation = off
bitvec_write_field(dest, wp,0x0,1); // P0 = off
bitvec_write_field(dest, wp,0x0,1); // USF_GRANULARITY
bitvec_write_field(dest, wp,0x1,1); // switch TFI : on
bitvec_write_field(dest, wp,tbf->tfi(),5);// TFI
bitvec_write_field(dest, wp,0x0,1); //
bitvec_write_field(dest, wp,0x0,1); // TBF Starting Time = off
if (alpha || gamma) {
bitvec_write_field(dest, wp,0x1,1); // Timeslot Allocation with Power Control
bitvec_write_field(dest, wp,alpha,4); // ALPHA
} else
bitvec_write_field(dest, wp,0x0,1); // Timeslot Allocation
for (ts = 0; ts < 8; ts++) {
if (tbf->pdch[ts]) {
bitvec_write_field(dest, wp,0x1,1); // USF_TN(i): on
bitvec_write_field(dest, wp,tbf->dir.ul.usf[ts],3); // USF_TN(i)
if (alpha || gamma)
bitvec_write_field(dest, wp,gamma,5); // GAMMA power control parameter
} else
bitvec_write_field(dest, wp,0x0,1); // USF_TN(i): off
}
// bitvec_write_field(dest, wp,0x0,1); // Measurement Mapping struct not present
}
/* generate downlink assignment */
void Encoding::write_packet_downlink_assignment(RlcMacDownlink_t * block, uint8_t old_tfi,
uint8_t old_downlink, struct gprs_rlcmac_tbf *tbf, uint8_t poll,
uint8_t alpha, uint8_t gamma, int8_t ta_idx, uint8_t ta_ts)
{
// Packet downlink assignment TS 44.060 11.2.7
uint8_t tn;
block->PAYLOAD_TYPE = 0x1; // RLC/MAC control block that does not include the optional octets of the RLC/MAC control header
block->RRBP = 0x0; // N+13
block->SP = poll; // RRBP field is valid
block->USF = 0x0; // Uplink state flag
block->u.Packet_Downlink_Assignment.MESSAGE_TYPE = 0x2; // Packet Downlink Assignment
block->u.Packet_Downlink_Assignment.PAGE_MODE = 0x0; // Normal Paging
block->u.Packet_Downlink_Assignment.Exist_PERSISTENCE_LEVEL = 0x0; // PERSISTENCE_LEVEL: off
block->u.Packet_Downlink_Assignment.ID.UnionType = 0x0; // TFI = on
block->u.Packet_Downlink_Assignment.ID.u.Global_TFI.UnionType = old_downlink; // 0=UPLINK TFI, 1=DL TFI
block->u.Packet_Downlink_Assignment.ID.u.Global_TFI.u.UPLINK_TFI = old_tfi; // TFI
block->u.Packet_Downlink_Assignment.MAC_MODE = 0x0; // Dynamic Allocation
block->u.Packet_Downlink_Assignment.RLC_MODE = 0x0; // RLC acknowledged mode
block->u.Packet_Downlink_Assignment.CONTROL_ACK = old_downlink; // NW establishes no new DL TBF for the MS with running timer T3192
block->u.Packet_Downlink_Assignment.TIMESLOT_ALLOCATION = 0; // timeslot(s)
for (tn = 0; tn < 8; tn++) {
if (tbf->pdch[tn])
block->u.Packet_Downlink_Assignment.TIMESLOT_ALLOCATION |= 0x80 >> tn; // timeslot(s)
}
block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.Exist_TIMING_ADVANCE_VALUE = 0x1; // TIMING_ADVANCE_VALUE = on
block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.TIMING_ADVANCE_VALUE = tbf->ta; // TIMING_ADVANCE_VALUE
if (ta_idx < 0) {
block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.Exist_IndexAndtimeSlot = 0x0; // TIMING_ADVANCE_INDEX = off
} else {
block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.Exist_IndexAndtimeSlot = 0x1; // TIMING_ADVANCE_INDEX = on
block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.TIMING_ADVANCE_INDEX = ta_idx; // TIMING_ADVANCE_INDEX
block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.TIMING_ADVANCE_TIMESLOT_NUMBER = ta_ts; // TIMING_ADVANCE_TS
}
block->u.Packet_Downlink_Assignment.Exist_P0_and_BTS_PWR_CTRL_MODE = 0x0; // POWER CONTROL = off
block->u.Packet_Downlink_Assignment.Exist_Frequency_Parameters = 0x1; // Frequency Parameters = on
block->u.Packet_Downlink_Assignment.Frequency_Parameters.TSC = tbf->tsc(); // Training Sequence Code (TSC)
block->u.Packet_Downlink_Assignment.Frequency_Parameters.UnionType = 0x0; // ARFCN = on
block->u.Packet_Downlink_Assignment.Frequency_Parameters.u.ARFCN = tbf->trx->arfcn; // ARFCN
block->u.Packet_Downlink_Assignment.Exist_DOWNLINK_TFI_ASSIGNMENT = 0x1; // DOWNLINK TFI ASSIGNMENT = on
block->u.Packet_Downlink_Assignment.DOWNLINK_TFI_ASSIGNMENT = tbf->tfi(); // TFI
block->u.Packet_Downlink_Assignment.Exist_Power_Control_Parameters = 0x1; // Power Control Parameters = on
block->u.Packet_Downlink_Assignment.Power_Control_Parameters.ALPHA = alpha; // ALPHA
for (tn = 0; tn < 8; tn++)
{
if (tbf->pdch[tn])
{
block->u.Packet_Downlink_Assignment.Power_Control_Parameters.Slot[tn].Exist = 0x1; // Slot[i] = on
block->u.Packet_Downlink_Assignment.Power_Control_Parameters.Slot[tn].GAMMA_TN = gamma; // GAMMA_TN
}
else
{
block->u.Packet_Downlink_Assignment.Power_Control_Parameters.Slot[tn].Exist = 0x0; // Slot[i] = off
}
}
block->u.Packet_Downlink_Assignment.Exist_TBF_Starting_Time = 0x0; // TBF Starting TIME = off
block->u.Packet_Downlink_Assignment.Exist_Measurement_Mapping = 0x0; // Measurement_Mapping = off
block->u.Packet_Downlink_Assignment.Exist_AdditionsR99 = 0x0; // AdditionsR99 = off
}
/* generate paging request */
int Encoding::write_paging_request(bitvec * dest, uint8_t *ptmsi, uint16_t ptmsi_len)
{
unsigned wp = 0;
int plen;
bitvec_write_field(dest, wp,0x0,4); // Skip Indicator
bitvec_write_field(dest, wp,0x6,4); // Protocol Discriminator
bitvec_write_field(dest, wp,0x21,8); // Paging Request Message Type
bitvec_write_field(dest, wp,0x0,4); // Page Mode
bitvec_write_field(dest, wp,0x0,4); // Channel Needed
// Mobile Identity
bitvec_write_field(dest, wp,ptmsi_len+1,8); // Mobile Identity length
bitvec_write_field(dest, wp,0xf,4); // unused
bitvec_write_field(dest, wp,0x4,4); // PTMSI type
for (int i = 0; i < ptmsi_len; i++)
{
bitvec_write_field(dest, wp,ptmsi[i],8); // PTMSI
}
if ((wp % 8)) {
LOGP(DRLCMACUL, LOGL_ERROR, "Length of PAG.REQ without rest "
"octets is not multiple of 8 bits, PLEASE FIX!\n");
exit (0);
}
plen = wp / 8;
bitvec_write_field(dest, wp,0x0,1); // "L" NLN(PCH) = off
bitvec_write_field(dest, wp,0x0,1); // "L" Priority1 = off
bitvec_write_field(dest, wp,0x1,1); // "L" Priority2 = off
bitvec_write_field(dest, wp,0x0,1); // "L" Group Call information = off
bitvec_write_field(dest, wp,0x0,1); // "H" Packet Page Indication 1 = packet paging procedure
bitvec_write_field(dest, wp,0x1,1); // "H" Packet Page Indication 2 = packet paging procedure
return plen;
}
/**
* The index of the array show_rbb is the bit position inside the rbb
* (show_rbb[63] relates to BSN ssn-1)
*/
void Encoding::encode_rbb(const char *show_rbb, uint8_t *rbb)
{
uint8_t rbb_byte = 0;
// RECEIVE_BLOCK_BITMAP
for (int i = 0; i < 64; i++) {
/* Set bit at the appropriate position (see 3GPP TS 04.60 9.1.8.1) */
if (show_rbb[i] == 'R')
rbb_byte |= 1<< (7-(i%8));
if((i%8) == 7) {
rbb[i/8] = rbb_byte;
rbb_byte = 0;
}
}
}
/* generate uplink ack */
void Encoding::write_packet_uplink_ack(struct gprs_rlcmac_bts *bts,
RlcMacDownlink_t * block, struct gprs_rlcmac_tbf *tbf,
uint8_t final)
{
// Packet Uplink Ack/Nack TS 44.060 11.2.28
char rbb[65];
tbf->dir.ul.window.update_rbb(rbb);
LOGP(DRLCMACUL, LOGL_DEBUG, "Encoding Ack/Nack for %s "
"(final=%d)\n", tbf_name(tbf), final);
block->PAYLOAD_TYPE = 0x1; // RLC/MAC control block that does not include the optional octets of the RLC/MAC control header
block->RRBP = 0x0; // N+13
block->SP = final; // RRBP field is valid, if it is final ack
block->USF = 0x0; // Uplink state flag
block->u.Packet_Uplink_Ack_Nack.MESSAGE_TYPE = 0x9; // Packet Downlink Assignment
block->u.Packet_Uplink_Ack_Nack.PAGE_MODE = 0x0; // Normal Paging
block->u.Packet_Uplink_Ack_Nack.UPLINK_TFI = tbf->tfi(); // Uplink TFI
block->u.Packet_Uplink_Ack_Nack.UnionType = 0x0; // PU_AckNack_GPRS = on
block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.CHANNEL_CODING_COMMAND = bts->initial_cs_ul - 1; // CS1
block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Ack_Nack_Description.FINAL_ACK_INDICATION = final; // FINAL ACK INDICATION
block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Ack_Nack_Description.STARTING_SEQUENCE_NUMBER = tbf->dir.ul.window.ssn(); // STARTING_SEQUENCE_NUMBER
encode_rbb(rbb, block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Ack_Nack_Description.RECEIVED_BLOCK_BITMAP);
/* rbb is not NULL terminated */
rbb[64] = 0;
LOGP(DRLCMACUL, LOGL_DEBUG, "- V(N): \"%s\" R=Received "
"I=Invalid\n", rbb);
block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.UnionType = 0x0; // Fixed Allocation Dummy = on
block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.u.FixedAllocationDummy = 0x0; // Fixed Allocation Dummy
block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Exist_AdditionsR99 = 0x0; // AdditionsR99 = off
block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Common_Uplink_Ack_Nack_Data.Exist_CONTENTION_RESOLUTION_TLLI = 0x1;
block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Common_Uplink_Ack_Nack_Data.CONTENTION_RESOLUTION_TLLI = tbf->tlli();
block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Common_Uplink_Ack_Nack_Data.Exist_Packet_Timing_Advance = 0x0;
block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Common_Uplink_Ack_Nack_Data.Exist_Extension_Bits = 0x0;
block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Common_Uplink_Ack_Nack_Data.Exist_Power_Control_Parameters = 0x0;
}
unsigned Encoding::write_packet_paging_request(bitvec * dest)
{
unsigned wp = 0;
bitvec_write_field(dest, wp,0x1,2); // Payload Type
bitvec_write_field(dest, wp,0x0,3); // No polling
bitvec_write_field(dest, wp,0x0,3); // Uplink state flag
bitvec_write_field(dest, wp,0x22,6); // MESSAGE TYPE
bitvec_write_field(dest, wp,0x0,2); // Page Mode
bitvec_write_field(dest, wp,0x0,1); // No PERSISTENCE_LEVEL
bitvec_write_field(dest, wp,0x0,1); // No NLN
return wp;
}
unsigned Encoding::write_repeated_page_info(bitvec * dest, unsigned& wp, uint8_t len,
uint8_t *identity, uint8_t chan_needed)
{
bitvec_write_field(dest, wp,0x1,1); // Repeated Page info exists
bitvec_write_field(dest, wp,0x1,1); // RR connection paging
if ((identity[0] & 0x07) == 4) {
bitvec_write_field(dest, wp,0x0,1); // TMSI
identity++;
len--;
} else {
bitvec_write_field(dest, wp,0x0,1); // MI
bitvec_write_field(dest, wp,len,4); // MI len
}
while (len) {
bitvec_write_field(dest, wp,*identity++,8); // MI data
len--;
}
bitvec_write_field(dest, wp,chan_needed,2); // CHANNEL_NEEDED
bitvec_write_field(dest, wp,0x0,1); // No eMLPP_PRIORITY
return wp;
}

68
src/encoding.h Normal file
View File

@ -0,0 +1,68 @@
/* encoding.cpp
*
* Copyright (C) 2012 Ivan Klyuchnikov
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
* Copyright (C) 2013 by Holger Hans Peter Freyther
*
* 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>
#include <gsm_rlcmac.h>
struct gprs_rlcmac_bts;
struct gprs_rlcmac_tbf;
struct bitvec;
/**
* I help with encoding data into CSN1 messages.
* TODO: Nobody can remember a function signature like this. One should
* fill out a struct with the request parameters and then hand the struct
* to the code.
*/
class Encoding {
public:
static int write_immediate_assignment(
struct gprs_rlcmac_bts *bts,
bitvec * dest, uint8_t downlink, uint8_t ra,
uint32_t ref_fn, uint8_t ta, uint16_t arfcn, uint8_t ts, uint8_t tsc,
uint8_t tfi, uint8_t usf, uint32_t tlli, uint8_t polling,
uint32_t fn, uint8_t single_block, uint8_t alpha, uint8_t gamma,
int8_t ta_idx);
static void write_packet_uplink_assignment(
struct gprs_rlcmac_bts *bts,
bitvec * dest, uint8_t old_tfi,
uint8_t old_downlink, uint32_t tlli, uint8_t use_tlli,
struct gprs_rlcmac_tbf *tbf, uint8_t poll, uint8_t alpha,
uint8_t gamma, int8_t ta_idx);
static void write_packet_downlink_assignment(RlcMacDownlink_t * block, uint8_t old_tfi,
uint8_t old_downlink, struct gprs_rlcmac_tbf *tbf, uint8_t poll,
uint8_t alpha, uint8_t gamma, int8_t ta_idx, uint8_t ta_ts);
static void encode_rbb(const char *show_rbb, uint8_t *rbb);
static void write_packet_uplink_ack(struct gprs_rlcmac_bts *bts, RlcMacDownlink_t * block, struct gprs_rlcmac_tbf *tbf,
uint8_t final);
static int write_paging_request(bitvec * dest, uint8_t *ptmsi, uint16_t ptmsi_len);
static unsigned write_repeated_page_info(bitvec * dest, unsigned& wp, uint8_t len,
uint8_t *identity, uint8_t chan_needed);
static unsigned write_packet_paging_request(bitvec * dest);
};

View File

@ -21,6 +21,7 @@
#include <gprs_rlcmac.h>
#include <gprs_bssgp_pcu.h>
#include <pcu_l1_if.h>
#include <bts.h>
#include <tbf.h>
static struct gprs_bssgp_pcu the_pcu = { 0, };
@ -93,7 +94,6 @@ static int parse_ra_cap_ms_class(struct tlv_parsed *tp)
}
if (bitvec_read_field(block, rp, 1)) // SMS Present
bitvec_read_field(block, rp, 4); // SMS Value
bitvec_read_field(block, rp, 4); // SMS Value
}
bitvec_free(block);
@ -121,7 +121,7 @@ static int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp)
data = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_LLC_PDU);
len = TLVP_LEN(tp, BSSGP_IE_LLC_PDU);
if (len > sizeof(gprs_rlcmac_tbf::llc_frame))
if (len > sizeof(gprs_llc::frame))
{
LOGP(DBSSGP, LOGL_NOTICE, "BSSGP TLLI=0x%08x Rx UL-UD IE_LLC_PDU too large\n", tlli);
return bssgp_tx_status(BSSGP_CAUSE_COND_IE_ERR, NULL, msg);
@ -152,7 +152,7 @@ static int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp)
LOGP(DBSSGP, LOGL_INFO, "LLC [SGSN -> PCU] = TLLI: 0x%08x IMSI: %s len: %d\n", tlli, imsi, len);
return tbf_handle(the_pcu.bts, tlli, imsi, ms_class, delay_csec, data, len);
return gprs_rlcmac_tbf::handle(the_pcu.bts, tlli, imsi, ms_class, delay_csec, data, len);
}
int gprs_bssgp_pcu_rx_paging_ps(struct msgb *msg, struct tlv_parsed *tp)

View File

@ -42,6 +42,8 @@ struct bssgp_bvc_ctx *btsctx_alloc(uint16_t bvci, uint16_t nsei);
#define NS_HDR_LEN 4
#define IE_LLC_PDU 14
struct gprs_rlcmac_bts;
struct gprs_bssgp_pcu {
struct gprs_nsvc *nsvc;
struct bssgp_bvc_ctx *bctx;

File diff suppressed because it is too large Load Diff

View File

@ -32,68 +32,16 @@ extern "C" {
}
#endif
/* generate a diagram for debugging timing issues */
//#define DEBUG_DIAGRAM
/* This special feature will delay assignment of downlink TBF by one second,
* in case there is already a TBF.
* This is usefull to debug downlink establishment during packet idle mode.
*/
//#define DEBUG_DL_ASS_IDLE
/*
* PDCH instanc
*/
struct gprs_rlcmac_tbf;
struct gprs_rlcmac_pdch {
uint8_t enable; /* TS is enabled */
uint8_t tsc; /* TSC of this slot */
uint8_t next_ul_tfi; /* next uplink TBF/TFI to schedule (0..31) */
uint8_t next_dl_tfi; /* next downlink TBF/TFI to schedule (0..31) */
struct gprs_rlcmac_tbf *ul_tbf[32]; /* array of UL TBF, by UL TFI */
struct gprs_rlcmac_tbf *dl_tbf[32]; /* array of DL TBF, by DL TFI */
struct llist_head paging_list; /* list of paging messages */
uint32_t last_rts_fn; /* store last frame number of RTS */
};
struct gprs_rlcmac_trx {
void *fl1h;
uint16_t arfcn;
struct gprs_rlcmac_pdch pdch[8];
struct gprs_rlcmac_tbf *ul_tbf[32]; /* array of UL TBF, by UL TFI */
struct gprs_rlcmac_tbf *dl_tbf[32]; /* array of DL TBF, by DL TFI */
};
struct gprs_rlcmac_bts {
uint8_t bsic;
uint8_t fc_interval;
uint8_t cs1;
uint8_t cs2;
uint8_t cs3;
uint8_t cs4;
uint8_t initial_cs_dl, initial_cs_ul;
uint8_t force_cs; /* 0=use from BTS 1=use from VTY */
uint16_t force_llc_lifetime; /* overrides lifetime from SGSN */
uint8_t t3142;
uint8_t t3169;
uint8_t t3191;
uint16_t t3193_msec;
uint8_t t3195;
uint8_t n3101;
uint8_t n3103;
uint8_t n3105;
struct gprs_rlcmac_trx trx[8];
int (*alloc_algorithm)(struct gprs_rlcmac_bts *bts,
struct gprs_rlcmac_tbf *old_tbf,
struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single);
uint32_t alloc_algorithm_curst; /* options to customize algorithm */
uint8_t force_two_phase;
uint8_t alpha, gamma;
};
extern struct gprs_rlcmac_bts *gprs_rlcmac_bts;
struct gprs_rlcmac_bts;
struct BTS;
#ifdef __cplusplus
/*
@ -105,17 +53,6 @@ struct gprs_rlcmac_paging {
uint8_t identity_lv[9];
};
/*
* single block allocation entry
*/
struct gprs_rlcmac_sba {
struct llist_head list;
uint8_t trx;
uint8_t ts;
uint32_t fn;
uint8_t ta;
};
/*
* coding scheme info
*/
@ -125,14 +62,6 @@ struct gprs_rlcmac_cs {
uint8_t block_payload;
};
extern struct gprs_rlcmac_cs gprs_rlcmac_cs[];
#ifdef DEBUG_DIAGRAM
void debug_diagram(int diag, const char *format, ...);
#else
#define debug_diagram(a, b, args...) ;
#endif
int gprs_rlcmac_received_lost(struct gprs_rlcmac_tbf *tbf, uint16_t received,
uint16_t lost);
@ -146,10 +75,6 @@ int gprs_rlcmac_rssi_rep(struct gprs_rlcmac_tbf *tbf);
int gprs_rlcmac_dl_bw(struct gprs_rlcmac_tbf *tbf, uint16_t octets);
int sba_alloc(uint8_t *_trx, uint8_t *_ts, uint32_t *_fn, uint8_t ta);
struct gprs_rlcmac_sba *sba_find(uint8_t trx, uint8_t ts, uint32_t fn);
/* TS 44.060 Section 10.4.7 Table 10.4.7.1: Payload Type field */
enum gprs_rlcmac_block_type {
GPRS_RLCMAC_DATA_BLOCK = 0x0,
@ -158,88 +83,15 @@ enum gprs_rlcmac_block_type {
GPRS_RLCMAC_RESERVED = 0x3
};
int gprs_rlcmac_rcv_block(uint8_t trx, uint8_t ts, uint8_t *data, uint8_t len,
uint32_t fn, int8_t rssi);
int write_immediate_assignment(bitvec * dest, uint8_t downlink, uint8_t ra,
uint32_t ref_fn, uint8_t ta, uint16_t arfcn, uint8_t ts, uint8_t tsc,
uint8_t tfi, uint8_t usf, uint32_t tlli, uint8_t polling,
uint32_t fn, uint8_t single_block, uint8_t alpha, uint8_t gamma,
int8_t ta_idx);
void write_packet_uplink_assignment(bitvec * dest, uint8_t old_tfi,
uint8_t old_downlink, uint32_t tlli, uint8_t use_tlli,
struct gprs_rlcmac_tbf *tbf, uint8_t poll, uint8_t alpha,
uint8_t gamma, int8_t ta_idx);
void write_packet_downlink_assignment(RlcMacDownlink_t * block, uint8_t old_tfi,
uint8_t old_downlink, struct gprs_rlcmac_tbf *tbf, uint8_t poll,
uint8_t alpha, uint8_t gamma, int8_t ta_idx, uint8_t ta_ts);
void write_packet_uplink_ack(RlcMacDownlink_t * block, struct gprs_rlcmac_tbf *tbf,
uint8_t final);
int gprs_rlcmac_tx_ul_ud(gprs_rlcmac_tbf *tbf);
int gprs_rlcmac_poll_timeout(struct gprs_rlcmac_tbf *tbf);
int gprs_rlcmac_sba_timeout(struct gprs_rlcmac_sba *sba);
int gprs_rlcmac_rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta);
int gprs_rlcmac_rcv_control_block(bitvec *rlc_block, uint8_t trx, uint8_t ts,
uint32_t fn);
struct msgb *gprs_rlcmac_send_packet_uplink_assignment(
struct gprs_rlcmac_tbf *tbf, uint32_t fn);
struct msgb *gprs_rlcmac_send_packet_downlink_assignment(
struct gprs_rlcmac_tbf *tbf, uint32_t fn);
void gprs_rlcmac_trigger_downlink_assignment(struct gprs_rlcmac_tbf *tbf,
struct gprs_rlcmac_tbf *old_tbf, const char *imsi);
int gprs_rlcmac_downlink_ack(struct gprs_rlcmac_tbf *tbf, uint8_t final,
uint8_t ssn, uint8_t *rbb);
int gprs_rlcmac_paging_request(uint8_t *ptmsi, uint16_t ptmsi_len,
const char *imsi);
unsigned write_packet_paging_request(bitvec * dest);
unsigned write_repeated_page_info(bitvec * dest, unsigned& wp, uint8_t len,
uint8_t *identity, uint8_t chan_needed);
int gprs_rlcmac_rcv_data_block_acknowledged(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);
struct msgb *gprs_rlcmac_send_uplink_ack(struct gprs_rlcmac_tbf *tbf,
uint32_t fn);
int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn,
int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
uint8_t trx, uint8_t ts, uint16_t arfcn,
uint32_t fn, uint8_t block_nr);
int gprs_rlcmac_imm_ass_cnf(uint8_t *data, uint32_t fn);
int gprs_rlcmac_add_paging(uint8_t chan_needed, uint8_t *identity_lv);
struct gprs_rlcmac_paging *gprs_rlcmac_dequeue_paging(
struct gprs_rlcmac_pdch *pdch);
struct msgb *gprs_rlcmac_send_packet_paging_request(
struct gprs_rlcmac_pdch *pdch);
int remember_timing_advance(uint32_t tlli, uint8_t ta);
int recall_timing_advance(uint32_t tlli);
int flush_timing_advance(void);
extern "C" {
#endif
int alloc_algorithm_a(struct gprs_rlcmac_bts *bts,

File diff suppressed because it is too large Load Diff

View File

@ -28,6 +28,7 @@
/*
* downlink measurement
*/
#warning "TODO: trigger the measurement report from the pollcontroller and use it for flow control"
/* received Measurement Report */
int gprs_rlcmac_meas_rep(Packet_Measurement_Report_t *pmr)
@ -100,7 +101,7 @@ int gprs_rlcmac_rssi_rep(struct gprs_rlcmac_tbf *tbf)
return -EINVAL;
LOGP(DRLCMACMEAS, LOGL_INFO, "UL RSSI of TLLI=0x%08x: %d dBm\n",
tbf->tlli, tbf->meas.rssi_sum / tbf->meas.rssi_num);
tbf->tlli(), tbf->meas.rssi_sum / tbf->meas.rssi_num);
return 0;
}
@ -123,7 +124,7 @@ int gprs_rlcmac_received_lost(struct gprs_rlcmac_tbf *tbf, uint16_t received,
return -EINVAL;
LOGP(DRLCMACMEAS, LOGL_DEBUG, "DL Loss of TLLI 0x%08x: Received: %4d "
"Lost: %4d Sum: %4d\n", tbf->tlli, received, lost, sum);
"Lost: %4d Sum: %4d\n", tbf->tlli(), received, lost, sum);
tbf->meas.dl_loss_received += received;
tbf->meas.dl_loss_lost += lost;
@ -154,7 +155,7 @@ int gprs_rlcmac_lost_rep(struct gprs_rlcmac_tbf *tbf)
return -EINVAL;
LOGP(DRLCMACMEAS, LOGL_INFO, "DL packet loss of IMSI=%s / TLLI=0x%08x: "
"%d%%\n", tbf->meas.imsi, tbf->tlli,
"%d%%\n", tbf->imsi(), tbf->tlli(),
tbf->meas.dl_loss_lost * 100 / sum);
return 0;
@ -179,7 +180,7 @@ int gprs_rlcmac_dl_bw(struct gprs_rlcmac_tbf *tbf, uint16_t octets)
return 0;
LOGP(DRLCMACMEAS, LOGL_INFO, "DL Bandwitdh of IMSI=%s / TLLI=0x%08x: "
"%d KBits/s\n", tbf->meas.imsi, tbf->tlli,
"%d KBits/s\n", tbf->imsi(), tbf->tlli(),
tbf->meas.dl_bw_octets / elapsed);
/* reset bandwidth values timestamp */

View File

@ -20,9 +20,11 @@
#include <gprs_bssgp_pcu.h>
#include <gprs_rlcmac.h>
#include <pcu_l1_if.h>
#include <bts.h>
#include <tbf.h>
uint32_t sched_poll(uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr,
static uint32_t sched_poll(struct gprs_rlcmac_bts *bts,
uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr,
struct gprs_rlcmac_tbf **poll_tbf,
struct gprs_rlcmac_tbf **ul_ass_tbf,
struct gprs_rlcmac_tbf **dl_ass_tbf,
@ -36,9 +38,9 @@ uint32_t sched_poll(uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr,
if ((block_nr % 3) == 2)
poll_fn ++;
poll_fn = poll_fn % 2715648;
llist_for_each_entry(tbf, &gprs_rlcmac_ul_tbfs, list) {
llist_for_each_entry(tbf, &bts->ul_tbfs, list) {
/* this trx, this ts */
if (tbf->trx != trx || tbf->control_ts != ts)
if (tbf->trx->trx_no != trx || tbf->control_ts != ts)
continue;
/* polling for next uplink block */
if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
@ -50,10 +52,11 @@ uint32_t sched_poll(uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr,
*dl_ass_tbf = tbf;
if (tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_SEND_ASS)
*ul_ass_tbf = tbf;
#warning "Is this supposed to be fair? The last TBF for each wins? Maybe use llist_add_tail and skip once we have all states?"
}
llist_for_each_entry(tbf, &gprs_rlcmac_dl_tbfs, list) {
llist_for_each_entry(tbf, &bts->dl_tbfs, list) {
/* this trx, this ts */
if (tbf->trx != trx || tbf->control_ts != ts)
if (tbf->trx->trx_no != trx || tbf->control_ts != ts)
continue;
/* polling for next uplink block */
if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
@ -68,31 +71,14 @@ uint32_t sched_poll(uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr,
return poll_fn;
}
uint32_t sched_sba(uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr)
{
uint32_t sba_fn;
struct gprs_rlcmac_sba *sba;
/* check special TBF for events */
sba_fn = fn + 4;
if ((block_nr % 3) == 2)
sba_fn ++;
sba_fn = sba_fn % 2715648;
sba = sba_find(trx, ts, sba_fn);
if (sba)
return sba_fn;
return 0xffffffff;
}
uint8_t sched_select_uplink(uint8_t trx, uint8_t ts, uint32_t fn,
static uint8_t sched_select_uplink(uint8_t trx, uint8_t ts, uint32_t fn,
uint8_t block_nr, struct gprs_rlcmac_pdch *pdch)
{
struct gprs_rlcmac_tbf *tbf;
uint8_t usf = 0x07;
uint8_t i, tfi;
/* select uplink ressource */
/* select uplink resource */
for (i = 0, tfi = pdch->next_ul_tfi; i < 32;
i++, tfi = (tfi + 1) & 31) {
tbf = pdch->ul_tbf[tfi];
@ -110,9 +96,9 @@ uint8_t sched_select_uplink(uint8_t trx, uint8_t ts, uint32_t fn,
usf = tbf->dir.ul.usf[ts];
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: TRX=%d "
"TS=%d FN=%d block_nr=%d scheduling USF=%d for "
"required uplink ressource of UL TBF=%d\n", trx, ts, fn,
"required uplink resource of UL TFI=%d\n", trx, ts, fn,
block_nr, usf, tfi);
/* next TBF to handle ressource is the next one */
/* next TBF to handle resource is the next one */
pdch->next_ul_tfi = (tfi + 1) & 31;
break;
}
@ -120,7 +106,8 @@ uint8_t sched_select_uplink(uint8_t trx, uint8_t ts, uint32_t fn,
return usf;
}
struct msgb *sched_select_ctrl_msg(uint8_t trx, uint8_t ts, uint32_t fn,
static struct msgb *sched_select_ctrl_msg(
uint8_t trx, uint8_t ts, uint32_t fn,
uint8_t block_nr, struct gprs_rlcmac_pdch *pdch,
struct gprs_rlcmac_tbf *ul_ass_tbf,
struct gprs_rlcmac_tbf *dl_ass_tbf,
@ -128,33 +115,40 @@ struct msgb *sched_select_ctrl_msg(uint8_t trx, uint8_t ts, uint32_t fn,
{
struct msgb *msg = NULL;
struct gprs_rlcmac_tbf *tbf = NULL;
struct gprs_rlcmac_tbf *next_list[3] = { ul_ass_tbf, dl_ass_tbf, ul_ack_tbf };
/* schedule PACKET UPLINK ASSIGNMENT (1st priority) */
if (ul_ass_tbf) {
tbf = ul_ass_tbf;
msg = gprs_rlcmac_send_packet_uplink_assignment(tbf, fn);
}
/* schedule PACKET DOWNLINK ASSIGNMENT (2nd priotiry) */
if (!msg && dl_ass_tbf) {
tbf = dl_ass_tbf;
msg = gprs_rlcmac_send_packet_downlink_assignment(tbf, fn);
}
/* schedule PACKET UPLINK ACK (3rd priority) */
if (!msg && ul_ack_tbf) {
tbf = ul_ack_tbf;
msg = gprs_rlcmac_send_uplink_ack(tbf, fn);
for (size_t i = 0; i < ARRAY_SIZE(next_list); ++i) {
tbf = next_list[(pdch->next_ctrl_prio + i) % 3];
if (!tbf)
continue;
if (tbf == ul_ass_tbf)
msg = tbf->create_ul_ass(fn);
else if (tbf == dl_ass_tbf)
msg = tbf->create_dl_ass(fn);
else
msg = tbf->create_ul_ack(fn);
if (!msg) {
tbf = NULL;
continue;
}
pdch->next_ctrl_prio += i + 1;
pdch->next_ctrl_prio %= 3;
break;
}
/* any message */
if (msg) {
tbf->rotate_in_list();
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling control "
"message at RTS for %s TBF=%d (TRX=%d, TS=%d)\n",
(tbf->direction == GPRS_RLCMAC_UL_TBF)
? "UL" : "DL", tbf->tfi, trx, ts);
"message at RTS for %s (TRX=%d, TS=%d)\n",
tbf_name(tbf), trx, ts);
return msg;
}
/* schedule PACKET PAGING REQUEST */
if (!llist_empty(&pdch->paging_list))
msg = gprs_rlcmac_send_packet_paging_request(pdch);
msg = pdch->packet_paging_request();
if (msg) {
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling paging request "
"message at RTS for (TRX=%d, TS=%d)\n", trx, ts);
@ -164,14 +158,15 @@ struct msgb *sched_select_ctrl_msg(uint8_t trx, uint8_t ts, uint32_t fn,
return NULL;
}
struct msgb *sched_select_downlink(uint8_t trx, uint8_t ts, uint32_t fn,
static struct msgb *sched_select_downlink(struct gprs_rlcmac_bts *bts,
uint8_t trx, uint8_t ts, uint32_t fn,
uint8_t block_nr, struct gprs_rlcmac_pdch *pdch)
{
struct msgb *msg = NULL;
struct gprs_rlcmac_tbf *tbf = NULL;
uint8_t i, tfi;
/* select downlink ressource */
/* select downlink resource */
for (i = 0, tfi = pdch->next_dl_tfi; i < 32;
i++, tfi = (tfi + 1) & 31) {
tbf = pdch->dl_tbf[tfi];
@ -191,18 +186,18 @@ struct msgb *sched_select_downlink(uint8_t trx, uint8_t ts, uint32_t fn,
continue;
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling data message at "
"RTS for DL TBF=%d (TRX=%d, TS=%d)\n", tfi, trx, ts);
/* next TBF to handle ressource is the next one */
"RTS for DL TFI=%d (TRX=%d, TS=%d)\n", tfi, trx, ts);
/* next TBF to handle resource is the next one */
pdch->next_dl_tfi = (tfi + 1) & 31;
/* generate DL data block */
msg = gprs_rlcmac_send_data_block_acknowledged(tbf, fn,
ts);
msg = tbf->create_dl_acked_block(fn, ts);
break;
}
return msg;
}
static uint8_t rlcmac_dl_idle[23] = {
static const uint8_t rlcmac_dl_idle[23] = {
0x47, /* control without optional header octets, no polling, USF=111 */
0x94, /* dummy downlink control message, paging mode 00 */
0x2b, /* no persistance level, 7 bits spare pattern */
@ -210,7 +205,7 @@ static uint8_t rlcmac_dl_idle[23] = {
0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b
};
struct msgb *sched_dummy(void)
static struct msgb *sched_dummy(void)
{
struct msgb *msg;
@ -222,10 +217,10 @@ struct msgb *sched_dummy(void)
return msg;
}
int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn,
int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
uint8_t trx, uint8_t ts, uint16_t arfcn,
uint32_t fn, uint8_t block_nr)
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_pdch *pdch;
struct gprs_rlcmac_tbf *poll_tbf = NULL, *dl_ass_tbf = NULL,
*ul_ass_tbf = NULL, *ul_ack_tbf = NULL;
@ -233,11 +228,13 @@ int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn,
struct msgb *msg = NULL;
uint32_t poll_fn, sba_fn;
#warning "ARFCN... it is already in the TRX..... is it consistent with it?"
if (trx >= 8 || ts >= 8)
return -EINVAL;
pdch = &bts->trx[trx].pdch[ts];
if (!pdch->enable) {
if (!pdch->is_enabled()) {
LOGP(DRLCMACSCHED, LOGL_ERROR, "Received RTS on disabled PDCH: "
"TRX=%d TS=%d\n", trx, ts);
return -EIO;
@ -246,25 +243,24 @@ int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn,
/* store last frame number of RTS */
pdch->last_rts_fn = fn;
poll_fn = sched_poll(trx, ts, fn, block_nr, &poll_tbf, &ul_ass_tbf,
poll_fn = sched_poll(bts, trx, ts, fn, block_nr, &poll_tbf, &ul_ass_tbf,
&dl_ass_tbf, &ul_ack_tbf);
/* check uplink ressource for polling */
/* check uplink resource for polling */
if (poll_tbf)
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: TRX=%d "
"TS=%d FN=%d block_nr=%d scheduling free USF for "
"polling at FN=%d of %s TFI=%d\n", trx, ts, fn,
"polling at FN=%d of %s\n", trx, ts, fn,
block_nr, poll_fn,
(poll_tbf->direction == GPRS_RLCMAC_UL_TBF)
? "UL" : "DL", poll_tbf->tfi);
tbf_name(poll_tbf));
/* use free USF */
/* else. check for sba */
else if ((sba_fn = sched_sba(trx, ts, fn, block_nr) != 0xffffffff))
else if ((sba_fn = bts->bts->sba()->sched(trx, ts, fn, block_nr) != 0xffffffff))
LOGP(DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: TRX=%d "
"TS=%d FN=%d block_nr=%d scheduling free USF for "
"single block allocation at FN=%d\n", trx, ts, fn,
block_nr, sba_fn);
/* use free USF */
/* else, we search for uplink ressource */
/* else, we search for uplink resource */
else
usf = sched_select_uplink(trx, ts, fn, block_nr, pdch);
@ -274,7 +270,7 @@ int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn,
/* Prio 2: select data message for downlink */
if (!msg)
msg = sched_select_downlink(trx, ts, fn, block_nr, pdch);
msg = sched_select_downlink(bts, trx, ts, fn, block_nr, pdch);
/* Prio 3: send dummy contol message */
if (!msg)

View File

@ -0,0 +1,681 @@
/* gprs_rlcmac.cpp
*
* Copyright (C) 2012 Ivan Klyuchnikov
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
* Copyright (C) 2013 by Holger Hans Peter Freyther
*
* 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 <gprs_rlcmac.h>
#include <gprs_debug.h>
#include <bts.h>
#include <tbf.h>
#include <errno.h>
/* 3GPP TS 05.02 Annex B.1 */
#define MS_NA 255 /* N/A */
#define MS_A 254 /* 1 with hopping, 0 without */
#define MS_B 253 /* 1 with hopping, 0 without (change Rx to Tx)*/
#define MS_C 252 /* 1 with hopping, 0 without (change Tx to Rx)*/
struct gprs_ms_multislot_class {
uint8_t rx, tx, sum; /* Maximum Number of Slots: RX, Tx, Sum Rx+Tx */
uint8_t ta, tb, ra, rb; /* Minimum Number of Slots */
uint8_t type; /* Type of Mobile */
};
static const struct gprs_ms_multislot_class gprs_ms_multislot_class[32] = {
/* M-S Class Rx Tx Sum Tta Ttb Tra Trb Type */
/* N/A */ { MS_NA,MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA },
/* 1 */ { 1, 1, 2, 3, 2, 4, 2, 1 },
/* 2 */ { 2, 1, 3, 3, 2, 3, 1, 1 },
/* 3 */ { 2, 2, 3, 3, 2, 3, 1, 1 },
/* 4 */ { 3, 1, 4, 3, 1, 3, 1, 1 },
/* 5 */ { 2, 2, 4, 3, 1, 3, 1, 1 },
/* 6 */ { 3, 2, 4, 3, 1, 3, 1, 1 },
/* 7 */ { 3, 3, 4, 3, 1, 3, 1, 1 },
/* 8 */ { 4, 1, 5, 3, 1, 2, 1, 1 },
/* 9 */ { 3, 2, 5, 3, 1, 2, 1, 1 },
/* 10 */ { 4, 2, 5, 3, 1, 2, 1, 1 },
/* 11 */ { 4, 3, 5, 3, 1, 2, 1, 1 },
/* 12 */ { 4, 4, 5, 2, 1, 2, 1, 1 },
/* 13 */ { 3, 3, MS_NA, MS_NA, MS_A, 3, MS_A, 2 },
/* 14 */ { 4, 4, MS_NA, MS_NA, MS_A, 3, MS_A, 2 },
/* 15 */ { 5, 5, MS_NA, MS_NA, MS_A, 3, MS_A, 2 },
/* 16 */ { 6, 6, MS_NA, MS_NA, MS_A, 2, MS_A, 2 },
/* 17 */ { 7, 7, MS_NA, MS_NA, MS_A, 1, 0, 2 },
/* 18 */ { 8, 8, MS_NA, MS_NA, 0, 0, 0, 2 },
/* 19 */ { 6, 2, MS_NA, 3, MS_B, 2, MS_C, 1 },
/* 20 */ { 6, 3, MS_NA, 3, MS_B, 2, MS_C, 1 },
/* 21 */ { 6, 4, MS_NA, 3, MS_B, 2, MS_C, 1 },
/* 22 */ { 6, 4, MS_NA, 2, MS_B, 2, MS_C, 1 },
/* 23 */ { 6, 6, MS_NA, 2, MS_B, 2, MS_C, 1 },
/* 24 */ { 8, 2, MS_NA, 3, MS_B, 2, MS_C, 1 },
/* 25 */ { 8, 3, MS_NA, 3, MS_B, 2, MS_C, 1 },
/* 26 */ { 8, 4, MS_NA, 3, MS_B, 2, MS_C, 1 },
/* 27 */ { 8, 4, MS_NA, 2, MS_B, 2, MS_C, 1 },
/* 28 */ { 8, 6, MS_NA, 2, MS_B, 2, MS_C, 1 },
/* 29 */ { 8, 8, MS_NA, 2, MS_B, 2, MS_C, 1 },
/* N/A */ { MS_NA,MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA },
/* N/A */ { MS_NA,MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA, MS_NA },
};
static inline int8_t find_free_usf(struct gprs_rlcmac_pdch *pdch)
{
struct gprs_rlcmac_tbf *tbf;
uint8_t usf_map = 0;
uint8_t tfi, usf;
/* make map of used USF */
for (tfi = 0; tfi < 32; tfi++) {
tbf = pdch->ul_tbf[tfi];
if (!tbf)
continue;
usf_map |= (1 << tbf->dir.ul.usf[pdch->ts_no]);
}
/* look for USF, don't use USF=7 */
for (usf = 0; usf < 7; usf++) {
if (!(usf_map & (1 << usf)))
return usf;
}
return -1;
}
static int find_enabled_pdch(struct gprs_rlcmac_trx *trx, const uint8_t start_ts)
{
int ts;
for (ts = start_ts; ts < 8; ts++) {
struct gprs_rlcmac_pdch *pdch;
pdch = &trx->pdch[ts];
if (!pdch->is_enabled()) {
LOGP(DRLCMAC, LOGL_DEBUG, "- Skipping TS %d, because "
"not enabled\n", ts);
continue;
}
return ts;
}
return 8;
}
static void assign_uplink_tbf_usf(
struct gprs_rlcmac_pdch *pdch,
struct gprs_rlcmac_tbf *tbf, int8_t usf)
{
tbf->trx->ul_tbf[tbf->tfi()] = tbf;
pdch->ul_tbf[tbf->tfi()] = tbf;
tbf->pdch[pdch->ts_no] = pdch;
tbf->dir.ul.usf[pdch->ts_no] = usf;
}
static void assign_dlink_tbf(
struct gprs_rlcmac_pdch *pdch,
struct gprs_rlcmac_tbf *tbf)
{
tbf->trx->dl_tbf[tbf->tfi()] = tbf;
pdch->dl_tbf[tbf->tfi()] = tbf;
tbf->pdch[pdch->ts_no] = pdch;
}
/* Slot Allocation: Algorithm A
*
* Assign single slot for uplink and downlink
*/
int alloc_algorithm_a(struct gprs_rlcmac_bts *bts,
struct gprs_rlcmac_tbf *old_tbf,
struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single)
{
struct gprs_rlcmac_pdch *pdch;
uint8_t ts;
LOGP(DRLCMAC, LOGL_DEBUG, "Slot Allocation (Algorithm A) for class "
"%d\n", tbf->ms_class);
ts = find_enabled_pdch(tbf->trx, 0);
if (ts == 8)
return -EINVAL;
pdch = &tbf->trx->pdch[ts];
if (tbf->direction == GPRS_RLCMAC_UL_TBF) {
int8_t usf; /* must be signed */
/* if USF available */
usf = find_free_usf(pdch);
if (usf < 0) {
LOGP(DRLCMAC, LOGL_NOTICE, "- Failed "
"allocating TS=%d, no USF available\n", ts);
return -EBUSY;
}
LOGP(DRLCMAC, LOGL_DEBUG, "- Assign uplink "
"TS=%d USF=%d\n", ts, usf);
assign_uplink_tbf_usf(pdch, tbf, usf);
} else {
LOGP(DRLCMAC, LOGL_DEBUG, "- Assign downlink TS=%d\n", ts);
assign_dlink_tbf(pdch, tbf);
}
/* the only one TS is the common TS */
tbf->first_ts = tbf->first_common_ts = ts;
return 0;
}
/*
* Select a window of Rx slots if available.
* The maximum allowed slots depend on RX or the window of available
* slots. This must be done for uplink TBF also, because it is the basis
* for calculating control slot and uplink slot(s).
*/
static uint8_t select_dl_slots(struct gprs_rlcmac_trx *trx,
const int ms_type, const int ms_max_rxslots,
uint8_t *out_rx_win_min, uint8_t *out_rx_win_max)
{
uint8_t rx_window = 0;
int rx_window_size = 0;
int8_t last_tsc = -1; /* must be signed */
uint8_t rx_win_min = 0, rx_win_max = 0;
for (int ts_no = 0; ts_no < 8; ts_no++) {
struct gprs_rlcmac_pdch *pdch;
pdch = &trx->pdch[ts_no];
/* check if enabled */
if (!pdch->is_enabled()) {
LOGP(DRLCMAC, LOGL_DEBUG, "- Skipping TS %d, because "
"not enabled\n", ts_no);
if (ms_type == 1 && rx_window)
goto inc_window;
continue;
}
/* check if TSC changes */
if (last_tsc < 0)
last_tsc = pdch->tsc;
else if (last_tsc != pdch->tsc) {
LOGP(DRLCMAC, LOGL_ERROR, "Skipping TS %d of TRX=%d, "
"because it has different TSC than lower TS "
"of TRX. In order to allow multislot, all "
"slots must be configured with the same "
"TSC!\n", ts_no, trx->trx_no);
if (ms_type == 1 && rx_window)
goto inc_window;
continue;
}
if (!rx_window)
rx_win_min = ts_no;
rx_window |= (1 << ts_no);
LOGP(DRLCMAC, LOGL_DEBUG, "- Selected DL TS %d\n", ts_no);
/* range of window (required for Type 1) */
rx_win_max = ts_no;
inc_window:
if (++rx_window_size == ms_max_rxslots) {
LOGP(DRLCMAC, LOGL_DEBUG, "- Done, because slots / "
"window reached maximum alowed Rx size\n");
break;
}
if (ms_type == 1 && rx_window_size == 5) {
LOGP(DRLCMAC, LOGL_DEBUG, "- Done, because slots / "
"window reached maximum supported Rx size of "
"this algorithm\n");
break;
}
}
LOGP(DRLCMAC, LOGL_DEBUG, "- Selected slots for RX: "
"(TS=0)\"%c%c%c%c%c%c%c%c\"(TS=7)\n",
((rx_window & 0x01)) ? 'D' : '.',
((rx_window & 0x02)) ? 'D' : '.',
((rx_window & 0x04)) ? 'D' : '.',
((rx_window & 0x08)) ? 'D' : '.',
((rx_window & 0x10)) ? 'D' : '.',
((rx_window & 0x20)) ? 'D' : '.',
((rx_window & 0x40)) ? 'D' : '.',
((rx_window & 0x80)) ? 'D' : '.');
*out_rx_win_min = rx_win_min;
*out_rx_win_max = rx_win_max;
return rx_window;
}
static int reduce_rx_window(const int ms_type, const struct gprs_rlcmac_tbf *old_tbf,
const int Tt, const int Tr,
int *rx_window,
uint8_t *rx_win_min, uint8_t *rx_win_max)
{
if (ms_type != 1)
return 0;
if (!old_tbf)
return 0;
if (old_tbf->direction != GPRS_RLCMAC_UL_TBF)
return 0;
uint8_t collide = 0, ul_usage = 0;
/* calculate mask of colliding slots */
for (uint8_t ts_no = 0; ts_no < 8; ts_no++) {
int j;
if (!old_tbf->pdch[ts_no])
continue;
ul_usage |= (1 << ts_no);
/* mark bits from TS-t .. TS+r */
for (j = ts_no - Tt; j != ((ts_no + Tr + 1) & 7); j = (j + 1) & 7)
collide |= (1 << j);
}
LOGP(DRLCMAC, LOGL_DEBUG, "- Not allowed slots due to existing "
"UL allocation: (TS=0)\"%c%c%c%c%c%c%c%c\"(TS=7) "
" D=downlink x=not usable\n",
((ul_usage & 0x01)) ? 'D' : ((collide & 0x01))?'x':'.',
((ul_usage & 0x02)) ? 'D' : ((collide & 0x02))?'x':'.',
((ul_usage & 0x04)) ? 'D' : ((collide & 0x04))?'x':'.',
((ul_usage & 0x08)) ? 'D' : ((collide & 0x08))?'x':'.',
((ul_usage & 0x10)) ? 'D' : ((collide & 0x10))?'x':'.',
((ul_usage & 0x20)) ? 'D' : ((collide & 0x20))?'x':'.',
((ul_usage & 0x40)) ? 'D' : ((collide & 0x40))?'x':'.',
((ul_usage & 0x80)) ? 'D' : ((collide & 0x80))?'x':'.');
/*
* Uplink/Downlink in GSM is shifted by three timeslots. Make
* sure they don't collide.
*/
*rx_window &= ~(collide << 3);
*rx_window &= ~(collide >> 5);
LOGP(DRLCMAC, LOGL_DEBUG, "- Remaining slots for RX: "
"(TS=0)\"%c%c%c%c%c%c%c%c\"(TS=7)\n",
((*rx_window & 0x01)) ? 'D' : '.',
((*rx_window & 0x02)) ? 'D' : '.',
((*rx_window & 0x04)) ? 'D' : '.',
((*rx_window & 0x08)) ? 'D' : '.',
((*rx_window & 0x10)) ? 'D' : '.',
((*rx_window & 0x20)) ? 'D' : '.',
((*rx_window & 0x40)) ? 'D' : '.',
((*rx_window & 0x80)) ? 'D' : '.');
if (!*rx_window) {
LOGP(DRLCMAC, LOGL_NOTICE, "No suitable downlink slots "
"available with current uplink assignment\n");
return -EBUSY;
}
return 0;
}
/* shrink range of rx_win_min and rx_win_max */
static void shrink_rx_window(uint8_t *rx_win_min, uint8_t *rx_win_max, int rx_window)
{
/* calculate new min/max */
for (uint8_t ts_no = *rx_win_min; ts_no <= *rx_win_max; ts_no++) {
if ((rx_window & (1 << ts_no)))
break;
*rx_win_min = ts_no + 1;
LOGP(DRLCMAC, LOGL_DEBUG, "- TS is unused, so "
"raising start of DL window to %d\n",
*rx_win_min);
}
for (uint8_t ts_no = *rx_win_max; ts_no >= *rx_win_min; ts_no--) {
if ((rx_window & (1 << ts_no)))
break;
*rx_win_max = ts_no - 1;
LOGP(DRLCMAC, LOGL_DEBUG, "- TS is unused, so "
"lowering end of DL window to %d\n",
*rx_win_max);
}
}
/*
* reduce window, to allow at least one uplink TX slot
* this is only required for Type 1
*/
static uint8_t update_rx_win_max(const int ms_type, const int Tt,
const int Tr, uint8_t rx_win_min, uint8_t rx_win_max)
{
if (ms_type != 1)
return rx_win_max;
if (rx_win_max - rx_win_min + 1 + Tt + 1 + Tr > 8) {
rx_win_max = rx_win_min + 7 - Tt - 1 - Tr;
LOGP(DRLCMAC, LOGL_DEBUG, "- Reduce RX window due to time "
"contraints to %d slots\n", rx_win_max - rx_win_min + 1);
}
return rx_win_max;
}
static void tx_win_from_rx(const int ms_type,
uint8_t rx_win_min, uint8_t rx_win_max,
int Tt, int Tr,
uint8_t *tx_win_min, uint8_t *tx_win_max,
uint8_t *tx_range)
{
if (ms_type == 1) {
/* calculate TX window (shifted by 3 timeslots)
* it uses the space between tx_win_max and tx_win_min */
*tx_win_min = (rx_win_max - 2 + Tt) & 7;
*tx_win_max = (rx_win_min + 4 - Tr) & 7;
} else {
/* TX and RX simultaniously */
*tx_win_min = rx_win_min;
*tx_win_max = 7;
}
*tx_range = (*tx_win_max - *tx_win_min + 1) & 7;
/* if TX window fills complete range */
if (*tx_range == 0)
*tx_range = 8;
LOGP(DRLCMAC, LOGL_DEBUG, "- TX-Window is: %d..%d\n", *tx_win_min,
*tx_win_max);
}
/*
* Select a window of Tx slots if available.
* The maximum allowed slots depend on TX or the window of available
* slots.
*/
static int select_ul_slots(gprs_rlcmac_trx *trx,
const int ms_type, const int ms_max_txslots,
uint8_t tx_win_min, uint8_t tx_range,
int8_t *usf, int8_t *first_common_ts, uint8_t rx_window)
{
int tsc = -1;
uint8_t tx_window = 0;
int i;
uint8_t ts_no;
for (ts_no = tx_win_min, i = 0; i < tx_range; ts_no = (ts_no + 1) & 7, i++) {
gprs_rlcmac_pdch *pdch = &trx->pdch[ts_no];
/* check if enabled */
if (!pdch->is_enabled()) {
LOGP(DRLCMAC, LOGL_DEBUG, "- Skipping TS %d, "
"because not enabled\n", ts_no);
if (ms_type == 1 && tx_window)
goto inc_window;
continue;
}
/* check if used as downlink */
if (!(rx_window & (1 << ts_no))) {
LOGP(DRLCMAC, LOGL_DEBUG, "- Skipping TS %d, "
"because not a downlink slot\n", ts_no);
if (ms_type == 1 && tx_window)
goto inc_window;
continue;
}
/* check if TSC changes */
if (tsc < 0)
tsc = pdch->tsc;
else if (tsc != pdch->tsc) {
LOGP(DRLCMAC, LOGL_ERROR, "Skipping TS %d of "
"TRX=%d, because it has different TSC "
"than lower TS of TRX. In order to "
"allow multislot, all slots must be "
"configured with the same TSC!\n",
ts_no, trx->trx_no);
if (ms_type == 1)
goto inc_window;
continue;
}
/* check for free usf */
usf[ts_no] = find_free_usf(pdch);
if (usf[ts_no] < 0) {
LOGP(DRLCMAC, LOGL_DEBUG, "- Skipping TS %d, "
"because no USF available\n", ts_no);
if (ms_type == 1)
goto inc_window;
continue;
}
if (!tx_window)
*first_common_ts = ts_no;
tx_window |= (1 << ts_no);
LOGP(DRLCMAC, LOGL_DEBUG, "- Selected UL TS %d\n", ts_no);
inc_window:
if (1 && ms_type == 1) { /* FIXME: multislot UL assignment */
LOGP(DRLCMAC, LOGL_DEBUG, "- Done, because "
"1 slot assigned\n");
break;
}
if (i+1 == ms_max_txslots) {
LOGP(DRLCMAC, LOGL_DEBUG, "- Done, because "
"slots / window reached maximum "
"allowed Tx size\n");
break;
}
}
LOGP(DRLCMAC, LOGL_DEBUG, "- Selected TX window: "
"(TS=0)\"%c%c%c%c%c%c%c%c\"(TS=7)\n",
((tx_window & 0x01)) ? 'U' : '.',
((tx_window & 0x02)) ? 'U' : '.',
((tx_window & 0x04)) ? 'U' : '.',
((tx_window & 0x08)) ? 'U' : '.',
((tx_window & 0x10)) ? 'U' : '.',
((tx_window & 0x20)) ? 'U' : '.',
((tx_window & 0x40)) ? 'U' : '.',
((tx_window & 0x80)) ? 'U' : '.');
if (!tx_window) {
LOGP(DRLCMAC, LOGL_NOTICE, "No suitable uplink slots "
"available\n");
return -EBUSY;
}
return tx_window;
}
/*
* Assign the first common ts, which is used for control or
* single slot.
*/
static int select_first_ts(gprs_rlcmac_trx *trx, uint8_t tx_win_min,
uint8_t tx_range, uint8_t rx_window)
{
uint8_t ts_no;
int i;
for (ts_no = tx_win_min, i = 0; i < tx_range; ts_no = (ts_no + 1) & 7, i++) {
gprs_rlcmac_pdch *pdch = &trx->pdch[ts_no];
/* check if enabled */
if (!pdch->is_enabled()) {
LOGP(DRLCMAC, LOGL_DEBUG, "- Skipping TS %d, "
"because not enabled\n", ts_no);
continue;
}
/* check if used as downlink */
if (!(rx_window & (1 << ts_no))) {
LOGP(DRLCMAC, LOGL_DEBUG, "- Skipping TS %d, "
"because not a downlink slot\n", ts_no);
continue;
}
return ts_no;
}
return -1;
}
/* Slot Allocation: Algorithm B
*
* Assign as many downlink slots as possible.
* Assign one uplink slot. (With free USF)
*
*/
int alloc_algorithm_b(struct gprs_rlcmac_bts *bts,
struct gprs_rlcmac_tbf *old_tbf,
struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single)
{
const struct gprs_ms_multislot_class *ms_class;
uint8_t Tx, Sum; /* Maximum Number of Slots: RX, Tx, Sum Rx+Tx */
uint8_t Tta, Ttb, Tra, Trb, Tt, Tr; /* Minimum Number of Slots */
uint8_t Type; /* Type of Mobile */
int rx_window;
static const char *digit[10] = { "0","1","2","3","4","5","6","7","8","9" };
int8_t usf[8] = { -1, -1, -1, -1, -1, -1, -1, -1 }; /* must be signed */
int8_t first_common_ts = -1;
uint8_t ts;
uint8_t slotcount = 0;
if (tbf->ms_class >= 32) {
LOGP(DRLCMAC, LOGL_ERROR, "Multislot class %d out of range.\n",
tbf->ms_class);
return -EINVAL;
}
if (tbf->ms_class) {
ms_class = &gprs_ms_multislot_class[tbf->ms_class];
LOGP(DRLCMAC, LOGL_DEBUG, "Slot Allocation (Algorithm B) for "
"class %d\n", tbf->ms_class);
} else {
ms_class = &gprs_ms_multislot_class[12];
LOGP(DRLCMAC, LOGL_DEBUG, "Slot Allocation (Algorithm B) for "
"unknow class (assuming 12)\n");
}
if (ms_class->tx == MS_NA) {
LOGP(DRLCMAC, LOGL_NOTICE, "Multislot class %d not "
"applicable.\n", tbf->ms_class);
return -EINVAL;
}
Tx = ms_class->tx;
Sum = ms_class->sum;
Tta = ms_class->ta;
Ttb = ms_class->tb;
Tra = ms_class->ra;
Trb = ms_class->rb;
Type = ms_class->type;
/* Tta and Ttb may depend on hopping or frequency change */
if (Ttb == MS_A || Ttb == MS_B)
Ttb = 0;
if (Trb == MS_A || Trb == MS_C)
Trb = 0;
LOGP(DRLCMAC, LOGL_DEBUG, "- Rx=%d Tx=%d Sum Rx+Tx=%s Tta=%s Ttb=%d "
" Tra=%d Trb=%d Type=%d\n", ms_class->rx, Tx,
(Sum == MS_NA) ? "N/A" : digit[Sum],
(Tta == MS_NA) ? "N/A" : digit[Tta], Ttb, Tra, Trb, Type);
/* select the values for time contraints */
/* applicable to type 1 and type 2 */
Tt = Ttb;
Tr = Trb;
uint8_t rx_win_min, rx_win_max;
rx_window = select_dl_slots(tbf->trx, ms_class->type, ms_class->rx,
&rx_win_min, &rx_win_max);
/* reduce window, if existing uplink slots collide RX window */
int rc = reduce_rx_window(ms_class->type, old_tbf, Tt, Tr,
&rx_window, &rx_win_min, &rx_win_max);
if (rc < 0)
return rc;
shrink_rx_window(&rx_win_min, &rx_win_max, rx_window);
rx_win_max = update_rx_win_max(ms_class->type, Tt, Tr,
rx_win_min, rx_win_max);
shrink_rx_window(&rx_win_min, &rx_win_max, rx_window);
LOGP(DRLCMAC, LOGL_DEBUG, "- RX-Window is: %d..%d\n", rx_win_min,
rx_win_max);
/* calculate TX window */
uint8_t tx_win_min, tx_win_max, tx_range;
tx_win_from_rx(ms_class->type, rx_win_min, rx_win_max, Tt, Tr,
&tx_win_min, &tx_win_max, &tx_range);
/* select UL slots but in both cases assign first_common_ts */
uint8_t tx_window = 0;
if (tbf->direction == GPRS_RLCMAC_UL_TBF) {
rc = select_ul_slots(tbf->trx, ms_class->type, ms_class->tx,
tx_win_min, tx_range, usf,
&first_common_ts, rx_window);
if (rc < 0)
return rc;
tx_window = rc;
} else {
first_common_ts = select_first_ts(tbf->trx, tx_win_min,
tx_range, rx_window);
}
#warning "first_common_ts might be different if there was no free USF for the new uplink assignment"
if (first_common_ts < 0) {
LOGP(DRLCMAC, LOGL_NOTICE, "No first common slots available\n");
return -EINVAL;
}
if (tbf->direction == GPRS_RLCMAC_DL_TBF) {
/* assign downlink */
if (rx_window == 0) {
LOGP(DRLCMAC, LOGL_NOTICE, "No downlink slots "
"available\n");
return -EINVAL;
}
for (ts = 0; ts < 8; ts++) {
if ((rx_window & (1 << ts))) {
/* be sure to select a single downlink slots
* that can be used for uplink, if multiple
* slots are assigned later. */
if (single && first_common_ts != ts)
continue;
LOGP(DRLCMAC, LOGL_DEBUG, "- Assigning DL TS "
"%d\n", ts);
assign_dlink_tbf(&tbf->trx->pdch[ts], tbf);
slotcount++;
if (slotcount == 1)
tbf->first_ts = ts;
if (single)
break;
}
}
} else {
for (ts = 0; ts < 8; ts++) {
if ((tx_window & (1 << ts))) {
LOGP(DRLCMAC, LOGL_DEBUG, "- Assigning UL TS "
"%d\n", ts);
assign_uplink_tbf_usf(&tbf->trx->pdch[ts], tbf, usf[ts]);
slotcount++;
if (slotcount == 1)
tbf->first_ts = ts;
if (single)
break;
}
}
}
if (single && slotcount) {
LOGP(DRLCMAC, LOGL_INFO, "Using single slot at TS %d for %s\n",
tbf->first_ts,
(tbf->direction == GPRS_RLCMAC_DL_TBF) ? "DL" : "UL");
} else {
LOGP(DRLCMAC, LOGL_INFO, "Using %d slots for %s\n", slotcount,
(tbf->direction == GPRS_RLCMAC_DL_TBF) ? "DL" : "UL");
}
if (slotcount == 0)
return -EBUSY;
tbf->first_common_ts = first_common_ts;
return 0;
}

View File

@ -33,9 +33,21 @@ static int *nearest_p;
#include <limits.h>
#include <gsm_timer.h>
#include <pcu_l1_if.h>
#include <bts.h>
static struct rb_root timer_root = RB_ROOT;
/*
* TODO: make this depend on the BTS. This means that
* all time functions schedule based on the BTS they
* are scheduled on.
*/
static int get_current_fn()
{
return BTS::main_bts()->current_frame_number();
}
static void __add_gsm_timer(struct osmo_gsm_timer_list *timer)
{
struct rb_node **new_node = &(timer_root.rb_node);

142
src/llc.cpp Normal file
View File

@ -0,0 +1,142 @@
/* Copied from tbf.cpp
*
* Copyright (C) 2012 Ivan Klyuchnikov
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
* Copyright (C) 2013 by Holger Hans Peter Freyther
*
* 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 <tbf.h>
#include <bts.h>
#include <stdio.h>
extern "C" {
#include <osmocom/core/msgb.h>
}
/* reset LLC frame */
void gprs_llc::reset()
{
m_index = 0;
m_length = 0;
memset(frame, 0x42, sizeof(frame));
}
void gprs_llc::reset_frame_space()
{
m_index = 0;
}
void gprs_llc::enqueue(struct msgb *llc_msg)
{
m_queue_size += 1;
msgb_enqueue(&queue, llc_msg);
}
void gprs_llc::put_frame(const uint8_t *data, size_t len)
{
/* only put frames when we are empty */
OSMO_ASSERT(m_index == 0 && m_length == 0);
append_frame(data, len);
}
void gprs_llc::append_frame(const uint8_t *data, size_t len)
{
/* TODO: bounds check */
memcpy(frame + m_length, data, len);
m_length += len;
}
void gprs_llc::clear(BTS *bts)
{
struct msgb *msg;
while ((msg = msgb_dequeue(&queue))) {
bts->llc_dropped_frame();
msgb_free(msg);
}
m_queue_size = 0;
}
void gprs_llc::init()
{
INIT_LLIST_HEAD(&queue);
m_queue_size = 0;
m_avg_queue_delay = 0;
reset();
}
#define ALPHA 0.5f
struct msgb *gprs_llc::dequeue()
{
struct msgb *msg;
struct timeval *tv, tv_now, tv_result;
uint32_t lifetime;
msg = msgb_dequeue(&queue);
if (!msg)
return NULL;
m_queue_size -= 1;
/* take the second time */
gettimeofday(&tv_now, NULL);
tv = (struct timeval *)&msg->data[sizeof(*tv)];
timersub(&tv_now, tv, &tv_result);
lifetime = tv_result.tv_sec*1000 + tv_result.tv_usec/1000;
m_avg_queue_delay = m_avg_queue_delay * ALPHA + lifetime * (1-ALPHA);
return msg;
}
void gprs_llc::calc_pdu_lifetime(BTS *bts, const uint16_t pdu_delay_csec, struct timeval *tv)
{
uint16_t delay_csec;
if (bts->bts_data()->force_llc_lifetime)
delay_csec = bts->bts_data()->force_llc_lifetime;
else
delay_csec = pdu_delay_csec;
/* keep timestamp at 0 for infinite delay */
if (delay_csec == 0xffff) {
memset(tv, 0, sizeof(*tv));
return;
}
/* calculate timestamp of timeout */
struct timeval now, csec;
gettimeofday(&now, NULL);
csec.tv_usec = (delay_csec % 100) * 10000;
csec.tv_sec = delay_csec / 100;
timeradd(&now, &csec, tv);
}
bool gprs_llc::is_frame_expired(struct timeval *tv_now, struct timeval *tv)
{
/* Timeout is infinite */
if (tv->tv_sec == 0 && tv->tv_usec == 0)
return false;
return timercmp(tv_now, tv, >);
}

92
src/llc.h Normal file
View File

@ -0,0 +1,92 @@
/*
* Copyright (C) 2013 by Holger Hans Peter Freyther
*
* 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>
#include <string.h>
#define LLC_MAX_LEN 1543
/**
* I represent the LLC data to a MS
*/
struct gprs_llc {
static void calc_pdu_lifetime(BTS *bts, const uint16_t pdu_delay_csec, struct timeval *tv);
static bool is_frame_expired(struct timeval *now, struct timeval *tv);
void init();
void reset();
void reset_frame_space();
void enqueue(struct msgb *llc_msg);
struct msgb *dequeue();
void put_frame(const uint8_t *data, size_t len);
void append_frame(const uint8_t *data, size_t len);
void consume(size_t len);
void consume(uint8_t *data, size_t len);
void clear(BTS *bts);
uint16_t chunk_size() const;
uint16_t remaining_space() const;
uint16_t frame_length() const;
bool fits_in_current_frame(uint8_t size) const;
uint8_t frame[LLC_MAX_LEN]; /* current DL or UL frame */
uint16_t m_index; /* current write/read position of frame */
uint16_t m_length; /* len of current DL LLC_frame, 0 == no frame */
struct llist_head queue; /* queued LLC DL data */
uint32_t m_avg_queue_delay; /* Average delay of data going through the queue */
size_t m_queue_size;
};
inline uint16_t gprs_llc::chunk_size() const
{
return m_length - m_index;
}
inline uint16_t gprs_llc::remaining_space() const
{
return LLC_MAX_LEN - m_length;
}
inline uint16_t gprs_llc::frame_length() const
{
return m_length;
}
inline void gprs_llc::consume(size_t len)
{
m_index += len;
}
inline void gprs_llc::consume(uint8_t *data, size_t len)
{
/* copy and increment index */
memcpy(data, frame + m_index, len);
consume(len);
}
inline bool gprs_llc::fits_in_current_frame(uint8_t chunk_size) const
{
return m_length + chunk_size <= LLC_MAX_LEN;
}

View File

@ -37,6 +37,7 @@ extern "C" {
#include <gprs_debug.h>
#include <gprs_bssgp_pcu.h>
#include <pcuif_proto.h>
#include <bts.h>
#include <tbf.h>
// FIXME: move this, when changed from c++ to c.
@ -49,19 +50,6 @@ int l1if_pdch_req(void *obj, uint8_t ts, int is_ptcch, uint32_t fn,
extern void *tall_pcu_ctx;
// Variable for storage current FN.
int frame_number;
int get_current_fn()
{
return frame_number;
}
void set_current_fn(int fn)
{
frame_number = fn;
}
/*
* PCU messages
*/
@ -137,7 +125,7 @@ void pcu_l1if_tx_pdtch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
uint32_t fn, uint8_t block_nr)
{
#ifdef ENABLE_SYSMODSP
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_bts *bts = bts_main_data();
if (bts->trx[trx].fl1h)
l1if_pdch_req(bts->trx[trx].fl1h, ts, 0, fn, arfcn, block_nr,
@ -153,7 +141,7 @@ void pcu_l1if_tx_ptcch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
uint32_t fn, uint8_t block_nr)
{
#ifdef ENABLE_SYSMODSP
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_bts *bts = bts_main_data();
if (bts->trx[trx].fl1h)
l1if_pdch_req(bts->trx[trx].fl1h, ts, 1, fn, arfcn, block_nr,
@ -192,10 +180,13 @@ void pcu_l1if_tx_pch(bitvec * block, int plen, const char *imsi)
pcu_tx_data_req(0, 0, PCU_IF_SAPI_PCH, 0, 0, 0, data, 23+3);
}
extern "C" int pcu_rx_data_ind_pdtch(uint8_t trx, uint8_t ts, uint8_t *data,
extern "C" int pcu_rx_data_ind_pdtch(uint8_t trx_no, uint8_t ts_no, uint8_t *data,
uint8_t len, uint32_t fn, int8_t rssi)
{
return gprs_rlcmac_rcv_block(trx, ts, data, len, fn, rssi);
struct gprs_rlcmac_pdch *pdch;
pdch = &bts_main_data()->trx[trx_no].pdch[ts_no];
return pdch->rcv_block(data, len, fn, rssi);
}
static int pcu_rx_data_ind(struct gsm_pcu_if_data *data_ind)
@ -232,8 +223,7 @@ static int pcu_rx_data_cnf(struct gsm_pcu_if_data *data_cnf)
switch (data_cnf->sapi) {
case PCU_IF_SAPI_PCH:
if (data_cnf->data[2] == 0x3f)
rc = gprs_rlcmac_imm_ass_cnf(data_cnf->data,
data_cnf->fn);
BTS::main_bts()->rcv_imm_ass_cnf(data_cnf->data, data_cnf->fn);
break;
default:
LOGP(DL1IF, LOGL_ERROR, "Received PCU data confirm with "
@ -248,7 +238,8 @@ static int pcu_rx_data_cnf(struct gsm_pcu_if_data *data_cnf)
extern "C" int pcu_rx_rts_req_pdtch(uint8_t trx, uint8_t ts, uint16_t arfcn,
uint32_t fn, uint8_t block_nr)
{
return gprs_rlcmac_rcv_rts_block(trx, ts, arfcn, fn, block_nr);
return gprs_rlcmac_rcv_rts_block(bts_main_data(),
trx, ts, arfcn, fn, block_nr);
}
static int pcu_rx_rts_req(struct gsm_pcu_if_rts_req *rts_req)
@ -292,7 +283,8 @@ static int pcu_rx_rach_ind(struct gsm_pcu_if_rach_ind *rach_ind)
switch (rach_ind->sapi) {
case PCU_IF_SAPI_RACH:
rc = gprs_rlcmac_rcv_rach(rach_ind->ra, rach_ind->fn,
rc = BTS::main_bts()->rcv_rach(
rach_ind->ra, rach_ind->fn,
rach_ind->qta);
break;
default:
@ -304,31 +296,9 @@ static int pcu_rx_rach_ind(struct gsm_pcu_if_rach_ind *rach_ind)
return rc;
}
int flush_pdch(struct gprs_rlcmac_pdch *pdch, uint8_t trx, uint8_t ts)
{
struct gprs_rlcmac_paging *pag;
struct gprs_rlcmac_sba *sba, *sba2;
/* kick all TBF on slot */
gprs_rlcmac_tbf::free_all(pdch);
/* flush all pending paging messages */
while ((pag = gprs_rlcmac_dequeue_paging(pdch)))
talloc_free(pag);
llist_for_each_entry_safe(sba, sba2, &gprs_rlcmac_sbas, list) {
if (sba->trx == trx && sba->ts == ts) {
llist_del(&sba->list);
talloc_free(sba);
}
}
return 0;
}
static int pcu_rx_info_ind(struct gsm_pcu_if_info_ind *info_ind)
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_bts *bts = bts_main_data();
struct gprs_bssgp_pcu *pcu;
struct gprs_rlcmac_pdch *pdch;
struct in_addr ia;
@ -351,11 +321,8 @@ bssgp_failed:
/* free all TBF */
for (trx = 0; trx < 8; trx++) {
bts->trx[trx].arfcn = info_ind->trx[trx].arfcn;
for (ts = 0; ts < 8; ts++) {
if (bts->trx[trx].pdch[ts].enable)
flush_pdch(&bts->trx[trx].pdch[ts],
trx, ts);
}
for (ts = 0; ts < 8; ts++)
bts->trx[trx].pdch[ts].free_resources();
}
gprs_bssgp_destroy_or_exit();
return 0;
@ -474,7 +441,7 @@ bssgp_failed:
pdch = &bts->trx[trx].pdch[ts];
if ((info_ind->trx[trx].pdch_mask & (1 << ts))) {
/* FIXME: activate dynamically at RLCMAC */
if (!pdch->enable) {
if (!pdch->is_enabled()) {
#ifdef ENABLE_SYSMODSP
if ((info_ind->flags &
PCU_IF_FLAG_SYSMO))
@ -482,17 +449,16 @@ bssgp_failed:
bts->trx[trx].fl1h, ts);
#endif
pcu_tx_act_req(trx, ts, 1);
INIT_LLIST_HEAD(&pdch->paging_list);
pdch->enable = 1;
pdch->enable();
}
pdch->tsc = info_ind->trx[trx].tsc[ts];
LOGP(DL1IF, LOGL_INFO, "PDCH: trx=%d ts=%d\n",
trx, ts);
} else {
if (pdch->enable) {
if (pdch->is_enabled()) {
pcu_tx_act_req(trx, ts, 0);
pdch->enable = 0;
flush_pdch(pdch, trx, ts);
pdch->free_resources();
pdch->disable();
}
}
}
@ -503,9 +469,6 @@ bssgp_failed:
static int pcu_rx_time_ind(struct gsm_pcu_if_time_ind *time_ind)
{
struct gprs_rlcmac_tbf *tbf;
struct gprs_rlcmac_sba *sba, *sba2;
uint32_t elapsed;
uint8_t fn13 = time_ind->fn % 13;
/* omit frame numbers not starting at a MAC block */
@ -515,33 +478,7 @@ static int pcu_rx_time_ind(struct gsm_pcu_if_time_ind *time_ind)
// LOGP(DL1IF, LOGL_DEBUG, "Time indication received: %d\n",
// time_ind->fn % 52);
set_current_fn(time_ind->fn);
/* check for poll timeout */
llist_for_each_entry(tbf, &gprs_rlcmac_ul_tbfs, list) {
if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED) {
elapsed = (frame_number + 2715648 - tbf->poll_fn)
% 2715648;
if (elapsed >= 20 && elapsed < 2715400)
gprs_rlcmac_poll_timeout(tbf);
}
}
llist_for_each_entry(tbf, &gprs_rlcmac_dl_tbfs, list) {
if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED) {
elapsed = (frame_number + 2715648 - tbf->poll_fn)
% 2715648;
if (elapsed >= 20 && elapsed < 2715400)
gprs_rlcmac_poll_timeout(tbf);
}
}
llist_for_each_entry_safe(sba, sba2, &gprs_rlcmac_sbas, list) {
elapsed = (frame_number + 2715648 - sba->fn) % 2715648;
if (elapsed >= 20 && elapsed < 2715400) {
/* sba will be freed here */
gprs_rlcmac_sba_timeout(sba);
}
}
BTS::main_bts()->set_current_frame_number(time_ind->fn);
return 0;
}
@ -550,7 +487,7 @@ static int pcu_rx_pag_req(struct gsm_pcu_if_pag_req *pag_req)
LOGP(DL1IF, LOGL_DEBUG, "Paging request received: chan_needed=%d "
"length=%d\n", pag_req->chan_needed, pag_req->identity_lv[0]);
return gprs_rlcmac_add_paging(pag_req->chan_needed,
return BTS::main_bts()->add_paging(pag_req->chan_needed,
pag_req->identity_lv);
}

View File

@ -32,8 +32,6 @@ extern "C" {
#ifdef __cplusplus
}
int get_current_fn();
void pcu_l1if_tx_pdtch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
uint32_t fn, uint8_t block_nr);
void pcu_l1if_tx_ptcch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,

View File

@ -27,27 +27,23 @@
#include <getopt.h>
#include <signal.h>
#include <sched.h>
#include <bts.h>
extern "C" {
#include "pcu_vty.h"
#include <osmocom/vty/telnet_interface.h>
#include <osmocom/vty/logging.h>
}
struct gprs_rlcmac_bts *gprs_rlcmac_bts;
extern struct gprs_nsvc *nsvc;
uint16_t spoof_mcc = 0, spoof_mnc = 0;
static int config_given = 0;
static const char *config_file = "osmo-pcu.cfg";
static char *config_file = strdup("osmo-pcu.cfg");
extern struct vty_app_info pcu_vty_info;
void *tall_pcu_ctx;
extern void *bv_tall_ctx;
static int quit = 0;
static int rt_prio = -1;
#ifdef DEBUG_DIAGRAM
extern struct timeval diagram_time;
#endif
static void print_help()
{
printf( "Some useful options:\n"
@ -91,6 +87,7 @@ static void handle_options(int argc, char **argv)
exit(0);
break;
case 'c':
free(config_file);
config_file = strdup(optarg);
config_given = 1;
break;
@ -163,10 +160,7 @@ int main(int argc, char *argv[])
return -ENOMEM;
bv_tall_ctx = tall_pcu_ctx;
bts = gprs_rlcmac_bts = talloc_zero(tall_pcu_ctx,
struct gprs_rlcmac_bts);
if (!gprs_rlcmac_bts)
return -ENOMEM;
bts = bts_main_data();
bts->fc_interval = 1;
bts->initial_cs_dl = bts->initial_cs_ul = 1;
bts->cs1 = 1;
@ -244,18 +238,13 @@ int main(int argc, char *argv[])
osmo_gsm_timers_update();
osmo_select_main(0);
#ifdef DEBUG_DIAGRAM
gettimeofday(&diagram_time, NULL);
#endif
}
telnet_exit();
pcu_l1if_close();
flush_timing_advance();
talloc_free(gprs_rlcmac_bts);
bts->bts->timing_advance()->flush();
talloc_report_full(tall_pcu_ctx, stderr);
talloc_free(tall_pcu_ctx);

View File

@ -6,6 +6,7 @@
#include <osmocom/core/linuxlist.h>
#include "pcu_vty.h"
#include "gprs_rlcmac.h"
#include "bts.h"
enum node_type pcu_vty_go_parent(struct vty *vty)
{
@ -76,7 +77,7 @@ gDEFUN(ournode_end, ournode_end_cmd, "end",
static int config_write_pcu(struct vty *vty)
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_bts *bts = bts_main_data();
vty_out(vty, "pcu%s", VTY_NEWLINE);
vty_out(vty, " flow-control-interval %d%s", bts->fc_interval,
@ -102,6 +103,7 @@ static int config_write_pcu(struct vty *vty)
vty_out(vty, " alpha %d%s", bts->alpha, VTY_NEWLINE);
vty_out(vty, " gamma %d%s", bts->gamma * 2, VTY_NEWLINE);
return CMD_SUCCESS;
}
/* per-BTS configuration */
@ -121,7 +123,7 @@ DEFUN(cfg_pcu_fc_interval,
"Interval between sending subsequent Flow Control PDUs\n"
"Interval time in seconds\n")
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->fc_interval = atoi(argv[0]);
@ -134,7 +136,7 @@ DEFUN(cfg_pcu_cs,
"Set the Coding Scheme to be used, (overrides BTS config)\n"
"Initial CS used\nAlternative uplink CS")
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_bts *bts = bts_main_data();
uint8_t cs = atoi(argv[0]);
bts->force_cs = 1;
@ -152,7 +154,7 @@ DEFUN(cfg_pcu_no_cs,
"no cs",
NO_STR "Don't force given Coding Scheme, (use BTS config)\n")
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->force_cs = 0;
@ -168,7 +170,7 @@ DEFUN(cfg_pcu_queue_lifetime,
"queue lifetime <1-65534>",
QUEUE_STR LIFETIME_STR "Lifetime in centi-seconds")
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_bts *bts = bts_main_data();
uint8_t csec = atoi(argv[0]);
bts->force_llc_lifetime = csec;
@ -181,7 +183,7 @@ DEFUN(cfg_pcu_queue_lifetime_inf,
"queue lifetime infinite",
QUEUE_STR LIFETIME_STR "Infinite lifetime")
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->force_llc_lifetime = 0xffff;
@ -194,7 +196,7 @@ DEFUN(cfg_pcu_no_queue_lifetime,
NO_STR QUEUE_STR "Disable lifetime limit of LLC frame (use value given "
"by SGSN)\n")
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->force_llc_lifetime = 0;
@ -208,7 +210,7 @@ DEFUN(cfg_pcu_alloc,
"PACCH\nSingle slot is assigned only\nMultiple slots are assigned for "
"semi-duplex operation")
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_bts *bts = bts_main_data();
switch (argv[0][0]) {
case 'a':
@ -227,7 +229,7 @@ DEFUN(cfg_pcu_two_phase,
"two-phase-access",
"Force two phase access when MS requests single phase access\n")
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->force_two_phase = 1;
@ -239,7 +241,7 @@ DEFUN(cfg_pcu_no_two_phase,
"no two-phase-access",
NO_STR "Only use two phase access when requested my MS\n")
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->force_two_phase = 0;
@ -253,7 +255,7 @@ DEFUN(cfg_pcu_alpha,
"NOTE: Be sure to set Alpha value at System information 13 too.\n"
"Alpha in units of 0.1\n")
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->alpha = atoi(argv[0]);
@ -266,13 +268,43 @@ DEFUN(cfg_pcu_gamma,
"Gamma parameter for MS power control in units of dB (see TS 05.08)\n"
"Gamma in even unit of dBs\n")
{
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->gamma = atoi(argv[0]) / 2;
return CMD_SUCCESS;
}
DEFUN(show_bts_stats,
show_bts_stats_cmd,
"show bts statistics",
SHOW_STR "BTS related functionality\nStatistics\n")
{
vty_out_rate_ctr_group(vty, "", bts_main_data_stats());
return CMD_SUCCESS;
}
DEFUN(show_tbf,
show_tbf_cmd,
"show tbf all",
SHOW_STR "information about all current TBFs\n")
{
struct gprs_rlcmac_bts *bts = bts_main_data();
struct llist_head *tbf;
vty_out(vty, "UL TBFs%s", VTY_NEWLINE);
llist_for_each(tbf, &bts->ul_tbfs) {
tbf_print_vty_info(vty, tbf);
}
vty_out(vty, "%sDL TBFs%s", VTY_NEWLINE, VTY_NEWLINE);
llist_for_each(tbf, &bts->dl_tbfs) {
tbf_print_vty_info(vty, tbf);
}
return CMD_SUCCESS;
}
static const char pcu_copyright[] =
"Copyright (C) 2012 by Ivan Kluchnikov <kluchnikovi@gmail.com> and \r\n"
" Andreas Eversberg <jolly@eversberg.eu>\r\n"
@ -310,5 +342,8 @@ int pcu_vty_init(const struct log_info *cat)
install_element(PCU_NODE, &cfg_pcu_gamma_cmd);
install_element(PCU_NODE, &ournode_end_cmd);
install_element_ve(&show_bts_stats_cmd);
install_element_ve(&show_tbf_cmd);
return 0;
}

63
src/poll_controller.cpp Normal file
View File

@ -0,0 +1,63 @@
/* poll_controller.h
*
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
* Copyright (C) 2013 by Holger Hans Peter Freyther
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <poll_controller.h>
#include <bts.h>
#include <tbf.h>
PollController::PollController(BTS& bts)
: m_bts(bts)
{}
void PollController::expireTimedout(int frame_number)
{
struct gprs_rlcmac_bts *bts = m_bts.bts_data();
struct gprs_rlcmac_tbf *tbf;
struct gprs_rlcmac_sba *sba, *sba2;
uint32_t elapsed;
/* check for poll timeout */
llist_for_each_entry(tbf, &bts->ul_tbfs, list) {
if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED) {
elapsed = (frame_number + 2715648 - tbf->poll_fn)
% 2715648;
if (elapsed >= 20 && elapsed < 2715400)
tbf->poll_timeout();
}
}
llist_for_each_entry(tbf, &bts->dl_tbfs, list) {
if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED) {
elapsed = (frame_number + 2715648 - tbf->poll_fn)
% 2715648;
if (elapsed >= 20 && elapsed < 2715400)
tbf->poll_timeout();
}
}
llist_for_each_entry_safe(sba, sba2, &m_bts.sba()->m_sbas, list) {
elapsed = (frame_number + 2715648 - sba->fn) % 2715648;
if (elapsed >= 20 && elapsed < 2715400) {
/* sba will be freed here */
m_bts.sba()->timeout(sba);
}
}
}

46
src/poll_controller.h Normal file
View File

@ -0,0 +1,46 @@
/* poll_controller.h
*
* Copyright (C) 2013 by Holger Hans Peter Freyther
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#pragma once
struct gprs_rlcmac_bts;
class BTS;
/**
* I belong to a BTS and I am responsible for finding TBFs and
* SBAs that should have been polled and execute the timeout
* action on them.
*/
class PollController {
public:
PollController(BTS& bts);
void expireTimedout(int frame_number);
private:
BTS& m_bts;
private:
/* disable copying to avoid slicing */
PollController(const PollController&);
PollController& operator=(const PollController&);
};

220
src/rlc.cpp Normal file
View File

@ -0,0 +1,220 @@
/*
* Copyright (C) 2013 by Holger Hans Peter Freyther
*
* 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 "tbf.h"
#include "bts.h"
#include "gprs_debug.h"
extern "C" {
#include <osmocom/core/utils.h>
}
uint8_t *gprs_rlc_data::prepare(size_t block_data_len)
{
/* todo.. only set it once if it turns out to be a bottleneck */
memset(block, 0x0, sizeof(block));
memset(block, 0x2b, block_data_len);
return block;
}
void gprs_rlc_data::put_data(const uint8_t *data, size_t data_len)
{
memcpy(block, data, data_len);
len = data_len;
}
void gprs_rlc_v_b::reset()
{
for (size_t i = 0; i < ARRAY_SIZE(m_v_b); ++i)
mark_invalid(i);
}
int gprs_rlc_dl_window::resend_needed()
{
for (uint16_t bsn = v_a(); bsn != v_s(); bsn = (bsn + 1) & mod_sns()) {
if (m_v_b.is_nacked(bsn) || m_v_b.is_resend(bsn))
return bsn;
}
return -1;
}
int gprs_rlc_dl_window::mark_for_resend()
{
int resend = 0;
for (uint16_t bsn = v_a(); bsn != v_s(); bsn = (bsn + 1) & mod_sns()) {
if (m_v_b.is_unacked(bsn)) {
/* mark to be re-send */
m_v_b.mark_resend(bsn);
resend += 1;
}
}
return resend;
}
int gprs_rlc_dl_window::count_unacked()
{
uint16_t unacked = 0;
uint16_t bsn;
for (bsn = v_a(); bsn != v_s(); bsn = (bsn + 1) & mod_sns()) {
if (!m_v_b.is_acked(bsn))
unacked += 1;
}
return unacked;
}
static uint16_t bitnum_to_bsn(int bitnum, uint16_t ssn, uint16_t mod_sns)
{
return (ssn - 1 - bitnum) & mod_sns;
}
void gprs_rlc_dl_window::update(BTS *bts, char *show_rbb, uint8_t ssn,
uint16_t *lost, uint16_t *received)
{
/* SSN - 1 is in range V(A)..V(S)-1 */
for (int bitpos = 0; bitpos < ws(); bitpos++) {
uint16_t bsn = bitnum_to_bsn(bitpos, ssn, mod_sns());
if (bsn == ((v_a() - 1) & mod_sns()))
break;
if (show_rbb[ws() - 1 - bitpos] == 'R') {
LOGP(DRLCMACDL, LOGL_DEBUG, "- got ack for BSN=%d\n", bsn);
if (!m_v_b.is_acked(bsn))
*received += 1;
m_v_b.mark_acked(bsn);
} else {
LOGP(DRLCMACDL, LOGL_DEBUG, "- got NACK for BSN=%d\n", bsn);
m_v_b.mark_nacked(bsn);
bts->rlc_nacked();
*lost += 1;
}
}
}
int gprs_rlc_dl_window::move_window()
{
int i;
uint16_t bsn;
int moved = 0;
for (i = 0, bsn = v_a(); bsn != v_s(); i++, bsn = (bsn + 1) & mod_sns()) {
if (m_v_b.is_acked(bsn)) {
m_v_b.mark_invalid(bsn);
moved += 1;
} else
break;
}
return moved;
}
void gprs_rlc_dl_window::show_state(char *show_v_b)
{
int i;
uint16_t bsn;
for (i = 0, bsn = v_a(); bsn != v_s(); i++, bsn = (bsn + 1) & mod_sns()) {
uint16_t index = bsn & mod_sns_half();
switch(m_v_b.get_state(index)) {
case GPRS_RLC_DL_BSN_INVALID:
show_v_b[i] = 'I';
break;
case GPRS_RLC_DL_BSN_ACKED:
show_v_b[i] = 'A';
break;
case GPRS_RLC_DL_BSN_RESEND:
show_v_b[i] = 'X';
break;
case GPRS_RLC_DL_BSN_NACKED:
show_v_b[i] = 'N';
break;
default:
show_v_b[i] = '?';
}
}
show_v_b[i] = '\0';
}
void gprs_rlc_v_n::reset()
{
for (size_t i = 0; i < ARRAY_SIZE(m_v_n); ++i)
m_v_n[i] = GPRS_RLC_UL_BSN_INVALID;
}
/* Update the receive block bitmap */
void gprs_rlc_ul_window::update_rbb(char *rbb)
{
int i;
for (i=0; i < ws(); i++) {
if (m_v_n.is_received(ssn()-1-i))
rbb[ws()-1-i] = 'R';
else
rbb[ws()-1-i] = 'I';
}
}
/* Raise V(R) to highest received sequence number not received. */
void gprs_rlc_ul_window::raise_v_r(const uint16_t bsn)
{
uint16_t offset_v_r;
offset_v_r = (bsn + 1 - v_r()) & mod_sns();
/* Positive offset, so raise. */
if (offset_v_r < (sns() >> 1)) {
while (offset_v_r--) {
if (offset_v_r) /* all except the received block */
m_v_n.mark_missing(v_r());
raise_v_r_to(1);
}
LOGP(DRLCMACUL, LOGL_DEBUG, "- Raising V(R) to %d\n", v_r());
}
}
/*
* Raise V(Q) if possible. This is looped until there is a gap
* (non received block) or the window is empty.
*/
uint16_t gprs_rlc_ul_window::raise_v_q()
{
uint16_t count = 0;
while (v_q() != v_r()) {
if (!m_v_n.is_received(v_q()))
break;
LOGP(DRLCMACUL, LOGL_DEBUG, "- Taking block %d out, raising "
"V(Q) to %d\n", v_q(), (v_q() + 1) & mod_sns());
raise_v_q(1);
count += 1;
}
return count;
}
uint16_t gprs_rlc_ul_window::receive_bsn(const uint16_t bsn)
{
m_v_n.mark_received(bsn);
raise_v_r(bsn);
return raise_v_q();
}

422
src/rlc.h Normal file
View File

@ -0,0 +1,422 @@
/* 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>
#define RLC_MAX_SNS 128 /* GPRS, must be power of 2 */
#define RLC_MAX_WS 64 /* max window size */
#define RLC_MAX_LEN 54 /* CS-4 including spare bits */
class BTS;
struct gprs_rlc_v_n;
/* The state of a BSN in the send/receive window */
enum gprs_rlc_ul_bsn_state {
GPRS_RLC_UL_BSN_INVALID,
GPRS_RLC_UL_BSN_RECEIVED,
GPRS_RLC_UL_BSN_MISSING,
GPRS_RLC_UL_BSN_MAX,
};
enum gprs_rlc_dl_bsn_state {
GPRS_RLC_DL_BSN_INVALID,
GPRS_RLC_DL_BSN_NACKED,
GPRS_RLC_DL_BSN_ACKED,
GPRS_RLC_DL_BSN_UNACKED,
GPRS_RLC_DL_BSN_RESEND,
GPRS_RLC_DL_BSN_MAX,
};
static inline uint16_t mod_sns_half()
{
return (RLC_MAX_SNS / 2) - 1;
}
struct gprs_rlc_data {
uint8_t *prepare(size_t block_data_length);
void put_data(const uint8_t *data, size_t len);
/* block history */
uint8_t block[RLC_MAX_LEN];
/* block len of history */
uint8_t len;
};
/*
* I hold the currently transferred blocks and will provide
* the routines to manipulate these arrays.
*/
struct gprs_rlc {
gprs_rlc_data *block(int bsn);
gprs_rlc_data m_blocks[RLC_MAX_SNS/2];
};
/**
* TODO: for GPRS/EDGE maybe make sns a template parameter
* so we create specialized versions...
*/
struct gprs_rlc_v_b {
/* Check for an individual frame */
bool is_unacked(int bsn) const;
bool is_nacked(int bsn) const;
bool is_acked(int bsn) const;
bool is_resend(int bsn) const;
bool is_invalid(int bsn) const;
gprs_rlc_dl_bsn_state get_state(int bsn) const;
/* Mark a RLC frame for something */
void mark_unacked(int bsn);
void mark_nacked(int bsn);
void mark_acked(int bsn);
void mark_resend(int bsn);
void mark_invalid(int bsn);
void reset();
private:
bool is_state(int bsn, const gprs_rlc_dl_bsn_state state) const;
void mark(int bsn, const gprs_rlc_dl_bsn_state state);
gprs_rlc_dl_bsn_state m_v_b[RLC_MAX_SNS/2]; /* acknowledge state array */
};
/**
* TODO: The UL/DL code could/should share a baseclass but
* we are using llist_for_each_entry for the TBF which
* requires everything which creates a requirement for a POD
* type and in < C++11 something that is using even if the
* most simple form of inheritance is not a POD anymore.
*/
struct gprs_rlc_dl_window {
const uint16_t mod_sns() const;
const uint16_t sns() const;
const uint16_t ws() const;
bool window_stalled() const;
bool window_empty() const;
void increment_send();
void raise(int moves);
const uint16_t v_s() const;
const uint16_t v_s_mod(int offset) const;
const uint16_t v_a() const;
const int16_t distance() const;
/* Methods to manage reception */
int resend_needed();
int mark_for_resend();
void update(BTS *bts, char *show_rbb, uint8_t ssn,
uint16_t *lost, uint16_t *received);
int move_window();
void show_state(char *show_rbb);
int count_unacked();
uint16_t m_v_s; /* send state */
uint16_t m_v_a; /* ack state */
gprs_rlc_v_b m_v_b;
};
struct gprs_rlc_v_n {
void reset();
void mark_received(int bsn);
void mark_missing(int bsn);
bool is_received(int bsn) const;
gprs_rlc_ul_bsn_state state(int bsn) const;
private:
bool is_state(int bsn, const gprs_rlc_ul_bsn_state state) const;
void mark(int bsn, const gprs_rlc_ul_bsn_state state);
gprs_rlc_ul_bsn_state m_v_n[RLC_MAX_SNS/2]; /* receive state array */
};
struct gprs_rlc_ul_window {
const uint16_t mod_sns() const;
const uint16_t sns() const;
const uint16_t ws() const;
const uint16_t v_r() const;
const uint16_t v_q() const;
const uint16_t ssn() const;
bool is_in_window(uint8_t bsn) const;
void update_rbb(char *rbb);
void raise_v_r_to(int moves);
void raise_v_r(const uint16_t bsn);
uint16_t raise_v_q();
void raise_v_q(int);
uint16_t receive_bsn(const uint16_t bsn);
uint16_t m_v_r; /* receive state */
uint16_t m_v_q; /* receive window state */
gprs_rlc_v_n m_v_n;
};
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));
}
inline bool gprs_rlc_v_b::is_state(int bsn, const gprs_rlc_dl_bsn_state type) const
{
return m_v_b[bsn & mod_sns_half()] == type;
}
inline void gprs_rlc_v_b::mark(int bsn, const gprs_rlc_dl_bsn_state type)
{
m_v_b[bsn & mod_sns_half()] = type;
}
inline bool gprs_rlc_v_b::is_nacked(int bsn) const
{
return is_state(bsn, GPRS_RLC_DL_BSN_NACKED);
}
inline bool gprs_rlc_v_b::is_acked(int bsn) const
{
return is_state(bsn, GPRS_RLC_DL_BSN_ACKED);
}
inline bool gprs_rlc_v_b::is_unacked(int bsn) const
{
return is_state(bsn, GPRS_RLC_DL_BSN_UNACKED);
}
inline bool gprs_rlc_v_b::is_resend(int bsn) const
{
return is_state(bsn, GPRS_RLC_DL_BSN_RESEND);
}
inline bool gprs_rlc_v_b::is_invalid(int bsn) const
{
return is_state(bsn, GPRS_RLC_DL_BSN_INVALID);
}
inline gprs_rlc_dl_bsn_state gprs_rlc_v_b::get_state(int bsn) const
{
return m_v_b[bsn & mod_sns_half()];
}
inline void gprs_rlc_v_b::mark_resend(int bsn)
{
return mark(bsn, GPRS_RLC_DL_BSN_RESEND);
}
inline void gprs_rlc_v_b::mark_unacked(int bsn)
{
return mark(bsn, GPRS_RLC_DL_BSN_UNACKED);
}
inline void gprs_rlc_v_b::mark_acked(int bsn)
{
return mark(bsn, GPRS_RLC_DL_BSN_ACKED);
}
inline void gprs_rlc_v_b::mark_nacked(int bsn)
{
return mark(bsn, GPRS_RLC_DL_BSN_NACKED);
}
inline void gprs_rlc_v_b::mark_invalid(int bsn)
{
return mark(bsn, GPRS_RLC_DL_BSN_INVALID);
}
inline const uint16_t gprs_rlc_dl_window::sns() const
{
return RLC_MAX_SNS;
}
inline const uint16_t gprs_rlc_dl_window::ws() const
{
return RLC_MAX_WS;
}
inline const uint16_t gprs_rlc_dl_window::mod_sns() const
{
return sns() - 1;
}
inline const uint16_t gprs_rlc_dl_window::v_s() const
{
return m_v_s;
}
inline const uint16_t gprs_rlc_dl_window::v_s_mod(int offset) const
{
return (m_v_s + offset) & mod_sns();
}
inline const uint16_t gprs_rlc_dl_window::v_a() const
{
return m_v_a;
}
inline bool gprs_rlc_dl_window::window_stalled() const
{
return ((m_v_s - m_v_a) & mod_sns()) == ws();
}
inline bool gprs_rlc_dl_window::window_empty() const
{
return m_v_s == m_v_a;
}
inline void gprs_rlc_dl_window::increment_send()
{
m_v_s = (m_v_s + 1) & mod_sns();
}
inline void gprs_rlc_dl_window::raise(int moves)
{
m_v_a = (m_v_a + moves) & mod_sns();
}
inline const int16_t gprs_rlc_dl_window::distance() const
{
return (m_v_s - m_v_a) & mod_sns();
}
inline bool gprs_rlc_ul_window::is_in_window(uint8_t bsn) const
{
uint16_t offset_v_q;
/* current block relative to lowest unreceived block */
offset_v_q = (bsn - m_v_q) & mod_sns();
/* If out of window (may happen if blocks below V(Q) are received
* again. */
return offset_v_q < ws();
}
inline const uint16_t gprs_rlc_ul_window::sns() const
{
return RLC_MAX_SNS;
}
inline const uint16_t gprs_rlc_ul_window::ws() const
{
return RLC_MAX_WS;
}
inline const uint16_t gprs_rlc_ul_window::mod_sns() const
{
return sns() - 1;
}
inline const uint16_t gprs_rlc_ul_window::v_r() const
{
return m_v_r;
}
inline const uint16_t gprs_rlc_ul_window::v_q() const
{
return m_v_q;
}
inline const uint16_t gprs_rlc_ul_window::ssn() const
{
return m_v_r;
}
inline void gprs_rlc_ul_window::raise_v_r_to(int moves)
{
m_v_r = (m_v_r + moves) & mod_sns();
}
inline void gprs_rlc_ul_window::raise_v_q(int incr)
{
m_v_q = (m_v_q + incr) & mod_sns();
}
inline void gprs_rlc_v_n::mark_received(int bsn)
{
return mark(bsn, GPRS_RLC_UL_BSN_RECEIVED);
}
inline void gprs_rlc_v_n::mark_missing(int bsn)
{
return mark(bsn, GPRS_RLC_UL_BSN_MISSING);
}
inline bool gprs_rlc_v_n::is_received(int bsn) const
{
return is_state(bsn, GPRS_RLC_UL_BSN_RECEIVED);
}
inline bool gprs_rlc_v_n::is_state(int bsn, gprs_rlc_ul_bsn_state type) const
{
return m_v_n[bsn & mod_sns_half()] == type;
}
inline void gprs_rlc_v_n::mark(int bsn, gprs_rlc_ul_bsn_state type)
{
m_v_n[bsn & mod_sns_half()] = type;
}
inline gprs_rlc_ul_bsn_state gprs_rlc_v_n::state(int bsn) const
{
return m_v_n[bsn & mod_sns_half()];
}
inline gprs_rlc_data *gprs_rlc::block(int bsn)
{
return &m_blocks[bsn & mod_sns_half()];
}

149
src/sba.cpp Normal file
View File

@ -0,0 +1,149 @@
/* sba.cpp
*
* Copyright (C) 2012 Ivan Klyuchnikov
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
* Copyright (C) 2013 by Holger Hans Peter Freyther
*
* 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 <sba.h>
#include <gprs_rlcmac.h>
#include <gprs_debug.h>
#include <bts.h>
extern "C" {
#include <osmocom/core/talloc.h>
}
#include <errno.h>
extern void *tall_pcu_ctx;
/* starting time for assigning single slot
* This offset must be a multiple of 13. */
#define AGCH_START_OFFSET 52
SBAController::SBAController(BTS &bts)
: m_bts(bts)
{
INIT_LLIST_HEAD(&m_sbas);
}
int SBAController::alloc(
uint8_t *_trx, uint8_t *_ts, uint32_t *_fn, uint8_t ta)
{
struct gprs_rlcmac_pdch *pdch;
struct gprs_rlcmac_sba *sba;
uint8_t trx, ts;
uint32_t fn;
sba = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_sba);
if (!sba)
return -ENOMEM;
for (trx = 0; trx < 8; trx++) {
for (ts = 0; ts < 8; ts++) {
pdch = &m_bts.bts_data()->trx[trx].pdch[ts];
if (!pdch->is_enabled())
continue;
break;
}
if (ts < 8)
break;
}
if (trx == 8) {
LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH available.\n");
talloc_free(sba);
return -EINVAL;
}
fn = (pdch->last_rts_fn + AGCH_START_OFFSET) % 2715648;
sba->trx_no = trx;
sba->ts_no = ts;
sba->fn = fn;
sba->ta = ta;
llist_add(&sba->list, &m_sbas);
m_bts.sba_allocated();
*_trx = trx;
*_ts = ts;
*_fn = fn;
return 0;
}
gprs_rlcmac_sba *SBAController::find(uint8_t trx, uint8_t ts, uint32_t fn)
{
struct gprs_rlcmac_sba *sba;
llist_for_each_entry(sba, &m_sbas, list) {
if (sba->trx_no == trx && sba->ts_no == ts && sba->fn == fn)
return sba;
}
return NULL;
}
gprs_rlcmac_sba *SBAController::find(const gprs_rlcmac_pdch *pdch, uint32_t fn)
{
return find(pdch->trx_no(), pdch->ts_no, fn);
}
uint32_t SBAController::sched(uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr)
{
uint32_t sba_fn;
struct gprs_rlcmac_sba *sba;
/* check special TBF for events */
sba_fn = fn + 4;
if ((block_nr % 3) == 2)
sba_fn ++;
sba_fn = sba_fn % 2715648;
sba = find(trx, ts, sba_fn);
if (sba)
return sba_fn;
return 0xffffffff;
}
int SBAController::timeout(struct gprs_rlcmac_sba *sba)
{
LOGP(DRLCMAC, LOGL_NOTICE, "Poll timeout for SBA\n");
m_bts.sba_timedout();
free_sba(sba);
return 0;
}
void SBAController::free_sba(gprs_rlcmac_sba *sba)
{
m_bts.sba_freed();
llist_del(&sba->list);
talloc_free(sba);
}
void SBAController::free_resources(struct gprs_rlcmac_pdch *pdch)
{
struct gprs_rlcmac_sba *sba, *sba2;
const uint8_t trx_no = pdch->trx->trx_no;
const uint8_t ts_no = pdch->ts_no;
llist_for_each_entry_safe(sba, sba2, &m_sbas, list) {
if (sba->trx_no == trx_no && sba->ts_no == ts_no)
free_sba(sba);
}
}

69
src/sba.h Normal file
View File

@ -0,0 +1,69 @@
/*
*
* Copyright (C) 2012 Ivan Klyuchnikov
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
* Copyright (C) 2013 by Holger Hans Peter Freyther
*
* 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" {
#include <osmocom/core/linuxlist.h>
}
class BTS;
class PollController;
struct gprs_rlcmac_sba;
struct gprs_rlcmac_pdch;
/*
* single block allocation entry
*/
struct gprs_rlcmac_sba {
struct llist_head list;
uint8_t trx_no;
uint8_t ts_no;
uint32_t fn;
uint8_t ta;
};
/**
* I help to manage SingleBlockAssignment (SBA).
*
* TODO: Add a flush method..
*/
class SBAController {
friend class PollController;
public:
SBAController(BTS &bts);
int alloc(uint8_t *_trx, uint8_t *_ts, uint32_t *_fn, uint8_t ta);
gprs_rlcmac_sba *find(uint8_t trx, uint8_t ts, uint32_t fn);
gprs_rlcmac_sba *find(const gprs_rlcmac_pdch *pdch, uint32_t fn);
uint32_t sched(uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr);
int timeout(struct gprs_rlcmac_sba *sba);
void free_resources(struct gprs_rlcmac_pdch *pdch);
void free_sba(gprs_rlcmac_sba *sba);
private:
BTS &m_bts;
llist_head m_sbas;
};

View File

@ -24,6 +24,7 @@
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>

View File

@ -170,8 +170,8 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1h,
gsmtap_send(fl1h->gsmtap, data_ind->u16Arfcn | GSMTAP_ARFCN_F_UPLINK,
data_ind->u8Tn, GSMTAP_CHANNEL_PACCH, 0,
data_ind->u32Fn, 0, 0, data_ind->msgUnitParam.u8Buffer,
data_ind->msgUnitParam.u8Size);
data_ind->u32Fn, 0, 0, data_ind->msgUnitParam.u8Buffer+1,
data_ind->msgUnitParam.u8Size-1);
DEBUGP(DL1IF, "Rx PH-DATA.ind %s (hL2 %08x): %s",
get_value_string(femtobts_l1sapi_names, data_ind->sapi),

View File

@ -36,6 +36,7 @@ extern "C" {
#include <gprs_debug.h>
#include <gprs_bssgp_pcu.h>
#include <pcuif_proto.h>
#include <bts.h>
#include <tbf.h>
extern void *tall_pcu_ctx;
@ -81,7 +82,7 @@ int pcu_sock_send(struct msgb *msg)
static void pcu_sock_close(struct pcu_sock_state *state, int lost)
{
struct osmo_fd *bfd = &state->conn_bfd;
struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
struct gprs_rlcmac_bts *bts = bts_main_data();
uint8_t trx, ts;
LOGP(DL1IF, LOGL_NOTICE, "PCU socket has %s connection\n",
@ -106,7 +107,8 @@ static void pcu_sock_close(struct pcu_sock_state *state, int lost)
}
#endif
for (ts = 0; ts < 8; ts++)
bts->trx[trx].pdch[ts].enable = 0;
bts->trx[trx].pdch[ts].disable();
#warning "NOT ALL RESOURCES are freed in this case... inconsistent with the other code. Share the code with pcu_l1if.c for the reset."
gprs_rlcmac_tbf::free_all(&bts->trx[trx]);
}

142
src/ta.cpp Normal file
View File

@ -0,0 +1,142 @@
/*
* ta.cpp timing advance handling
* Copyright (C) 2012 Ivan Klyuchnikov
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
* Copyright (C) 2013 by Holger Hans Peter Freyther
*
* 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 <ta.h>
#include <gprs_rlcmac.h>
extern "C" {
#include <osmocom/core/talloc.h>
}
#include <errno.h>
extern void *tall_pcu_ctx;
/*
* timing advance memory
*/
/* enable to debug timing advance memory */
//#define DEBUG_TA
struct gprs_rlcmac_ta {
struct llist_head list;
uint32_t tlli;
uint8_t ta;
};
TimingAdvance::TimingAdvance()
: m_ta_len(0)
{
INIT_LLIST_HEAD(&m_ta_list);
}
/* remember timing advance of a given TLLI */
int TimingAdvance::remember(uint32_t tlli, uint8_t ta)
{
struct gprs_rlcmac_ta *ta_entry;
/* check for existing entry */
llist_for_each_entry(ta_entry, &m_ta_list, list) {
if (ta_entry->tlli == tlli) {
#ifdef DEBUG_TA
fprintf(stderr, "update %08x %d\n", tlli, ta);
#endif
ta_entry->ta = ta;
/* relink to end of list */
llist_del(&ta_entry->list);
llist_add_tail(&ta_entry->list, &m_ta_list);
return 0;
}
}
#ifdef DEBUG_TA
fprintf(stderr, "remember %08x %d\n", tlli, ta);
#endif
/* if list is full, remove oldest entry */
if (m_ta_len == 30) {
ta_entry = llist_entry(m_ta_list.next,
struct gprs_rlcmac_ta, list);
llist_del(&ta_entry->list);
talloc_free(ta_entry);
m_ta_len--;
}
/* create new TA entry */
ta_entry = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_ta);
if (!ta_entry)
return -ENOMEM;
ta_entry->tlli = tlli;
ta_entry->ta = ta;
llist_add_tail(&ta_entry->list, &m_ta_list);
m_ta_len++;
return 0;
}
int TimingAdvance::recall(uint32_t tlli)
{
struct gprs_rlcmac_ta *ta_entry;
uint8_t ta;
llist_for_each_entry(ta_entry, &m_ta_list, list) {
if (ta_entry->tlli == tlli) {
ta = ta_entry->ta;
#ifdef DEBUG_TA
fprintf(stderr, "recall %08x %d\n", tlli, ta);
#endif
return ta;
}
}
#ifdef DEBUG_TA
fprintf(stderr, "no entry for %08x\n", tlli);
#endif
return -EINVAL;
}
int TimingAdvance::flush()
{
struct gprs_rlcmac_ta *ta_entry;
int count = 0;
while (!llist_empty(&m_ta_list)) {
ta_entry = llist_entry(m_ta_list.next,
struct gprs_rlcmac_ta, list);
#ifdef DEBUG_TA
fprintf(stderr, "flush entry %08x %d\n", ta_entry->tlli,
ta_entry->ta);
#endif
llist_del(&ta_entry->list);
talloc_free(ta_entry);
count++;
}
m_ta_len = 0;
return count;
}
int TimingAdvance::update(uint32_t, uint32_t new_tlli, uint8_t ta)
{
/* for now just add the new entry and don't bother about the old one */
return remember(new_tlli, ta);
}

40
src/ta.h Normal file
View File

@ -0,0 +1,40 @@
/*
* Copyright (C) 2012 Ivan Klyuchnikov
* Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
* Copyright (C) 2013 by Holger Hans Peter Freyther
*
* 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
extern "C" {
#include <osmocom/core/linuxlist.h>
}
#include <stdint.h>
class TimingAdvance {
public:
TimingAdvance();
int update(uint32_t old_tlli, uint32_t new_tlli, uint8_t ta);
int remember(uint32_t tlli, uint8_t ta);
int recall(uint32_t tlli);
int flush();
private:
size_t m_ta_len;
llist_head m_ta_list;
};

File diff suppressed because it is too large Load Diff

208
src/tbf.h
View File

@ -18,19 +18,22 @@
#pragma once
#ifdef __cplusplus
#include "gprs_rlcmac.h"
#include "llc.h"
#include "rlc.h"
#include <stdint.h>
struct bssgp_bvc_ctx;
struct rlc_ul_header;
struct msgb;
/*
* TBF instance
*/
#define LLC_MAX_LEN 1543
#define RLC_MAX_SNS 128 /* GPRS, must be power of 2 */
#define RLC_MAX_WS 64 /* max window size */
#define RLC_MAX_LEN 54 /* CS-4 including spare bits */
#define Tassign_agch 0,200000 /* waiting after IMM.ASS confirm */
#define Tassign_pacch 2,0 /* timeout for pacch assigment */
@ -81,11 +84,6 @@ enum gprs_rlcmac_tbf_direction {
#define GPRS_RLCMAC_FLAG_TO_DL_ASS 7
#define GPRS_RLCMAC_FLAG_TO_MASK 0xf0 /* timeout bits */
extern struct llist_head gprs_rlcmac_ul_tbfs; /* list of uplink TBFs */
extern struct llist_head gprs_rlcmac_dl_tbfs; /* list of downlink TBFs */
extern struct llist_head gprs_rlcmac_sbas; /* list of single block allocs */
struct gprs_rlcmac_tbf {
static void free_all(struct gprs_rlcmac_trx *trx);
@ -95,17 +93,60 @@ struct gprs_rlcmac_tbf {
bool state_is_not(enum gprs_rlcmac_tbf_state rhs) const;
void set_state(enum gprs_rlcmac_tbf_state new_state);
/* TODO: add the gettimeofday as parameter */
struct msgb *llc_dequeue(bssgp_bvc_ctx *bctx);
/* TODO: extract LLC class? */
int assemble_forward_llc(const gprs_rlc_data *data);
struct msgb *create_dl_acked_block(uint32_t fn, uint8_t ts);
struct msgb *create_dl_ass(uint32_t fn);
struct msgb *create_ul_ass(uint32_t fn);
struct msgb *create_ul_ack(uint32_t fn);
int snd_dl_ack(uint8_t final, uint8_t ssn, uint8_t *rbb);
int snd_ul_ud();
/* blocks were acked */
int rcv_data_block_acknowledged(const uint8_t *data, size_t len, int8_t rssi);
/* dispatch Unitdata.DL messages */
static int handle(struct gprs_rlcmac_bts *bts,
const uint32_t tlli, const char *imsi, const uint8_t ms_class,
const uint16_t delay_csec, const uint8_t *data, const uint16_t len);
uint8_t tsc() const;
int rlcmac_diag();
int update();
void handle_timeout();
void stop_timer();
void stop_t3191();
void poll_timeout();
/** tlli handling */
void update_tlli(uint32_t tlli);
uint32_t tlli() const;
bool is_tlli_valid() const;
void tlli_mark_valid();
uint8_t tfi() const;
const char *imsi() const;
void assign_imsi(const char *imsi);
uint16_t sns() const;
time_t created_ts() const;
/* attempt to make things a bit more fair */
void rotate_in_list();
struct llist_head list;
uint32_t state_flags;
enum gprs_rlcmac_tbf_direction direction;
uint8_t tfi;
uint32_t tlli;
uint8_t tlli_valid;
uint8_t trx;
uint16_t arfcn;
uint8_t tsc;
struct gprs_rlcmac_trx *trx;
uint8_t first_ts; /* first TS used by TBF */
uint8_t first_common_ts; /* first TS that the phone can send and
reveive simultaniously */
@ -113,10 +154,8 @@ struct gprs_rlcmac_tbf {
uint8_t ms_class;
struct gprs_rlcmac_pdch *pdch[8]; /* list of PDCHs allocated to TBF */
uint16_t ta;
uint8_t llc_frame[LLC_MAX_LEN]; /* current DL or UL frame */
uint16_t llc_index; /* current write/read position of frame */
uint16_t llc_length; /* len of current DL LLC_frame, 0 == no frame */
struct llist_head llc_queue; /* queued LLC DL data */
gprs_llc m_llc;
enum gprs_rlcmac_tbf_dl_ass_state dl_ass_state;
enum gprs_rlcmac_tbf_ul_ass_state ul_ass_state;
@ -125,9 +164,6 @@ struct gprs_rlcmac_tbf {
enum gprs_rlcmac_tbf_poll_state poll_state;
uint32_t poll_fn; /* frame number to poll */
uint16_t ws; /* window size */
uint16_t sns; /* sequence number space */
/* Please note that all variables here will be reset when changing
* from WAIT RELEASE back to FLOW state (re-use of TBF).
* All states that need reset must be in this struct, so this is why
@ -135,19 +171,12 @@ struct gprs_rlcmac_tbf {
*/
union {
struct {
uint16_t bsn; /* block sequence number */
uint16_t v_s; /* send state */
uint16_t v_a; /* ack state */
char v_b[RLC_MAX_SNS/2]; /* acknowledge state array */
gprs_rlc_dl_window window;
int32_t tx_counter; /* count all transmitted blocks */
char imsi[16]; /* store IMSI for PCH retransmission */
uint8_t wait_confirm; /* wait for CCCH IMM.ASS cnf */
} dl;
struct {
uint16_t bsn; /* block sequence number */
uint16_t v_r; /* receive state */
uint16_t v_q; /* receive window state */
char v_n[RLC_MAX_SNS/2]; /* receive state array */
gprs_rlc_ul_window window;
int32_t rx_counter; /* count all received blocks */
uint8_t n3103; /* N3103 counter */
uint8_t usf[8]; /* list USFs per PDCH (timeslot) */
@ -155,8 +184,8 @@ struct gprs_rlcmac_tbf {
uint8_t final_ack_sent; /* set if we sent final ack */
} ul;
} dir;
uint8_t rlc_block[RLC_MAX_SNS/2][RLC_MAX_LEN]; /* block history */
uint8_t rlc_block_len[RLC_MAX_SNS/2]; /* block len of history */
gprs_rlc m_rlc;
uint8_t n3105; /* N3105 counter */
@ -169,8 +198,6 @@ struct gprs_rlcmac_tbf {
unsigned int num_fT_exp; /* number of consecutive fT expirations */
struct {
char imsi[16];
struct timeval dl_bw_tv; /* timestamp for dl bw calculation */
uint32_t dl_bw_octets; /* number of octets since bw_tv */
@ -186,47 +213,56 @@ struct gprs_rlcmac_tbf {
uint8_t cs; /* current coding scheme */
#ifdef DEBUG_DIAGRAM
int diag; /* number where TBF is presented in diagram */
int diag_new; /* used to format output of new TBF */
#endif
/* these should become protected but only after gprs_rlcmac_data.c
* stops to iterate over all tbf in its current form */
enum gprs_rlcmac_tbf_state state;
/* store the BTS this TBF belongs to */
BTS *bts;
/*
* private fields. We can't make it private as it is breaking the
* llist macros.
*/
uint32_t m_tlli;
uint8_t m_tlli_valid;
uint8_t m_tfi;
time_t m_created_ts;
/* store IMSI for look-up and PCH retransmission */
char m_imsi[16];
protected:
int update_window(const uint8_t ssn, const uint8_t *rbb);
int maybe_start_new_window();
void reuse_tbf(const uint8_t *data, const uint16_t len);
gprs_rlcmac_bts *bts_data() const;
bool dl_window_stalled() const;
int extract_tlli(const uint8_t *data, const size_t len);
void maybe_schedule_uplink_acknack(const rlc_ul_header *rh);
int append_data(const uint8_t ms_class,
const uint16_t pdu_delay_csec,
const uint8_t *data, const uint16_t len);
struct msgb *create_dl_acked_block(const uint32_t fn, const uint8_t ts,
const int index, const bool fin_first_ack);
struct msgb *create_new_bsn(const uint32_t fn, const uint8_t ts);
};
/* dispatch Unitdata.DL messages */
int tbf_handle(struct gprs_rlcmac_bts *bts,
const uint32_t tlli, const char *imsi, const uint8_t ms_class,
const uint16_t delay_csec, const uint8_t *data, const uint16_t len);
struct gprs_rlcmac_tbf *tbf_alloc_ul(struct gprs_rlcmac_bts *bts,
int8_t use_trx, uint8_t ms_class,
uint32_t tlli, uint8_t ta, struct gprs_rlcmac_tbf *dl_tbf);
int tfi_find_free(struct gprs_rlcmac_bts *bts, enum gprs_rlcmac_tbf_direction dir,
uint8_t *_trx, int8_t use_trx);
struct gprs_rlcmac_tbf *tbf_alloc(struct gprs_rlcmac_bts *bts,
struct gprs_rlcmac_tbf *old_tbf,
enum gprs_rlcmac_tbf_direction dir, uint8_t tfi, uint8_t trx,
uint8_t ms_class, uint8_t single_slot);
struct gprs_rlcmac_tbf *tbf_by_tfi(struct gprs_rlcmac_bts *bts,
uint8_t tfi, uint8_t trx,
enum gprs_rlcmac_tbf_direction dir);
struct gprs_rlcmac_tbf *tbf_by_tlli(uint32_t tlli,
enum gprs_rlcmac_tbf_direction dir);
struct gprs_rlcmac_tbf *tbf_by_poll_fn(uint32_t fn, uint8_t trx, uint8_t ts);
void tbf_free(struct gprs_rlcmac_tbf *tbf);
int tbf_update(struct gprs_rlcmac_tbf *tbf);
int tbf_assign_control_ts(struct gprs_rlcmac_tbf *tbf);
void tbf_new_state(struct gprs_rlcmac_tbf *tbf,
@ -235,11 +271,6 @@ void tbf_new_state(struct gprs_rlcmac_tbf *tbf,
void tbf_timer_start(struct gprs_rlcmac_tbf *tbf, unsigned int T,
unsigned int seconds, unsigned int microseconds);
void tbf_timer_stop(struct gprs_rlcmac_tbf *tbf);
void tbf_timer_cb(void *_tbf);
inline bool gprs_rlcmac_tbf::state_is(enum gprs_rlcmac_tbf_state rhs) const
{
return state == rhs;
@ -254,3 +285,50 @@ inline void gprs_rlcmac_tbf::set_state(enum gprs_rlcmac_tbf_state new_state)
{
state = new_state;
}
inline uint32_t gprs_rlcmac_tbf::tlli() const
{
return m_tlli;
}
inline bool gprs_rlcmac_tbf::is_tlli_valid() const
{
return m_tlli_valid;
}
inline uint8_t gprs_rlcmac_tbf::tfi() const
{
return m_tfi;
}
inline const char *gprs_rlcmac_tbf::imsi() const
{
return m_imsi;
}
inline uint16_t gprs_rlcmac_tbf::sns() const
{
/* assume dl/ul do the same thing */
return dir.dl.window.sns();
}
const char *tbf_name(gprs_rlcmac_tbf *tbf);
inline time_t gprs_rlcmac_tbf::created_ts() const
{
return m_created_ts;
}
#endif
#ifdef __cplusplus
extern "C" {
#endif
#include <osmocom/vty/command.h>
#include <osmocom/vty/vty.h>
void tbf_print_vty_info(struct vty *vty, llist_head *tbf);
#ifdef __cplusplus
}
#endif

View File

@ -1,6 +1,6 @@
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGB_CFLAGS) $(LIBOSMOGSM_CFLAGS) -I$(top_srcdir)/src/
check_PROGRAMS = rlcmac/RLCMACTest
check_PROGRAMS = rlcmac/RLCMACTest alloc/AllocTest tbf/TbfTest types/TypesTest
noinst_PROGRAMS = emu/pcu_emu
rlcmac_RLCMACTest_SOURCES = rlcmac/RLCMACTest.cpp
@ -9,6 +9,22 @@ rlcmac_RLCMACTest_LDADD = \
$(LIBOSMOCORE_LIBS) \
$(COMMON_LA)
alloc_AllocTest_SOURCES = alloc/AllocTest.cpp
alloc_AllocTest_LDADD = \
$(top_builddir)/src/libgprs.la \
$(LIBOSMOGB_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOCORE_LIBS) \
$(COMMON_LA)
tbf_TbfTest_SOURCES = tbf/TbfTest.cpp
tbf_TbfTest_LDADD = \
$(LIBOSMOGB_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOCORE_LIBS) \
$(COMMON_LA) \
$(top_builddir)/src/libgprs.la
emu_pcu_emu_SOURCES = emu/pcu_emu.cpp emu/test_replay_gprs_attach.cpp \
emu/openbsc_clone.c emu/openbsc_clone.h emu/gprs_tests.h \
emu/test_pdp_activation.cpp
@ -19,6 +35,14 @@ emu_pcu_emu_LDADD = \
$(LIBOSMOCORE_LIBS) \
$(COMMON_LA)
types_TypesTest_SOURCES = types/TypesTest.cpp
types_TypesTest_LDADD = \
$(LIBOSMOGB_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOCORE_LIBS) \
$(COMMON_LA) \
$(top_builddir)/src/libgprs.la
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
$(srcdir)/package.m4: $(top_srcdir)/configure.ac
@ -40,7 +64,10 @@ $(srcdir)/package.m4: $(top_srcdir)/configure.ac
EXTRA_DIST = \
testsuite.at $(srcdir)/package.m4 $(TESTSUITE) \
rlcmac/RLCMACTest.ok rlcmac/RLCMACTest.err
rlcmac/RLCMACTest.ok rlcmac/RLCMACTest.err \
alloc/AllocTest.ok alloc/AllocTest.err \
tbf/TbfTest.ok tbf/TbfTest.err \
types/TypesTest.ok types/TypesTest.err
DISTCLEANFILES = atconfig

407
tests/alloc/AllocTest.cpp Normal file
View File

@ -0,0 +1,407 @@
/* AllocTest.cpp
*
* Copyright (C) 2013 by Holger Hans Peter Freyther
*
* 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 "gprs_rlcmac.h"
#include "gprs_debug.h"
#include "tbf.h"
#include "bts.h"
#include <string.h>
#include <stdio.h>
extern "C" {
#include <osmocom/core/application.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
}
/* globals used by the code */
void *tall_pcu_ctx;
int16_t spoof_mnc = 0, spoof_mcc = 0;
static void test_alloc_a(gprs_rlcmac_tbf_direction dir, const int count)
{
int tfi;
uint8_t used_trx;
BTS the_bts;
struct gprs_rlcmac_bts *bts;
struct gprs_rlcmac_tbf *tbfs[33] = { 0, };
printf("Testing alloc_a direction(%d)\n", dir);
bts = the_bts.bts_data();
bts->alloc_algorithm = alloc_algorithm_a;
struct gprs_rlcmac_trx *trx = &bts->trx[0];
trx->pdch[2].enable();
trx->pdch[3].enable();
/**
* Currently alloc_a will only allocate from the first
* PDCH and all possible usf's. We run out of usf's before
* we are out of tfi's. Observe this and make sure that at
* least this part is working okay.
*/
for (int i = 0; i < count; ++i) {
struct gprs_rlcmac_tbf *tbf;
tfi = the_bts.tfi_find_free(dir, &used_trx, 0);
OSMO_ASSERT(tfi >= 0);
tbfs[i] = tbf_alloc(bts, NULL, dir, tfi, used_trx, 0, 0);
}
/* Now check that there are still some TFIs */
tfi = the_bts.tfi_find_free(dir, &used_trx, 0);
switch (dir) {
case GPRS_RLCMAC_UL_TBF:
OSMO_ASSERT(tfi >= 0);
break;
case GPRS_RLCMAC_DL_TBF:
OSMO_ASSERT(tfi < 0);
break;
}
OSMO_ASSERT(!tbf_alloc(bts, NULL, dir, tfi, used_trx, 0, 0));
for (int i = 0; i < ARRAY_SIZE(tbfs); ++i)
if (tbfs[i])
tbf_free(tbfs[i]);
tfi = the_bts.tfi_find_free(dir, &used_trx, 0);
OSMO_ASSERT(tfi >= 0);
tbfs[tfi] = tbf_alloc(bts, NULL, dir, tfi, used_trx, 0, 0);
OSMO_ASSERT(tbfs[tfi]);
tbf_free(tbfs[tfi]);
}
static void test_alloc_a()
{
test_alloc_a(GPRS_RLCMAC_DL_TBF, 32);
test_alloc_a(GPRS_RLCMAC_UL_TBF, 7);
}
static void dump_assignment(struct gprs_rlcmac_tbf *tbf, const char *dir)
{
for (int i = 0; i < ARRAY_SIZE(tbf->pdch); ++i)
if (tbf->pdch[i])
printf("PDCH[%d] is used for %s\n", i, dir);
printf("PDCH[%d] is control_ts for %s\n", tbf->control_ts, dir);
printf("PDCH[%d] is first common for %s\n", tbf->first_common_ts, dir);
}
static void test_alloc_b(int ms_class)
{
printf("Going to test multislot assignment MS_CLASS=%d\n", ms_class);
/*
* PDCH is on TS 6,7,8 and we start with a UL allocation and
* then follow two DL allocations (once single, once normal).
*
* Uplink assigned and still available..
*/
{
BTS the_bts;
struct gprs_rlcmac_bts *bts;
struct gprs_rlcmac_trx *trx;
int tfi;
uint8_t ts_no, trx_no;
gprs_rlcmac_tbf *ul_tbf, *dl_tbf;
printf("Testing UL then DL assignment.\n");
bts = the_bts.bts_data();
bts->alloc_algorithm = alloc_algorithm_b;
trx = &bts->trx[0];
trx->pdch[5].enable();
trx->pdch[6].enable();
trx->pdch[7].enable();
tfi = the_bts.tfi_find_free(GPRS_RLCMAC_UL_TBF, &trx_no, -1);
OSMO_ASSERT(tfi >= 0);
ul_tbf = tbf_alloc(bts, NULL, GPRS_RLCMAC_UL_TBF, tfi, trx_no, ms_class, 1);
OSMO_ASSERT(ul_tbf);
dump_assignment(ul_tbf, "UL");
/* assume final ack has not been sent */
tfi = the_bts.tfi_find_free(GPRS_RLCMAC_UL_TBF, &trx_no, -1);
OSMO_ASSERT(tfi >= 0);
dl_tbf = tbf_alloc(bts, ul_tbf, GPRS_RLCMAC_DL_TBF, tfi, trx_no, ms_class, 0);
OSMO_ASSERT(dl_tbf);
dump_assignment(dl_tbf, "DL");
OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
tbf_free(dl_tbf);
tbf_free(ul_tbf);
}
/**
* Test with the other order.. first DL and then UL
*/
{
BTS the_bts;
struct gprs_rlcmac_bts *bts;
struct gprs_rlcmac_trx *trx;
int tfi;
uint8_t ts_no, trx_no;
gprs_rlcmac_tbf *ul_tbf, *dl_tbf;
printf("Testing DL then UL assignment followed by update\n");
bts = the_bts.bts_data();
bts->alloc_algorithm = alloc_algorithm_b;
trx = &bts->trx[0];
trx->pdch[5].enable();
trx->pdch[6].enable();
trx->pdch[7].enable();
tfi = the_bts.tfi_find_free(GPRS_RLCMAC_UL_TBF, &trx_no, -1);
OSMO_ASSERT(tfi >= 0);
dl_tbf = tbf_alloc(bts, NULL, GPRS_RLCMAC_DL_TBF, tfi, trx_no, ms_class, 1);
dl_tbf->m_tlli = 0x23;
dl_tbf->m_tlli_valid = true;
OSMO_ASSERT(dl_tbf);
dump_assignment(dl_tbf, "DL");
tfi = the_bts.tfi_find_free(GPRS_RLCMAC_UL_TBF, &trx_no, -1);
OSMO_ASSERT(tfi >= 0);
ul_tbf = tbf_alloc(bts, dl_tbf, GPRS_RLCMAC_UL_TBF, tfi, trx_no, ms_class, 0);
ul_tbf->m_tlli = 0x23;
ul_tbf->m_tlli_valid = true;
ul_tbf->dir.ul.contention_resolution_done = 1;
OSMO_ASSERT(ul_tbf);
dump_assignment(ul_tbf, "UL");
OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
/* now update the dl_tbf */
dl_tbf->update();
dump_assignment(dl_tbf, "DL");
OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
tbf_free(dl_tbf);
tbf_free(ul_tbf);
}
/* Andreas osmocom-pcu example */
{
BTS the_bts;
struct gprs_rlcmac_bts *bts;
struct gprs_rlcmac_trx *trx;
int tfi;
uint8_t ts_no, trx_no;
gprs_rlcmac_tbf *ul_tbf, *dl_tbf;
printf("Testing jolly example\n");
bts = the_bts.bts_data();
bts->alloc_algorithm = alloc_algorithm_b;
trx = &bts->trx[0];
trx->pdch[1].enable();
trx->pdch[2].enable();
trx->pdch[3].enable();
trx->pdch[4].enable();
tfi = the_bts.tfi_find_free(GPRS_RLCMAC_UL_TBF, &trx_no, -1);
OSMO_ASSERT(tfi >= 0);
ul_tbf = tbf_alloc(bts, NULL, GPRS_RLCMAC_UL_TBF, tfi, trx_no, ms_class, 0);
OSMO_ASSERT(ul_tbf);
dump_assignment(ul_tbf, "UL");
/* assume final ack has not been sent */
tfi = the_bts.tfi_find_free(GPRS_RLCMAC_UL_TBF, &trx_no, -1);
OSMO_ASSERT(tfi >= 0);
dl_tbf = tbf_alloc(bts, ul_tbf, GPRS_RLCMAC_DL_TBF, tfi, trx_no, ms_class, 0);
OSMO_ASSERT(dl_tbf);
dump_assignment(dl_tbf, "DL");
OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
tbf_free(dl_tbf);
tbf_free(ul_tbf);
}
}
#define ENABLE_PDCH(ts_no, enable_flag, trx) \
if (enable_flag) \
trx->pdch[ts_no].enable();
static void test_alloc_b(bool ts0, bool ts1, bool ts2, bool ts3, bool ts4, bool ts5, bool ts6, bool ts7, int ms_class)
{
/* we can test the allocation failures differently */
if (!ts0 && !ts1 && !ts2 && !ts3 && !ts4 && !ts5 && !ts6 && !ts7)
return;
printf("Mass test: TS0(%c%c%c%c%c%c%c%c)TS7 MS_Class=%d\n",
ts0 ? 'O' : 'x',
ts1 ? 'O' : 'x',
ts2 ? 'O' : 'x',
ts3 ? 'O' : 'x',
ts4 ? 'O' : 'x',
ts5 ? 'O' : 'x',
ts6 ? 'O' : 'x',
ts7 ? 'O' : 'x', ms_class);
fflush(stdout);
{
BTS the_bts;
struct gprs_rlcmac_bts *bts;
struct gprs_rlcmac_trx *trx;
int tfi;
uint8_t ts_no, trx_no;
gprs_rlcmac_tbf *ul_tbf, *dl_tbf;
bts = the_bts.bts_data();
bts->alloc_algorithm = alloc_algorithm_b;
trx = &bts->trx[0];
ENABLE_PDCH(0, ts0, trx);
ENABLE_PDCH(1, ts1, trx);
ENABLE_PDCH(2, ts2, trx);
ENABLE_PDCH(3, ts3, trx);
ENABLE_PDCH(4, ts4, trx);
ENABLE_PDCH(5, ts5, trx);
ENABLE_PDCH(6, ts6, trx);
ENABLE_PDCH(7, ts7, trx);
tfi = the_bts.tfi_find_free(GPRS_RLCMAC_UL_TBF, &trx_no, -1);
OSMO_ASSERT(tfi >= 0);
ul_tbf = tbf_alloc(bts, NULL, GPRS_RLCMAC_UL_TBF, tfi, trx_no, ms_class, 1);
OSMO_ASSERT(ul_tbf);
/* assume final ack has not been sent */
tfi = the_bts.tfi_find_free(GPRS_RLCMAC_UL_TBF, &trx_no, -1);
OSMO_ASSERT(tfi >= 0);
dl_tbf = tbf_alloc(bts, ul_tbf, GPRS_RLCMAC_DL_TBF, tfi, trx_no, ms_class, 0);
OSMO_ASSERT(dl_tbf);
/* verify that both are on the same ts */
OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
tbf_free(dl_tbf);
tbf_free(ul_tbf);
}
/**
* Test with the other order.. first DL and then UL
*/
{
BTS the_bts;
struct gprs_rlcmac_bts *bts;
struct gprs_rlcmac_trx *trx;
int tfi;
uint8_t ts_no, trx_no;
gprs_rlcmac_tbf *ul_tbf, *dl_tbf;
bts = the_bts.bts_data();
bts->alloc_algorithm = alloc_algorithm_b;
trx = &bts->trx[0];
ENABLE_PDCH(0, ts0, trx);
ENABLE_PDCH(1, ts1, trx);
ENABLE_PDCH(2, ts2, trx);
ENABLE_PDCH(3, ts3, trx);
ENABLE_PDCH(4, ts4, trx);
ENABLE_PDCH(5, ts5, trx);
ENABLE_PDCH(6, ts6, trx);
ENABLE_PDCH(7, ts7, trx);
tfi = the_bts.tfi_find_free(GPRS_RLCMAC_UL_TBF, &trx_no, -1);
OSMO_ASSERT(tfi >= 0);
dl_tbf = tbf_alloc(bts, NULL, GPRS_RLCMAC_DL_TBF, tfi, trx_no, ms_class, 1);
OSMO_ASSERT(dl_tbf);
dl_tbf->m_tlli = 0x23;
dl_tbf->m_tlli_valid = true;
tfi = the_bts.tfi_find_free(GPRS_RLCMAC_UL_TBF, &trx_no, -1);
OSMO_ASSERT(tfi >= 0);
ul_tbf = tbf_alloc(bts, dl_tbf, GPRS_RLCMAC_UL_TBF, tfi, trx_no, ms_class, 0);
OSMO_ASSERT(ul_tbf);
ul_tbf->m_tlli = 0x23;
ul_tbf->m_tlli_valid = true;
ul_tbf->dir.ul.contention_resolution_done = 1;
OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
/* now update the dl_tbf */
dl_tbf->update();
OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
tbf_free(dl_tbf);
tbf_free(ul_tbf);
}
}
static void test_all_alloc_b()
{
/* it is a bit crazy... */
for (uint8_t ts0 = 0; ts0 < 2; ++ts0)
for (uint8_t ts1 = 0; ts1 < 2; ++ts1)
for (uint8_t ts2 = 0; ts2 < 2; ++ts2)
for (uint8_t ts3 = 0; ts3 < 2; ++ts3)
for (uint8_t ts4 = 0; ts4 < 2; ++ts4)
for (uint8_t ts5 = 0; ts5 < 2; ++ts5)
for (uint8_t ts6 = 0; ts6 < 2; ++ts6)
for (uint8_t ts7 = 0; ts7 < 2; ++ts7)
for (int ms_class = 0; ms_class < 30; ++ms_class)
test_alloc_b(ts0, ts1, ts2, ts3, ts4, ts5, ts6, ts7, ms_class);
}
static void test_alloc_b()
{
for (int i = 0; i < 30; ++i)
test_alloc_b(i);
test_all_alloc_b();
}
int main(int argc, char **argv)
{
tall_pcu_ctx = talloc_named_const(NULL, 1, "moiji-mobile AllocTest context");
if (!tall_pcu_ctx)
abort();
msgb_set_talloc_ctx(tall_pcu_ctx);
osmo_init_logging(&gprs_log_info);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_filename(osmo_stderr_target, 0);
test_alloc_a();
test_alloc_b();
return EXIT_SUCCESS;
}
/*
* stubs that should not be reached
*/
extern "C" {
void l1if_pdch_req() { abort(); }
void l1if_connect_pdch() { abort(); }
void l1if_close_pdch() { abort(); }
void l1if_open_pdch() { abort(); }
}

View File

@ -0,0 +1,2 @@
No TFI available.
- Failed allocating TS=2, no USF available

8617
tests/alloc/AllocTest.ok Normal file

File diff suppressed because it is too large Load Diff

View File

@ -29,6 +29,7 @@ extern "C" {
#include <gprs_bssgp_pcu.h>
#include <gprs_rlcmac.h>
#include <bts.h>
#include <stdlib.h>
#include <sys/types.h>
@ -38,7 +39,6 @@ static int current_test;
/* Extern data to please the underlying code */
void *tall_pcu_ctx;
struct gprs_rlcmac_bts *gprs_rlcmac_bts;
int16_t spoof_mnc = 0, spoof_mcc = 0;
extern void test_replay_gprs_attach(struct gprs_bssgp_pcu *pcu);
@ -62,13 +62,9 @@ struct gprs_test all_tests[] = {
test_pdp_activation_data),
};
struct gprs_rlcmac_bts *create_bts()
static void init_main_bts()
{
struct gprs_rlcmac_bts *bts;
bts = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_bts);
if (!bts)
return NULL;
struct gprs_rlcmac_bts *bts = bts_main_data();
bts->fc_interval = 100;
bts->initial_cs_dl = bts->initial_cs_ul = 1;
bts->cs1 = 1;
@ -84,8 +80,6 @@ struct gprs_rlcmac_bts *create_bts()
if (!bts->alloc_algorithm)
bts->alloc_algorithm = alloc_algorithm_b;
return bts;
}
static void bvci_unblocked(struct gprs_bssgp_pcu *pcu)
@ -123,11 +117,8 @@ int main(int argc, char **argv)
vty_init(&pcu_vty_info);
pcu_vty_init(&gprs_log_info);
gprs_rlcmac_bts = create_bts();
if (!gprs_rlcmac_bts)
abort();
create_and_connect_bssgp(gprs_rlcmac_bts, INADDR_LOOPBACK, 23000);
init_main_bts();
create_and_connect_bssgp(bts_main_data(), INADDR_LOOPBACK, 23000);
for (;;)
osmo_select_main(0);

View File

@ -78,7 +78,8 @@ void test_replay_gprs_data(struct gprs_bssgp_pcu *pcu, struct msgb *msg, struct
OSMO_ASSERT(ph.cmd == GPRS_LLC_UI);
OSMO_ASSERT(ph.sapi == 1);
OSMO_ASSERT(ph.seq_tx == next_wanted_nu++);
OSMO_ASSERT(ph.seq_tx == next_wanted_nu);
next_wanted_nu += 1;
/* this test just wants to see messages... no further data is sent */
if (next_wanted_nu == 6) {

104
tests/tbf/TbfTest.cpp Normal file
View File

@ -0,0 +1,104 @@
/*
* TbfTest.cpp
*
* Copyright (C) 2013 by Holger Hans Peter Freyther
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "bts.h"
#include "tbf.h"
#include "gprs_debug.h"
extern "C" {
#include <osmocom/core/application.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
}
void *tall_pcu_ctx;
int16_t spoof_mnc = 0, spoof_mcc = 0;
static void test_tbf_tlli_update()
{
BTS the_bts;
the_bts.bts_data()->alloc_algorithm = alloc_algorithm_a;
the_bts.bts_data()->trx[0].pdch[2].enable();
the_bts.bts_data()->trx[0].pdch[3].enable();
/*
* Make a uplink and downlink allocation
*/
gprs_rlcmac_tbf *dl_tbf = tbf_alloc(the_bts.bts_data(),
NULL, GPRS_RLCMAC_DL_TBF, 0,
0, 0, 0);
dl_tbf->update_tlli(0x2342);
dl_tbf->tlli_mark_valid();
dl_tbf->ta = 4;
the_bts.timing_advance()->remember(0x2342, dl_tbf->ta);
gprs_rlcmac_tbf *ul_tbf = tbf_alloc(the_bts.bts_data(),
ul_tbf, GPRS_RLCMAC_UL_TBF, 0,
0, 0, 0);
ul_tbf->update_tlli(0x2342);
ul_tbf->tlli_mark_valid();
OSMO_ASSERT(the_bts.tbf_by_tlli(0x2342, GPRS_RLCMAC_DL_TBF) == dl_tbf);
OSMO_ASSERT(the_bts.tbf_by_tlli(0x2342, GPRS_RLCMAC_UL_TBF) == ul_tbf);
/*
* Now check.. that DL changes and that the timing advance
* has changed.
*/
dl_tbf->update_tlli(0x4232);
OSMO_ASSERT(!the_bts.tbf_by_tlli(0x2342, GPRS_RLCMAC_DL_TBF));
OSMO_ASSERT(!the_bts.tbf_by_tlli(0x2342, GPRS_RLCMAC_UL_TBF));
OSMO_ASSERT(the_bts.tbf_by_tlli(0x4232, GPRS_RLCMAC_DL_TBF) == dl_tbf);
OSMO_ASSERT(the_bts.tbf_by_tlli(0x4232, GPRS_RLCMAC_UL_TBF) == ul_tbf);
OSMO_ASSERT(the_bts.timing_advance()->recall(0x4232) == 4);
}
int main(int argc, char **argv)
{
tall_pcu_ctx = talloc_named_const(NULL, 1, "moiji-mobile TbfTest context");
if (!tall_pcu_ctx)
abort();
msgb_set_talloc_ctx(tall_pcu_ctx);
osmo_init_logging(&gprs_log_info);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_filename(osmo_stderr_target, 0);
test_tbf_tlli_update();
return EXIT_SUCCESS;
}
/*
* stubs that should not be reached
*/
extern "C" {
void l1if_pdch_req() { abort(); }
void l1if_connect_pdch() { abort(); }
void l1if_close_pdch() { abort(); }
void l1if_open_pdch() { abort(); }
}

3
tests/tbf/TbfTest.err Normal file
View File

@ -0,0 +1,3 @@
TBF(TFI=0 TLLI=0x00000000 DIR=DL) changing tlli from TLLI=0x00000000 TLLI=0x00002342 ul_changed=0
TBF(TFI=0 TLLI=0x00000000 DIR=UL) changing tlli from TLLI=0x00000000 TLLI=0x00002342 ul_changed=0
TBF(TFI=0 TLLI=0x00002342 DIR=DL) changing tlli from TLLI=0x00002342 TLLI=0x00004232 ul_changed=1

0
tests/tbf/TbfTest.ok Normal file
View File

View File

@ -6,5 +6,26 @@ AT_SETUP([rlcmac])
AT_KEYWORDS([rlcmac])
cat $abs_srcdir/rlcmac/RLCMACTest.ok > expout
cat $abs_srcdir/rlcmac/RLCMACTest.err > experr
AT_CHECK([$abs_top_builddir/tests/rlcmac/RLCMACTest], [0], [expout], [experr])
AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/rlcmac/RLCMACTest], [0], [expout], [experr])
AT_CLEANUP
AT_SETUP([ts_alloc])
AT_KEYWORDS([ts_alloc])
cat $abs_srcdir/alloc/AllocTest.ok > expout
cat $abs_srcdir/alloc/AllocTest.err > experr
AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/alloc/AllocTest], [0], [expout], [experr])
AT_CLEANUP
AT_SETUP([tbf])
AT_KEYWORDS([tbf])
cat $abs_srcdir/tbf/TbfTest.ok > expout
cat $abs_srcdir/tbf/TbfTest.err > experr
AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/tbf/TbfTest], [0], [expout], [experr])
AT_CLEANUP
AT_SETUP([types])
AT_KEYWORDS([types])
cat $abs_srcdir/types/TypesTest.ok > expout
cat $abs_srcdir/types/TypesTest.err > experr
AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/types/TypesTest], [0], [expout], [experr])
AT_CLEANUP

377
tests/types/TypesTest.cpp Normal file
View File

@ -0,0 +1,377 @@
/*
* TypesTest.cpp Test the primitive data types
*
* Copyright (C) 2013 by Holger Hans Peter Freyther
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 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 Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "bts.h"
#include "tbf.h"
#include "gprs_debug.h"
#include "encoding.h"
#include "decoding.h"
extern "C" {
#include <osmocom/core/application.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/utils.h>
}
#define OSMO_ASSERT_STR_EQ(a, b) \
do { \
if (strcmp(a, b)) { \
printf("String mismatch:\nGot:\t%s\nWant:\t%s\n", a, b); \
OSMO_ASSERT(false); \
} \
} while (0)
void *tall_pcu_ctx;
int16_t spoof_mnc = 0, spoof_mcc = 0;
static void test_llc(void)
{
{
uint8_t data[LLC_MAX_LEN] = {1, 2, 3, 4, };
uint8_t out;
gprs_llc llc;
llc.init();
OSMO_ASSERT(llc.chunk_size() == 0);
OSMO_ASSERT(llc.remaining_space() == LLC_MAX_LEN);
OSMO_ASSERT(llc.frame_length() == 0);
llc.put_frame(data, 2);
OSMO_ASSERT(llc.remaining_space() == LLC_MAX_LEN - 2);
OSMO_ASSERT(llc.frame_length() == 2);
OSMO_ASSERT(llc.chunk_size() == 2);
OSMO_ASSERT(llc.frame[0] == 1);
OSMO_ASSERT(llc.frame[1] == 2);
llc.append_frame(&data[3], 1);
OSMO_ASSERT(llc.remaining_space() == LLC_MAX_LEN - 3);
OSMO_ASSERT(llc.frame_length() == 3);
OSMO_ASSERT(llc.chunk_size() == 3);
/* consume two bytes */
llc.consume(&out, 1);
OSMO_ASSERT(llc.remaining_space() == LLC_MAX_LEN - 3);
OSMO_ASSERT(llc.frame_length() == 3);
OSMO_ASSERT(llc.chunk_size() == 2);
/* check that the bytes are as we expected */
OSMO_ASSERT(llc.frame[0] == 1);
OSMO_ASSERT(llc.frame[1] == 2);
OSMO_ASSERT(llc.frame[2] == 4);
/* now fill the frame */
llc.append_frame(data, llc.remaining_space() - 1);
OSMO_ASSERT(llc.fits_in_current_frame(1));
OSMO_ASSERT(!llc.fits_in_current_frame(2));
}
}
static void test_rlc()
{
{
struct gprs_rlc_data rlc = { 0, };
memset(rlc.block, 0x23, RLC_MAX_LEN);
uint8_t *p = rlc.prepare(20);
OSMO_ASSERT(p == rlc.block);
for (int i = 0; i < 20; ++i)
OSMO_ASSERT(p[i] == 0x2B);
for (int i = 20; i < RLC_MAX_LEN; ++i)
OSMO_ASSERT(p[i] == 0x0);
}
}
static void test_rlc_v_b()
{
{
gprs_rlc_v_b vb;
vb.reset();
for (size_t i = 0; i < RLC_MAX_SNS; ++i)
OSMO_ASSERT(vb.is_invalid(i));
vb.mark_unacked(23);
OSMO_ASSERT(vb.is_unacked(23));
vb.mark_nacked(23);
OSMO_ASSERT(vb.is_nacked(23));
vb.mark_acked(23);
OSMO_ASSERT(vb.is_acked(23));
vb.mark_resend(23);
OSMO_ASSERT(vb.is_resend(23));
vb.mark_invalid(23);
OSMO_ASSERT(vb.is_invalid(23));
}
}
static void test_rlc_v_n()
{
{
gprs_rlc_v_n vn;
vn.reset();
OSMO_ASSERT(!vn.is_received(0x23));
OSMO_ASSERT(vn.state(0x23) == GPRS_RLC_UL_BSN_INVALID);
vn.mark_received(0x23);
OSMO_ASSERT(vn.is_received(0x23));
OSMO_ASSERT(vn.state(0x23) == GPRS_RLC_UL_BSN_RECEIVED);
vn.mark_missing(0x23);
OSMO_ASSERT(!vn.is_received(0x23));
OSMO_ASSERT(vn.state(0x23) == GPRS_RLC_UL_BSN_MISSING);
}
}
static void test_rlc_dl_ul_basic()
{
{
gprs_rlc_dl_window dl_win = { 0, };
OSMO_ASSERT(dl_win.window_empty());
OSMO_ASSERT(!dl_win.window_stalled());
OSMO_ASSERT(dl_win.distance() == 0);
dl_win.increment_send();
OSMO_ASSERT(!dl_win.window_empty());
OSMO_ASSERT(!dl_win.window_stalled());
OSMO_ASSERT(dl_win.distance() == 1);
for (int i = 1; i < 64; ++i) {
dl_win.increment_send();
OSMO_ASSERT(!dl_win.window_empty());
OSMO_ASSERT(dl_win.distance() == i + 1);
}
OSMO_ASSERT(dl_win.distance() == 64);
OSMO_ASSERT(dl_win.window_stalled());
dl_win.raise(1);
OSMO_ASSERT(dl_win.distance() == 63);
OSMO_ASSERT(!dl_win.window_stalled());
for (int i = 62; i >= 0; --i) {
dl_win.raise(1);
OSMO_ASSERT(dl_win.distance() == i);
}
OSMO_ASSERT(dl_win.distance() == 0);
OSMO_ASSERT(dl_win.window_empty());
dl_win.increment_send();
dl_win.increment_send();
dl_win.increment_send();
dl_win.increment_send();
OSMO_ASSERT(dl_win.distance() == 4);
for (int i = 0; i < 128; ++i) {
dl_win.increment_send();
dl_win.increment_send();
dl_win.raise(2);
OSMO_ASSERT(dl_win.distance() == 4);
}
}
{
gprs_rlc_ul_window ul_win = { 0, };
int count;
const char *rbb;
char win_rbb[65];
uint8_t bin_rbb[8];
win_rbb[64] = '\0';
ul_win.m_v_n.reset();
OSMO_ASSERT(ul_win.is_in_window(0));
OSMO_ASSERT(ul_win.is_in_window(63));
OSMO_ASSERT(!ul_win.is_in_window(64));
OSMO_ASSERT(!ul_win.m_v_n.is_received(0));
rbb = "IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII";
OSMO_ASSERT(ul_win.ssn() == 0);
ul_win.update_rbb(win_rbb);
OSMO_ASSERT_STR_EQ(win_rbb, rbb);
Encoding::encode_rbb(win_rbb, bin_rbb);
printf("rbb: %s\n", osmo_hexdump(bin_rbb, sizeof(bin_rbb)));
Decoding::extract_rbb(bin_rbb, win_rbb);
OSMO_ASSERT_STR_EQ(win_rbb, rbb);
/* simulate to have received 0, 1 and 5 */
OSMO_ASSERT(ul_win.is_in_window(0));
count = ul_win.receive_bsn(0);
OSMO_ASSERT(ul_win.m_v_n.is_received(0));
OSMO_ASSERT(ul_win.v_q() == 1);
OSMO_ASSERT(ul_win.v_r() == 1);
OSMO_ASSERT(count == 1);
rbb = "IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIR";
OSMO_ASSERT(ul_win.ssn() == 1);
ul_win.update_rbb(win_rbb);
OSMO_ASSERT_STR_EQ(win_rbb, rbb);
Encoding::encode_rbb(win_rbb, bin_rbb);
printf("rbb: %s\n", osmo_hexdump(bin_rbb, sizeof(bin_rbb)));
Decoding::extract_rbb(bin_rbb, win_rbb);
OSMO_ASSERT_STR_EQ(win_rbb, rbb);
OSMO_ASSERT(ul_win.is_in_window(1));
count = ul_win.receive_bsn(1);
OSMO_ASSERT(ul_win.m_v_n.is_received(0));
OSMO_ASSERT(ul_win.v_q() == 2);
OSMO_ASSERT(ul_win.v_r() == 2);
OSMO_ASSERT(count == 1);
rbb = "IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIRR";
OSMO_ASSERT(ul_win.ssn() == 2);
ul_win.update_rbb(win_rbb);
OSMO_ASSERT_STR_EQ(win_rbb, rbb);
Encoding::encode_rbb(win_rbb, bin_rbb);
printf("rbb: %s\n", osmo_hexdump(bin_rbb, sizeof(bin_rbb)));
Decoding::extract_rbb(bin_rbb, win_rbb);
OSMO_ASSERT_STR_EQ(win_rbb, rbb);
OSMO_ASSERT(ul_win.is_in_window(5));
count = ul_win.receive_bsn(5);
OSMO_ASSERT(ul_win.m_v_n.is_received(0));
OSMO_ASSERT(ul_win.v_q() == 2);
OSMO_ASSERT(ul_win.v_r() == 6);
OSMO_ASSERT(count == 0);
rbb = "IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIRRIIIR";
OSMO_ASSERT(ul_win.ssn() == 6);
ul_win.update_rbb(win_rbb);
OSMO_ASSERT_STR_EQ(win_rbb, rbb);
Encoding::encode_rbb(win_rbb, bin_rbb);
printf("rbb: %s\n", osmo_hexdump(bin_rbb, sizeof(bin_rbb)));
Decoding::extract_rbb(bin_rbb, win_rbb);
OSMO_ASSERT_STR_EQ(win_rbb, rbb);
OSMO_ASSERT(ul_win.is_in_window(65));
OSMO_ASSERT(ul_win.is_in_window(2));
OSMO_ASSERT(ul_win.m_v_n.is_received(5));
count = ul_win.receive_bsn(65);
OSMO_ASSERT(count == 0);
OSMO_ASSERT(ul_win.m_v_n.is_received(5));
OSMO_ASSERT(ul_win.v_q() == 2);
OSMO_ASSERT(ul_win.v_r() == 66);
rbb = "IIIRIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIR";
OSMO_ASSERT(ul_win.ssn() == 66);
ul_win.update_rbb(win_rbb);
OSMO_ASSERT_STR_EQ(win_rbb, rbb);
Encoding::encode_rbb(win_rbb, bin_rbb);
printf("rbb: %s\n", osmo_hexdump(bin_rbb, sizeof(bin_rbb)));
Decoding::extract_rbb(bin_rbb, win_rbb);
OSMO_ASSERT_STR_EQ(win_rbb, rbb);
OSMO_ASSERT(ul_win.is_in_window(2));
OSMO_ASSERT(!ul_win.is_in_window(66));
count = ul_win.receive_bsn(2);
OSMO_ASSERT(count == 1);
OSMO_ASSERT(ul_win.v_q() == 3);
OSMO_ASSERT(ul_win.v_r() == 66);
OSMO_ASSERT(ul_win.is_in_window(66));
count = ul_win.receive_bsn(66);
OSMO_ASSERT(count == 0);
OSMO_ASSERT(ul_win.v_q() == 3);
OSMO_ASSERT(ul_win.v_r() == 67);
for (int i = 3; i <= 67; ++i) {
ul_win.receive_bsn(i);
}
OSMO_ASSERT(ul_win.v_q() == 68);
OSMO_ASSERT(ul_win.v_r() == 68);
count = ul_win.receive_bsn(68);
OSMO_ASSERT(ul_win.v_q() == 69);
OSMO_ASSERT(ul_win.v_r() == 69);
OSMO_ASSERT(count == 1);
/* now test the wrapping */
OSMO_ASSERT(ul_win.is_in_window(4));
OSMO_ASSERT(!ul_win.is_in_window(5));
count = ul_win.receive_bsn(4);
OSMO_ASSERT(count == 0);
}
{
int count;
uint8_t rbb[8];
uint16_t lost = 0, recv = 0;
char show_rbb[65];
BTS dummy_bts;
gprs_rlc_dl_window dl_win = { 0, };
dl_win.m_v_b.reset();
OSMO_ASSERT(dl_win.window_empty());
OSMO_ASSERT(!dl_win.window_stalled());
OSMO_ASSERT(dl_win.distance() == 0);
dl_win.increment_send();
OSMO_ASSERT(!dl_win.window_empty());
OSMO_ASSERT(!dl_win.window_stalled());
OSMO_ASSERT(dl_win.distance() == 1);
for (int i = 0; i < 35; ++i) {
dl_win.increment_send();
OSMO_ASSERT(!dl_win.window_empty());
OSMO_ASSERT(dl_win.distance() == i + 2);
}
uint8_t rbb_cmp[8] = { 0x00, 0x00, 0x00, 0x07, 0xff, 0xff, 0xff, 0xff };
Decoding::extract_rbb(rbb_cmp, show_rbb);
printf("show_rbb: %s\n", show_rbb);
dl_win.update(&dummy_bts, show_rbb, 35, &lost, &recv);
OSMO_ASSERT(lost == 0);
OSMO_ASSERT(recv == 35);
}
}
int main(int argc, char **argv)
{
osmo_init_logging(&gprs_log_info);
log_set_use_color(osmo_stderr_target, 0);
log_set_print_filename(osmo_stderr_target, 0);
printf("Making some basic type testing.\n");
test_llc();
test_rlc();
test_rlc_v_b();
test_rlc_v_n();
test_rlc_dl_ul_basic();
return EXIT_SUCCESS;
}
/*
* stubs that should not be reached
*/
extern "C" {
void l1if_pdch_req() { abort(); }
void l1if_connect_pdch() { abort(); }
void l1if_close_pdch() { abort(); }
void l1if_open_pdch() { abort(); }
}

View File

7
tests/types/TypesTest.ok Normal file
View File

@ -0,0 +1,7 @@
Making some basic type testing.
rbb: 00 00 00 00 00 00 00 00
rbb: 00 00 00 00 00 00 00 01
rbb: 00 00 00 00 00 00 00 03
rbb: 00 00 00 00 00 00 00 31
rbb: 10 00 00 00 00 00 00 01
show_rbb: IIIIIIIIIIIIIIIIIIIIIIIIIIIIIRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR