diff options
-rw-r--r-- | include/osmo-bts/bts.h | 3 | ||||
-rw-r--r-- | include/osmo-bts/bts_model.h | 1 | ||||
-rw-r--r-- | src/common/bts.c | 5 | ||||
-rw-r--r-- | src/common/rsl.c | 10 | ||||
-rw-r--r-- | src/common/vty.c | 14 | ||||
-rw-r--r-- | src/osmo-bts-sysmo/l1_if.c | 84 | ||||
-rw-r--r-- | src/osmo-bts-sysmo/l1_if.h | 2 | ||||
-rw-r--r-- | src/osmo-bts-sysmo/oml.c | 39 | ||||
-rw-r--r-- | tests/sysmobts/sysmobts_test.c | 57 | ||||
-rw-r--r-- | tests/sysmobts/sysmobts_test.ok | 1 |
10 files changed, 201 insertions, 15 deletions
diff --git a/include/osmo-bts/bts.h b/include/osmo-bts/bts.h index e4899736..bfa2dc3e 100644 --- a/include/osmo-bts/bts.h +++ b/include/osmo-bts/bts.h @@ -39,5 +39,8 @@ void load_timer_start(struct gsm_bts *bts); void bts_update_status(enum bts_global_status which, int on); +int trx_ms_pwr_ctrl_is_osmo(struct gsm_bts_trx *trx); + + #endif /* _BTS_H */ diff --git a/include/osmo-bts/bts_model.h b/include/osmo-bts/bts_model.h index 200d02d2..176b7877 100644 --- a/include/osmo-bts/bts_model.h +++ b/include/osmo-bts/bts_model.h @@ -47,5 +47,6 @@ void bts_model_config_write_trx(struct vty *vty, struct gsm_bts_trx *trx); int bts_model_oml_estab(struct gsm_bts *bts); int bts_model_change_power(struct gsm_bts_trx *trx, int p_trxout_mdBm); +int bts_model_adjst_ms_pwr(struct gsm_lchan *lchan); #endif diff --git a/src/common/bts.c b/src/common/bts.c index f6ddfaa0..65b115a8 100644 --- a/src/common/bts.c +++ b/src/common/bts.c @@ -606,3 +606,8 @@ int bts_supports_cipher(struct gsm_bts_role_bts *bts, int rsl_cipher) sup = (1 << (rsl_cipher - 2)) & bts->support.ciphers; return sup > 0; } + +int trx_ms_pwr_ctrl_is_osmo(struct gsm_bts_trx *trx) +{ + return trx->ms_power_control == 1; +} diff --git a/src/common/rsl.c b/src/common/rsl.c index ff9833d4..80940a4e 100644 --- a/src/common/rsl.c +++ b/src/common/rsl.c @@ -735,6 +735,11 @@ static int rsl_rx_chan_activ(struct msgb *msg) return rsl_tx_chan_act_nack(lchan, RSL_ERR_EQUIPMENT_FAIL); } + /* Initialize channel defaults */ + lchan->ms_power = ms_pwr_ctl_lvl(lchan->ts->trx->bts->band, 0); + lchan->ms_power_ctrl.current = lchan->ms_power; + lchan->ms_power_ctrl.fixed = 0; + rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)); /* 9.3.3 Activation Type */ @@ -774,8 +779,11 @@ static int rsl_rx_chan_activ(struct msgb *msg) if (TLVP_PRESENT(&tp, RSL_IE_BS_POWER)) lchan->bs_power = *TLVP_VAL(&tp, RSL_IE_BS_POWER); /* 9.3.13 MS Power */ - if (TLVP_PRESENT(&tp, RSL_IE_MS_POWER)) + if (TLVP_PRESENT(&tp, RSL_IE_MS_POWER)) { lchan->ms_power = *TLVP_VAL(&tp, RSL_IE_MS_POWER); + lchan->ms_power_ctrl.current = lchan->ms_power; + lchan->ms_power_ctrl.fixed = 0; + } /* 9.3.24 Timing Advance */ if (TLVP_PRESENT(&tp, RSL_IE_TIMING_ADVANCE)) lchan->rqd_ta = *TLVP_VAL(&tp, RSL_IE_TIMING_ADVANCE); diff --git a/src/common/vty.c b/src/common/vty.c index d81a8980..3354fb98 100644 --- a/src/common/vty.c +++ b/src/common/vty.c @@ -198,6 +198,9 @@ static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts) tpp->ramp.step_size_mdB, VTY_NEWLINE); vty_out(vty, " power-ramp step-interval %d%s", tpp->ramp.step_interval_sec, VTY_NEWLINE); + vty_out(vty, " ms-power-control %s%s", + trx->ms_power_control == 0 ? "dsp" : "osmo", + VTY_NEWLINE); bts_model_config_write_trx(vty, trx); } @@ -460,6 +463,16 @@ DEFUN(cfg_trx_pr_step_interval, cfg_trx_pr_step_interval_cmd, return CMD_SUCCESS; } +DEFUN(cfg_trx_ms_power_control, cfg_trx_ms_power_control_cmd, + "ms-power-control (dsp|osmo)", + "Mobile Station Power Level Control (change requires restart)\n" + "Handled by DSP\n" "Handled by OsmoBTS\n") +{ + struct gsm_bts_trx *trx = vty->index; + + trx->ms_power_control = argv[0][0] == 'd' ? 0 : 1; + return CMD_SUCCESS; +} /* ====================================================================== @@ -657,6 +670,7 @@ int bts_vty_init(const struct log_info *cat) install_element(TRX_NODE, &cfg_trx_pr_max_initial_cmd); install_element(TRX_NODE, &cfg_trx_pr_step_size_cmd); install_element(TRX_NODE, &cfg_trx_pr_step_interval_cmd); + install_element(TRX_NODE, &cfg_trx_ms_power_control_cmd); install_element(ENABLE_NODE, &bts_t_t_l_jitter_buf_cmd); diff --git a/src/osmo-bts-sysmo/l1_if.c b/src/osmo-bts-sysmo/l1_if.c index 32e82d83..9423cfe0 100644 --- a/src/osmo-bts-sysmo/l1_if.c +++ b/src/osmo-bts-sysmo/l1_if.c @@ -50,6 +50,7 @@ #include <osmo-bts/pcu_if.h> #include <osmo-bts/handover.h> #include <osmo-bts/cbch.h> +#include <osmo-bts/bts_model.h> #include <sysmocom/femtobts/superfemto.h> #include <sysmocom/femtobts/gsml1prim.h> @@ -521,10 +522,11 @@ static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1, /* resolve the L2 entity using rts_ind->hLayer2 */ lchan = l1if_hLayer_to_lchan(trx, rts_ind->hLayer2); le = &lchan->lapdm_ch.lapdm_acch; - /* if the DSP is taking care of power control - * (ul_power_target==0), then this value will be - * overridden. */ - msu_param->u8Buffer[0] = lchan->ms_power; + /* + * if the DSP is taking care of power control, + * then this value will be overridden. + */ + msu_param->u8Buffer[0] = lchan->ms_power_ctrl.current; msu_param->u8Buffer[1] = lchan->rqd_ta; rc = lapdm_phsap_dequeue_prim(le, &pp); if (rc < 0) { @@ -773,6 +775,14 @@ static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_i data_ind->msgUnitParam.u8Size); break; } + + /* + * Handle power control + */ + l1if_ms_pwr_ctrl(lchan, fl1->ul_power_target, + data_ind->msgUnitParam.u8Buffer[0] & 0x1f, + data_ind->measParam.fRssi); + /* Some brilliant engineer decided that the ordering of * fields on the Um interface is different from the * order of fields in RLS. See TS 04.04 (Chapter 7.2) @@ -1686,3 +1696,69 @@ int l1if_rf_clock_info_correct(struct femtol1_hdl *fl1h) } #endif + +/* + * Check if manual power control is needed + * Check if fixed power was selected + * Check if the MS is already using our level if not + * the value is bogus.. + * TODO: Add a timeout.. e.g. if the ms is not capable of reaching + * the value we have set. + */ +inline int l1if_ms_pwr_ctrl(struct gsm_lchan *lchan, const int ul_power_target, + const uint8_t ms_power, const float rxLevel) +{ + float rx; + int cur_dBm, new_dBm, new_pwr; + const enum gsm_band band = lchan->ts->trx->bts->band; + + if (!trx_ms_pwr_ctrl_is_osmo(lchan->ts->trx)) + return 0; + if (lchan->ms_power_ctrl.fixed) + return 0; + + /* The phone hasn't reached the power level yet */ + if (lchan->ms_power_ctrl.current != ms_power) + return 0; + + /* + * What is the difference between what we want and received? + * Ignore a margin that is within the range of measurement + * and MS output issues. + */ + rx = ul_power_target - rxLevel; + if (rx >= 0 && rx < 1.5f) + return 0; + if (rx < 0 && rx > -1.5f) + return 0; + + /* We don't really care about the truncation of int + float */ + cur_dBm = ms_pwr_dbm(band, ms_power); + new_dBm = cur_dBm + rx; + + /* Clamp negative values and do it depending on the band */ + if (new_dBm < 0) + new_dBm = 0; + + switch (band) { + case GSM_BAND_1800: + /* If MS_TX_PWR_MAX_CCH is set the values 29, + * 30, 31 are not used. Avoid specifying a dBm + * that would lead to these power levels. The + * phone might not be able to reach them. */ + if (new_dBm > 30) + new_dBm = 30; + break; + default: + break; + } + + new_pwr = ms_pwr_ctl_lvl(band, new_dBm); + if (lchan->ms_power_ctrl.current != new_pwr) { + lchan->ms_power_ctrl.current = new_pwr; + bts_model_adjst_ms_pwr(lchan); + return 1; + } + + return 0; +} diff --git a/src/osmo-bts-sysmo/l1_if.h b/src/osmo-bts-sysmo/l1_if.h index 5efa25b4..5f05f1df 100644 --- a/src/osmo-bts-sysmo/l1_if.h +++ b/src/osmo-bts-sysmo/l1_if.h @@ -132,4 +132,6 @@ int bts_check_for_ciph_cmd(struct femtol1_hdl *fl1h, void bts_check_for_first_ciphrd(struct femtol1_hdl *fl1h, GsmL1_MsgUnitParam_t *msgUnitParam, struct gsm_lchan *lchan); +inline int l1if_ms_pwr_ctrl(struct gsm_lchan *lchan, const int uplink_target, + const uint8_t ms_power, const float rxLevel); #endif /* _FEMTO_L1_H */ diff --git a/src/osmo-bts-sysmo/oml.c b/src/osmo-bts-sysmo/oml.c index 48c93710..b1e3ac5c 100644 --- a/src/osmo-bts-sysmo/oml.c +++ b/src/osmo-bts-sysmo/oml.c @@ -335,7 +335,8 @@ static int trx_init(struct gsm_bts_trx *trx) dev_par->u16Arfcn = trx->arfcn; dev_par->u16BcchArfcn = trx->bts->c0->arfcn; dev_par->u8NbTsc = trx->bts->bsic & 7; - dev_par->fRxPowerLevel = fl1h->ul_power_target; + dev_par->fRxPowerLevel = trx_ms_pwr_ctrl_is_osmo(trx) + ? 0.0 : fl1h->ul_power_target; dev_par->fTxPowerLevel = 0.0; LOGP(DL1C, LOGL_NOTICE, "Init TRX (ARFCN %u, TSC %u, RxPower % 2f dBm, " @@ -922,12 +923,16 @@ static int mph_send_activate_req(struct gsm_lchan *lchan, struct sapi_cmd *cmd) case GsmL1_Sapi_Prach: lch_par->prach.u8Bsic = lchan->ts->trx->bts->bsic; break; - case GsmL1_Sapi_Pdtch: - case GsmL1_Sapi_Pacch: case GsmL1_Sapi_Sacch: /* - * TODO: For the SACCH we need to set the u8MsPowerLevel when - * doing manual MS power control. */ + * For the SACCH we need to set the u8MsPowerLevel when + * doing manual MS power control. + */ + if (trx_ms_pwr_ctrl_is_osmo(lchan->ts->trx)) + lch_par->sacch.u8MsPowerLevel = lchan->ms_power_ctrl.current; + /* fall through */ + case GsmL1_Sapi_Pdtch: + case GsmL1_Sapi_Pacch: /* * Be sure that every packet is received, even if it * fails. In this case the length might be lower or 0. @@ -1169,7 +1174,8 @@ err: static int mph_send_config_logchpar(struct gsm_lchan *lchan, struct sapi_cmd *cmd) { - struct femtol1_hdl *fl1h = trx_femtol1_hdl(lchan->ts->trx); + struct gsm_bts_trx *trx = lchan->ts->trx; + struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx); struct msgb *msg = l1p_msgb_alloc(); GsmL1_MphConfigReq_t *conf_req; GsmL1_LogChParam_t *lch_par; @@ -1179,7 +1185,7 @@ static int mph_send_config_logchpar(struct gsm_lchan *lchan, struct sapi_cmd *cm /* update multi-rate config */ conf_req = prim_init(msgb_l1prim(msg), GsmL1_PrimId_MphConfigReq, fl1h); conf_req->cfgParamId = GsmL1_ConfigParamId_SetLogChParams; - conf_req->cfgParams.setLogChParams.sapi = lchan_to_GsmL1_Sapi_t(lchan); + conf_req->cfgParams.setLogChParams.sapi = cmd->sapi; conf_req->cfgParams.setLogChParams.u8Tn = lchan->ts->nr; conf_req->cfgParams.setLogChParams.subCh = lchan_to_GsmL1_SubCh_t(lchan); conf_req->cfgParams.setLogChParams.dir = cmd->dir; @@ -1188,6 +1194,10 @@ static int mph_send_config_logchpar(struct gsm_lchan *lchan, struct sapi_cmd *cm lch_par = &conf_req->cfgParams.setLogChParams.logChParams; lchan2lch_par(lch_par, lchan); + /* Update the MS Power Level */ + if (cmd->sapi == GsmL1_Sapi_Sacch && trx_ms_pwr_ctrl_is_osmo(trx)) + lch_par->sacch.u8MsPowerLevel = lchan->ms_power_ctrl.current; + /* FIXME: update encryption */ LOGP(DL1C, LOGL_INFO, "%s MPH-CONFIG.req (%s) ", @@ -1205,19 +1215,19 @@ static int mph_send_config_logchpar(struct gsm_lchan *lchan, struct sapi_cmd *cm return l1if_gsm_req_compl(fl1h, msg, chmod_modif_compl_cb, NULL); } -static void enqueue_sapi_logchpar_cmd(struct gsm_lchan *lchan, int dir) +static void enqueue_sapi_logchpar_cmd(struct gsm_lchan *lchan, int dir, GsmL1_Sapi_t sapi) { struct sapi_cmd *cmd = talloc_zero(lchan->ts->trx, struct sapi_cmd); cmd->dir = dir; + cmd->sapi = sapi; cmd->type = SAPI_CMD_CONFIG_LOGCH_PARAM; queue_sapi_command(lchan, cmd); } static int tx_confreq_logchpar(struct gsm_lchan *lchan, uint8_t direction) { - enqueue_sapi_logchpar_cmd(lchan, direction); - + enqueue_sapi_logchpar_cmd(lchan, direction, lchan_to_GsmL1_Sapi_t(lchan)); return 0; } @@ -1300,6 +1310,15 @@ int l1if_set_ciphering(struct femtol1_hdl *fl1h, return 0; } +int bts_model_adjst_ms_pwr(struct gsm_lchan *lchan) +{ + if (lchan->state != LCHAN_S_ACTIVE) + return -1; + + enqueue_sapi_logchpar_cmd(lchan, GsmL1_Dir_RxUplink, GsmL1_Sapi_Sacch); + return 0; +} + int bts_model_rsl_mode_modify(struct gsm_lchan *lchan) { if (lchan->state != LCHAN_S_ACTIVE) diff --git a/tests/sysmobts/sysmobts_test.c b/tests/sysmobts/sysmobts_test.c index acbc09c9..7db12485 100644 --- a/tests/sysmobts/sysmobts_test.c +++ b/tests/sysmobts/sysmobts_test.c @@ -179,11 +179,68 @@ static void test_sysmobts_cipher(void) OSMO_ASSERT(lchan.ciph_state == LCHAN_CIPH_TXRX_REQ); } +static void test_sysmobts_loop(void) +{ + struct gsm_bts bts; + struct gsm_bts_trx trx; + struct gsm_bts_trx_ts ts; + struct gsm_lchan *lchan; + int ret; + + memset(&bts, 0, sizeof(bts)); + memset(&trx, 0, sizeof(trx)); + memset(&ts, 0, sizeof(ts)); + + lchan = &ts.lchan[0]; + lchan->ts = &ts; + ts.trx = &trx; + trx.bts = &bts; + bts.band = GSM_BAND_1800; + trx.ms_power_control = 1; + + printf("Testing sysmobts power control\n"); + + /* Simply clamping */ + lchan->state = LCHAN_S_NONE; + lchan->ms_power_ctrl.current = ms_pwr_ctl_lvl(GSM_BAND_1800, 0); + OSMO_ASSERT(lchan->ms_power_ctrl.current == 15); + ret = l1if_ms_pwr_ctrl(lchan, -75, lchan->ms_power_ctrl.current, -60); + OSMO_ASSERT(ret == 0); + OSMO_ASSERT(lchan->ms_power_ctrl.current == 15); + + + /* + * Now 15 dB too little and we should power it up. Could be a + * power level of 7 or 8 for 15 dBm + */ + ret = l1if_ms_pwr_ctrl(lchan, -75, lchan->ms_power_ctrl.current, -90); + OSMO_ASSERT(ret == 1); + OSMO_ASSERT(lchan->ms_power_ctrl.current == 7); + + /* It should be clamped to level 0 and 30 dBm */ + ret = l1if_ms_pwr_ctrl(lchan, -75, lchan->ms_power_ctrl.current, -100); + OSMO_ASSERT(ret == 1); + OSMO_ASSERT(lchan->ms_power_ctrl.current == 0); + + /* Fix it and jump down */ + lchan->ms_power_ctrl.fixed = 1; + ret = l1if_ms_pwr_ctrl(lchan, -75, lchan->ms_power_ctrl.current, -60); + OSMO_ASSERT(ret == 0); + OSMO_ASSERT(lchan->ms_power_ctrl.current == 0); + + /* And leave it again */ + lchan->ms_power_ctrl.fixed = 0; + ret = l1if_ms_pwr_ctrl(lchan, -75, lchan->ms_power_ctrl.current, -40); + OSMO_ASSERT(ret == 1); + OSMO_ASSERT(lchan->ms_power_ctrl.current == 15); +} + int main(int argc, char **argv) { printf("Testing sysmobts routines\n"); test_sysmobts_auto_band(); test_sysmobts_cipher(); + test_sysmobts_loop(); return 0; } diff --git a/tests/sysmobts/sysmobts_test.ok b/tests/sysmobts/sysmobts_test.ok index 1f534172..07d79fd3 100644 --- a/tests/sysmobts/sysmobts_test.ok +++ b/tests/sysmobts/sysmobts_test.ok @@ -17,3 +17,4 @@ Checking PCS to PCS PCS to PCS band(1) arfcn(512) want(3) got(3) PCS to PCS band(8) arfcn(128) want(0) got(0) PCS to PCS band(2) arfcn(438) want(-1) got(-1) +Testing sysmobts power control |