Merge remote-tracking branch 'origin/sysmocom/master'
This commit is contained in:
commit
8f3520579a
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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';
|
||||
}
|
|
@ -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);
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
};
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
1539
src/gprs_rlcmac.cpp
1539
src/gprs_rlcmac.cpp
File diff suppressed because it is too large
Load Diff
|
@ -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
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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, >);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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&);
|
||||
};
|
|
@ -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();
|
||||
}
|
|
@ -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()];
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -24,6 +24,7 @@
|
|||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
};
|
1632
src/tbf.cpp
1632
src/tbf.cpp
File diff suppressed because it is too large
Load Diff
208
src/tbf.h
208
src/tbf.h
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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(); }
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
No TFI available.
|
||||
- Failed allocating TS=2, no USF available
|
File diff suppressed because it is too large
Load Diff
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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(); }
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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(); }
|
||||
}
|
|
@ -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
|
Loading…
Reference in New Issue