Merge branch 'sysmocom/tbf-split'

This commit is contained in:
Holger Hans Peter Freyther 2014-08-12 19:25:32 +02:00
commit ad1fcccf60
8 changed files with 1152 additions and 1073 deletions

View File

@ -42,6 +42,8 @@ libgprs_la_SOURCES = \
pcu_l1_if.cpp \
pcu_vty.c \
tbf.cpp \
tbf_ul.cpp \
tbf_dl.cpp \
bts.cpp \
poll_controller.cpp \
encoding.cpp \

View File

@ -467,7 +467,7 @@ int BTS::rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta)
return -EBUSY;
}
tbf->ta = qta >> 2;
tbf_new_state(tbf, GPRS_RLCMAC_FLOW);
tbf->set_state(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",
@ -515,7 +515,7 @@ void BTS::trigger_dl_ass(
dl_tbf->ta = old_tbf->ta;
dl_tbf->was_releasing = dl_tbf->state_is(GPRS_RLCMAC_WAIT_RELEASE);
/* change state */
tbf_new_state(dl_tbf, GPRS_RLCMAC_ASSIGN);
dl_tbf->set_state(GPRS_RLCMAC_ASSIGN);
dl_tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_PACCH);
/* start timer */
tbf_timer_start(dl_tbf, 0, Tassign_pacch);
@ -527,7 +527,7 @@ void BTS::trigger_dl_ass(
}
dl_tbf->was_releasing = dl_tbf->state_is(GPRS_RLCMAC_WAIT_RELEASE);
/* change state */
tbf_new_state(dl_tbf, GPRS_RLCMAC_ASSIGN);
dl_tbf->set_state(GPRS_RLCMAC_ASSIGN);
dl_tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_CCCH);
dl_tbf->assign_imsi(imsi);
/* send immediate assignment */
@ -774,7 +774,7 @@ void gprs_rlcmac_pdch::rcv_control_ack(Packet_Control_Acknowledgement_t *packet,
"TBF is gone TLLI=0x%08x\n", tlli);
return;
}
tbf_new_state(tbf, GPRS_RLCMAC_FLOW);
tbf->set_state(GPRS_RLCMAC_FLOW);
/* stop pending assignment timer */
tbf->stop_timer();
if ((tbf->state_flags &
@ -800,7 +800,7 @@ void gprs_rlcmac_pdch::rcv_control_ack(Packet_Control_Acknowledgement_t *packet,
"TBF is gone TLLI=0x%08x\n", tlli);
return;
}
tbf_new_state(tbf, GPRS_RLCMAC_FLOW);
tbf->set_state(GPRS_RLCMAC_FLOW);
if ((tbf->state_flags &
(1 << GPRS_RLCMAC_FLAG_TO_UL_ASS))) {
tbf->state_flags &=

View File

@ -62,10 +62,10 @@ struct gprs_rlcmac_cs {
uint8_t block_payload;
};
int gprs_rlcmac_received_lost(struct gprs_rlcmac_tbf *tbf, uint16_t received,
int gprs_rlcmac_received_lost(struct gprs_rlcmac_dl_tbf *tbf, uint16_t received,
uint16_t lost);
int gprs_rlcmac_lost_rep(struct gprs_rlcmac_tbf *tbf);
int gprs_rlcmac_lost_rep(struct gprs_rlcmac_dl_tbf *tbf);
int gprs_rlcmac_meas_rep(Packet_Measurement_Report_t *pmr);
@ -73,7 +73,7 @@ int gprs_rlcmac_rssi(struct gprs_rlcmac_tbf *tbf, int8_t rssi);
int gprs_rlcmac_rssi_rep(struct gprs_rlcmac_tbf *tbf);
int gprs_rlcmac_dl_bw(struct gprs_rlcmac_tbf *tbf, uint16_t octets);
int gprs_rlcmac_dl_bw(struct gprs_rlcmac_dl_tbf *tbf, uint16_t octets);
/* TS 44.060 Section 10.4.7 Table 10.4.7.1: Payload Type field */
enum gprs_rlcmac_block_type {

View File

@ -112,10 +112,10 @@ int gprs_rlcmac_rssi_rep(struct gprs_rlcmac_tbf *tbf)
*/
/* Lost frames reported from RLCMAC layer */
int gprs_rlcmac_received_lost(struct gprs_rlcmac_tbf *tbf, uint16_t received,
int gprs_rlcmac_received_lost(struct gprs_rlcmac_dl_tbf *tbf, uint16_t received,
uint16_t lost)
{
struct timeval now_tv, *loss_tv = &tbf->meas.dl_loss_tv;
struct timeval now_tv, *loss_tv = &tbf->m_bw.dl_loss_tv;
uint32_t elapsed;
uint16_t sum = received + lost;
@ -126,8 +126,8 @@ int gprs_rlcmac_received_lost(struct gprs_rlcmac_tbf *tbf, uint16_t received,
LOGP(DRLCMACMEAS, LOGL_DEBUG, "DL Loss of TLLI 0x%08x: Received: %4d "
"Lost: %4d Sum: %4d\n", tbf->tlli(), received, lost, sum);
tbf->meas.dl_loss_received += received;
tbf->meas.dl_loss_lost += lost;
tbf->m_bw.dl_loss_received += received;
tbf->m_bw.dl_loss_lost += lost;
gettimeofday(&now_tv, NULL);
elapsed = ((now_tv.tv_sec - loss_tv->tv_sec) << 7)
@ -139,16 +139,16 @@ int gprs_rlcmac_received_lost(struct gprs_rlcmac_tbf *tbf, uint16_t received,
/* reset lost values and timestamp */
memcpy(loss_tv, &now_tv, sizeof(struct timeval));
tbf->meas.dl_loss_received = 0;
tbf->meas.dl_loss_lost = 0;
tbf->m_bw.dl_loss_received = 0;
tbf->m_bw.dl_loss_lost = 0;
return 0;
}
/* Give Lost report */
int gprs_rlcmac_lost_rep(struct gprs_rlcmac_tbf *tbf)
int gprs_rlcmac_lost_rep(struct gprs_rlcmac_dl_tbf *tbf)
{
uint16_t sum = tbf->meas.dl_loss_lost + tbf->meas.dl_loss_received;
uint16_t sum = tbf->m_bw.dl_loss_lost + tbf->m_bw.dl_loss_received;
/* No measurement values */
if (!sum)
@ -156,7 +156,7 @@ int gprs_rlcmac_lost_rep(struct gprs_rlcmac_tbf *tbf)
LOGP(DRLCMACMEAS, LOGL_INFO, "DL packet loss of IMSI=%s / TLLI=0x%08x: "
"%d%%\n", tbf->imsi(), tbf->tlli(),
tbf->meas.dl_loss_lost * 100 / sum);
tbf->m_bw.dl_loss_lost * 100 / sum);
return 0;
}
@ -166,12 +166,12 @@ int gprs_rlcmac_lost_rep(struct gprs_rlcmac_tbf *tbf)
* downlink bandwidth
*/
int gprs_rlcmac_dl_bw(struct gprs_rlcmac_tbf *tbf, uint16_t octets)
int gprs_rlcmac_dl_bw(struct gprs_rlcmac_dl_tbf *tbf, uint16_t octets)
{
struct timeval now_tv, *bw_tv = &tbf->meas.dl_bw_tv;
struct timeval now_tv, *bw_tv = &tbf->m_bw.dl_bw_tv;
uint32_t elapsed;
tbf->meas.dl_bw_octets += octets;
tbf->m_bw.dl_bw_octets += octets;
gettimeofday(&now_tv, NULL);
elapsed = ((now_tv.tv_sec - bw_tv->tv_sec) << 7)
@ -181,11 +181,11 @@ int gprs_rlcmac_dl_bw(struct gprs_rlcmac_tbf *tbf, uint16_t octets)
LOGP(DRLCMACMEAS, LOGL_INFO, "DL Bandwitdh of IMSI=%s / TLLI=0x%08x: "
"%d KBits/s\n", tbf->imsi(), tbf->tlli(),
tbf->meas.dl_bw_octets / elapsed);
tbf->m_bw.dl_bw_octets / elapsed);
/* reset bandwidth values timestamp */
memcpy(bw_tv, &now_tv, sizeof(struct timeval));
tbf->meas.dl_bw_octets = 0;
tbf->m_bw.dl_bw_octets = 0;
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -23,6 +23,7 @@
#include "gprs_rlcmac.h"
#include "llc.h"
#include "rlc.h"
#include <gprs_debug.h>
#include <stdint.h>
@ -115,15 +116,8 @@ 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_ass(uint32_t fn);
struct msgb *create_ul_ass(uint32_t fn);
int snd_ul_ud();
uint8_t tsc() const;
@ -186,17 +180,9 @@ struct gprs_rlcmac_tbf {
unsigned int num_fT_exp; /* number of consecutive fT expirations */
struct {
struct timeval dl_bw_tv; /* timestamp for dl bw calculation */
uint32_t dl_bw_octets; /* number of octets since bw_tv */
struct timeval rssi_tv; /* timestamp for rssi calculation */
int32_t rssi_sum; /* sum of rssi values */
int rssi_num; /* number of rssi values added since rssi_tv */
struct timeval dl_loss_tv; /* timestamp for loss calculation */
uint16_t dl_loss_lost; /* sum of lost packets */
uint16_t dl_loss_received; /* sum of received packets */
} meas;
uint8_t cs; /* current coding scheme */
@ -232,6 +218,7 @@ protected:
int extract_tlli(const uint8_t *data, const size_t len);
static const char *tbf_state_name[6];
};
@ -253,9 +240,6 @@ void tbf_free(struct gprs_rlcmac_tbf *tbf);
int tbf_assign_control_ts(struct gprs_rlcmac_tbf *tbf);
void tbf_new_state(struct gprs_rlcmac_tbf *tbf,
enum gprs_rlcmac_tbf_state state);
void tbf_timer_start(struct gprs_rlcmac_tbf *tbf, unsigned int T,
unsigned int seconds, unsigned int microseconds);
@ -269,8 +253,13 @@ inline bool gprs_rlcmac_tbf::state_is_not(enum gprs_rlcmac_tbf_state rhs) const
return state != rhs;
}
const char *tbf_name(gprs_rlcmac_tbf *tbf);
inline void gprs_rlcmac_tbf::set_state(enum gprs_rlcmac_tbf_state new_state)
{
LOGP(DRLCMAC, LOGL_DEBUG, "%s changes state from %s to %s\n",
tbf_name(this),
tbf_state_name[state], tbf_state_name[new_state]);
state = new_state;
}
@ -294,8 +283,6 @@ inline const char *gprs_rlcmac_tbf::imsi() const
return m_imsi;
}
const char *tbf_name(gprs_rlcmac_tbf *tbf);
inline time_t gprs_rlcmac_tbf::created_ts() const
{
return m_created_ts;
@ -314,6 +301,9 @@ struct gprs_rlcmac_dl_tbf : public gprs_rlcmac_tbf {
int rcvd_dl_ack(uint8_t final, uint8_t ssn, uint8_t *rbb);
struct msgb *create_dl_acked_block(uint32_t fn, uint8_t ts);
/* TODO: add the gettimeofday as parameter */
struct msgb *llc_dequeue(bssgp_bvc_ctx *bctx);
/* 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
@ -323,6 +313,15 @@ struct gprs_rlcmac_dl_tbf : public gprs_rlcmac_tbf {
int32_t m_tx_counter; /* count all transmitted blocks */
uint8_t m_wait_confirm; /* wait for CCCH IMM.ASS cnf */
struct {
struct timeval dl_bw_tv; /* timestamp for dl bw calculation */
uint32_t dl_bw_octets; /* number of octets since bw_tv */
struct timeval dl_loss_tv; /* timestamp for loss calculation */
uint16_t dl_loss_lost; /* sum of lost packets */
uint16_t dl_loss_received; /* sum of received packets */
} m_bw;
protected:
struct msgb *create_new_bsn(const uint32_t fn, const uint8_t ts);
struct msgb *create_dl_acked_block(const uint32_t fn, const uint8_t ts,
@ -339,6 +338,10 @@ struct gprs_rlcmac_ul_tbf : public gprs_rlcmac_tbf {
/* blocks were acked */
int rcv_data_block_acknowledged(const uint8_t *data, size_t len, int8_t rssi);
/* TODO: extract LLC class? */
int assemble_forward_llc(const gprs_rlc_data *data);
int snd_ul_ud();
/* 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

671
src/tbf_dl.cpp Normal file
View File

@ -0,0 +1,671 @@
/* 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 <bts.h>
#include <tbf.h>
#include <rlc.h>
#include <gprs_rlcmac.h>
#include <gprs_debug.h>
#include <gprs_bssgp_pcu.h>
#include <decoding.h>
extern "C" {
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
}
#include <errno.h>
#include <string.h>
/* After sending these frames, we poll for ack/nack. */
#define POLL_ACK_AFTER_FRAMES 20
static const struct gprs_rlcmac_cs gprs_rlcmac_cs[] = {
/* frame length data block max payload */
{ 0, 0, 0 },
{ 23, 23, 20 }, /* CS-1 */
{ 34, 33, 30 }, /* CS-2 */
{ 40, 39, 36 }, /* CS-3 */
{ 54, 53, 50 }, /* CS-4 */
};
extern "C" {
int bssgp_tx_llc_discarded(struct bssgp_bvc_ctx *bctx, uint32_t tlli,
uint8_t num_frames, uint32_t num_octets);
}
static inline void tbf_update_ms_class(struct gprs_rlcmac_tbf *tbf,
const uint8_t ms_class)
{
if (!tbf->ms_class && ms_class)
tbf->ms_class = ms_class;
}
int gprs_rlcmac_dl_tbf::append_data(const uint8_t ms_class,
const uint16_t pdu_delay_csec,
const uint8_t *data, const uint16_t len)
{
LOGP(DRLCMAC, LOGL_INFO, "%s append\n", tbf_name(this));
if (state_is(GPRS_RLCMAC_WAIT_RELEASE)) {
LOGP(DRLCMAC, LOGL_DEBUG,
"%s in WAIT RELEASE state "
"(T3193), so reuse TBF\n", tbf_name(this));
tbf_update_ms_class(this, ms_class);
reuse_tbf(data, len);
} else {
/* the TBF exists, so we must write it in the queue
* we prepend lifetime in front of PDU */
struct timeval *tv;
struct msgb *llc_msg = msgb_alloc(len + sizeof(*tv) * 2,
"llc_pdu_queue");
if (!llc_msg)
return -ENOMEM;
tv = (struct timeval *)msgb_put(llc_msg, sizeof(*tv));
gprs_llc::calc_pdu_lifetime(bts, pdu_delay_csec, tv);
tv = (struct timeval *)msgb_put(llc_msg, sizeof(*tv));
gettimeofday(tv, NULL);
memcpy(msgb_put(llc_msg, len), data, len);
m_llc.enqueue(llc_msg);
tbf_update_ms_class(this, ms_class);
}
return 0;
}
static struct gprs_rlcmac_dl_tbf *tbf_lookup_dl(BTS *bts,
const uint32_t tlli, const char *imsi)
{
/* TODO: look up by IMSI first, then tlli, then old_tlli */
return bts->dl_tbf_by_tlli(tlli);
}
static int tbf_new_dl_assignment(struct gprs_rlcmac_bts *bts,
const char *imsi,
const uint32_t tlli, const uint8_t ms_class,
const uint8_t *data, const uint16_t len)
{
uint8_t trx, ta, ss;
int8_t use_trx;
struct gprs_rlcmac_ul_tbf *ul_tbf, *old_ul_tbf;
struct gprs_rlcmac_dl_tbf *dl_tbf;
int8_t tfi; /* must be signed */
int rc;
/* check for uplink data, so we copy our informations */
#warning "Do the same look up for IMSI, TLLI and OLD_TLLI"
#warning "Refactor the below lines... into a new method"
ul_tbf = bts->bts->ul_tbf_by_tlli(tlli);
if (ul_tbf && ul_tbf->m_contention_resolution_done
&& !ul_tbf->m_final_ack_sent) {
use_trx = ul_tbf->trx->trx_no;
ta = ul_tbf->ta;
ss = 0;
old_ul_tbf = ul_tbf;
} else {
use_trx = -1;
/* we already have an uplink TBF, so we use that TA */
if (ul_tbf)
ta = ul_tbf->ta;
else {
/* recall TA */
rc = bts->bts->timing_advance()->recall(tlli);
if (rc < 0) {
LOGP(DRLCMAC, LOGL_NOTICE, "TA unknown"
", assuming 0\n");
ta = 0;
} else
ta = rc;
}
ss = 1; /* PCH assignment only allows one timeslot */
old_ul_tbf = NULL;
}
// Create new TBF (any TRX)
#warning "Copy and paste with alloc_ul_tbf"
tfi = bts->bts->tfi_find_free(GPRS_RLCMAC_DL_TBF, &trx, use_trx);
if (tfi < 0) {
LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource\n");
/* FIXME: send reject */
return -EBUSY;
}
/* set number of downlink slots according to multislot class */
dl_tbf = tbf_alloc_dl_tbf(bts, ul_tbf, tfi, trx, ms_class, ss);
if (!dl_tbf) {
LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource\n");
/* FIXME: send reject */
return -EBUSY;
}
dl_tbf->m_tlli = tlli;
dl_tbf->m_tlli_valid = 1;
dl_tbf->ta = ta;
LOGP(DRLCMAC, LOGL_DEBUG, "%s [DOWNLINK] START\n", tbf_name(dl_tbf));
/* new TBF, so put first frame */
dl_tbf->m_llc.put_frame(data, len);
dl_tbf->bts->llc_frame_sched();
/* Store IMSI for later look-up and PCH retransmission */
dl_tbf->assign_imsi(imsi);
/* trigger downlink assignment and set state to ASSIGN.
* we don't use old_downlink, so the possible uplink is used
* to trigger downlink assignment. if there is no uplink,
* AGCH is used. */
dl_tbf->bts->trigger_dl_ass(dl_tbf, old_ul_tbf, imsi);
return 0;
}
/**
* TODO: split into unit test-able parts...
*/
int gprs_rlcmac_dl_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_dl_tbf *dl_tbf;
/* check for existing TBF */
dl_tbf = tbf_lookup_dl(bts->bts, tlli, imsi);
if (dl_tbf) {
int rc = dl_tbf->append_data(ms_class, delay_csec, data, len);
if (rc >= 0)
dl_tbf->assign_imsi(imsi);
return rc;
}
return tbf_new_dl_assignment(bts, imsi, tlli, ms_class, data, len);
}
struct msgb *gprs_rlcmac_dl_tbf::llc_dequeue(bssgp_bvc_ctx *bctx)
{
struct msgb *msg;
struct timeval *tv, tv_now;
uint32_t octets = 0, frames = 0;
gettimeofday(&tv_now, NULL);
while ((msg = m_llc.dequeue())) {
tv = (struct timeval *)msg->data;
msgb_pull(msg, sizeof(*tv));
msgb_pull(msg, sizeof(*tv));
if (gprs_llc::is_frame_expired(&tv_now, tv)) {
LOGP(DRLCMACDL, LOGL_NOTICE, "%s Discarding LLC PDU "
"because lifetime limit reached. Queue size %zu\n",
tbf_name(this), m_llc.m_queue_size);
bts->llc_timedout_frame();
frames++;
octets += msg->len;
msgb_free(msg);
continue;
}
break;
}
if (frames) {
if (frames > 0xff)
frames = 0xff;
if (octets > 0xffffff)
octets = 0xffffff;
bssgp_tx_llc_discarded(bctx, m_tlli, frames, octets);
}
return msg;
}
/*
* Create DL data block
* The messages are fragmented and forwarded as data blocks.
*/
struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(uint32_t fn, uint8_t ts)
{
LOGP(DRLCMACDL, LOGL_DEBUG, "%s downlink (V(A)==%d .. "
"V(S)==%d)\n", tbf_name(this),
m_window.v_a(), m_window.v_s());
do_resend:
/* check if there is a block with negative acknowledgement */
int resend_bsn = m_window.resend_needed();
if (resend_bsn >= 0) {
LOGP(DRLCMACDL, LOGL_DEBUG, "- Resending BSN %d\n", resend_bsn);
/* re-send block with negative aknowlegement */
m_window.m_v_b.mark_unacked(resend_bsn);
bts->rlc_resent();
return create_dl_acked_block(fn, ts, resend_bsn, false);
}
/* if the window has stalled, or transfer is complete,
* send an unacknowledged block */
if (state_is(GPRS_RLCMAC_FINISHED) || dl_window_stalled()) {
if (state_is(GPRS_RLCMAC_FINISHED)) {
LOGP(DRLCMACDL, LOGL_DEBUG, "- Restarting at BSN %d, "
"because all blocks have been transmitted.\n",
m_window.v_a());
bts->rlc_restarted();
} else {
LOGP(DRLCMACDL, LOGL_NOTICE, "- Restarting at BSN %d, "
"because all window is stalled.\n",
m_window.v_a());
bts->rlc_stalled();
}
/* If V(S) == V(A) and finished state, we would have received
* acknowledgement of all transmitted block. In this case we
* would have transmitted the final block, and received ack
* from MS. But in this case we did not receive the final ack
* indication from MS. This should never happen if MS works
* correctly. */
if (m_window.window_empty()) {
LOGP(DRLCMACDL, LOGL_DEBUG, "- MS acked all blocks, "
"so we re-transmit final block!\n");
/* we just send final block again */
int16_t index = m_window.v_s_mod(-1);
bts->rlc_resent();
return create_dl_acked_block(fn, ts, index, false);
}
/* cycle through all unacked blocks */
int resend = m_window.mark_for_resend();
/* At this point there should be at least one unacked block
* to be resent. If not, this is an software error. */
if (resend == 0) {
LOGP(DRLCMACDL, LOGL_ERROR, "Software error: "
"There are no unacknowledged blocks, but V(A) "
" != V(S). PLEASE FIX!\n");
/* we just send final block again */
int16_t index = m_window.v_s_mod(-1);
return create_dl_acked_block(fn, ts, index, false);
}
goto do_resend;
}
return create_new_bsn(fn, ts);
}
struct msgb *gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, const uint8_t ts)
{
struct rlc_dl_header *rh;
struct rlc_li_field *li;
struct msgb *msg;
uint8_t *delimiter, *data, *e_pointer;
uint16_t space, chunk;
gprs_rlc_data *rlc_data;
bool first_fin_ack = false;
const uint16_t bsn = m_window.v_s();
LOGP(DRLCMACDL, LOGL_DEBUG, "- Sending new block at BSN %d\n",
m_window.v_s());
#warning "Selection of the CS doesn't belong here"
if (cs == 0) {
cs = bts_data()->initial_cs_dl;
if (cs < 1 || cs > 4)
cs = 1;
}
/* total length of block, including spare bits */
const uint8_t block_length = gprs_rlcmac_cs[cs].block_length;
/* length of usable data of block, w/o spare bits, inc. MAC */
const uint8_t block_data_len = gprs_rlcmac_cs[cs].block_data;
/* now we still have untransmitted LLC data, so we fill mac block */
rlc_data = m_rlc.block(bsn);
data = rlc_data->prepare(block_data_len);
rh = (struct rlc_dl_header *)data;
rh->pt = 0; /* Data Block */
rh->rrbp = rh->s_p = 0; /* Polling, set later, if required */
rh->usf = 7; /* will be set at scheduler */
rh->pr = 0; /* FIXME: power reduction */
rh->tfi = m_tfi; /* TFI */
rh->fbi = 0; /* Final Block Indicator, set late, if true */
rh->bsn = bsn; /* Block Sequence Number */
rh->e = 0; /* Extension bit, maybe set later */
e_pointer = data + 2; /* points to E of current chunk */
data += sizeof(*rh);
delimiter = data; /* where next length header would be stored */
space = block_data_len - sizeof(*rh);
while (1) {
chunk = m_llc.chunk_size();
/* if chunk will exceed block limit */
if (chunk > space) {
LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d "
"larger than space (%d) left in block: copy "
"only remaining space, and we are done\n",
chunk, space);
/* block is filled, so there is no extension */
*e_pointer |= 0x01;
/* fill only space */
m_llc.consume(data, space);
/* return data block as message */
break;
}
/* if FINAL chunk would fit precisely in space left */
if (chunk == space && llist_empty(&m_llc.queue)) {
LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d "
"would exactly fit into space (%d): because "
"this is a final block, we don't add length "
"header, and we are done\n", chunk, space);
LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for "
"%s that fits precisely in last block: "
"len=%d\n", tbf_name(this), m_llc.frame_length());
gprs_rlcmac_dl_bw(this, m_llc.frame_length());
/* block is filled, so there is no extension */
*e_pointer |= 0x01;
/* fill space */
m_llc.consume(data, space);
m_llc.reset();
/* final block */
rh->fbi = 1; /* we indicate final block */
set_state(GPRS_RLCMAC_FINISHED);
/* return data block as message */
break;
}
/* if chunk would fit exactly in space left */
if (chunk == space) {
LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d "
"would exactly fit into space (%d): add length "
"header with LI=0, to make frame extend to "
"next block, and we are done\n", chunk, space);
/* make space for delimiter */
if (delimiter != data)
memmove(delimiter + 1, delimiter,
data - delimiter);
data++;
space--;
/* add LI with 0 length */
li = (struct rlc_li_field *)delimiter;
li->e = 1; /* not more extension */
li->m = 0; /* shall be set to 0, in case of li = 0 */
li->li = 0; /* chunk fills the complete space */
// no need to set e_pointer nor increase delimiter
/* fill only space, which is 1 octet less than chunk */
m_llc.consume(data, space);
/* return data block as message */
break;
}
LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d is less "
"than remaining space (%d): add length header to "
"to delimit LLC frame\n", chunk, space);
/* the LLC frame chunk ends in this block */
/* make space for delimiter */
if (delimiter != data)
memmove(delimiter + 1, delimiter, data - delimiter);
data++;
space--;
/* add LI to delimit frame */
li = (struct rlc_li_field *)delimiter;
li->e = 0; /* Extension bit, maybe set later */
li->m = 0; /* will be set later, if there is more LLC data */
li->li = chunk; /* length of chunk */
e_pointer = delimiter; /* points to E of current delimiter */
delimiter++;
/* copy (rest of) LLC frame to space and reset later */
m_llc.consume(data, chunk);
data += chunk;
space -= chunk;
LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for %s"
"len=%d\n", tbf_name(this), m_llc.frame_length());
gprs_rlcmac_dl_bw(this, m_llc.frame_length());
m_llc.reset();
/* dequeue next LLC frame, if any */
msg = llc_dequeue(gprs_bssgp_pcu_current_bctx());
if (msg) {
LOGP(DRLCMACDL, LOGL_INFO, "- Dequeue next LLC for "
"%s (len=%d)\n", tbf_name(this), msg->len);
m_llc.put_frame(msg->data, msg->len);
bts->llc_frame_sched();
msgb_free(msg);
}
/* if we have more data and we have space left */
if (space > 0 && m_llc.frame_length()) {
li->m = 1; /* we indicate more frames to follow */
continue;
}
/* if we don't have more LLC frames */
if (!m_llc.frame_length()) {
LOGP(DRLCMACDL, LOGL_DEBUG, "-- Final block, so we "
"done.\n");
li->e = 1; /* we cannot extend */
rh->fbi = 1; /* we indicate final block */
first_fin_ack = true;
/* + 1 indicates: first final ack */
set_state(GPRS_RLCMAC_FINISHED);
break;
}
/* we have no space left */
LOGP(DRLCMACDL, LOGL_DEBUG, "-- No space left, so we are "
"done.\n");
li->e = 1; /* we cannot extend */
break;
}
LOGP(DRLCMACDL, LOGL_DEBUG, "data block: %s\n",
osmo_hexdump(rlc_data->block, block_length));
#warning "move this up?"
rlc_data->len = block_length;
/* raise send state and set ack state array */
m_window.m_v_b.mark_unacked(bsn);
m_window.increment_send();
return create_dl_acked_block(fn, ts, bsn, first_fin_ack);
}
struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
const uint32_t fn, const uint8_t ts,
const int index, const bool first_fin_ack)
{
uint8_t *data;
struct rlc_dl_header *rh;
struct msgb *dl_msg;
uint8_t len;
/* get data and header from current block */
data = m_rlc.block(index)->block;
len = m_rlc.block(index)->len;
rh = (struct rlc_dl_header *)data;
/* Clear Polling, if still set in history buffer */
rh->s_p = 0;
/* poll after POLL_ACK_AFTER_FRAMES frames, or when final block is tx.
*/
if (m_tx_counter >= POLL_ACK_AFTER_FRAMES || first_fin_ack) {
if (first_fin_ack) {
LOGP(DRLCMACDL, LOGL_DEBUG, "- Scheduling Ack/Nack "
"polling, because first final block sent.\n");
} else {
LOGP(DRLCMACDL, LOGL_DEBUG, "- Scheduling Ack/Nack "
"polling, because %d blocks sent.\n",
POLL_ACK_AFTER_FRAMES);
}
/* scheduling not possible, because: */
if (poll_state != GPRS_RLCMAC_POLL_NONE)
LOGP(DRLCMACDL, LOGL_DEBUG, "Polling is already "
"sheduled for %s, so we must wait for "
"requesting downlink ack\n", tbf_name(this));
else if (control_ts != ts)
LOGP(DRLCMACDL, LOGL_DEBUG, "Polling cannot be "
"sheduled in this TS %d, waiting for "
"TS %d\n", ts, control_ts);
#warning "What happens to the first_fin_ack in case something is already scheduled?"
else if (bts->sba()->find(trx->trx_no, ts, (fn + 13) % 2715648))
LOGP(DRLCMACDL, LOGL_DEBUG, "Polling cannot be "
"sheduled, because single block alllocation "
"already exists\n");
else {
LOGP(DRLCMACDL, LOGL_DEBUG, "Polling sheduled in this "
"TS %d\n", ts);
m_tx_counter = 0;
/* start timer whenever we send the final block */
if (rh->fbi == 1)
tbf_timer_start(this, 3191, bts_data()->t3191, 0);
/* schedule polling */
poll_state = GPRS_RLCMAC_POLL_SCHED;
poll_fn = (fn + 13) % 2715648;
/* set polling in header */
rh->rrbp = 0; /* N+13 */
rh->s_p = 1; /* Polling */
}
}
/* return data block as message */
dl_msg = msgb_alloc(len, "rlcmac_dl_data");
if (!dl_msg)
return NULL;
/* Increment TX-counter */
m_tx_counter++;
memcpy(msgb_put(dl_msg, len), data, len);
bts->rlc_sent();
return dl_msg;
}
int gprs_rlcmac_dl_tbf::update_window(const uint8_t ssn, const uint8_t *rbb)
{
int16_t dist; /* must be signed */
uint16_t lost = 0, received = 0;
char show_rbb[65];
char show_v_b[RLC_MAX_SNS + 1];
const uint16_t mod_sns = m_window.mod_sns();
Decoding::extract_rbb(rbb, show_rbb);
/* show received array in debug (bit 64..1) */
LOGP(DRLCMACDL, LOGL_DEBUG, "- ack: (BSN=%d)\"%s\""
"(BSN=%d) R=ACK I=NACK\n", (ssn - 64) & mod_sns,
show_rbb, (ssn - 1) & mod_sns);
/* apply received array to receive state (SSN-64..SSN-1) */
/* calculate distance of ssn from V(S) */
dist = (m_window.v_s() - ssn) & mod_sns;
/* check if distance is less than distance V(A)..V(S) */
if (dist >= m_window.distance()) {
/* this might happpen, if the downlink assignment
* was not received by ms and the ack refers
* to previous TBF
* FIXME: we should implement polling for
* control ack!*/
LOGP(DRLCMACDL, LOGL_NOTICE, "- ack range is out of "
"V(A)..V(S) range %s Free TBF!\n", tbf_name(this));
return 1; /* indicate to free TBF */
}
m_window.update(bts, show_rbb, ssn,
&lost, &received);
/* report lost and received packets */
gprs_rlcmac_received_lost(this, received, lost);
/* raise V(A), if possible */
m_window.raise(m_window.move_window());
/* show receive state array in debug (V(A)..V(S)-1) */
m_window.show_state(show_v_b);
LOGP(DRLCMACDL, LOGL_DEBUG, "- V(B): (V(A)=%d)\"%s\""
"(V(S)-1=%d) A=Acked N=Nacked U=Unacked "
"X=Resend-Unacked I=Invalid\n",
m_window.v_a(), show_v_b,
m_window.v_s_mod(-1));
if (state_is(GPRS_RLCMAC_FINISHED) && m_window.window_empty()) {
LOGP(DRLCMACDL, LOGL_NOTICE, "Received acknowledge of "
"all blocks, but without final ack "
"inidcation (don't worry)\n");
}
return 0;
}
int gprs_rlcmac_dl_tbf::maybe_start_new_window()
{
struct msgb *msg;
uint16_t received;
LOGP(DRLCMACDL, LOGL_DEBUG, "- Final ACK received.\n");
/* range V(A)..V(S)-1 */
received = m_window.count_unacked();
/* report all outstanding packets as received */
gprs_rlcmac_received_lost(this, received, 0);
set_state(GPRS_RLCMAC_WAIT_RELEASE);
/* check for LLC PDU in the LLC Queue */
msg = llc_dequeue(gprs_bssgp_pcu_current_bctx());
if (!msg) {
/* no message, start T3193, change state to RELEASE */
LOGP(DRLCMACDL, LOGL_DEBUG, "- No new message, so we release.\n");
/* start T3193 */
tbf_timer_start(this, 3193,
bts_data()->t3193_msec / 1000,
(bts_data()->t3193_msec % 1000) * 1000);
return 0;
}
/* we have more data so we will re-use this tbf */
reuse_tbf(msg->data, msg->len);
msgb_free(msg);
return 0;
}
int gprs_rlcmac_dl_tbf::rcvd_dl_ack(uint8_t final_ack, uint8_t ssn, uint8_t *rbb)
{
LOGP(DRLCMACDL, LOGL_DEBUG, "%s downlink acknowledge\n", tbf_name(this));
if (!final_ack)
return update_window(ssn, rbb);
return maybe_start_new_window();
}
void gprs_rlcmac_dl_tbf::reuse_tbf(const uint8_t *data, const uint16_t len)
{
bts->tbf_reused();
m_llc.put_frame(data, len);
bts->llc_frame_sched();
/* reset rlc states */
m_tx_counter = 0;
m_wait_confirm = 0;
m_window.reset();
/* keep to flags */
state_flags &= GPRS_RLCMAC_FLAG_TO_MASK;
state_flags &= ~(1 << GPRS_RLCMAC_FLAG_CCCH);
update();
LOGP(DRLCMAC, LOGL_DEBUG, "%s Trigger dowlink assignment on PACCH, "
"because another LLC PDU has arrived in between\n",
tbf_name(this));
bts->trigger_dl_ass(this, this, NULL);
}
bool gprs_rlcmac_dl_tbf::dl_window_stalled() const
{
return m_window.window_stalled();
}

