mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-pcu.git
synced 2025-11-03 13:43:32 +00:00
Compare commits
19 Commits
pespin/mas
...
jerlbeck/w
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
407851b865 | ||
|
|
196ddc58a4 | ||
|
|
77a23a7de4 | ||
|
|
e75e76defe | ||
|
|
67728acc7a | ||
|
|
967e4dd2c3 | ||
|
|
0a96390122 | ||
|
|
155cf8442a | ||
|
|
bd8c689ed4 | ||
|
|
618babae12 | ||
|
|
2f7228b15f | ||
|
|
e61b652e1b | ||
|
|
53a32b8e95 | ||
|
|
6634635cf5 | ||
|
|
5823b20f2f | ||
|
|
6659c516fa | ||
|
|
54fff3b3e4 | ||
|
|
3bea905e0d | ||
|
|
4ff709c3ac |
98
src/bts.cpp
98
src/bts.cpp
@@ -522,6 +522,17 @@ void BTS::snd_dl_ass(gprs_rlcmac_tbf *tbf, uint8_t poll, const char *imsi)
|
||||
}
|
||||
|
||||
|
||||
GprsMs *BTS::ms_alloc(uint8_t ms_class)
|
||||
{
|
||||
GprsMs *ms;
|
||||
ms = ms_store().create_ms();
|
||||
|
||||
ms->set_timeout(m_bts.ms_idle_sec);
|
||||
ms->set_ms_class(ms_class);
|
||||
|
||||
return ms;
|
||||
}
|
||||
|
||||
/*
|
||||
* PDCH code below. TODO: move to a separate file
|
||||
*/
|
||||
@@ -657,7 +668,8 @@ void gprs_rlcmac_pdch::add_paging(struct gprs_rlcmac_paging *pag)
|
||||
*
|
||||
* 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)
|
||||
int gprs_rlcmac_pdch::rcv_data_block_acknowledged(uint8_t *data, uint8_t len,
|
||||
struct pcu_l1_meas *meas)
|
||||
{
|
||||
struct gprs_rlcmac_ul_tbf *tbf;
|
||||
struct rlc_ul_header *rh = (struct rlc_ul_header *)data;
|
||||
@@ -692,7 +704,7 @@ int gprs_rlcmac_pdch::rcv_data_block_acknowledged(uint8_t *data, uint8_t len, in
|
||||
return 0;
|
||||
}
|
||||
|
||||
return tbf->rcv_data_block_acknowledged(data, len, rssi);
|
||||
return tbf->rcv_data_block_acknowledged(data, len, meas);
|
||||
}
|
||||
|
||||
void gprs_rlcmac_pdch::rcv_control_ack(Packet_Control_Acknowledgement_t *packet, uint32_t fn)
|
||||
@@ -711,7 +723,6 @@ void gprs_rlcmac_pdch::rcv_control_ack(Packet_Control_Acknowledgement_t *packet,
|
||||
fn, tlli, trx_no(), ts_no);
|
||||
return;
|
||||
}
|
||||
tbf->update_tlli(tlli);
|
||||
tbf->update_ms(tlli, GPRS_RLCMAC_UL_TBF);
|
||||
|
||||
LOGP(DRLCMAC, LOGL_DEBUG, "RX: [PCU <- BTS] %s Packet Control Ack\n", tbf_name(tbf));
|
||||
@@ -789,11 +800,70 @@ void gprs_rlcmac_pdch::rcv_control_ack(Packet_Control_Acknowledgement_t *packet,
|
||||
"at no request\n");
|
||||
}
|
||||
|
||||
static void get_rx_qual_meas(struct pcu_l1_meas *meas, uint8_t rx_qual_enc)
|
||||
{
|
||||
static const int16_t rx_qual_map[] = {
|
||||
0, /* 0,14 % */
|
||||
0, /* 0,28 % */
|
||||
1, /* 0,57 % */
|
||||
1, /* 1,13 % */
|
||||
2, /* 2,26 % */
|
||||
5, /* 4,53 % */
|
||||
9, /* 9,05 % */
|
||||
18, /* 18,10 % */
|
||||
};
|
||||
|
||||
meas->set_ms_rx_qual(rx_qual_map[
|
||||
OSMO_MIN(rx_qual_enc, ARRAY_SIZE(rx_qual_map)-1)
|
||||
]);
|
||||
}
|
||||
|
||||
static void get_meas(struct pcu_l1_meas *meas,
|
||||
const Packet_Resource_Request_t *qr)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
meas->set_ms_c_value(qr->C_VALUE);
|
||||
if (qr->Exist_SIGN_VAR)
|
||||
meas->set_ms_sign_var((qr->SIGN_VAR + 2) / 4); /* SIGN_VAR * 0.25 dB */
|
||||
|
||||
for (i = 0; i < OSMO_MIN(ARRAY_SIZE(qr->Slot), ARRAY_SIZE(meas->ts)); i++)
|
||||
{
|
||||
if (qr->Slot[i].Exist) {
|
||||
LOGP(DRLCMAC, LOGL_INFO,
|
||||
"Packet resource request: i_level[%d] = %d\n",
|
||||
i, qr->Slot[i].I_LEVEL);
|
||||
meas->set_ms_i_level(i, -2 * qr->Slot[i].I_LEVEL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void get_meas(struct pcu_l1_meas *meas,
|
||||
const Channel_Quality_Report_t *qr)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
get_rx_qual_meas(meas, qr->RXQUAL);
|
||||
meas->set_ms_c_value(qr->C_VALUE);
|
||||
meas->set_ms_sign_var((qr->SIGN_VAR + 2) / 4); /* SIGN_VAR * 0.25 dB */
|
||||
|
||||
for (i = 0; i < OSMO_MIN(ARRAY_SIZE(qr->Slot), ARRAY_SIZE(meas->ts)); i++)
|
||||
{
|
||||
if (qr->Slot[i].Exist) {
|
||||
LOGP(DRLCMAC, LOGL_INFO,
|
||||
"Channel quality report: i_level[%d] = %d\n",
|
||||
i, qr->Slot[i].I_LEVEL_TN);
|
||||
meas->set_ms_i_level(i, -2 * qr->Slot[i].I_LEVEL_TN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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_dl_tbf *tbf;
|
||||
int rc;
|
||||
struct pcu_l1_meas meas;
|
||||
|
||||
tfi = ack_nack->DOWNLINK_TFI;
|
||||
tbf = bts()->dl_tbf_by_poll_fn(fn, trx_no(), ts_no);
|
||||
@@ -835,11 +905,16 @@ void gprs_rlcmac_pdch::rcv_control_dl_ack_nack(Packet_Downlink_Ack_Nack_t *ack_n
|
||||
|
||||
/* This call will register the new TBF with the MS on success */
|
||||
tbf_alloc_ul(bts_data(), tbf->trx->trx_no, tbf->ms_class(),
|
||||
tbf->tlli(), tbf->ta(), tbf);
|
||||
tbf->tlli(), tbf->ta(), tbf->ms());
|
||||
|
||||
/* schedule uplink assignment */
|
||||
tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_SEND_ASS;
|
||||
}
|
||||
/* get measurements */
|
||||
if (tbf->ms()) {
|
||||
get_meas(&meas, &ack_nack->Channel_Quality_Report);
|
||||
tbf->ms()->update_l1_meas(&meas);
|
||||
}
|
||||
}
|
||||
|
||||
void gprs_rlcmac_pdch::rcv_resource_request(Packet_Resource_Request_t *request, uint32_t fn)
|
||||
@@ -852,6 +927,7 @@ void gprs_rlcmac_pdch::rcv_resource_request(Packet_Resource_Request_t *request,
|
||||
uint32_t tlli = request->ID.u.TLLI;
|
||||
uint8_t ms_class = 0;
|
||||
uint8_t ta = 0;
|
||||
struct pcu_l1_meas meas;
|
||||
|
||||
GprsMs *ms = bts()->ms_by_tlli(tlli);
|
||||
/* Keep the ms, even if it gets idle temporarily */
|
||||
@@ -897,7 +973,7 @@ void gprs_rlcmac_pdch::rcv_resource_request(Packet_Resource_Request_t *request,
|
||||
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");
|
||||
ul_tbf = tbf_alloc_ul(bts_data(), trx_no(), ms_class, tlli, ta, NULL);
|
||||
ul_tbf = tbf_alloc_ul(bts_data(), trx_no(), ms_class, tlli, ta, ms);
|
||||
if (!ul_tbf)
|
||||
return;
|
||||
|
||||
@@ -906,6 +982,12 @@ void gprs_rlcmac_pdch::rcv_resource_request(Packet_Resource_Request_t *request,
|
||||
ul_tbf->control_ts = ts_no;
|
||||
/* schedule uplink assignment */
|
||||
ul_tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_SEND_ASS;
|
||||
|
||||
/* get measurements */
|
||||
if (ul_tbf->ms()) {
|
||||
get_meas(&meas, request);
|
||||
ul_tbf->ms()->update_l1_meas(&meas);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -932,7 +1014,6 @@ void gprs_rlcmac_pdch::rcv_resource_request(Packet_Resource_Request_t *request,
|
||||
"RX: [PCU <- BTS] %s FIXME: Packet resource request\n",
|
||||
tbf_name(ul_tbf));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void gprs_rlcmac_pdch::rcv_measurement_report(Packet_Measurement_Report_t *report, uint32_t fn)
|
||||
@@ -995,7 +1076,8 @@ int gprs_rlcmac_pdch::rcv_control_block(
|
||||
|
||||
|
||||
/* received RLC/MAC block from L1 */
|
||||
int gprs_rlcmac_pdch::rcv_block(uint8_t *data, uint8_t len, uint32_t fn, int8_t rssi)
|
||||
int gprs_rlcmac_pdch::rcv_block(uint8_t *data, uint8_t len, uint32_t fn,
|
||||
struct pcu_l1_meas *meas)
|
||||
{
|
||||
unsigned payload = data[0] >> 6;
|
||||
bitvec *block;
|
||||
@@ -1003,7 +1085,7 @@ int gprs_rlcmac_pdch::rcv_block(uint8_t *data, uint8_t len, uint32_t fn, int8_t
|
||||
|
||||
switch (payload) {
|
||||
case GPRS_RLCMAC_DATA_BLOCK:
|
||||
rc = rcv_data_block_acknowledged(data, len, rssi);
|
||||
rc = rcv_data_block_acknowledged(data, len, meas);
|
||||
break;
|
||||
case GPRS_RLCMAC_CONTROL_BLOCK:
|
||||
block = bitvec_alloc(len);
|
||||
|
||||
12
src/bts.h
12
src/bts.h
@@ -37,6 +37,7 @@ extern "C" {
|
||||
#include <stdint.h>
|
||||
|
||||
struct BTS;
|
||||
struct GprsMs;
|
||||
|
||||
/*
|
||||
* PDCH instance
|
||||
@@ -56,7 +57,8 @@ struct gprs_rlcmac_pdch {
|
||||
void disable();
|
||||
|
||||
/* dispatching of messages */
|
||||
int rcv_block(uint8_t *data, uint8_t len, uint32_t fn, int8_t rssi);
|
||||
int rcv_block(uint8_t *data, uint8_t len, uint32_t fn,
|
||||
struct pcu_l1_meas *meas);
|
||||
|
||||
gprs_rlcmac_bts *bts_data() const;
|
||||
BTS *bts() const;
|
||||
@@ -80,7 +82,8 @@ struct gprs_rlcmac_pdch {
|
||||
|
||||
#ifdef __cplusplus
|
||||
private:
|
||||
int rcv_data_block_acknowledged(uint8_t *data, uint8_t len, int8_t rssi);
|
||||
int rcv_data_block_acknowledged(uint8_t *data, uint8_t len,
|
||||
struct pcu_l1_meas *meas);
|
||||
int rcv_control_block(bitvec *rlc_block, uint32_t fn);
|
||||
|
||||
void rcv_control_ack(Packet_Control_Acknowledgement_t *, uint32_t fn);
|
||||
@@ -136,7 +139,7 @@ struct gprs_rlcmac_bts {
|
||||
uint8_t n3105;
|
||||
struct gprs_rlcmac_trx trx[8];
|
||||
int (*alloc_algorithm)(struct gprs_rlcmac_bts *bts,
|
||||
struct gprs_rlcmac_tbf *old_tbf,
|
||||
struct GprsMs *ms,
|
||||
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;
|
||||
@@ -146,6 +149,8 @@ struct gprs_rlcmac_bts {
|
||||
uint8_t cs_adj_enabled;
|
||||
uint8_t cs_adj_upper_limit;
|
||||
uint8_t cs_adj_lower_limit;
|
||||
struct {int16_t low; int16_t high;} cs_lqual_ranges[4];
|
||||
uint16_t cs_downgrade_threshold; /* downgrade if less packets left (DL) */
|
||||
|
||||
/* TBF handling, make private or move into TBFController */
|
||||
/* list of uplink TBFs */
|
||||
@@ -224,6 +229,7 @@ public:
|
||||
|
||||
GprsMsStorage &ms_store();
|
||||
GprsMs *ms_by_tlli(uint32_t tlli, uint32_t old_tlli = 0);
|
||||
GprsMs *ms_alloc(uint8_t ms_class);
|
||||
|
||||
/*
|
||||
* Statistics
|
||||
|
||||
@@ -804,8 +804,8 @@ struct bssgp_bvc_ctx *gprs_bssgp_pcu_current_bctx(void)
|
||||
return the_pcu.bctx;
|
||||
}
|
||||
|
||||
void gprs_bssgp_update_queue_delay(struct timeval *tv_recv,
|
||||
struct timeval *tv_now)
|
||||
void gprs_bssgp_update_queue_delay(const struct timeval *tv_recv,
|
||||
const struct timeval *tv_now)
|
||||
{
|
||||
struct timeval *delay_sum = &the_pcu.queue_delay_sum;
|
||||
struct timeval tv_delay;
|
||||
|
||||
@@ -83,7 +83,7 @@ void gprs_bssgp_destroy(void);
|
||||
|
||||
struct bssgp_bvc_ctx *gprs_bssgp_pcu_current_bctx(void);
|
||||
|
||||
void gprs_bssgp_update_queue_delay(struct timeval *tv_recv,
|
||||
struct timeval *tv_now);
|
||||
void gprs_bssgp_update_queue_delay(const struct timeval *tv_recv,
|
||||
const struct timeval *tv_now);
|
||||
|
||||
#endif // GPRS_BSSGP_PCU_H
|
||||
|
||||
117
src/gprs_ms.cpp
117
src/gprs_ms.cpp
@@ -90,7 +90,8 @@ GprsMs::GprsMs(BTS *bts, uint32_t tlli) :
|
||||
m_is_idle(true),
|
||||
m_ref(0),
|
||||
m_list(this),
|
||||
m_delay(0)
|
||||
m_delay(0),
|
||||
m_nack_rate_dl(0)
|
||||
{
|
||||
LOGP(DRLCMAC, LOGL_INFO, "Creating MS object, TLLI = 0x%08x\n", tlli);
|
||||
|
||||
@@ -239,7 +240,7 @@ void GprsMs::detach_tbf(gprs_rlcmac_tbf *tbf)
|
||||
if (tbf->ms() == this)
|
||||
tbf->set_ms(NULL);
|
||||
|
||||
if (!m_dl_tbf && !m_ul_tbf)
|
||||
if (!m_dl_tbf && !m_ul_tbf && tlli() != 0)
|
||||
start_timer();
|
||||
|
||||
update_status();
|
||||
@@ -367,7 +368,7 @@ void GprsMs::update_error_rate(gprs_rlcmac_tbf *tbf, int error_rate)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts_data;
|
||||
int64_t now;
|
||||
uint8_t max_cs_ul = 4, max_cs_dl = 4;
|
||||
uint8_t max_cs_dl = 4;
|
||||
|
||||
OSMO_ASSERT(m_bts != NULL);
|
||||
bts_data = m_bts->bts_data();
|
||||
@@ -377,18 +378,17 @@ void GprsMs::update_error_rate(gprs_rlcmac_tbf *tbf, int error_rate)
|
||||
|
||||
now = now_msec();
|
||||
|
||||
if (bts_data->max_cs_ul)
|
||||
max_cs_ul = bts_data->max_cs_ul;
|
||||
|
||||
if (bts_data->max_cs_dl)
|
||||
max_cs_dl = bts_data->max_cs_dl;
|
||||
|
||||
/* TODO: Check for TBF direction */
|
||||
/* TODO: Support different CS values for UL and DL */
|
||||
|
||||
m_nack_rate_dl = error_rate;
|
||||
|
||||
if (error_rate > bts_data->cs_adj_upper_limit) {
|
||||
if (m_current_cs_dl > 1) {
|
||||
m_current_cs_dl -= 1;
|
||||
m_current_cs_ul = m_current_cs_dl;
|
||||
LOGP(DRLCMACDL, LOGL_INFO,
|
||||
"MS (IMSI %s): High error rate %d%%, "
|
||||
"reducing CS level to %d\n",
|
||||
@@ -399,12 +399,10 @@ void GprsMs::update_error_rate(gprs_rlcmac_tbf *tbf, int error_rate)
|
||||
if (m_current_cs_dl < max_cs_dl) {
|
||||
if (now - m_last_cs_not_low > 1000) {
|
||||
m_current_cs_dl += 1;
|
||||
if (m_current_cs_dl <= max_cs_ul)
|
||||
m_current_cs_ul = m_current_cs_dl;
|
||||
|
||||
LOGP(DRLCMACDL, LOGL_INFO,
|
||||
"MS (IMSI %s): Low error rate %d%%, "
|
||||
"increasing CS level to %d\n",
|
||||
"increasing DL CS level to %d\n",
|
||||
imsi(), error_rate, m_current_cs_dl);
|
||||
m_last_cs_not_low = now;
|
||||
} else {
|
||||
@@ -421,3 +419,102 @@ void GprsMs::update_error_rate(gprs_rlcmac_tbf *tbf, int error_rate)
|
||||
m_last_cs_not_low = now;
|
||||
}
|
||||
}
|
||||
|
||||
void GprsMs::update_l1_meas(const pcu_l1_meas *meas)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts_data;
|
||||
uint8_t max_cs_ul = 4;
|
||||
unsigned i;
|
||||
|
||||
OSMO_ASSERT(m_bts != NULL);
|
||||
bts_data = m_bts->bts_data();
|
||||
|
||||
if (bts_data->max_cs_ul)
|
||||
max_cs_ul = bts_data->max_cs_ul;
|
||||
|
||||
if (meas->have_link_qual) {
|
||||
int old_link_qual = meas->link_qual;
|
||||
int low = bts_data->cs_lqual_ranges[current_cs_ul()-1].low;
|
||||
int high = bts_data->cs_lqual_ranges[current_cs_ul()-1].high;
|
||||
uint8_t new_cs_ul = m_current_cs_ul;
|
||||
|
||||
if (m_l1_meas.have_link_qual)
|
||||
old_link_qual = m_l1_meas.link_qual;
|
||||
|
||||
if (meas->link_qual < low && old_link_qual < low)
|
||||
new_cs_ul = m_current_cs_ul - 1;
|
||||
else if (meas->link_qual > high && old_link_qual > high &&
|
||||
m_current_cs_ul < max_cs_ul)
|
||||
new_cs_ul = m_current_cs_ul + 1;
|
||||
|
||||
if (m_current_cs_ul != new_cs_ul) {
|
||||
LOGP(DRLCMACDL, LOGL_INFO,
|
||||
"MS (IMSI %s): "
|
||||
"Link quality %ddB (%ddB) left window [%d, %d], "
|
||||
"modifying uplink CS level: %d -> %d\n",
|
||||
imsi(), meas->link_qual, old_link_qual,
|
||||
low, high,
|
||||
m_current_cs_ul, new_cs_ul);
|
||||
|
||||
m_current_cs_ul = new_cs_ul;
|
||||
}
|
||||
}
|
||||
|
||||
if (meas->have_rssi)
|
||||
m_l1_meas.set_rssi(meas->rssi);
|
||||
if (meas->have_bto)
|
||||
m_l1_meas.set_bto(meas->bto);
|
||||
if (meas->have_ber)
|
||||
m_l1_meas.set_ber(meas->ber);
|
||||
if (meas->have_link_qual)
|
||||
m_l1_meas.set_link_qual(meas->link_qual);
|
||||
|
||||
if (meas->have_ms_rx_qual)
|
||||
m_l1_meas.set_ms_rx_qual(meas->ms_rx_qual);
|
||||
if (meas->have_ms_c_value)
|
||||
m_l1_meas.set_ms_c_value(meas->ms_c_value);
|
||||
if (meas->have_ms_sign_var)
|
||||
m_l1_meas.set_ms_sign_var(meas->ms_sign_var);
|
||||
|
||||
if (meas->have_ms_i_level) {
|
||||
for (i = 0; i < ARRAY_SIZE(meas->ts); ++i) {
|
||||
if (meas->ts[i].have_ms_i_level)
|
||||
m_l1_meas.set_ms_i_level(i, meas->ts[i].ms_i_level);
|
||||
else
|
||||
m_l1_meas.ts[i].have_ms_i_level = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t GprsMs::current_cs_dl() const
|
||||
{
|
||||
uint8_t cs = m_current_cs_dl;
|
||||
size_t unencoded_octets;
|
||||
|
||||
if (!m_bts)
|
||||
return cs;
|
||||
|
||||
unencoded_octets = m_llc_queue.octets();
|
||||
|
||||
/* If the DL TBF is active, add number of unencoded chunk octets */
|
||||
if (m_dl_tbf)
|
||||
unencoded_octets = m_dl_tbf->m_llc.chunk_size();
|
||||
|
||||
/* There are many unencoded octets, don't reduce */
|
||||
if (unencoded_octets >= m_bts->bts_data()->cs_downgrade_threshold)
|
||||
return cs;
|
||||
|
||||
/* RF conditions are good, don't reduce */
|
||||
if (m_nack_rate_dl < m_bts->bts_data()->cs_adj_lower_limit)
|
||||
return cs;
|
||||
|
||||
/* The throughput would probably be better if the CS level was reduced */
|
||||
cs -= 1;
|
||||
|
||||
/* CS-2 doesn't gain throughput with small packets, further reduce to CS-1 */
|
||||
if (cs == 2)
|
||||
cs -= 1;
|
||||
|
||||
return cs;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ struct gprs_rlcmac_ul_tbf;
|
||||
|
||||
#include "cxx_linuxlist.h"
|
||||
#include "llc.h"
|
||||
#include "pcu_l1_if.h"
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/timer.h>
|
||||
@@ -96,6 +97,10 @@ public:
|
||||
LListHead<GprsMs>& list() {return this->m_list;}
|
||||
const LListHead<GprsMs>& list() const {return this->m_list;}
|
||||
|
||||
void update_l1_meas(const pcu_l1_meas *meas);
|
||||
const pcu_l1_meas* l1_meas() const {return &m_l1_meas;};
|
||||
unsigned nack_rate_dl() const;
|
||||
|
||||
/* internal use */
|
||||
static void timeout(void *priv_);
|
||||
|
||||
@@ -132,6 +137,9 @@ private:
|
||||
unsigned m_delay;
|
||||
|
||||
int64_t m_last_cs_not_low;
|
||||
|
||||
pcu_l1_meas m_l1_meas;
|
||||
unsigned m_nack_rate_dl;
|
||||
};
|
||||
|
||||
inline uint32_t GprsMs::tlli() const
|
||||
@@ -162,11 +170,6 @@ inline uint8_t GprsMs::ms_class() const
|
||||
return m_ms_class;
|
||||
}
|
||||
|
||||
inline uint8_t GprsMs::current_cs_dl() const
|
||||
{
|
||||
return m_current_cs_dl;
|
||||
}
|
||||
|
||||
inline uint8_t GprsMs::current_cs_ul() const
|
||||
{
|
||||
return m_current_cs_ul;
|
||||
@@ -187,3 +190,8 @@ inline const gprs_llc_queue *GprsMs::llc_queue() const
|
||||
return &m_llc_queue;
|
||||
}
|
||||
|
||||
inline unsigned GprsMs::nack_rate_dl() const
|
||||
{
|
||||
return m_nack_rate_dl;
|
||||
}
|
||||
|
||||
|
||||
@@ -80,6 +80,18 @@ GprsMs *GprsMsStorage::get_ms(uint32_t tlli, uint32_t old_tlli, const char *imsi
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GprsMs *GprsMsStorage::create_ms()
|
||||
{
|
||||
GprsMs *ms;
|
||||
|
||||
ms = new GprsMs(m_bts, 0);
|
||||
|
||||
ms->set_callback(this);
|
||||
llist_add(&ms->list(), &m_list);
|
||||
|
||||
return ms;
|
||||
}
|
||||
|
||||
GprsMs *GprsMsStorage::create_ms(uint32_t tlli, enum gprs_rlcmac_tbf_direction dir)
|
||||
{
|
||||
GprsMs *ms = get_ms(tlli);
|
||||
@@ -87,15 +99,12 @@ GprsMs *GprsMsStorage::create_ms(uint32_t tlli, enum gprs_rlcmac_tbf_direction d
|
||||
if (ms)
|
||||
return ms;
|
||||
|
||||
ms = new GprsMs(m_bts, 0);
|
||||
ms = create_ms();
|
||||
|
||||
if (dir == GPRS_RLCMAC_UL_TBF)
|
||||
ms->set_tlli(tlli);
|
||||
else
|
||||
ms->confirm_tlli(tlli);
|
||||
|
||||
ms->set_callback(this);
|
||||
llist_add(&ms->list(), &m_list);
|
||||
|
||||
return ms;
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ public:
|
||||
|
||||
GprsMs *get_ms(uint32_t tlli, uint32_t old_tlli = 0, const char *imsi = 0) const;
|
||||
GprsMs *create_ms(uint32_t tlli, enum gprs_rlcmac_tbf_direction dir);
|
||||
GprsMs *create_ms();
|
||||
|
||||
const LListHead<GprsMs>& ms_list() const {return m_list;}
|
||||
private:
|
||||
|
||||
@@ -42,6 +42,7 @@ extern "C" {
|
||||
struct gprs_rlcmac_tbf;
|
||||
struct gprs_rlcmac_bts;
|
||||
struct BTS;
|
||||
struct GprsMs;
|
||||
|
||||
#ifdef __cplusplus
|
||||
/*
|
||||
@@ -95,11 +96,11 @@ int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_bts *bts,
|
||||
extern "C" {
|
||||
#endif
|
||||
int alloc_algorithm_a(struct gprs_rlcmac_bts *bts,
|
||||
struct gprs_rlcmac_tbf *old_tbf,
|
||||
struct GprsMs *ms,
|
||||
struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single);
|
||||
|
||||
int alloc_algorithm_b(struct gprs_rlcmac_bts *bts,
|
||||
struct gprs_rlcmac_tbf *old_tbf,
|
||||
struct GprsMs *ms,
|
||||
struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <gprs_debug.h>
|
||||
#include <bts.h>
|
||||
#include <tbf.h>
|
||||
#include <gprs_ms.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
@@ -139,7 +140,7 @@ static void assign_dlink_tbf(
|
||||
* Assign single slot for uplink and downlink
|
||||
*/
|
||||
int alloc_algorithm_a(struct gprs_rlcmac_bts *bts,
|
||||
struct gprs_rlcmac_tbf *old_tbf,
|
||||
GprsMs *ms,
|
||||
struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single)
|
||||
{
|
||||
struct gprs_rlcmac_pdch *pdch;
|
||||
@@ -261,16 +262,21 @@ inc_window:
|
||||
return rx_window;
|
||||
}
|
||||
|
||||
static int reduce_rx_window(const int ms_type, const struct gprs_rlcmac_tbf *old_tbf,
|
||||
static int reduce_rx_window(const int ms_type, const GprsMs *ms,
|
||||
const int Tt, const int Tr,
|
||||
int *rx_window,
|
||||
uint8_t *rx_win_min, uint8_t *rx_win_max)
|
||||
{
|
||||
gprs_rlcmac_ul_tbf *ul_tbf;
|
||||
|
||||
if (ms_type != 1)
|
||||
return 0;
|
||||
if (!old_tbf)
|
||||
if (!ms)
|
||||
return 0;
|
||||
if (old_tbf->direction != GPRS_RLCMAC_UL_TBF)
|
||||
|
||||
ul_tbf = ms->ul_tbf();
|
||||
|
||||
if (!ul_tbf)
|
||||
return 0;
|
||||
|
||||
uint8_t collide = 0, ul_usage = 0;
|
||||
@@ -278,7 +284,7 @@ static int reduce_rx_window(const int ms_type, const struct gprs_rlcmac_tbf *old
|
||||
/* calculate mask of colliding slots */
|
||||
for (uint8_t ts_no = 0; ts_no < 8; ts_no++) {
|
||||
int j;
|
||||
if (!old_tbf->pdch[ts_no])
|
||||
if (!ul_tbf->pdch[ts_no])
|
||||
continue;
|
||||
|
||||
ul_usage |= (1 << ts_no);
|
||||
@@ -525,7 +531,7 @@ static int select_first_ts(gprs_rlcmac_trx *trx, uint8_t tx_win_min,
|
||||
*
|
||||
*/
|
||||
int alloc_algorithm_b(struct gprs_rlcmac_bts *bts,
|
||||
struct gprs_rlcmac_tbf *old_tbf,
|
||||
GprsMs *ms,
|
||||
struct gprs_rlcmac_tbf *tbf, uint32_t cust, uint8_t single)
|
||||
{
|
||||
const struct gprs_ms_multislot_class *ms_class;
|
||||
@@ -592,7 +598,7 @@ int alloc_algorithm_b(struct gprs_rlcmac_bts *bts,
|
||||
|
||||
|
||||
/* reduce window, if existing uplink slots collide RX window */
|
||||
int rc = reduce_rx_window(ms_class->type, old_tbf, Tt, Tr,
|
||||
int rc = reduce_rx_window(ms_class->type, ms, Tt, Tr,
|
||||
&rx_window, &rx_win_min, &rx_win_max);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
29
src/llc.cpp
29
src/llc.cpp
@@ -100,12 +100,23 @@ void gprs_llc_queue::init()
|
||||
{
|
||||
INIT_LLIST_HEAD(&m_queue);
|
||||
m_queue_size = 0;
|
||||
m_queue_octets = 0;
|
||||
m_avg_queue_delay = 0;
|
||||
}
|
||||
|
||||
void gprs_llc_queue::enqueue(struct msgb *llc_msg)
|
||||
void gprs_llc_queue::enqueue(struct msgb *llc_msg, const MetaInfo *info)
|
||||
{
|
||||
static const MetaInfo def_meta = {{0}};
|
||||
MetaInfo *meta_storage;
|
||||
|
||||
osmo_static_assert(sizeof(*info) <= sizeof(llc_msg->cb), info_does_not_fit);
|
||||
|
||||
m_queue_size += 1;
|
||||
m_queue_octets += msgb_length(llc_msg);
|
||||
|
||||
meta_storage = (MetaInfo *)&llc_msg->cb[0];
|
||||
*meta_storage = info ? *info : def_meta;
|
||||
|
||||
msgb_enqueue(&m_queue, llc_msg);
|
||||
}
|
||||
|
||||
@@ -120,27 +131,34 @@ void gprs_llc_queue::clear(BTS *bts)
|
||||
}
|
||||
|
||||
m_queue_size = 0;
|
||||
m_queue_octets = 0;
|
||||
}
|
||||
|
||||
#define ALPHA 0.5f
|
||||
|
||||
struct msgb *gprs_llc_queue::dequeue()
|
||||
struct msgb *gprs_llc_queue::dequeue(const MetaInfo **info)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct timeval *tv, tv_now, tv_result;
|
||||
uint32_t lifetime;
|
||||
|
||||
const MetaInfo *meta_storage;
|
||||
|
||||
msg = msgb_dequeue(&m_queue);
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
meta_storage = (MetaInfo *)&msg->cb[0];
|
||||
|
||||
if (info)
|
||||
*info = meta_storage;
|
||||
|
||||
m_queue_size -= 1;
|
||||
m_queue_octets -= msgb_length(msg);
|
||||
|
||||
/* take the second time */
|
||||
gettimeofday(&tv_now, NULL);
|
||||
tv = (struct timeval *)&msg->data[sizeof(*tv)];
|
||||
timersub(&tv_now, tv, &tv_result);
|
||||
timersub(&tv_now, &meta_storage->recv_time, &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);
|
||||
@@ -171,7 +189,8 @@ void gprs_llc_queue::calc_pdu_lifetime(BTS *bts, const uint16_t pdu_delay_csec,
|
||||
timeradd(&now, &csec, tv);
|
||||
}
|
||||
|
||||
bool gprs_llc_queue::is_frame_expired(struct timeval *tv_now, struct timeval *tv)
|
||||
bool gprs_llc_queue::is_frame_expired(const struct timeval *tv_now,
|
||||
const struct timeval *tv)
|
||||
{
|
||||
/* Timeout is infinite */
|
||||
if (tv->tv_sec == 0 && tv->tv_usec == 0)
|
||||
|
||||
27
src/llc.h
27
src/llc.h
@@ -18,8 +18,13 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
}
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#define LLC_MAX_LEN 1543
|
||||
|
||||
@@ -59,20 +64,29 @@ struct gprs_llc {
|
||||
* I store the LLC frames that come from the SGSN.
|
||||
*/
|
||||
struct gprs_llc_queue {
|
||||
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);
|
||||
struct MetaInfo {
|
||||
struct timeval recv_time;
|
||||
struct timeval expire_time;
|
||||
};
|
||||
|
||||
static void calc_pdu_lifetime(BTS *bts, const uint16_t pdu_delay_csec,
|
||||
struct timeval *tv);
|
||||
static bool is_frame_expired(const struct timeval *now,
|
||||
const struct timeval *tv);
|
||||
static bool is_user_data_frame(uint8_t *data, size_t len);
|
||||
|
||||
void init();
|
||||
|
||||
void enqueue(struct msgb *llc_msg);
|
||||
struct msgb *dequeue();
|
||||
void enqueue(struct msgb *llc_msg, const MetaInfo *info = 0);
|
||||
struct msgb *dequeue(const MetaInfo **info = 0);
|
||||
void clear(BTS *bts);
|
||||
size_t size() const;
|
||||
size_t octets() const;
|
||||
|
||||
private:
|
||||
uint32_t m_avg_queue_delay; /* Average delay of data going through the queue */
|
||||
size_t m_queue_size;
|
||||
size_t m_queue_octets;
|
||||
struct llist_head m_queue; /* queued LLC DL data */
|
||||
|
||||
};
|
||||
@@ -114,3 +128,8 @@ inline size_t gprs_llc_queue::size() const
|
||||
{
|
||||
return this ? m_queue_size : 0;
|
||||
}
|
||||
|
||||
inline size_t gprs_llc_queue::octets() const
|
||||
{
|
||||
return this ? m_queue_octets : 0;
|
||||
}
|
||||
|
||||
@@ -181,17 +181,19 @@ void pcu_l1if_tx_pch(bitvec * block, int plen, const char *imsi)
|
||||
}
|
||||
|
||||
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)
|
||||
uint8_t len, uint32_t fn, struct pcu_l1_meas *meas)
|
||||
{
|
||||
struct gprs_rlcmac_pdch *pdch;
|
||||
|
||||
pdch = &bts_main_data()->trx[trx_no].pdch[ts_no];
|
||||
return pdch->rcv_block(data, len, fn, rssi);
|
||||
return pdch->rcv_block(data, len, fn, meas);
|
||||
}
|
||||
|
||||
static int pcu_rx_data_ind(struct gsm_pcu_if_data *data_ind)
|
||||
{
|
||||
int rc = 0;
|
||||
pcu_l1_meas meas;
|
||||
meas.set_rssi(data_ind->rssi);
|
||||
|
||||
LOGP(DL1IF, LOGL_DEBUG, "Data indication received: sapi=%d arfcn=%d "
|
||||
"block=%d data=%s\n", data_ind->sapi,
|
||||
@@ -202,7 +204,7 @@ static int pcu_rx_data_ind(struct gsm_pcu_if_data *data_ind)
|
||||
case PCU_IF_SAPI_PDTCH:
|
||||
rc = pcu_rx_data_ind_pdtch(data_ind->trx_nr, data_ind->ts_nr,
|
||||
data_ind->data, data_ind->len, data_ind->fn,
|
||||
data_ind->rssi);
|
||||
&meas);
|
||||
break;
|
||||
default:
|
||||
LOGP(DL1IF, LOGL_ERROR, "Received PCU data indication with "
|
||||
|
||||
@@ -31,7 +31,81 @@ extern "C" {
|
||||
#include <osmocom/gsm/gsm_utils.h>
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* L1 Measurement values
|
||||
*/
|
||||
|
||||
struct pcu_l1_meas_ts {
|
||||
unsigned have_ms_i_level:1;
|
||||
|
||||
int16_t ms_i_level; /* I_LEVEL in dB */
|
||||
|
||||
#ifdef __cplusplus
|
||||
pcu_l1_meas_ts& set_ms_i_level(int16_t v) {
|
||||
ms_i_level = v; have_ms_i_level = 1; return *this;
|
||||
}
|
||||
|
||||
pcu_l1_meas_ts() :
|
||||
have_ms_i_level(0)
|
||||
{}
|
||||
#endif
|
||||
};
|
||||
|
||||
struct pcu_l1_meas {
|
||||
unsigned have_rssi:1;
|
||||
unsigned have_ber:1;
|
||||
unsigned have_bto:1;
|
||||
unsigned have_link_qual:1;
|
||||
unsigned have_ms_rx_qual:1;
|
||||
unsigned have_ms_c_value:1;
|
||||
unsigned have_ms_sign_var:1;
|
||||
unsigned have_ms_i_level:1;
|
||||
|
||||
int8_t rssi; /* RSSI in dBm */
|
||||
uint8_t ber; /* Bit error rate in % */
|
||||
int16_t bto; /* Burst timing offset in quarter bits */
|
||||
int16_t link_qual; /* Link quality in dB */
|
||||
int16_t ms_rx_qual; /* MS RXQUAL value in % */
|
||||
int16_t ms_c_value; /* C value in dB */
|
||||
int16_t ms_sign_var; /* SIGN_VAR in dB */
|
||||
|
||||
struct pcu_l1_meas_ts ts[8];
|
||||
|
||||
#ifdef __cplusplus
|
||||
pcu_l1_meas& set_rssi(int8_t v) { rssi = v; have_rssi = 1; return *this;}
|
||||
pcu_l1_meas& set_ber(uint8_t v) { ber = v; have_ber = 1; return *this;}
|
||||
pcu_l1_meas& set_bto(int16_t v) { bto = v; have_bto = 1; return *this;}
|
||||
pcu_l1_meas& set_link_qual(int16_t v) {
|
||||
link_qual = v; have_link_qual = 1; return *this;
|
||||
}
|
||||
pcu_l1_meas& set_ms_rx_qual(int16_t v) {
|
||||
ms_rx_qual = v; have_ms_rx_qual = 1; return *this;
|
||||
}
|
||||
pcu_l1_meas& set_ms_c_value(int16_t v) {
|
||||
ms_c_value = v; have_ms_c_value = 1; return *this;
|
||||
}
|
||||
pcu_l1_meas& set_ms_sign_var(int16_t v) {
|
||||
ms_sign_var = v; have_ms_sign_var = 1; return *this;
|
||||
}
|
||||
pcu_l1_meas& set_ms_i_level(size_t idx, int16_t v) {
|
||||
ts[idx].set_ms_i_level(v); have_ms_i_level = 1; return *this;
|
||||
}
|
||||
pcu_l1_meas() :
|
||||
have_rssi(0),
|
||||
have_ber(0),
|
||||
have_bto(0),
|
||||
have_link_qual(0),
|
||||
have_ms_rx_qual(0),
|
||||
have_ms_c_value(0),
|
||||
have_ms_sign_var(0),
|
||||
have_ms_i_level(0)
|
||||
{}
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
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,
|
||||
@@ -57,6 +131,6 @@ int pcu_rx_rts_req_pdtch(uint8_t trx, uint8_t ts, uint16_t arfcn,
|
||||
extern "C"
|
||||
#endif
|
||||
int pcu_rx_data_ind_pdtch(uint8_t trx, uint8_t ts, uint8_t *data,
|
||||
uint8_t len, uint32_t fn, int8_t rssi);
|
||||
uint8_t len, uint32_t fn, struct pcu_l1_meas *meas);
|
||||
|
||||
#endif // PCU_L1_IF_H
|
||||
|
||||
@@ -178,6 +178,16 @@ int main(int argc, char *argv[])
|
||||
bts->cs_adj_lower_limit = 10; /* Increase CS if the error rate is below */
|
||||
bts->max_cs_ul = 4;
|
||||
bts->max_cs_dl = 4;
|
||||
/* CS-1 to CS-4 */
|
||||
bts->cs_lqual_ranges[0].low = -256;
|
||||
bts->cs_lqual_ranges[0].high = 6;
|
||||
bts->cs_lqual_ranges[1].low = 5;
|
||||
bts->cs_lqual_ranges[1].high = 8;
|
||||
bts->cs_lqual_ranges[2].low = 7;
|
||||
bts->cs_lqual_ranges[2].high = 13;
|
||||
bts->cs_lqual_ranges[3].low = 12;
|
||||
bts->cs_lqual_ranges[3].high = 256;
|
||||
bts->cs_downgrade_threshold = 200;
|
||||
|
||||
msgb_set_talloc_ctx(tall_pcu_ctx);
|
||||
|
||||
|
||||
@@ -90,6 +90,21 @@ static int config_write_pcu(struct vty *vty)
|
||||
else
|
||||
vty_out(vty, " no cs threshold%s", VTY_NEWLINE);
|
||||
|
||||
if (bts->cs_downgrade_threshold)
|
||||
vty_out(vty, " cs downgrade-threshold %d%s",
|
||||
bts->cs_downgrade_threshold, VTY_NEWLINE);
|
||||
else
|
||||
vty_out(vty, " no cs downgrade-threshold%s", VTY_NEWLINE);
|
||||
|
||||
vty_out(vty, " cs link-quality-ranges cs1 %d cs2 %d %d cs3 %d %d cs4 %d%s",
|
||||
bts->cs_lqual_ranges[0].high,
|
||||
bts->cs_lqual_ranges[1].low,
|
||||
bts->cs_lqual_ranges[1].high,
|
||||
bts->cs_lqual_ranges[2].low,
|
||||
bts->cs_lqual_ranges[2].high,
|
||||
bts->cs_lqual_ranges[3].low,
|
||||
VTY_NEWLINE);
|
||||
|
||||
if (bts->force_llc_lifetime == 0xffff)
|
||||
vty_out(vty, " queue lifetime infinite%s", VTY_NEWLINE);
|
||||
else if (bts->force_llc_lifetime)
|
||||
@@ -600,6 +615,67 @@ DEFUN(cfg_pcu_no_cs_err_limits,
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
#define CS_DOWNGRADE_STR "set threshold for data size based CS downgrade\n"
|
||||
DEFUN(cfg_pcu_cs_downgrade_thrsh,
|
||||
cfg_pcu_cs_downgrade_thrsh_cmd,
|
||||
"cs downgrade-threshold <1-10000>",
|
||||
CS_STR CS_DOWNGRADE_STR "downgrade if less octets left\n")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->cs_downgrade_threshold = atoi(argv[0]);
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
DEFUN(cfg_pcu_no_cs_downgrade_thrsh,
|
||||
cfg_pcu_no_cs_downgrade_thrsh_cmd,
|
||||
"no cs downgrade-threshold",
|
||||
CS_STR CS_DOWNGRADE_STR)
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
bts->cs_downgrade_threshold = 0;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
DEFUN(cfg_pcu_cs_lqual_ranges,
|
||||
cfg_pcu_cs_lqual_ranges_cmd,
|
||||
"cs link-quality-ranges cs1 <0-35> cs2 <0-35> <0-35> cs3 <0-35> <0-35> cs4 <0-35>",
|
||||
CS_STR "Set link quality ranges\n"
|
||||
"Set quality range for CS-1 (high value only)\n"
|
||||
"CS-1 high (dB)\n"
|
||||
"Set quality range for CS-2\n"
|
||||
"CS-2 low (dB)\n"
|
||||
"CS-2 high (dB)\n"
|
||||
"Set quality range for CS-3\n"
|
||||
"CS-3 low (dB)\n"
|
||||
"CS-3 high (dB)\n"
|
||||
"Set quality range for CS-4 (low value only)\n"
|
||||
"CS-4 low (dB)\n")
|
||||
{
|
||||
struct gprs_rlcmac_bts *bts = bts_main_data();
|
||||
|
||||
uint8_t cs1_high = atoi(argv[0]);
|
||||
uint8_t cs2_low = atoi(argv[1]);
|
||||
uint8_t cs2_high = atoi(argv[2]);
|
||||
uint8_t cs3_low = atoi(argv[3]);
|
||||
uint8_t cs3_high = atoi(argv[4]);
|
||||
uint8_t cs4_low = atoi(argv[5]);
|
||||
|
||||
bts->cs_lqual_ranges[0].high = cs1_high;
|
||||
bts->cs_lqual_ranges[1].low = cs2_low;
|
||||
bts->cs_lqual_ranges[1].high = cs2_high;
|
||||
bts->cs_lqual_ranges[2].low = cs3_low;
|
||||
bts->cs_lqual_ranges[2].high = cs3_high;
|
||||
bts->cs_lqual_ranges[3].low = cs4_low;
|
||||
|
||||
return CMD_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
DEFUN(show_tbf,
|
||||
show_tbf_cmd,
|
||||
"show tbf all",
|
||||
@@ -685,6 +761,9 @@ int pcu_vty_init(const struct log_info *cat)
|
||||
install_element(PCU_NODE, &cfg_pcu_no_cs_max_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_cs_err_limits_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_no_cs_err_limits_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_cs_downgrade_thrsh_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_no_cs_downgrade_thrsh_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_cs_lqual_ranges_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_queue_lifetime_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_queue_lifetime_inf_cmd);
|
||||
install_element(PCU_NODE, &cfg_pcu_no_queue_lifetime_cmd);
|
||||
|
||||
@@ -58,6 +58,8 @@ int pcu_vty_show_ms_all(struct vty *vty, struct gprs_rlcmac_bts *bts_data)
|
||||
|
||||
static int show_ms(struct vty *vty, GprsMs *ms)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
vty_out(vty, "MS TLLI=%08x, IMSI=%s%s", ms->tlli(), ms->imsi(), VTY_NEWLINE);
|
||||
vty_out(vty, " Timing advance (TA): %d%s", ms->ta(), VTY_NEWLINE);
|
||||
vty_out(vty, " Coding scheme uplink: CS-%d%s", ms->current_cs_ul(),
|
||||
@@ -67,6 +69,35 @@ static int show_ms(struct vty *vty, GprsMs *ms)
|
||||
vty_out(vty, " MS class: %d%s", ms->ms_class(), VTY_NEWLINE);
|
||||
vty_out(vty, " LLC queue length: %d%s", ms->llc_queue()->size(),
|
||||
VTY_NEWLINE);
|
||||
if (ms->l1_meas()->have_rssi)
|
||||
vty_out(vty, " RSSI: %d dBm%s",
|
||||
ms->l1_meas()->rssi, VTY_NEWLINE);
|
||||
if (ms->l1_meas()->have_ber)
|
||||
vty_out(vty, " Bit error rate: %d %%%s",
|
||||
ms->l1_meas()->ber, VTY_NEWLINE);
|
||||
if (ms->l1_meas()->have_link_qual)
|
||||
vty_out(vty, " Link quality: %d dB%s",
|
||||
ms->l1_meas()->link_qual, VTY_NEWLINE);
|
||||
if (ms->l1_meas()->have_bto)
|
||||
vty_out(vty, " Burst timing offset: %d/4 bit%s",
|
||||
ms->l1_meas()->bto, VTY_NEWLINE);
|
||||
if (ms->l1_meas()->have_ms_rx_qual)
|
||||
vty_out(vty, " Downlink NACK rate: %d %%%s",
|
||||
ms->nack_rate_dl(), VTY_NEWLINE);
|
||||
if (ms->l1_meas()->have_ms_rx_qual)
|
||||
vty_out(vty, " MS RX quality: %d %%%s",
|
||||
ms->l1_meas()->ms_rx_qual, VTY_NEWLINE);
|
||||
if (ms->l1_meas()->have_ms_c_value)
|
||||
vty_out(vty, " MS C value: %d dB%s",
|
||||
ms->l1_meas()->ms_c_value, VTY_NEWLINE);
|
||||
if (ms->l1_meas()->have_ms_sign_var)
|
||||
vty_out(vty, " MS SIGN variance: %d dB%s",
|
||||
ms->l1_meas()->ms_sign_var, VTY_NEWLINE);
|
||||
for (i = 0; i < ARRAY_SIZE(ms->l1_meas()->ts); ++i) {
|
||||
if (ms->l1_meas()->ts[i].have_ms_i_level)
|
||||
vty_out(vty, " MS I level (slot %d): %d dB%s",
|
||||
i, ms->l1_meas()->ts[i].ms_i_level, VTY_NEWLINE);
|
||||
}
|
||||
if (ms->ul_tbf())
|
||||
vty_out(vty, " Uplink TBF: TFI=%d, state=%s%s",
|
||||
ms->ul_tbf()->tfi(),
|
||||
|
||||
@@ -166,10 +166,23 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1h,
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void get_meas(struct pcu_l1_meas *meas, const GsmL1_MeasParam_t *l1_meas)
|
||||
{
|
||||
meas->rssi = (int8_t) (l1_meas->fRssi);
|
||||
meas->have_rssi = 1;
|
||||
meas->ber = (uint8_t) (l1_meas->fBer * 100);
|
||||
meas->have_ber = 1;
|
||||
meas->bto = (int16_t) (l1_meas->i16BurstTiming);
|
||||
meas->have_bto = 1;
|
||||
meas->link_qual = (int16_t) (l1_meas->fLinkQuality);
|
||||
meas->have_link_qual = 1;
|
||||
}
|
||||
|
||||
static int handle_ph_data_ind(struct femtol1_hdl *fl1h,
|
||||
GsmL1_PhDataInd_t *data_ind, struct msgb *l1p_msg)
|
||||
{
|
||||
int rc = 0;
|
||||
struct pcu_l1_meas meas = {0};
|
||||
|
||||
DEBUGP(DL1IF, "Rx PH-DATA.ind %s (hL2 %08x): %s\n",
|
||||
get_value_string(femtobts_l1sapi_names, data_ind->sapi),
|
||||
@@ -190,8 +203,7 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1h,
|
||||
data_ind->u32Fn, 0, 0, data_ind->msgUnitParam.u8Buffer+1,
|
||||
data_ind->msgUnitParam.u8Size-1);
|
||||
|
||||
|
||||
|
||||
get_meas(&meas, &data_ind->measParam);
|
||||
|
||||
switch (data_ind->sapi) {
|
||||
case GsmL1_Sapi_Pdtch:
|
||||
@@ -205,7 +217,7 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1h,
|
||||
data_ind->msgUnitParam.u8Buffer + 1,
|
||||
data_ind->msgUnitParam.u8Size - 1,
|
||||
data_ind->u32Fn,
|
||||
(int8_t) (data_ind->measParam.fRssi));
|
||||
&meas);
|
||||
break;
|
||||
case GsmL1_Sapi_Ptcch:
|
||||
// FIXME
|
||||
|
||||
83
src/tbf.cpp
83
src/tbf.cpp
@@ -158,23 +158,11 @@ void gprs_rlcmac_tbf::set_ms(GprsMs *ms)
|
||||
|
||||
void gprs_rlcmac_tbf::update_ms(uint32_t tlli, enum gprs_rlcmac_tbf_direction dir)
|
||||
{
|
||||
if (!ms()) {
|
||||
GprsMs *new_ms = bts->ms_store().get_ms(tlli);
|
||||
if (!new_ms) {
|
||||
new_ms = bts->ms_store().create_ms(tlli, dir);
|
||||
new_ms->set_timeout(bts->bts_data()->ms_idle_sec);
|
||||
}
|
||||
|
||||
if (dir == GPRS_RLCMAC_UL_TBF) {
|
||||
new_ms->set_ta(m_ta);
|
||||
}
|
||||
|
||||
if (m_ms_class)
|
||||
new_ms->set_ms_class(m_ms_class);
|
||||
|
||||
set_ms(new_ms);
|
||||
if (!ms())
|
||||
return;
|
||||
|
||||
if (!tlli)
|
||||
return;
|
||||
}
|
||||
|
||||
if (dir == GPRS_RLCMAC_UL_TBF)
|
||||
ms()->set_tlli(tlli);
|
||||
@@ -184,7 +172,7 @@ void gprs_rlcmac_tbf::update_ms(uint32_t tlli, enum gprs_rlcmac_tbf_direction di
|
||||
|
||||
gprs_rlcmac_ul_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)
|
||||
uint32_t tlli, uint8_t ta, GprsMs *ms)
|
||||
{
|
||||
uint8_t trx;
|
||||
struct gprs_rlcmac_ul_tbf *tbf;
|
||||
@@ -199,7 +187,7 @@ gprs_rlcmac_ul_tbf *tbf_alloc_ul(struct gprs_rlcmac_bts *bts,
|
||||
return NULL;
|
||||
}
|
||||
/* use multislot class of downlink TBF */
|
||||
tbf = tbf_alloc_ul_tbf(bts, dl_tbf, tfi, trx, ms_class, 0);
|
||||
tbf = tbf_alloc_ul_tbf(bts, ms, tfi, trx, ms_class, 0);
|
||||
if (!tbf) {
|
||||
LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource\n");
|
||||
/* FIXME: send reject */
|
||||
@@ -274,7 +262,6 @@ void tbf_free(struct gprs_rlcmac_tbf *tbf)
|
||||
|
||||
int gprs_rlcmac_tbf::update()
|
||||
{
|
||||
struct gprs_rlcmac_tbf *ul_tbf = NULL;
|
||||
struct gprs_rlcmac_bts *bts_data = bts->bts_data();
|
||||
int rc;
|
||||
|
||||
@@ -283,11 +270,8 @@ int gprs_rlcmac_tbf::update()
|
||||
if (direction != GPRS_RLCMAC_DL_TBF)
|
||||
return -EINVAL;
|
||||
|
||||
if (ms())
|
||||
ul_tbf = ms()->ul_tbf();
|
||||
|
||||
tbf_unlink_pdch(this);
|
||||
rc = bts_data->alloc_algorithm(bts_data, ul_tbf, this, bts_data->alloc_algorithm_curst, 0);
|
||||
rc = bts_data->alloc_algorithm(bts_data, ms(), this, bts_data->alloc_algorithm_curst, 0);
|
||||
/* if no resource */
|
||||
if (rc < 0) {
|
||||
LOGP(DRLCMAC, LOGL_ERROR, "No resource after update???\n");
|
||||
@@ -450,7 +434,7 @@ void gprs_rlcmac_tbf::poll_timeout()
|
||||
}
|
||||
|
||||
static int setup_tbf(struct gprs_rlcmac_tbf *tbf, struct gprs_rlcmac_bts *bts,
|
||||
struct gprs_rlcmac_tbf *old_tbf, uint8_t tfi, uint8_t trx,
|
||||
GprsMs *ms, uint8_t tfi, uint8_t trx,
|
||||
uint8_t ms_class, uint8_t single_slot)
|
||||
{
|
||||
int rc;
|
||||
@@ -470,7 +454,7 @@ static int setup_tbf(struct gprs_rlcmac_tbf *tbf, struct gprs_rlcmac_bts *bts,
|
||||
tbf->trx = &bts->trx[trx];
|
||||
tbf->set_ms_class(ms_class);
|
||||
/* select algorithm */
|
||||
rc = bts->alloc_algorithm(bts, old_tbf, tbf, bts->alloc_algorithm_curst,
|
||||
rc = bts->alloc_algorithm(bts, ms, tbf, bts->alloc_algorithm_curst,
|
||||
single_slot);
|
||||
/* if no resource */
|
||||
if (rc < 0) {
|
||||
@@ -488,12 +472,14 @@ static int setup_tbf(struct gprs_rlcmac_tbf *tbf, struct gprs_rlcmac_bts *bts,
|
||||
gettimeofday(&tbf->meas.rssi_tv, NULL);
|
||||
|
||||
tbf->m_llc.init();
|
||||
tbf->set_ms(ms);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
struct gprs_rlcmac_ul_tbf *tbf_alloc_ul_tbf(struct gprs_rlcmac_bts *bts,
|
||||
struct gprs_rlcmac_tbf *old_tbf, uint8_t tfi, uint8_t trx,
|
||||
GprsMs *ms, uint8_t tfi, uint8_t trx,
|
||||
uint8_t ms_class, uint8_t single_slot)
|
||||
{
|
||||
struct gprs_rlcmac_ul_tbf *tbf;
|
||||
@@ -512,7 +498,11 @@ struct gprs_rlcmac_ul_tbf *tbf_alloc_ul_tbf(struct gprs_rlcmac_bts *bts,
|
||||
return NULL;
|
||||
|
||||
tbf->direction = GPRS_RLCMAC_UL_TBF;
|
||||
rc = setup_tbf(tbf, bts, old_tbf, tfi, trx, ms_class, single_slot);
|
||||
|
||||
if (!ms)
|
||||
ms = bts->bts->ms_alloc(ms_class);
|
||||
|
||||
rc = setup_tbf(tbf, bts, ms, tfi, trx, ms_class, single_slot);
|
||||
/* if no resource */
|
||||
if (rc < 0) {
|
||||
talloc_free(tbf);
|
||||
@@ -522,17 +512,11 @@ struct gprs_rlcmac_ul_tbf *tbf_alloc_ul_tbf(struct gprs_rlcmac_bts *bts,
|
||||
llist_add(&tbf->list.list, &bts->ul_tbfs);
|
||||
tbf->bts->tbf_ul_created();
|
||||
|
||||
if (old_tbf && old_tbf->ms())
|
||||
tbf->set_ms(old_tbf->ms());
|
||||
|
||||
if (tbf->ms())
|
||||
tbf->ms()->attach_ul_tbf(tbf);
|
||||
|
||||
return tbf;
|
||||
}
|
||||
|
||||
struct gprs_rlcmac_dl_tbf *tbf_alloc_dl_tbf(struct gprs_rlcmac_bts *bts,
|
||||
struct gprs_rlcmac_tbf *old_tbf, uint8_t tfi, uint8_t trx,
|
||||
GprsMs *ms, uint8_t tfi, uint8_t trx,
|
||||
uint8_t ms_class, uint8_t single_slot)
|
||||
{
|
||||
struct gprs_rlcmac_dl_tbf *tbf;
|
||||
@@ -551,7 +535,11 @@ struct gprs_rlcmac_dl_tbf *tbf_alloc_dl_tbf(struct gprs_rlcmac_bts *bts,
|
||||
return NULL;
|
||||
|
||||
tbf->direction = GPRS_RLCMAC_DL_TBF;
|
||||
rc = setup_tbf(tbf, bts, old_tbf, tfi, trx, ms_class, single_slot);
|
||||
|
||||
if (!ms)
|
||||
ms = bts->bts->ms_alloc(ms_class);
|
||||
|
||||
rc = setup_tbf(tbf, bts, ms, tfi, trx, ms_class, single_slot);
|
||||
/* if no resource */
|
||||
if (rc < 0) {
|
||||
talloc_free(tbf);
|
||||
@@ -567,12 +555,6 @@ struct gprs_rlcmac_dl_tbf *tbf_alloc_dl_tbf(struct gprs_rlcmac_bts *bts,
|
||||
gettimeofday(&tbf->m_bw.dl_bw_tv, NULL);
|
||||
gettimeofday(&tbf->m_bw.dl_loss_tv, NULL);
|
||||
|
||||
if (old_tbf && old_tbf->ms())
|
||||
tbf->set_ms(old_tbf->ms());
|
||||
|
||||
if (tbf->ms())
|
||||
tbf->ms()->attach_dl_tbf(tbf);
|
||||
|
||||
return tbf;
|
||||
}
|
||||
|
||||
@@ -847,10 +829,6 @@ void gprs_rlcmac_tbf::free_all(struct gprs_rlcmac_pdch *pdch)
|
||||
}
|
||||
}
|
||||
|
||||
void gprs_rlcmac_tbf::update_tlli(uint32_t tlli)
|
||||
{
|
||||
}
|
||||
|
||||
int gprs_rlcmac_tbf::extract_tlli(const uint8_t *data, const size_t len)
|
||||
{
|
||||
struct gprs_rlcmac_tbf *dl_tbf = NULL;
|
||||
@@ -860,6 +838,8 @@ int gprs_rlcmac_tbf::extract_tlli(const uint8_t *data, const size_t len)
|
||||
int rc;
|
||||
GprsMs *old_ms;
|
||||
|
||||
OSMO_ASSERT(direction == GPRS_RLCMAC_UL_TBF);
|
||||
|
||||
/* no TLLI yet */
|
||||
if (!rh->ti) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA TFI=%d without "
|
||||
@@ -880,15 +860,20 @@ int gprs_rlcmac_tbf::extract_tlli(const uint8_t *data, const size_t len)
|
||||
dl_tbf = old_ms->dl_tbf();
|
||||
ul_tbf = old_ms->ul_tbf();
|
||||
|
||||
set_ms(old_ms);
|
||||
if (!ms())
|
||||
set_ms(old_ms);
|
||||
|
||||
/* there might be an active and valid downlink TBF */
|
||||
if (!ms()->dl_tbf() && dl_tbf)
|
||||
/* Move it to the current MS */
|
||||
dl_tbf->set_ms(ms());
|
||||
}
|
||||
|
||||
update_tlli(new_tlli);
|
||||
/* The TLLI has been taken from an UL message */
|
||||
update_ms(new_tlli, GPRS_RLCMAC_UL_TBF);
|
||||
LOGP(DRLCMACUL, LOGL_INFO, "Decoded premier TLLI=0x%08x of "
|
||||
"UL DATA TFI=%d.\n", tlli(), rh->tfi);
|
||||
if (dl_tbf) {
|
||||
if (dl_tbf && dl_tbf->ms() != ms()) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE, "Got RACH from "
|
||||
"TLLI=0x%08x while %s still exists. "
|
||||
"Killing pending DL TBF\n", tlli(),
|
||||
@@ -896,7 +881,7 @@ int gprs_rlcmac_tbf::extract_tlli(const uint8_t *data, const size_t len)
|
||||
tbf_free(dl_tbf);
|
||||
dl_tbf = NULL;
|
||||
}
|
||||
if (ul_tbf) {
|
||||
if (ul_tbf && ul_tbf->ms() != ms()) {
|
||||
LOGP(DRLCMACUL, LOGL_NOTICE, "Got RACH from "
|
||||
"TLLI=0x%08x while %s still exists. "
|
||||
"Killing pending UL TBF\n", tlli(),
|
||||
|
||||
15
src/tbf.h
15
src/tbf.h
@@ -30,6 +30,7 @@
|
||||
struct bssgp_bvc_ctx;
|
||||
struct rlc_ul_header;
|
||||
struct msgb;
|
||||
struct pcu_l1_meas;
|
||||
class GprsMs;
|
||||
|
||||
/*
|
||||
@@ -138,7 +139,6 @@ struct gprs_rlcmac_tbf {
|
||||
void poll_timeout();
|
||||
|
||||
/** tlli handling */
|
||||
void update_tlli(uint32_t tlli);
|
||||
uint32_t tlli() const;
|
||||
bool is_tlli_valid() const;
|
||||
|
||||
@@ -240,16 +240,15 @@ private:
|
||||
|
||||
struct gprs_rlcmac_ul_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);
|
||||
uint32_t tlli, uint8_t ta, GprsMs *ms);
|
||||
|
||||
struct gprs_rlcmac_ul_tbf *tbf_alloc_ul_tbf(struct gprs_rlcmac_bts *bts,
|
||||
struct gprs_rlcmac_tbf *old_tbf,
|
||||
GprsMs *ms,
|
||||
uint8_t tfi, uint8_t trx,
|
||||
uint8_t ms_class, uint8_t single_slot);
|
||||
|
||||
struct gprs_rlcmac_dl_tbf *tbf_alloc_dl_tbf(struct gprs_rlcmac_bts *bts,
|
||||
struct gprs_rlcmac_tbf *old_tbf,
|
||||
uint8_t tfi, uint8_t trx,
|
||||
GprsMs *ms, uint8_t tfi, uint8_t trx,
|
||||
uint8_t ms_class, uint8_t single_slot);
|
||||
|
||||
void tbf_free(struct gprs_rlcmac_tbf *tbf);
|
||||
@@ -361,9 +360,10 @@ protected:
|
||||
int update_window(const uint8_t ssn, const uint8_t *rbb);
|
||||
int maybe_start_new_window();
|
||||
bool dl_window_stalled() const;
|
||||
void reuse_tbf(const uint8_t *data, const uint16_t len);
|
||||
void reuse_tbf();
|
||||
void start_llc_timer();
|
||||
int analyse_errors(char *show_rbb, uint8_t ssn);
|
||||
void schedule_next_frame();
|
||||
|
||||
struct osmo_timer_list m_llc_timer;
|
||||
};
|
||||
@@ -372,7 +372,8 @@ struct gprs_rlcmac_ul_tbf : public gprs_rlcmac_tbf {
|
||||
struct msgb *create_ul_ack(uint32_t fn);
|
||||
|
||||
/* blocks were acked */
|
||||
int rcv_data_block_acknowledged(const uint8_t *data, size_t len, int8_t rssi);
|
||||
int rcv_data_block_acknowledged(const uint8_t *data, size_t len,
|
||||
struct pcu_l1_meas *meas);
|
||||
|
||||
/* TODO: extract LLC class? */
|
||||
int assemble_forward_llc(const gprs_rlc_data *data);
|
||||
|
||||
145
src/tbf_dl.cpp
145
src/tbf_dl.cpp
@@ -99,36 +99,24 @@ int gprs_rlcmac_dl_tbf::append_data(const uint8_t ms_class,
|
||||
const uint8_t *data, const uint16_t len)
|
||||
{
|
||||
LOGP(DRLCMAC, LOGL_INFO, "%s append\n", tbf_name(this));
|
||||
gprs_llc_queue::MetaInfo info;
|
||||
struct msgb *llc_msg = msgb_alloc(len, "llc_pdu_queue");
|
||||
if (!llc_msg)
|
||||
return -ENOMEM;
|
||||
|
||||
gprs_llc_queue::calc_pdu_lifetime(bts, pdu_delay_csec, &info.expire_time);
|
||||
gettimeofday(&info.recv_time, NULL);
|
||||
memcpy(msgb_put(llc_msg, len), data, len);
|
||||
llc_queue()->enqueue(llc_msg, &info);
|
||||
tbf_update_ms_class(this, ms_class);
|
||||
start_llc_timer();
|
||||
|
||||
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 if (!have_data()) {
|
||||
m_llc.put_frame(data, len);
|
||||
bts->llc_frame_sched();
|
||||
/* it is no longer drained */
|
||||
m_last_dl_drained_fn = -1;
|
||||
tbf_update_ms_class(this, ms_class);
|
||||
start_llc_timer();
|
||||
} else {
|
||||
/* TODO: put this path into an llc_enqueue method */
|
||||
/* 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_queue::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);
|
||||
llc_queue()->enqueue(llc_msg);
|
||||
tbf_update_ms_class(this, ms_class);
|
||||
start_llc_timer();
|
||||
reuse_tbf();
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -149,7 +137,7 @@ static int tbf_new_dl_assignment(struct gprs_rlcmac_bts *bts,
|
||||
const char *imsi,
|
||||
const uint32_t tlli, const uint32_t tlli_old,
|
||||
const uint8_t ms_class,
|
||||
const uint8_t *data, const uint16_t len)
|
||||
struct gprs_rlcmac_dl_tbf **tbf)
|
||||
{
|
||||
uint8_t trx, ss;
|
||||
int8_t use_trx;
|
||||
@@ -167,6 +155,7 @@ static int tbf_new_dl_assignment(struct gprs_rlcmac_bts *bts,
|
||||
ul_tbf = ms->ul_tbf();
|
||||
ta = ms->ta();
|
||||
}
|
||||
/* TODO: if (!ms) create MS before tbf_alloc is called? */
|
||||
|
||||
if (ul_tbf && ul_tbf->m_contention_resolution_done
|
||||
&& !ul_tbf->m_final_ack_sent) {
|
||||
@@ -184,13 +173,10 @@ static int tbf_new_dl_assignment(struct gprs_rlcmac_bts *bts,
|
||||
tfi = bts->bts->tfi_find_free(GPRS_RLCMAC_DL_TBF, &trx, use_trx);
|
||||
if (tfi >= 0)
|
||||
/* set number of downlink slots according to multislot class */
|
||||
dl_tbf = tbf_alloc_dl_tbf(bts, ul_tbf, tfi, trx, ms_class, ss);
|
||||
dl_tbf = tbf_alloc_dl_tbf(bts, ms, tfi, trx, ms_class, ss);
|
||||
|
||||
if (!dl_tbf) {
|
||||
LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource\n");
|
||||
bssgp_tx_llc_discarded(gprs_bssgp_pcu_current_bctx(), tlli,
|
||||
1, len);
|
||||
bts->bts->llc_dropped_frame();
|
||||
return -EBUSY;
|
||||
}
|
||||
dl_tbf->update_ms(tlli, GPRS_RLCMAC_DL_TBF);
|
||||
@@ -198,10 +184,6 @@ static int tbf_new_dl_assignment(struct gprs_rlcmac_bts *bts,
|
||||
|
||||
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);
|
||||
|
||||
@@ -210,6 +192,7 @@ static int tbf_new_dl_assignment(struct gprs_rlcmac_bts *bts,
|
||||
* to trigger downlink assignment. if there is no uplink,
|
||||
* AGCH is used. */
|
||||
dl_tbf->bts->trigger_dl_ass(dl_tbf, old_ul_tbf);
|
||||
*tbf = dl_tbf;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -222,30 +205,40 @@ int gprs_rlcmac_dl_tbf::handle(struct gprs_rlcmac_bts *bts,
|
||||
const uint8_t *data, const uint16_t len)
|
||||
{
|
||||
struct gprs_rlcmac_dl_tbf *dl_tbf;
|
||||
int rc;
|
||||
GprsMs *ms;
|
||||
|
||||
/* check for existing TBF */
|
||||
dl_tbf = tbf_lookup_dl(bts->bts, tlli, tlli_old, imsi);
|
||||
if (dl_tbf) {
|
||||
int rc = dl_tbf->append_data(ms_class, delay_csec, data, len);
|
||||
if (rc >= 0)
|
||||
dl_tbf->assign_imsi(imsi);
|
||||
if (!dl_tbf) {
|
||||
rc = tbf_new_dl_assignment(bts, imsi, tlli, tlli_old, ms_class,
|
||||
&dl_tbf);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (dl_tbf->ms())
|
||||
dl_tbf->ms()->confirm_tlli(tlli);
|
||||
return rc;
|
||||
}
|
||||
OSMO_ASSERT(dl_tbf->ms() != NULL);
|
||||
ms = dl_tbf->ms();
|
||||
GprsMs::Guard guard(ms);
|
||||
|
||||
return tbf_new_dl_assignment(bts, imsi, tlli, tlli_old, ms_class, data, len);
|
||||
rc = dl_tbf->append_data(ms_class, delay_csec, data, len);
|
||||
|
||||
dl_tbf = ms->dl_tbf();
|
||||
|
||||
dl_tbf->assign_imsi(imsi);
|
||||
ms->confirm_tlli(tlli);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct msgb *gprs_rlcmac_dl_tbf::llc_dequeue(bssgp_bvc_ctx *bctx)
|
||||
{
|
||||
struct msgb *msg;
|
||||
struct timeval *tv_recv, *tv_disc;
|
||||
struct timeval tv_now, tv_now2;
|
||||
uint32_t octets = 0, frames = 0;
|
||||
struct timeval hyst_delta = {0, 0};
|
||||
const unsigned keep_small_thresh = 60;
|
||||
const gprs_llc_queue::MetaInfo *info;
|
||||
|
||||
if (bts_data()->llc_discard_csec)
|
||||
csecs_to_timeval(bts_data()->llc_discard_csec, &hyst_delta);
|
||||
@@ -253,11 +246,9 @@ struct msgb *gprs_rlcmac_dl_tbf::llc_dequeue(bssgp_bvc_ctx *bctx)
|
||||
gettimeofday(&tv_now, NULL);
|
||||
timeradd(&tv_now, &hyst_delta, &tv_now2);
|
||||
|
||||
while ((msg = llc_queue()->dequeue())) {
|
||||
tv_disc = (struct timeval *)msg->data;
|
||||
msgb_pull(msg, sizeof(*tv_disc));
|
||||
tv_recv = (struct timeval *)msg->data;
|
||||
msgb_pull(msg, sizeof(*tv_recv));
|
||||
while ((msg = llc_queue()->dequeue(&info))) {
|
||||
const struct timeval *tv_disc = &info->expire_time;
|
||||
const struct timeval *tv_recv = &info->recv_time;
|
||||
|
||||
gprs_bssgp_update_queue_delay(tv_recv, &tv_now);
|
||||
|
||||
@@ -385,22 +376,46 @@ do_resend:
|
||||
goto do_resend;
|
||||
}
|
||||
|
||||
void gprs_rlcmac_dl_tbf::schedule_next_frame()
|
||||
{
|
||||
struct msgb *msg;
|
||||
|
||||
if (m_llc.frame_length() != 0)
|
||||
return;
|
||||
|
||||
/* dequeue next LLC frame, if any */
|
||||
msg = llc_dequeue(gprs_bssgp_pcu_current_bctx());
|
||||
if (!msg)
|
||||
return;
|
||||
|
||||
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);
|
||||
m_last_dl_drained_fn = -1;
|
||||
}
|
||||
|
||||
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;
|
||||
const uint16_t bsn = m_window.v_s();
|
||||
uint8_t cs = 1;
|
||||
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "- Sending new block at BSN %d\n",
|
||||
m_window.v_s());
|
||||
if (m_llc.frame_length() == 0)
|
||||
schedule_next_frame();
|
||||
|
||||
cs = current_cs();
|
||||
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "- Sending new block at BSN %d, CS=%d\n",
|
||||
m_window.v_s(), cs);
|
||||
|
||||
OSMO_ASSERT(cs >= 1);
|
||||
OSMO_ASSERT(cs <= 4);
|
||||
|
||||
@@ -535,15 +550,7 @@ struct msgb *gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, const uint8_t
|
||||
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);
|
||||
m_last_dl_drained_fn = -1;
|
||||
}
|
||||
schedule_next_frame();
|
||||
/* if we have more data and we have space left */
|
||||
if (space > 0 && (m_llc.frame_length() || keep_open(fn))) {
|
||||
li->m = 1; /* we indicate more frames to follow */
|
||||
@@ -796,7 +803,6 @@ int gprs_rlcmac_dl_tbf::update_window(const uint8_t ssn, const uint8_t *rbb)
|
||||
|
||||
int gprs_rlcmac_dl_tbf::maybe_start_new_window()
|
||||
{
|
||||
struct msgb *msg;
|
||||
uint16_t received;
|
||||
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "- Final ACK received.\n");
|
||||
@@ -809,8 +815,7 @@ int gprs_rlcmac_dl_tbf::maybe_start_new_window()
|
||||
set_state(GPRS_RLCMAC_WAIT_RELEASE);
|
||||
|
||||
/* check for LLC PDU in the LLC Queue */
|
||||
msg = llc_dequeue(gprs_bssgp_pcu_current_bctx());
|
||||
if (!msg) {
|
||||
if (!have_data()) {
|
||||
/* no message, start T3193, change state to RELEASE */
|
||||
LOGP(DRLCMACDL, LOGL_DEBUG, "- No new message, so we release.\n");
|
||||
/* start T3193 */
|
||||
@@ -822,8 +827,7 @@ int gprs_rlcmac_dl_tbf::maybe_start_new_window()
|
||||
}
|
||||
|
||||
/* we have more data so we will re-use this tbf */
|
||||
reuse_tbf(msg->data, msg->len);
|
||||
msgb_free(msg);
|
||||
reuse_tbf();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -836,7 +840,7 @@ int gprs_rlcmac_dl_tbf::rcvd_dl_ack(uint8_t final_ack, uint8_t ssn, uint8_t *rbb
|
||||
return maybe_start_new_window();
|
||||
}
|
||||
|
||||
void gprs_rlcmac_dl_tbf::reuse_tbf(const uint8_t *data, const uint16_t len)
|
||||
void gprs_rlcmac_dl_tbf::reuse_tbf()
|
||||
{
|
||||
uint8_t trx;
|
||||
struct gprs_rlcmac_dl_tbf *new_tbf = NULL;
|
||||
@@ -851,18 +855,11 @@ void gprs_rlcmac_dl_tbf::reuse_tbf(const uint8_t *data, const uint16_t len)
|
||||
|
||||
if (!new_tbf) {
|
||||
LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource\n");
|
||||
bssgp_tx_llc_discarded(gprs_bssgp_pcu_current_bctx(), tlli(),
|
||||
1, len);
|
||||
bts->llc_dropped_frame();
|
||||
return;
|
||||
}
|
||||
|
||||
new_tbf->set_ms(ms());
|
||||
|
||||
/* Start with the passed frame */
|
||||
new_tbf->m_llc.put_frame(data, len);
|
||||
bts->llc_frame_sched();
|
||||
|
||||
/* reset rlc states */
|
||||
m_tx_counter = 0;
|
||||
m_wait_confirm = 0;
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <gprs_debug.h>
|
||||
#include <gprs_bssgp_pcu.h>
|
||||
#include <decoding.h>
|
||||
#include <pcu_l1_if.h>
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/msgb.h>
|
||||
@@ -261,10 +262,12 @@ struct msgb *gprs_rlcmac_ul_tbf::create_ul_ack(uint32_t fn)
|
||||
return msg;
|
||||
}
|
||||
|
||||
int gprs_rlcmac_ul_tbf::rcv_data_block_acknowledged(const uint8_t *data, size_t len, int8_t rssi)
|
||||
int gprs_rlcmac_ul_tbf::rcv_data_block_acknowledged(const uint8_t *data,
|
||||
size_t len, struct pcu_l1_meas *meas)
|
||||
{
|
||||
struct rlc_ul_header *rh = (struct rlc_ul_header *)data;
|
||||
int rc;
|
||||
int8_t rssi = meas->have_rssi ? meas->rssi : 0;
|
||||
|
||||
const uint16_t mod_sns = m_window.mod_sns();
|
||||
const uint16_t ws = m_window.ws();
|
||||
@@ -278,6 +281,10 @@ int gprs_rlcmac_ul_tbf::rcv_data_block_acknowledged(const uint8_t *data, size_t
|
||||
/* process RSSI */
|
||||
gprs_rlcmac_rssi(this, rssi);
|
||||
|
||||
/* store measurement values */
|
||||
if (ms())
|
||||
ms()->update_l1_meas(meas);
|
||||
|
||||
/* get TLLI */
|
||||
if (!this->is_tlli_valid()) {
|
||||
if (!extract_tlli(data, len))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOGB_CFLAGS) $(LIBOSMOGSM_CFLAGS) -I$(top_srcdir)/src/
|
||||
|
||||
check_PROGRAMS = rlcmac/RLCMACTest alloc/AllocTest tbf/TbfTest types/TypesTest ms/MsTest llist/LListTest
|
||||
check_PROGRAMS = rlcmac/RLCMACTest alloc/AllocTest tbf/TbfTest types/TypesTest ms/MsTest llist/LListTest llc/LlcTest
|
||||
noinst_PROGRAMS = emu/pcu_emu
|
||||
|
||||
rlcmac_RLCMACTest_SOURCES = rlcmac/RLCMACTest.cpp
|
||||
@@ -54,6 +54,17 @@ ms_MsTest_LDADD = \
|
||||
ms_MsTest_LDFLAGS = \
|
||||
-Wl,-u,bssgp_prim_cb
|
||||
|
||||
llc_LlcTest_SOURCES = llc/LlcTest.cpp
|
||||
llc_LlcTest_LDADD = \
|
||||
$(top_builddir)/src/libgprs.la \
|
||||
$(LIBOSMOGB_LIBS) \
|
||||
$(LIBOSMOGSM_LIBS) \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(COMMON_LA)
|
||||
|
||||
llc_LlcTest_LDFLAGS = \
|
||||
-Wl,-u,bssgp_prim_cb
|
||||
|
||||
llist_LListTest_SOURCES = llist/LListTest.cpp
|
||||
llist_LListTest_LDADD = \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
@@ -84,6 +95,7 @@ EXTRA_DIST = \
|
||||
tbf/TbfTest.ok tbf/TbfTest.err \
|
||||
types/TypesTest.ok types/TypesTest.err \
|
||||
ms/MsTest.ok ms/MsTest.err \
|
||||
llc/LlcTest.ok llc/LlcTest.err \
|
||||
llist/LListTest.ok llist/LListTest.err
|
||||
|
||||
DISTCLEANFILES = atconfig
|
||||
|
||||
@@ -37,14 +37,14 @@ void *tall_pcu_ctx;
|
||||
int16_t spoof_mnc = 0, spoof_mcc = 0;
|
||||
|
||||
static gprs_rlcmac_tbf *tbf_alloc(struct gprs_rlcmac_bts *bts,
|
||||
struct gprs_rlcmac_tbf *old_tbf, gprs_rlcmac_tbf_direction dir,
|
||||
GprsMs *ms, gprs_rlcmac_tbf_direction dir,
|
||||
uint8_t tfi, uint8_t trx,
|
||||
uint8_t ms_class, uint8_t single_slot)
|
||||
{
|
||||
if (dir == GPRS_RLCMAC_UL_TBF)
|
||||
return tbf_alloc_ul_tbf(bts, old_tbf, tfi, trx, ms_class, single_slot);
|
||||
return tbf_alloc_ul_tbf(bts, ms, tfi, trx, ms_class, single_slot);
|
||||
else
|
||||
return tbf_alloc_dl_tbf(bts, old_tbf, tfi, trx, ms_class, single_slot);
|
||||
return tbf_alloc_dl_tbf(bts, ms, tfi, trx, ms_class, single_slot);
|
||||
}
|
||||
|
||||
static void test_alloc_a(gprs_rlcmac_tbf_direction dir, const int count)
|
||||
@@ -152,7 +152,7 @@ static void test_alloc_b(int ms_class)
|
||||
/* 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_dl_tbf(bts, ul_tbf, tfi, trx_no, ms_class, 0);
|
||||
dl_tbf = tbf_alloc_dl_tbf(bts, ul_tbf->ms(), tfi, trx_no, ms_class, 0);
|
||||
OSMO_ASSERT(dl_tbf);
|
||||
dump_assignment(dl_tbf, "DL");
|
||||
|
||||
@@ -194,7 +194,7 @@ static void test_alloc_b(int ms_class)
|
||||
|
||||
tfi = the_bts.tfi_find_free(GPRS_RLCMAC_UL_TBF, &trx_no, -1);
|
||||
OSMO_ASSERT(tfi >= 0);
|
||||
ul_tbf = tbf_alloc_ul_tbf(bts, dl_tbf, tfi, trx_no, ms_class, 0);
|
||||
ul_tbf = tbf_alloc_ul_tbf(bts, dl_tbf->ms(), tfi, trx_no, ms_class, 0);
|
||||
ul_tbf->update_ms(0x23, GPRS_RLCMAC_UL_TBF);
|
||||
ul_tbf->m_contention_resolution_done = 1;
|
||||
OSMO_ASSERT(ul_tbf);
|
||||
@@ -241,7 +241,7 @@ static void test_alloc_b(int ms_class)
|
||||
/* 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_dl_tbf(bts, ul_tbf, tfi, trx_no, ms_class, 0);
|
||||
dl_tbf = tbf_alloc_dl_tbf(bts, ul_tbf->ms(), tfi, trx_no, ms_class, 0);
|
||||
OSMO_ASSERT(dl_tbf);
|
||||
dump_assignment(dl_tbf, "DL");
|
||||
|
||||
@@ -304,7 +304,7 @@ static void test_alloc_b(bool ts0, bool ts1, bool ts2, bool ts3, bool ts4, bool
|
||||
/* 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_dl_tbf(bts, ul_tbf, tfi, trx_no, ms_class, 0);
|
||||
dl_tbf = tbf_alloc_dl_tbf(bts, ul_tbf->ms(), tfi, trx_no, ms_class, 0);
|
||||
OSMO_ASSERT(dl_tbf);
|
||||
|
||||
/* verify that both are on the same ts */
|
||||
@@ -348,7 +348,7 @@ static void test_alloc_b(bool ts0, bool ts1, bool ts2, bool ts3, bool ts4, bool
|
||||
|
||||
tfi = the_bts.tfi_find_free(GPRS_RLCMAC_UL_TBF, &trx_no, -1);
|
||||
OSMO_ASSERT(tfi >= 0);
|
||||
ul_tbf = tbf_alloc_ul_tbf(bts, dl_tbf, tfi, trx_no, ms_class, 0);
|
||||
ul_tbf = tbf_alloc_ul_tbf(bts, dl_tbf->ms(), tfi, trx_no, ms_class, 0);
|
||||
OSMO_ASSERT(ul_tbf);
|
||||
ul_tbf->update_ms(0x23, GPRS_RLCMAC_UL_TBF);
|
||||
ul_tbf->m_contention_resolution_done = 1;
|
||||
|
||||
211
tests/llc/LlcTest.cpp
Normal file
211
tests/llc/LlcTest.cpp
Normal file
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* LlcTest.cpp
|
||||
*
|
||||
* Copyright (C) 2015 by Sysmocom s.f.m.c. GmbH
|
||||
*
|
||||
* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
#include <osmocom/core/linuxlist.h>
|
||||
}
|
||||
|
||||
#include "llc.h"
|
||||
#include "gprs_debug.h"
|
||||
|
||||
extern "C" {
|
||||
#include "pcu_vty.h"
|
||||
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/core/msgb.h>
|
||||
#include <osmocom/core/talloc.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/vty/vty.h>
|
||||
}
|
||||
|
||||
|
||||
void *tall_pcu_ctx;
|
||||
int16_t spoof_mnc = 0, spoof_mcc = 0;
|
||||
|
||||
static void enqueue_data(gprs_llc_queue *queue, const uint8_t *data, size_t len,
|
||||
gprs_llc_queue::MetaInfo *info = 0)
|
||||
{
|
||||
struct timeval *tv;
|
||||
uint8_t *msg_data;
|
||||
struct msgb *llc_msg = msgb_alloc(len + sizeof(*tv) * 2,
|
||||
"llc_pdu_queue");
|
||||
|
||||
msg_data = (uint8_t *)msgb_put(llc_msg, len);
|
||||
|
||||
memcpy(msg_data, data, len);
|
||||
|
||||
queue->enqueue(llc_msg, info);
|
||||
}
|
||||
|
||||
static void dequeue_and_check(gprs_llc_queue *queue, const uint8_t *exp_data,
|
||||
size_t len, const gprs_llc_queue::MetaInfo *exp_info = 0)
|
||||
{
|
||||
struct msgb *llc_msg;
|
||||
uint8_t *msg_data;
|
||||
const gprs_llc_queue::MetaInfo *info_res;
|
||||
|
||||
llc_msg = queue->dequeue(&info_res);
|
||||
OSMO_ASSERT(llc_msg != NULL);
|
||||
|
||||
fprintf(stderr, "dequeued msg, length %d (expected %d), data %s\n",
|
||||
msgb_length(llc_msg), len, msgb_hexdump(llc_msg));
|
||||
|
||||
OSMO_ASSERT(msgb_length(llc_msg) == len);
|
||||
msg_data = msgb_data(llc_msg);
|
||||
|
||||
OSMO_ASSERT(memcmp(msg_data, exp_data, len) == 0);
|
||||
|
||||
if (exp_info)
|
||||
OSMO_ASSERT(memcmp(exp_info, info_res, sizeof(*exp_info)) == 0);
|
||||
|
||||
msgb_free(llc_msg);
|
||||
}
|
||||
|
||||
static void enqueue_data(gprs_llc_queue *queue, const char *message,
|
||||
gprs_llc_queue::MetaInfo *info = 0)
|
||||
{
|
||||
enqueue_data(queue, (uint8_t *)(message), strlen(message), info);
|
||||
}
|
||||
|
||||
static void dequeue_and_check(gprs_llc_queue *queue, const char *exp_message,
|
||||
const gprs_llc_queue::MetaInfo *exp_info = 0)
|
||||
{
|
||||
dequeue_and_check(queue,
|
||||
(uint8_t *)(exp_message), strlen(exp_message), exp_info);
|
||||
}
|
||||
|
||||
static void test_llc_queue()
|
||||
{
|
||||
gprs_llc_queue queue;
|
||||
|
||||
printf("=== start %s ===\n", __func__);
|
||||
|
||||
queue.init();
|
||||
OSMO_ASSERT(queue.size() == 0);
|
||||
OSMO_ASSERT(queue.octets() == 0);
|
||||
|
||||
enqueue_data(&queue, "LLC message");
|
||||
OSMO_ASSERT(queue.size() == 1);
|
||||
OSMO_ASSERT(queue.octets() == 11);
|
||||
|
||||
enqueue_data(&queue, "other LLC message");
|
||||
OSMO_ASSERT(queue.size() == 2);
|
||||
OSMO_ASSERT(queue.octets() == 28);
|
||||
|
||||
dequeue_and_check(&queue, "LLC message");
|
||||
OSMO_ASSERT(queue.size() == 1);
|
||||
OSMO_ASSERT(queue.octets() == 17);
|
||||
|
||||
dequeue_and_check(&queue, "other LLC message");
|
||||
OSMO_ASSERT(queue.size() == 0);
|
||||
OSMO_ASSERT(queue.octets() == 0);
|
||||
|
||||
enqueue_data(&queue, "LLC");
|
||||
OSMO_ASSERT(queue.size() == 1);
|
||||
OSMO_ASSERT(queue.octets() == 3);
|
||||
|
||||
queue.clear(NULL);
|
||||
OSMO_ASSERT(queue.size() == 0);
|
||||
OSMO_ASSERT(queue.octets() == 0);
|
||||
|
||||
printf("=== end %s ===\n", __func__);
|
||||
}
|
||||
|
||||
static void test_llc_meta()
|
||||
{
|
||||
gprs_llc_queue queue;
|
||||
gprs_llc_queue::MetaInfo info1 = {
|
||||
.recv_time = {123456777, 123456},
|
||||
.expire_time = {123456789, 987654},
|
||||
};
|
||||
gprs_llc_queue::MetaInfo info2 = {
|
||||
.recv_time = {987654321, 547352},
|
||||
.expire_time = {987654327, 867252},
|
||||
};
|
||||
|
||||
printf("=== start %s ===\n", __func__);
|
||||
|
||||
queue.init();
|
||||
OSMO_ASSERT(queue.size() == 0);
|
||||
OSMO_ASSERT(queue.octets() == 0);
|
||||
|
||||
enqueue_data(&queue, "LLC message 1", &info1);
|
||||
enqueue_data(&queue, "LLC message 2", &info2);
|
||||
|
||||
dequeue_and_check(&queue, "LLC message 1", &info1);
|
||||
dequeue_and_check(&queue, "LLC message 2", &info2);
|
||||
|
||||
queue.clear(NULL);
|
||||
OSMO_ASSERT(queue.size() == 0);
|
||||
OSMO_ASSERT(queue.octets() == 0);
|
||||
|
||||
printf("=== end %s ===\n", __func__);
|
||||
}
|
||||
|
||||
static const struct log_info_cat default_categories[] = {
|
||||
{"DPCU", "", "GPRS Packet Control Unit (PCU)", LOGL_INFO, 1},
|
||||
};
|
||||
|
||||
static int filter_fn(const struct log_context *ctx,
|
||||
struct log_target *tar)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
const struct log_info debug_log_info = {
|
||||
filter_fn,
|
||||
(struct log_info_cat*)default_categories,
|
||||
ARRAY_SIZE(default_categories),
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct vty_app_info pcu_vty_info = {0};
|
||||
|
||||
tall_pcu_ctx = talloc_named_const(NULL, 1, "LlcTest context");
|
||||
if (!tall_pcu_ctx)
|
||||
abort();
|
||||
|
||||
msgb_set_talloc_ctx(tall_pcu_ctx);
|
||||
osmo_init_logging(&debug_log_info);
|
||||
log_set_use_color(osmo_stderr_target, 0);
|
||||
log_set_print_filename(osmo_stderr_target, 0);
|
||||
log_set_log_level(osmo_stderr_target, LOGL_INFO);
|
||||
|
||||
vty_init(&pcu_vty_info);
|
||||
pcu_vty_init(&debug_log_info);
|
||||
|
||||
test_llc_queue();
|
||||
test_llc_meta();
|
||||
|
||||
if (getenv("TALLOC_REPORT_FULL"))
|
||||
talloc_report_full(tall_pcu_ctx, stderr);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
void l1if_pdch_req() { abort(); }
|
||||
void l1if_connect_pdch() { abort(); }
|
||||
void l1if_close_pdch() { abort(); }
|
||||
void l1if_open_pdch() { abort(); }
|
||||
}
|
||||
4
tests/llc/LlcTest.err
Normal file
4
tests/llc/LlcTest.err
Normal file
@@ -0,0 +1,4 @@
|
||||
dequeued msg, length 11 (expected 11), data 4c 4c 43 20 6d 65 73 73 61 67 65
|
||||
dequeued msg, length 17 (expected 17), data 6f 74 68 65 72 20 4c 4c 43 20 6d 65 73 73 61 67 65
|
||||
dequeued msg, length 13 (expected 13), data 4c 4c 43 20 6d 65 73 73 61 67 65 20 31
|
||||
dequeued msg, length 13 (expected 13), data 4c 4c 43 20 6d 65 73 73 61 67 65 20 32
|
||||
4
tests/llc/LlcTest.ok
Normal file
4
tests/llc/LlcTest.ok
Normal file
@@ -0,0 +1,4 @@
|
||||
=== start test_llc_queue ===
|
||||
=== end test_llc_queue ===
|
||||
=== start test_llc_meta ===
|
||||
=== end test_llc_meta ===
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "gprs_debug.h"
|
||||
#include "gprs_ms.h"
|
||||
#include "gprs_ms_storage.h"
|
||||
#include "bts.h"
|
||||
|
||||
extern "C" {
|
||||
#include "pcu_vty.h"
|
||||
@@ -468,6 +469,42 @@ static void test_ms_timeout()
|
||||
printf("=== end %s ===\n", __func__);
|
||||
}
|
||||
|
||||
static void test_ms_cs_selection()
|
||||
{
|
||||
BTS the_bts;
|
||||
gprs_rlcmac_bts *bts = the_bts.bts_data();
|
||||
uint32_t tlli = 0xffeeddbb;
|
||||
|
||||
gprs_rlcmac_dl_tbf *dl_tbf;
|
||||
GprsMs *ms;
|
||||
|
||||
printf("=== start %s ===\n", __func__);
|
||||
|
||||
bts->initial_cs_dl = 4;
|
||||
bts->initial_cs_ul = 1;
|
||||
bts->cs_downgrade_threshold = 0;
|
||||
|
||||
ms = new GprsMs(&the_bts, tlli);
|
||||
|
||||
OSMO_ASSERT(ms->is_idle());
|
||||
|
||||
dl_tbf = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_dl_tbf);
|
||||
dl_tbf->direction = GPRS_RLCMAC_DL_TBF;
|
||||
|
||||
dl_tbf->set_ms(ms);
|
||||
OSMO_ASSERT(!ms->is_idle());
|
||||
|
||||
OSMO_ASSERT(ms->current_cs_dl() == 4);
|
||||
|
||||
bts->cs_downgrade_threshold = 200;
|
||||
|
||||
OSMO_ASSERT(ms->current_cs_dl() == 3);
|
||||
|
||||
talloc_free(dl_tbf);
|
||||
|
||||
printf("=== end %s ===\n", __func__);
|
||||
}
|
||||
|
||||
static const struct log_info_cat default_categories[] = {
|
||||
{"DPCU", "", "GPRS Packet Control Unit (PCU)", LOGL_INFO, 1},
|
||||
};
|
||||
@@ -507,6 +544,7 @@ int main(int argc, char **argv)
|
||||
test_ms_change_tlli();
|
||||
test_ms_storage();
|
||||
test_ms_timeout();
|
||||
test_ms_cs_selection();
|
||||
|
||||
if (getenv("TALLOC_REPORT_FULL"))
|
||||
talloc_report_full(tall_pcu_ctx, stderr);
|
||||
|
||||
@@ -54,3 +54,5 @@ Detaching TBF from MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000
|
||||
Detaching TBF from MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0x00000000 DIR=DL STATE=NULL)
|
||||
Timeout for MS object, TLLI = 0xffeeddbb
|
||||
Destroying MS object, TLLI = 0xffeeddbb
|
||||
Creating MS object, TLLI = 0xffeeddbb
|
||||
Attaching TBF to MS object, TLLI = 0xffeeddbb, TBF = TBF(TFI=0 TLLI=0xffeeddbb DIR=DL STATE=NULL)
|
||||
|
||||
@@ -16,3 +16,5 @@
|
||||
ms_active() was called
|
||||
ms_idle() was called
|
||||
=== end test_ms_timeout ===
|
||||
=== start test_ms_cs_selection ===
|
||||
=== end test_ms_cs_selection ===
|
||||
|
||||
@@ -76,14 +76,12 @@ static void test_tbf_tlli_update()
|
||||
gprs_rlcmac_tbf *dl_tbf = tbf_alloc_dl_tbf(the_bts.bts_data(),
|
||||
NULL, 0,
|
||||
0, 0, 0);
|
||||
dl_tbf->update_tlli(0x2342);
|
||||
dl_tbf->update_ms(0x2342, GPRS_RLCMAC_DL_TBF);
|
||||
dl_tbf->set_ta(4);
|
||||
|
||||
gprs_rlcmac_tbf *ul_tbf = tbf_alloc_ul_tbf(the_bts.bts_data(),
|
||||
dl_tbf, 0,
|
||||
dl_tbf->ms(), 0,
|
||||
0, 0, 0);
|
||||
ul_tbf->update_tlli(0x2342);
|
||||
ul_tbf->update_ms(0x2342, GPRS_RLCMAC_UL_TBF);
|
||||
|
||||
ms = the_bts.ms_by_tlli(0x2342);
|
||||
@@ -96,7 +94,6 @@ static void test_tbf_tlli_update()
|
||||
* Now check.. that DL changes and that the timing advance
|
||||
* has changed.
|
||||
*/
|
||||
dl_tbf->update_tlli(0x4232);
|
||||
dl_tbf->update_ms(0x4232, GPRS_RLCMAC_DL_TBF);
|
||||
|
||||
/* It is still there, since the new TLLI has not been used for UL yet */
|
||||
@@ -427,6 +424,75 @@ static void test_tbf_exhaustion()
|
||||
gprs_bssgp_destroy();
|
||||
}
|
||||
|
||||
static void test_tbf_dl_llc_loss()
|
||||
{
|
||||
BTS the_bts;
|
||||
gprs_rlcmac_bts *bts;
|
||||
uint8_t ts_no = 4;
|
||||
uint8_t ms_class = 45;
|
||||
int rc = 0;
|
||||
uint32_t tlli = 0xc0123456;
|
||||
const char *imsi = "001001000123456";
|
||||
unsigned delay_csec = 1000;
|
||||
GprsMs *ms;
|
||||
|
||||
uint8_t buf[19];
|
||||
|
||||
printf("=== start %s ===\n", __func__);
|
||||
|
||||
bts = the_bts.bts_data();
|
||||
setup_bts(&the_bts, ts_no);
|
||||
bts->ms_idle_sec = 10; /* keep the MS object */
|
||||
|
||||
gprs_bssgp_create_and_connect(bts, 33001, 0, 33001,
|
||||
1234, 1234, 1234, 1, 1, 0, 0, 0);
|
||||
|
||||
/* Handle LLC frame 1 */
|
||||
memset(buf, 1, sizeof(buf));
|
||||
rc = gprs_rlcmac_dl_tbf::handle(bts, tlli, 0, imsi, ms_class,
|
||||
delay_csec, buf, sizeof(buf));
|
||||
OSMO_ASSERT(rc >= 0);
|
||||
|
||||
ms = the_bts.ms_store().get_ms(0, 0, imsi);
|
||||
OSMO_ASSERT(ms != NULL);
|
||||
OSMO_ASSERT(ms->dl_tbf() != NULL);
|
||||
|
||||
/* Handle LLC frame 2 */
|
||||
memset(buf, 2, sizeof(buf));
|
||||
rc = gprs_rlcmac_dl_tbf::handle(bts, tlli, 0, imsi, ms_class,
|
||||
delay_csec, buf, sizeof(buf));
|
||||
OSMO_ASSERT(rc >= 0);
|
||||
|
||||
/* TBF establishment fails (timeout) */
|
||||
tbf_free(ms->dl_tbf());
|
||||
|
||||
/* Handle LLC frame 3 */
|
||||
memset(buf, 3, sizeof(buf));
|
||||
rc = gprs_rlcmac_dl_tbf::handle(bts, tlli, 0, imsi, ms_class,
|
||||
delay_csec, buf, sizeof(buf));
|
||||
OSMO_ASSERT(rc >= 0);
|
||||
|
||||
OSMO_ASSERT(ms->dl_tbf() != NULL);
|
||||
|
||||
/* Get first BSN */
|
||||
struct msgb *msg;
|
||||
int fn = 0;
|
||||
uint8_t expected_data = 1;
|
||||
|
||||
while (ms->dl_tbf()->have_data()) {
|
||||
msg = ms->dl_tbf()->create_dl_acked_block(fn += 4, 7);
|
||||
fprintf(stderr, "MSG = %s\n", msgb_hexdump(msg));
|
||||
OSMO_ASSERT(msgb_length(msg) == 23);
|
||||
OSMO_ASSERT(msgb_data(msg)[10] == expected_data);
|
||||
expected_data += 1;
|
||||
}
|
||||
OSMO_ASSERT(expected_data-1 == 3);
|
||||
|
||||
printf("=== end %s ===\n", __func__);
|
||||
|
||||
gprs_bssgp_destroy();
|
||||
}
|
||||
|
||||
static void test_tbf_single_phase()
|
||||
{
|
||||
BTS the_bts;
|
||||
@@ -438,6 +504,7 @@ static void test_tbf_single_phase()
|
||||
int tfi = 0;
|
||||
gprs_rlcmac_ul_tbf *ul_tbf;
|
||||
struct gprs_rlcmac_pdch *pdch;
|
||||
struct pcu_l1_meas meas;
|
||||
|
||||
printf("=== start %s ===\n", __func__);
|
||||
|
||||
@@ -462,7 +529,7 @@ static void test_tbf_single_phase()
|
||||
};
|
||||
|
||||
pdch = &the_bts.bts_data()->trx[trx_no].pdch[ts_no];
|
||||
pdch->rcv_block(&data_msg[0], sizeof(data_msg), fn, 0);
|
||||
pdch->rcv_block(&data_msg[0], sizeof(data_msg), fn, &meas);
|
||||
|
||||
ms = the_bts.ms_by_tlli(0xf1223344);
|
||||
OSMO_ASSERT(ms != NULL);
|
||||
@@ -491,6 +558,8 @@ static void test_tbf_two_phase()
|
||||
bitvec *rlc_block;
|
||||
uint8_t buf[64];
|
||||
int num_bytes;
|
||||
struct pcu_l1_meas meas;
|
||||
meas.set_rssi(31);
|
||||
|
||||
printf("=== start %s ===\n", __func__);
|
||||
|
||||
@@ -520,7 +589,7 @@ static void test_tbf_two_phase()
|
||||
bitvec_free(rlc_block);
|
||||
|
||||
pdch = &the_bts.bts_data()->trx[trx_no].pdch[ts_no];
|
||||
pdch->rcv_block(&buf[0], num_bytes, 2654270, 31);
|
||||
pdch->rcv_block(&buf[0], num_bytes, 2654270, &meas);
|
||||
|
||||
/* check the TBF */
|
||||
ul_tbf = the_bts.ul_tbf_by_tfi(tfi, trx_no);
|
||||
@@ -543,7 +612,7 @@ static void test_tbf_two_phase()
|
||||
uint8_t(1), /* BSN:7, E:1 */
|
||||
};
|
||||
|
||||
pdch->rcv_block(&data_msg[0], sizeof(data_msg), rts_fn, 31);
|
||||
pdch->rcv_block(&data_msg[0], sizeof(data_msg), rts_fn, &meas);
|
||||
|
||||
ms = the_bts.ms_by_tlli(0xf1223344);
|
||||
OSMO_ASSERT(ms != NULL);
|
||||
@@ -602,6 +671,7 @@ int main(int argc, char **argv)
|
||||
test_tbf_delayed_release();
|
||||
test_tbf_imsi();
|
||||
test_tbf_exhaustion();
|
||||
test_tbf_dl_llc_loss();
|
||||
test_tbf_single_phase();
|
||||
test_tbf_two_phase();
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -4,6 +4,8 @@
|
||||
=== end test_tbf_imsi ===
|
||||
=== start test_tbf_exhaustion ===
|
||||
=== end test_tbf_exhaustion ===
|
||||
=== start test_tbf_dl_llc_loss ===
|
||||
=== end test_tbf_dl_llc_loss ===
|
||||
=== start test_tbf_single_phase ===
|
||||
=== end test_tbf_single_phase ===
|
||||
=== start test_tbf_two_phase ===
|
||||
|
||||
@@ -37,6 +37,13 @@ cat $abs_srcdir/ms/MsTest.err > experr
|
||||
AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/ms/MsTest], [0], [expout], [experr])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([llc])
|
||||
AT_KEYWORDS([llc])
|
||||
cat $abs_srcdir/llc/LlcTest.ok > expout
|
||||
cat $abs_srcdir/llc/LlcTest.err > experr
|
||||
AT_CHECK([$OSMO_QEMU $abs_top_builddir/tests/llc/LlcTest], [0], [expout], [experr])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([llist])
|
||||
AT_KEYWORDS([llist])
|
||||
cat $abs_srcdir/llist/LListTest.ok > expout
|
||||
|
||||
Reference in New Issue
Block a user