From 07cd260d9d9a6da0b3856c87852ee8039640d745 Mon Sep 17 00:00:00 2001 From: Ivan Kluchnikov Date: Tue, 18 Jun 2013 21:55:22 +0400 Subject: Implemented estimated C/I algorithm for 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. --- src/gprs_bssgp_pcu.cpp | 13 +++++ src/gprs_debug.cpp | 1 + src/gprs_debug.h | 1 + src/gprs_rlcmac.cpp | 121 ++++++++++++++++++++++++++++++++++++++++++++--- src/gprs_rlcmac.h | 15 +++++- src/gprs_rlcmac_data.cpp | 78 ++++++++++++++++++++++++++++-- src/pcu_main.cpp | 10 ++++ src/pcu_vty.c | 46 ++++++++++++++++++ 8 files changed, 274 insertions(+), 11 deletions(-) diff --git a/src/gprs_bssgp_pcu.cpp b/src/gprs_bssgp_pcu.cpp index 54753b85..7c5fba52 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 6f9e310a..f39cc2c6 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 8b113a1b..fbf157cf 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 2ea0a614..6d4a1b21 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 c5af6029..17b34ef0 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 4f2b649a..1dabda17 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..f6d1f092 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 3ab6865c..454b31c9 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); -- cgit v1.2.3