418
src/tbf_ul.cpp Normal file
View File

@ -0,0 +1,418 @@
/* 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 <bts.h>
#include <tbf.h>
#include <rlc.h>
#include <encoding.h>
#include <gprs_rlcmac.h>
#include <gprs_debug.h>
#include <gprs_bssgp_pcu.h>
#include <decoding.h>
extern "C" {
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
}
#include <errno.h>
#include <string.h>
/* After receiving these frames, we send ack/nack. */
#define SEND_ACK_AFTER_FRAMES 20
extern void *tall_pcu_ctx;
/*
* Store received block data in LLC message(s) and forward to SGSN
* if complete.
*/
int gprs_rlcmac_ul_tbf::assemble_forward_llc(const gprs_rlc_data *_data)
{
const uint8_t *data = _data->block;
uint8_t len = _data->len;
const struct rlc_ul_header *rh = (const struct rlc_ul_header *) data;
uint8_t e, m;
struct rlc_li_field *li;
uint8_t frame_offset[16], offset = 0, chunk;
int i, frames = 0;
LOGP(DRLCMACUL, LOGL_DEBUG, "- Assembling frames: (len=%d)\n", len);
data += 3;
len -= 3;
e = rh->e; /* if extended */
m = 1; /* more frames, that means: the first frame */
/* Parse frame offsets from length indicator(s), if any. */
while (1) {
if (frames == (int)sizeof(frame_offset)) {
LOGP(DRLCMACUL, LOGL_ERROR, "%s too many frames in "
"block\n", tbf_name(this));
return -EINVAL;
}
frame_offset[frames++] = offset;
LOGP(DRLCMACUL, LOGL_DEBUG, "-- Frame %d starts at offset "
"%d\n", frames, offset);
if (!len)
break;
/* M == 0 and E == 0 is not allowed in this version. */
if (!m && !e) {
LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA "
"ignored, because M='0' and E='0'.\n",
tbf_name(this));
return 0;
}
/* no more frames in this segment */
if (e) {
break;
}
/* There is a new frame and an LI that delimits it. */
if (m) {
li = (struct rlc_li_field *)data;
LOGP(DRLCMACUL, LOGL_DEBUG, "-- Delimiter len=%d\n",
li->li);
/* Special case: LI == 0
* If the last segment would fit precisely into the
* rest of the RLC MAC block, there would be no way
* to delimit that this segment ends and is not
* continued in the next block.
* The special LI (0) is used to force the segment to
* extend into the next block, so it is delimited there.
* This LI must be skipped. Also it is the last LI.
*/
if (li->li == 0) {
data++;
len--;
m = 1; /* M is ignored, we know there is more */
break; /* handle E as '1', so we break! */
}
e = li->e;
m = li->m;
offset += li->li;
data++;
len--;
continue;
}
}
if (!m) {
LOGP(DRLCMACUL, LOGL_DEBUG, "- Last frame carries spare "
"data\n");
}
LOGP(DRLCMACUL, LOGL_DEBUG, "- Data length after length fields: %d\n",
len);
/* TLLI */
if (rh->ti) {
if (len < 4) {
LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA TLLI out of "
"frame border\n", tbf_name(this));
return -EINVAL;
}
data += 4;
len -= 4;
LOGP(DRLCMACUL, LOGL_DEBUG, "- Length after skipping TLLI: "
"%d\n", len);
}
/* PFI */
if (rh->pi) {
LOGP(DRLCMACUL, LOGL_ERROR, "ERROR: PFI not supported, "
"please disable in SYSTEM INFORMATION\n");
if (len < 1) {
LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA PFI out of "
"frame border\n", tbf_name(this));
return -EINVAL;
}
data++;
len--;
LOGP(DRLCMACUL, LOGL_DEBUG, "- Length after skipping PFI: "
"%d\n", len);
}
/* Now we have:
* - a list of frames offsets: frame_offset[]
* - number of frames: i
* - m == 0: Last frame carries spare data (end of TBF).
*/
/* Check if last offset would exceed frame. */
if (offset > len) {
LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA ignored, "
"because LI delimits data that exceeds block size.\n",
tbf_name(this));
return -EINVAL;
}
/* create LLC frames */
for (i = 0; i < frames; i++) {
/* last frame ? */
if (i == frames - 1) {
/* no more data in last frame */
if (!m)
break;
/* data until end of frame */
chunk = len - frame_offset[i];
} else {
/* data until next frame */
chunk = frame_offset[i + 1] - frame_offset[i];
}
if (!m_llc.fits_in_current_frame(chunk)) {
LOGP(DRLCMACUL, LOGL_NOTICE, "%s LLC frame exceeds "
"maximum size %u.\n", tbf_name(this),
m_llc.remaining_space());
chunk = m_llc.remaining_space();
}
m_llc.append_frame(data + frame_offset[i], chunk);
m_llc.consume(chunk);
/* not last frame. */
if (i != frames - 1) {
/* send frame to SGSN */
LOGP(DRLCMACUL, LOGL_INFO, "%s complete UL frame len=%d\n",
tbf_name(this) , m_llc.frame_length());
snd_ul_ud();
m_llc.reset();
/* also check if CV==0, because the frame may fill up the
* block precisely, then it is also complete. normally the
* frame would be extended into the next block with a 0-length
* delimiter added to this block. */
} else if (rh->cv == 0) {
/* send frame to SGSN */
LOGP(DRLCMACUL, LOGL_INFO, "%s complete UL frame "
"that fits precisely in last block: "
"len=%d\n", tbf_name(this), m_llc.frame_length());
snd_ul_ud();
m_llc.reset();
}
}
return 0;
}
struct msgb *gprs_rlcmac_ul_tbf::create_ul_ack(uint32_t fn)
{
int final = (state_is(GPRS_RLCMAC_FINISHED));
struct msgb *msg;
if (final) {
if (poll_state != GPRS_RLCMAC_POLL_NONE) {
LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already "
"sheduled for %s, so we must wait for "
"final uplink ack...\n", tbf_name(this));
return NULL;
}
if (bts->sba()->find(trx->trx_no, control_ts, (fn + 13) % 2715648)) {
LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already "
"scheduled for single block allocation...\n");
return NULL;
}
}
msg = msgb_alloc(23, "rlcmac_ul_ack");
if (!msg)
return NULL;
bitvec *ack_vec = bitvec_alloc(23);
if (!ack_vec) {
msgb_free(msg);
return NULL;
}
bitvec_unhex(ack_vec,
"2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
RlcMacDownlink_t * mac_control_block = (RlcMacDownlink_t *)talloc_zero(tall_pcu_ctx, RlcMacDownlink_t);
Encoding::write_packet_uplink_ack(bts_data(), mac_control_block, this, final);
encode_gsm_rlcmac_downlink(ack_vec, mac_control_block);
bitvec_pack(ack_vec, msgb_put(msg, 23));
bitvec_free(ack_vec);
talloc_free(mac_control_block);
/* now we must set this flag, so we are allowed to assign downlink
* TBF on PACCH. it is only allowed when TLLI is acknowledged. */
m_contention_resolution_done = 1;
if (final) {
poll_state = GPRS_RLCMAC_POLL_SCHED;
poll_fn = (fn + 13) % 2715648;
/* waiting for final acknowledge */
ul_ack_state = GPRS_RLCMAC_UL_ACK_WAIT_ACK;
m_final_ack_sent = 1;
} else
ul_ack_state = GPRS_RLCMAC_UL_ACK_NONE;
return msg;
}
int gprs_rlcmac_ul_tbf::rcv_data_block_acknowledged(const uint8_t *data, size_t len, int8_t rssi)
{
struct rlc_ul_header *rh = (struct rlc_ul_header *)data;
int rc;
const uint16_t mod_sns = m_window.mod_sns();
const uint16_t ws = m_window.ws();
this->state_flags |= (1 << GPRS_RLCMAC_FLAG_UL_DATA);
LOGP(DRLCMACUL, LOGL_DEBUG, "UL DATA TFI=%d received (V(Q)=%d .. "
"V(R)=%d)\n", rh->tfi, this->m_window.v_q(),
this->m_window.v_r());
/* process RSSI */
gprs_rlcmac_rssi(this, rssi);
/* get TLLI */
if (!this->is_tlli_valid()) {
if (!extract_tlli(data, len))
return 0;
/* already have TLLI, but we stille get another one */
} else if (rh->ti) {
uint32_t tlli;
rc = Decoding::tlli_from_ul_data(data, len, &tlli);
if (rc) {
LOGP(DRLCMACUL, LOGL_NOTICE, "Failed to decode TLLI "
"of UL DATA TFI=%d.\n", rh->tfi);
return 0;
}
if (tlli != this->tlli()) {
LOGP(DRLCMACUL, LOGL_NOTICE, "TLLI mismatch on UL "
"DATA TFI=%d. (Ignoring due to contention "
"resolution)\n", rh->tfi);
return 0;
}
}
/* restart T3169 */
tbf_timer_start(this, 3169, bts_data()->t3169, 0);
/* Increment RX-counter */
this->m_rx_counter++;
if (!m_window.is_in_window(rh->bsn)) {
LOGP(DRLCMACUL, LOGL_DEBUG, "- BSN %d out of window "
"%d..%d (it's normal)\n", rh->bsn,
m_window.v_q(),
(m_window.v_q() + ws - 1) & mod_sns);
maybe_schedule_uplink_acknack(rh);
return 0;
}
/* Write block to buffer and set receive state array. */
m_rlc.block(rh->bsn)->put_data(data, len);
LOGP(DRLCMACUL, LOGL_DEBUG, "- BSN %d storing in window (%d..%d)\n",
rh->bsn, m_window.v_q(),
(m_window.v_q() + ws - 1) & mod_sns);
/* Raise V(Q) if possible, and retrieve LLC frames from blocks.
* This is looped until there is a gap (non received block) or
* the window is empty.*/
const uint16_t v_q_beg = m_window.v_q();
const uint16_t count = m_window.receive_bsn(rh->bsn);
/* Retrieve LLC frames from blocks that are ready */
for (uint16_t i = 0; i < count; ++i) {
uint16_t index = (v_q_beg + i) & mod_sns;
assemble_forward_llc(m_rlc.block(index));
}
/* Check CV of last frame in buffer */
if (this->state_is(GPRS_RLCMAC_FLOW) /* still in flow state */
&& this->m_window.v_q() == this->m_window.v_r()) { /* if complete */
struct rlc_ul_header *last_rh = (struct rlc_ul_header *)
m_rlc.block((m_window.v_r() - 1) & mod_sns)->block;
LOGP(DRLCMACUL, LOGL_DEBUG, "- No gaps in received block, "
"last block: BSN=%d CV=%d\n", last_rh->bsn,
last_rh->cv);
if (last_rh->cv == 0) {
LOGP(DRLCMACUL, LOGL_DEBUG, "- Finished with UL "
"TBF\n");
set_state(GPRS_RLCMAC_FINISHED);
/* Reset N3103 counter. */
this->m_n3103 = 0;
}
}
/* If TLLI is included or if we received half of the window, we send
* an ack/nack */
maybe_schedule_uplink_acknack(rh);
return 0;
}
void gprs_rlcmac_ul_tbf::maybe_schedule_uplink_acknack(const rlc_ul_header *rh)
{
if (rh->si || rh->ti || state_is(GPRS_RLCMAC_FINISHED)
|| (m_rx_counter % SEND_ACK_AFTER_FRAMES) == 0) {
if (rh->si) {
LOGP(DRLCMACUL, LOGL_NOTICE, "- Scheduling Ack/Nack, "
"because MS is stalled.\n");
}
if (rh->ti) {
LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, "
"because TLLI is included.\n");
}
if (state_is(GPRS_RLCMAC_FINISHED)) {
LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, "
"because last block has CV==0.\n");
}
if ((m_rx_counter % SEND_ACK_AFTER_FRAMES) == 0) {
LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, "
"because %d frames received.\n",
SEND_ACK_AFTER_FRAMES);
}
if (ul_ack_state == GPRS_RLCMAC_UL_ACK_NONE) {
/* trigger sending at next RTS */
ul_ack_state = GPRS_RLCMAC_UL_ACK_SEND_ACK;
} else {
/* already triggered */
LOGP(DRLCMACUL, LOGL_DEBUG, "- Sending Ack/Nack is "
"already triggered, don't schedule!\n");
}
}
}
/* Send Uplink unit-data to SGSN. */
int gprs_rlcmac_ul_tbf::snd_ul_ud()
{
uint8_t qos_profile[3];
struct msgb *llc_pdu;
unsigned msg_len = NS_HDR_LEN + BSSGP_HDR_LEN + m_llc.frame_length();
struct bssgp_bvc_ctx *bctx = gprs_bssgp_pcu_current_bctx();
LOGP(DBSSGP, LOGL_INFO, "LLC [PCU -> SGSN] %s len=%d\n", tbf_name(this), m_llc.frame_length());
if (!bctx) {
LOGP(DBSSGP, LOGL_ERROR, "No bctx\n");
m_llc.reset_frame_space();
return -EIO;
}
llc_pdu = msgb_alloc_headroom(msg_len, msg_len,"llc_pdu");
uint8_t *buf = msgb_push(llc_pdu, TL16V_GROSS_LEN(sizeof(uint8_t)*m_llc.frame_length()));
tl16v_put(buf, BSSGP_IE_LLC_PDU, sizeof(uint8_t)*m_llc.frame_length(), m_llc.frame);
qos_profile[0] = QOS_PROFILE >> 16;
qos_profile[1] = QOS_PROFILE >> 8;
qos_profile[2] = QOS_PROFILE;
bssgp_tx_ul_ud(bctx, tlli(), qos_profile, llc_pdu);
m_llc.reset_frame_space();
return 0;
}