aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIvan Kluchnikov <kluchnikovi@gmail.com>2013-06-18 21:55:22 +0400
committerIvan Kluchnikov <kluchnikovi@gmail.com>2013-06-18 21:55:22 +0400
commit07cd260d9d9a6da0b3856c87852ee8039640d745 (patch)
tree3fb6057cb9fbe229fc040f10abb4725c46e8bf6c
parenta004e6a8233695abd417a97d6f81a802b605038a (diff)
Implemented estimated C/I algorithm for link adaptation.link_adaptation
Description of algorithm: 1. Start with initial coding scheme. 2. Transmit RLC blocks until the end of the reporting window (Packet Downlink Ack/Nack message is received) 3. Estimate the average C/I for the previous reporting window and change CS, if it is necessary. 4. If there is more data to transmit return to step 2. Added configuration options to enable/disable link adaptation and setting maximum C/I level for CS1, CS2, CS3, CS4.
-rw-r--r--src/gprs_bssgp_pcu.cpp13
-rw-r--r--src/gprs_debug.cpp1
-rw-r--r--src/gprs_debug.h1
-rw-r--r--src/gprs_rlcmac.cpp121
-rw-r--r--src/gprs_rlcmac.h15
-rw-r--r--src/gprs_rlcmac_data.cpp78
-rw-r--r--src/pcu_main.cpp10
-rw-r--r--src/pcu_vty.c46
8 files changed, 274 insertions, 11 deletions
diff --git a/src/gprs_bssgp_pcu.cpp b/src/gprs_bssgp_pcu.cpp
index 54753b8..7c5fba5 100644
--- a/src/gprs_bssgp_pcu.cpp
+++ b/src/gprs_bssgp_pcu.cpp
@@ -241,6 +241,19 @@ int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp)
return -EBUSY;
}
tbf->tlli = tlli;
+ struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
+ if (bts->cs_link_adaptation) {
+ /* set coding scheme */
+ int cs = recall_cs(tlli);
+ if (cs > 0) {
+ tbf->cs = cs;
+ LOGP(DCS, LOGL_NOTICE, "New DL TBF for tlli = 0x%08x recall CS = %d\n", tbf->tlli, tbf->cs);
+ } else {
+ tbf->cs = bts->initial_cs_dl;
+ remember_cs(tbf->tlli, tbf->cs);
+ LOGP(DCS, LOGL_NOTICE, "New DL TBF for tlli = 0x%08x remember CS = %d\n", tbf->tlli, tbf->cs);
+ }
+ }
tbf->tlli_valid = 1;
tbf->ta = ta;
diff --git a/src/gprs_debug.cpp b/src/gprs_debug.cpp
index 6f9e310..f39cc2c 100644
--- a/src/gprs_debug.cpp
+++ b/src/gprs_debug.cpp
@@ -41,6 +41,7 @@ static const struct log_info_cat default_categories[] = {
{"DRLCMACUL", "\033[1;36m", "GPRS RLC/MAC layer Uplink (RLCMAC)", LOGL_NOTICE, 1},
{"DRLCMACSCHED", "\033[0;36m", "GPRS RLC/MAC layer Scheduling (RLCMAC)", LOGL_NOTICE, 1},
{"DRLCMACMEAS", "\033[1;31m", "GPRS RLC/MAC layer Measurements (RLCMAC)", LOGL_INFO, 1},
+ {"DCS", "\033[1;36m", "Codding schemes", LOGL_NOTICE, 0},
{"DBSSGP","\033[1;34m", "GPRS BSS Gateway Protocol (BSSGP)", LOGL_INFO , 1},
{"DPCU", "\033[1;35m", "GPRS Packet Control Unit (PCU)", LOGL_NOTICE, 1},
};
diff --git a/src/gprs_debug.h b/src/gprs_debug.h
index 8b113a1..fbf157c 100644
--- a/src/gprs_debug.h
+++ b/src/gprs_debug.h
@@ -39,6 +39,7 @@ enum {
DRLCMACUL,
DRLCMACSCHED,
DRLCMACMEAS,
+ DCS,
DBSSGP,
DPCU,
aDebug_LastEntry
diff --git a/src/gprs_rlcmac.cpp b/src/gprs_rlcmac.cpp
index 2ea0a61..6d4a1b2 100644
--- a/src/gprs_rlcmac.cpp
+++ b/src/gprs_rlcmac.cpp
@@ -366,6 +366,17 @@ next_diagram:
tbf->ms_class = ms_class;
tbf->ws = 64;
tbf->sns = 128;
+
+ if (old_tbf) {
+ tbf->cs = old_tbf->cs;
+ LOGP(DCS, LOGL_NOTICE, "Allocate new %s TBF set CS = %d from OLD TBF\n",
+ (dir == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL", tbf->cs);
+ } else {
+ tbf->cs = bts->initial_cs_dl;
+ LOGP(DCS, LOGL_NOTICE, "Allocate new %s TBF set initial bts CS = %d\n",
+ (dir == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL", tbf->cs);
+ }
+
/* select algorithm */
rc = bts->alloc_algorithm(old_tbf, tbf, bts->alloc_algorithm_curst,
single_slot);
@@ -1359,7 +1370,7 @@ int write_immediate_assignment(bitvec * dest, uint8_t downlink, uint8_t ra,
uint32_t ref_fn, uint8_t ta, uint16_t arfcn, uint8_t ts, uint8_t tsc,
uint8_t tfi, uint8_t usf, uint32_t tlli,
uint8_t polling, uint32_t fn, uint8_t single_block, uint8_t alpha,
- uint8_t gamma, int8_t ta_idx)
+ uint8_t gamma, uint8_t cs, int8_t ta_idx)
{
unsigned wp = 0;
uint8_t plen;
@@ -1442,7 +1453,6 @@ int write_immediate_assignment(bitvec * dest, uint8_t downlink, uint8_t ra,
}
else
{
- struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
// GMS 04.08 10.5.2.37b 10.5.2.16
bitvec_write_field(dest, wp, 3, 2); // "HH"
bitvec_write_field(dest, wp, 0, 2); // "0" Packet Uplink Assignment
@@ -1472,7 +1482,7 @@ int write_immediate_assignment(bitvec * dest, uint8_t downlink, uint8_t ra,
bitvec_write_field(dest, wp, usf, 3); // USF
bitvec_write_field(dest, wp, 0, 1); // USF_GRANULARITY
bitvec_write_field(dest, wp, 0, 1); // "0" power control: Not Present
- bitvec_write_field(dest, wp, bts->initial_cs_ul-1, 2); // CHANNEL_CODING_COMMAND
+ bitvec_write_field(dest, wp, cs-1, 2); // CHANNEL_CODING_COMMAND
bitvec_write_field(dest, wp, 1, 1); // TLLI_BLOCK_CHANNEL_CODING
if (alpha) {
bitvec_write_field(dest, wp,0x1,1); // ALPHA = present
@@ -1496,7 +1506,6 @@ void write_packet_uplink_assignment(bitvec * dest, uint8_t old_tfi,
uint8_t gamma, int8_t ta_idx)
{
// TODO We should use our implementation of encode RLC/MAC Control messages.
- struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
unsigned wp = 0;
uint8_t ts;
@@ -1519,7 +1528,7 @@ void write_packet_uplink_assignment(bitvec * dest, uint8_t old_tfi,
}
bitvec_write_field(dest, wp,0x0,1); // Message escape
- bitvec_write_field(dest, wp,bts->initial_cs_ul-1, 2); // CHANNEL_CODING_COMMAND
+ bitvec_write_field(dest, wp,tbf->cs-1, 2); // CHANNEL_CODING_COMMAND
bitvec_write_field(dest, wp,0x1,1); // TLLI_BLOCK_CHANNEL_CODING
bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_VALUE = on
bitvec_write_field(dest, wp,tbf->ta,6); // TIMING_ADVANCE_VALUE
@@ -1687,7 +1696,6 @@ void write_packet_uplink_ack(RlcMacDownlink_t * block, struct gprs_rlcmac_tbf *t
char show_v_n[65];
- struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
uint8_t rbb = 0;
uint16_t i, bbn;
uint16_t mod_sns_half = (tbf->sns >> 1) - 1;
@@ -1706,7 +1714,7 @@ void write_packet_uplink_ack(RlcMacDownlink_t * block, struct gprs_rlcmac_tbf *t
block->u.Packet_Uplink_Ack_Nack.UPLINK_TFI = tbf->tfi; // Uplink TFI
block->u.Packet_Uplink_Ack_Nack.UnionType = 0x0; // PU_AckNack_GPRS = on
- block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.CHANNEL_CODING_COMMAND = bts->initial_cs_ul - 1; // CS1
+ block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.CHANNEL_CODING_COMMAND = tbf->cs - 1; // CS1
block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Ack_Nack_Description.FINAL_ACK_INDICATION = final; // FINAL ACK INDICATION
block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Ack_Nack_Description.STARTING_SEQUENCE_NUMBER = tbf->dir.ul.v_r; // STARTING_SEQUENCE_NUMBER
// RECEIVE_BLOCK_BITMAP
@@ -1923,3 +1931,102 @@ int flush_timing_advance(void)
return count;
}
+
+/*
+ channel coding schemes memory
+*/
+
+static LLIST_HEAD(gprs_rlcmac_tlli_cs_list);
+static int gprs_rlcmac_tlli_cs_num = 0;
+
+struct gprs_rlcmac_tlli_cs {
+ struct llist_head list;
+ uint32_t tlli;
+ uint8_t cs;
+};
+
+/* remember channel coding scheme of a given TLLI */
+int remember_cs(uint32_t tlli, uint8_t cs)
+{
+ struct gprs_rlcmac_tlli_cs *cs_entry;
+
+ /* check for existing entry */
+ llist_for_each_entry(cs_entry, &gprs_rlcmac_tlli_cs_list, list) {
+ if (cs_entry->tlli == tlli) {
+ cs_entry->cs = cs;
+ /* relink to end of list */
+ llist_del(&cs_entry->list);
+ llist_add_tail(&cs_entry->list, &gprs_rlcmac_tlli_cs_list);
+ return 0;
+ }
+ }
+
+ /* if list is full, remove oldest entry */
+ if (gprs_rlcmac_tlli_cs_num == 30) {
+ cs_entry = llist_entry(gprs_rlcmac_tlli_cs_list.next, struct gprs_rlcmac_tlli_cs, list);
+ llist_del(&cs_entry->list);
+ talloc_free(cs_entry);
+ gprs_rlcmac_tlli_cs_num--;
+ }
+
+ /* create new CS entry */
+ cs_entry = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_tlli_cs);
+ if (!cs_entry)
+ return -ENOMEM;
+
+ cs_entry->tlli = tlli;
+ cs_entry->cs = cs;
+ llist_add_tail(&cs_entry->list, &gprs_rlcmac_tlli_cs_list);
+ gprs_rlcmac_tlli_cs_num++;
+
+ return 0;
+}
+
+int recall_cs(uint32_t tlli)
+{
+ struct gprs_rlcmac_tlli_cs *cs_entry;
+ uint8_t cs;
+
+ llist_for_each_entry(cs_entry, &gprs_rlcmac_tlli_cs_list, list) {
+ if (cs_entry->tlli == tlli) {
+ cs = cs_entry->cs;
+ return cs;
+ }
+ }
+
+ return -EINVAL;
+}
+
+int flush_cs(void)
+{
+ struct gprs_rlcmac_tlli_cs *cs_entry;
+ int count = 0;
+
+ while (!llist_empty(&gprs_rlcmac_tlli_cs_list)) {
+ cs_entry = llist_entry(gprs_rlcmac_tlli_cs_list.next, struct gprs_rlcmac_tlli_cs, list);
+ llist_del(&cs_entry->list);
+ talloc_free(cs_entry);
+ count++;
+ }
+ gprs_rlcmac_tlli_cs_num = 0;
+
+ return count;
+}
+
+uint8_t link_adaptation(float i_level)
+{
+ uint8_t cs = 0;
+ struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
+
+ if (i_level*2 <= bts->cs1_ci_level) {
+ cs = 1;
+ } else if (i_level*2 <= bts->cs2_ci_level) {
+ cs = 2;
+ } else if (i_level*2 <= bts->cs3_ci_level) {
+ cs = 3;
+ } else if (i_level*2 <= bts->cs4_ci_level) {
+ cs = 4;
+ }
+
+ return cs;
+}
diff --git a/src/gprs_rlcmac.h b/src/gprs_rlcmac.h
index c5af602..17b34ef 100644
--- a/src/gprs_rlcmac.h
+++ b/src/gprs_rlcmac.h
@@ -73,6 +73,11 @@ struct gprs_rlcmac_bts {
uint8_t cs3;
uint8_t cs4;
uint8_t initial_cs_dl, initial_cs_ul;
+ uint8_t cs_link_adaptation; /* CS link adaptation */
+ float cs1_ci_level; /* maximum C/I level for CS1 */
+ float cs2_ci_level; /* maximum C/I level for CS2 */
+ float cs3_ci_level; /* maximum C/I level for CS3 */
+ float cs4_ci_level; /* maximum C/I level for CS4 */
uint8_t force_cs; /* 0=use from BTS 1=use from VTY */
uint16_t force_llc_lifetime; /* overrides lifetime from SGSN */
uint8_t t3142;
@@ -351,7 +356,7 @@ int gprs_rlcmac_rcv_block(uint8_t trx, uint8_t ts, uint8_t *data, uint8_t len,
int write_immediate_assignment(bitvec * dest, uint8_t downlink, uint8_t ra,
uint32_t ref_fn, uint8_t ta, uint16_t arfcn, uint8_t ts, uint8_t tsc,
uint8_t tfi, uint8_t usf, uint32_t tlli, uint8_t polling,
- uint32_t fn, uint8_t single_block, uint8_t alpha, uint8_t gamma,
+ uint32_t fn, uint8_t single_block, uint8_t alpha, uint8_t gamma, uint8_t cs,
int8_t ta_idx);
void write_packet_uplink_assignment(bitvec * dest, uint8_t old_tfi,
@@ -429,6 +434,14 @@ int recall_timing_advance(uint32_t tlli);
int flush_timing_advance(void);
+int remember_cs(uint32_t tlli, uint8_t cs);
+
+int recall_cs(uint32_t tlli);
+
+int flush_cs(void);
+
+uint8_t link_adaptation(float i_level);
+
extern "C" {
#endif
int alloc_algorithm_a(struct gprs_rlcmac_tbf *old_tbf,
diff --git a/src/gprs_rlcmac_data.cpp b/src/gprs_rlcmac_data.cpp
index 4f2b649..1dabda1 100644
--- a/src/gprs_rlcmac_data.cpp
+++ b/src/gprs_rlcmac_data.cpp
@@ -256,6 +256,23 @@ static struct gprs_rlcmac_tbf *alloc_ul_tbf(int8_t use_trx, uint8_t ms_class,
tbf->tlli_valid = 1; /* no contention resolution */
tbf->dir.ul.contention_resolution_done = 1;
tbf->ta = ta; /* use current TA */
+
+ if (bts->cs_link_adaptation) {
+ int cs = recall_cs(tlli);
+ if (cs > 0) {
+ tbf->cs = cs;
+ LOGP(DCS, LOGL_NOTICE, "New UL TBF for tlli = 0x%08x recall CS = %d\n", tbf->tlli, tbf->cs);
+ } else {
+ if (dl_tbf) {
+ tbf->cs = dl_tbf->cs;
+ } else {
+ tbf->cs = bts->initial_cs_ul;
+ }
+ remember_cs(tbf->tlli, tbf->cs);
+ LOGP(DCS, LOGL_NOTICE, "New UL TBF for tlli = 0x%08x remember CS = %d\n", tbf->tlli, tbf->cs);
+ }
+ }
+
tbf_new_state(tbf, GPRS_RLCMAC_ASSIGN);
tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_PACCH);
tbf_timer_start(tbf, 3169, bts->t3169, 0);
@@ -383,6 +400,42 @@ int gprs_rlcmac_rcv_control_block(bitvec *rlc_block, uint8_t trx, uint8_t ts,
"wrong TFI=%d, ignoring!\n", tfi);
break;
}
+
+ {
+ struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
+ if (bts->cs_link_adaptation) {
+ LOGP(DCS, LOGL_NOTICE, "PACKET DOWNLINK ACK TFI=%d TS %d\n", tfi, ts);
+ LOGP(DCS, LOGL_NOTICE, "PACKET DOWNLINK ACK C_VALUE =%d TS RXQUAL = %d SIGN_VAR = %d\n",
+ ul_control_block->u.Packet_Downlink_Ack_Nack.Channel_Quality_Report.C_VALUE,
+ ul_control_block->u.Packet_Downlink_Ack_Nack.Channel_Quality_Report.RXQUAL,
+ ul_control_block->u.Packet_Downlink_Ack_Nack.Channel_Quality_Report.SIGN_VAR
+ );
+ uint8_t i_level = 0;
+ uint8_t i_level_detect = 0;
+ for (uint8_t i = 0; i < 8; i++) {
+ if (ul_control_block->u.Packet_Downlink_Ack_Nack.Channel_Quality_Report.Slot[i].Exist) {
+ LOGP(DCS, LOGL_NOTICE, "TS = %d I_LEVEL = %d\n",
+ i, ul_control_block->u.Packet_Downlink_Ack_Nack.Channel_Quality_Report.Slot[i].I_LEVEL_TN);
+ i_level_detect++;
+ i_level += ul_control_block->u.Packet_Downlink_Ack_Nack.Channel_Quality_Report.Slot[i].I_LEVEL_TN;
+ }
+ }
+ if (i_level_detect > 0) {
+ uint8_t cs = link_adaptation(i_level/i_level_detect);
+ LOGP(DCS, LOGL_NOTICE, "Link adaptation for tlli = 0x%08x |"
+ "i_level_sum = %d | i_level_num = %d | I_LEVEL = %d dbm | CS = %d \n",
+ tbf->tlli, i_level, i_level_detect, (i_level/i_level_detect)*2, tbf->cs);
+ if (cs!= tbf->cs) {
+ remember_cs(tbf->tlli, cs);
+ tbf->cs = cs;
+ LOGP(DCS, LOGL_NOTICE, "Change CS for tlli = 0x%08x remember new CS = %d\n", tbf->tlli, tbf->cs);
+ } else {
+ LOGP(DCS, LOGL_NOTICE, "CS for tlli = 0x%08x not changed\n", tbf->tlli);
+ }
+ }
+ }
+ }
+
tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_DL_ACK);
if ((tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_TO_DL_ACK))) {
tbf->state_flags &= ~(1 << GPRS_RLCMAC_FLAG_TO_DL_ACK);
@@ -928,6 +981,17 @@ int gprs_rlcmac_rcv_data_block_acknowledged(uint8_t trx, uint8_t ts,
tbf->tlli_valid = 1;
/* store current timing advance */
remember_timing_advance(tbf->tlli, tbf->ta);
+ if (bts->cs_link_adaptation) {
+ /* set coding scheme */
+ int cs = recall_cs(tbf->tlli);
+ if (cs > 0) {
+ tbf->cs = cs;
+ LOGP(DCS, LOGL_NOTICE, "UL TBF for tlli = 0x%08x recall CS = %d\n", tbf->tlli, tbf->cs);
+ } else {
+ remember_cs(tbf->tlli, tbf->cs);
+ LOGP(DCS, LOGL_NOTICE, "UL TBF for tlli = 0x%08x remember CS = %d\n", tbf->tlli, tbf->cs);
+ }
+ }
/* already have TLLI, but we stille get another one */
} else if (rh->ti) {
uint32_t tlli;
@@ -1022,6 +1086,14 @@ int gprs_rlcmac_rcv_data_block_acknowledged(uint8_t trx, uint8_t ts,
* an ack/nack */
if (rh->si || rh->ti || tbf->state == GPRS_RLCMAC_FINISHED
|| (tbf->dir.ul.rx_counter % SEND_ACK_AFTER_FRAMES) == 0) {
+ if (bts->cs_link_adaptation) {
+ /* update coding scheme, if it is necessary */
+ int cs = recall_cs(tbf->tlli);
+ if ((cs > 0)&&(cs != tbf->cs)) {
+ tbf->cs = cs;
+ LOGP(DCS, LOGL_NOTICE, "UL TBF for tlli = 0x%08x update CS = %d\n", tbf->tlli, tbf->cs);
+ }
+ }
if (rh->si) {
LOGP(DRLCMACUL, LOGL_NOTICE, "- Scheduling Ack/Nack, "
"because MS is stalled.\n");
@@ -1205,12 +1277,12 @@ int gprs_rlcmac_rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta)
plen = write_immediate_assignment(immediate_assignment, 0, ra,
Fn, qta >> 2, bts->trx[trx].arfcn, ts,
bts->trx[trx].pdch[ts].tsc, 0, 0, 0, 0, sb_fn, 1,
- bts->alpha, bts->gamma, -1);
+ bts->alpha, bts->gamma, bts->initial_cs_ul, -1);
else
plen = write_immediate_assignment(immediate_assignment, 0, ra,
Fn, tbf->ta, tbf->arfcn, tbf->first_ts, tbf->tsc,
tbf->tfi, tbf->dir.ul.usf[tbf->first_ts], 0, 0, 0, 0,
- bts->alpha, bts->gamma, -1);
+ bts->alpha, bts->gamma,bts->initial_cs_ul, -1);
pcu_l1if_tx_agch(immediate_assignment, plen);
bitvec_free(immediate_assignment);
@@ -1831,7 +1903,7 @@ static void gprs_rlcmac_downlink_assignment(gprs_rlcmac_tbf *tbf, uint8_t poll,
plen = write_immediate_assignment(immediate_assignment, 1, 125,
(tbf->pdch[tbf->first_ts]->last_rts_fn + 21216) % 2715648, tbf->ta,
tbf->arfcn, tbf->first_ts, tbf->tsc, tbf->tfi, 0, tbf->tlli, poll,
- tbf->poll_fn, 0, bts->alpha, bts->gamma, -1);
+ tbf->poll_fn, 0, bts->alpha, bts->gamma, tbf->cs, -1);
pcu_l1if_tx_pch(immediate_assignment, plen, imsi);
bitvec_free(immediate_assignment);
}
diff --git a/src/pcu_main.cpp b/src/pcu_main.cpp
index 041831f6..f6d1f09 100644
--- a/src/pcu_main.cpp
+++ b/src/pcu_main.cpp
@@ -175,6 +175,14 @@ int main(int argc, char *argv[])
bts->n3105 = 8;
bts->alpha = 0; /* a = 0.0 */
+ /* turn on CS link adaptation */
+ bts->cs_link_adaptation = 1;
+ /* set CS */
+ bts->cs1_ci_level = 6.5;
+ bts->cs2_ci_level = 9;
+ bts->cs3_ci_level = 16.5;
+ bts->cs4_ci_level = 30;
+
msgb_set_talloc_ctx(tall_pcu_ctx);
osmo_init_logging(&gprs_log_info);
@@ -250,6 +258,8 @@ int main(int argc, char *argv[])
flush_timing_advance();
+ flush_cs();
+
talloc_free(gprs_rlcmac_bts);
talloc_report_full(tall_pcu_ctx, stderr);
diff --git a/src/pcu_vty.c b/src/pcu_vty.c
index 3ab6865..454b31c 100644
--- a/src/pcu_vty.c
+++ b/src/pcu_vty.c
@@ -88,6 +88,10 @@ static int config_write_pcu(struct vty *vty)
else
vty_out(vty, " cs %d %d%s", bts->initial_cs_dl,
bts->initial_cs_ul, VTY_NEWLINE);
+ if (bts->cs_link_adaptation)
+ vty_out(vty, " cs ci level %f %f %f %f%s", bts->cs1_ci_level,
+ bts->cs2_ci_level, bts->cs3_ci_level, bts->cs4_ci_level,
+ VTY_NEWLINE);
if (bts->force_llc_lifetime == 0xffff)
vty_out(vty, " queue lifetime infinite%s", VTY_NEWLINE);
else if (bts->force_llc_lifetime)
@@ -159,6 +163,45 @@ DEFUN(cfg_pcu_no_cs,
return CMD_SUCCESS;
}
+DEFUN(cfg_pcu_cs_link_adaptation,
+ cfg_pcu_cs_link_adaptation_cmd,
+ "cs link adaptation",
+ NO_STR "Turn on CS link adaptation\n")
+{
+ struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
+
+ bts->cs_link_adaptation = 1;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_pcu_no_cs_link_adaptation,
+ cfg_pcu_no_cs_link_adaptation_cmd,
+ "no cs link adaptation",
+ NO_STR "Turn off CS link adaptation\n")
+{
+ struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
+
+ bts->cs_link_adaptation = 0;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_pcu_cs_ci_level,
+ cfg_pcu_cs_ci_level_cmd,
+ "cs ci level <0-30> <0-30> <0-30> <0-30>",
+ NO_STR "Set maximum C/I level for CS1 CS2 CS3 CS4\n")
+{
+ struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
+
+ bts->cs1_ci_level = atoi(argv[0]);
+ bts->cs2_ci_level = atoi(argv[1]);
+ bts->cs3_ci_level = atoi(argv[2]);
+ bts->cs4_ci_level = atoi(argv[3]);
+
+ return CMD_SUCCESS;
+}
+
#define QUEUE_STR "Packet queue options\n"
#define LIFETIME_STR "Set lifetime limit of LLC frame in centi-seconds " \
"(overrides the value given by SGSN)\n"
@@ -300,6 +343,9 @@ int pcu_vty_init(const struct log_info *cat)
install_element(PCU_NODE, &cfg_pcu_no_two_phase_cmd);
install_element(PCU_NODE, &cfg_pcu_cs_cmd);
install_element(PCU_NODE, &cfg_pcu_no_cs_cmd);
+ install_element(PCU_NODE, &cfg_pcu_cs_link_adaptation_cmd);
+ install_element(PCU_NODE, &cfg_pcu_no_cs_link_adaptation_cmd);
+ install_element(PCU_NODE, &cfg_pcu_cs_ci_level_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);