aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/osmo-bts/bts.h3
-rw-r--r--include/osmo-bts/bts_model.h1
-rw-r--r--src/common/bts.c5
-rw-r--r--src/common/rsl.c10
-rw-r--r--src/common/vty.c14
-rw-r--r--src/osmo-bts-sysmo/l1_if.c84
-rw-r--r--src/osmo-bts-sysmo/l1_if.h2
-rw-r--r--src/osmo-bts-sysmo/oml.c39
-rw-r--r--tests/sysmobts/sysmobts_test.c57
-rw-r--r--tests/sysmobts/sysmobts_test.ok1
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 c7d36df8..1853a6e3 100644
--- a/src/common/bts.c
+++ b/src/common/bts.c
@@ -604,3 +604,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 11c7c715..2d93d1bc 100644
--- a/src/common/rsl.c
+++ b/src/common/rsl.c
@@ -712,6 +712,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 */
@@ -751,8 +756,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 6b57b0b6..a7884853 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\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;
+}
/* ======================================================================
@@ -644,6 +657,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 4b9ab3e8..f791a25c 100644
--- a/src/osmo-bts-sysmo/l1_if.c
+++ b/src/osmo-bts-sysmo/l1_if.c
@@ -49,6 +49,7 @@
#include <osmo-bts/measurement.h>
#include <osmo-bts/pcu_if.h>
#include <osmo-bts/handover.h>
+#include <osmo-bts/bts_model.h>
#include <sysmocom/femtobts/superfemto.h>
#include <sysmocom/femtobts/gsml1prim.h>
@@ -520,10 +521,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) {
@@ -768,6 +770,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)
@@ -1681,3 +1691,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 f2aabb9f..f7defa9c 100644
--- a/src/osmo-bts-sysmo/oml.c
+++ b/src/osmo-bts-sysmo/oml.c
@@ -328,7 +328,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, "
@@ -903,12 +904,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.
@@ -1150,7 +1155,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;
@@ -1160,7 +1166,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;
@@ -1169,6 +1175,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) ",
@@ -1186,19 +1196,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;
}
@@ -1281,6 +1291,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