osmo-bsc/src/osmo-bsc/lchan.c

151 lines
5.3 KiB
C

/* (C) 2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* (C) 2008-2018 by Harald Welte <laforge@gnumonks.org>
*
* 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 Affero 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/>.
*
*/
#include <stdint.h>
#include <stdbool.h>
#include <inttypes.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/bsc/lchan.h>
#include <osmocom/bsc/lchan_fsm.h>
#include <osmocom/bsc/gsm_data.h>
#include <osmocom/bsc/bts.h>
#include <osmocom/bsc/bts_trx.h>
#include <osmocom/bsc/abis_rsl.h>
void lchan_init(struct gsm_lchan *lchan, struct gsm_bts_trx_ts *ts, unsigned int nr)
{
lchan->ts = ts;
lchan->nr = nr;
lchan->type = GSM_LCHAN_NONE;
lchan_update_name(lchan);
}
void lchan_update_name(struct gsm_lchan *lchan)
{
struct gsm_bts_trx_ts *ts = lchan->ts;
if (lchan->name)
talloc_free(lchan->name);
lchan->name = talloc_asprintf(ts->trx, "(bts=%d,trx=%d,ts=%d,ss=%s%d)",
ts->trx->bts->nr, ts->trx->nr, ts->nr,
lchan->vamos.is_secondary ? "shadow" : "",
lchan->nr - (lchan->vamos.is_secondary ? ts->max_primary_lchans : 0));
}
/* If the lchan is currently active, return the duration since activation in milliseconds.
* Otherwise return 0. */
uint64_t gsm_lchan_active_duration_ms(const struct gsm_lchan *lchan)
{
struct timespec now, elapsed;
if (lchan->active_start.tv_sec == 0 && lchan->active_start.tv_nsec == 0)
return 0;
osmo_clock_gettime(CLOCK_MONOTONIC, &now);
timespecsub(&now, &lchan->active_start, &elapsed);
return elapsed.tv_sec * 1000 + elapsed.tv_nsec / 1000000;
}
/* For a VAMOS secondary shadow lchan, return its primary lchan. If the lchan is not a secondary lchan, return NULL. */
struct gsm_lchan *gsm_lchan_vamos_to_primary(const struct gsm_lchan *lchan_vamos)
{
struct gsm_lchan *lchan_primary;
if (!lchan_vamos || !lchan_vamos->vamos.is_secondary)
return NULL;
/* OsmoBSC currently does not support mixed TCH/F + TCH/H VAMOS multiplexes. Hence the primary <-> secondary
* relation is a simple index shift in the lchan array. If mixed multiplexes were allowed, a TCH/F primary might
* have two TCH/H VAMOS secondary lchans, etc. Fortunately, we don't need to care about that. */
lchan_primary = (struct gsm_lchan *)lchan_vamos - lchan_vamos->ts->max_primary_lchans;
if (!lchan_primary->fi)
return NULL;
return lchan_primary;
}
/* For a primary lchan, return its VAMOS secondary shadow lchan. If the lchan is not a primary lchan, return NULL. */
struct gsm_lchan *gsm_lchan_primary_to_vamos(const struct gsm_lchan *lchan_primary)
{
struct gsm_lchan *lchan_vamos;
if (!lchan_primary || lchan_primary->vamos.is_secondary)
return NULL;
/* OsmoBSC currently does not support mixed TCH/F + TCH/H VAMOS multiplexes. Hence the primary <-> secondary
* relation is a simple index shift in the lchan array. If mixed multiplexes were allowed, a TCH/F primary might
* have two TCH/H VAMOS secondary lchans, etc. Fortunately, we don't need to care about that. */
lchan_vamos = (struct gsm_lchan *)lchan_primary + lchan_primary->ts->max_primary_lchans;
if (!lchan_vamos->fi)
return NULL;
return lchan_vamos;
}
void lchan_update_ms_power_ctrl_level(struct gsm_lchan *lchan, int ms_power_dbm)
{
struct gsm_bts *bts = lchan->ts->trx->bts;
struct gsm_subscriber_connection *conn = lchan->conn;
int max_pwr_dbm_pwclass, new_pwr;
bool send_pwr_ctrl_msg = false;
LOG_LCHAN(lchan, LOGL_DEBUG,
"MS Power level update requested: %d dBm\n", ms_power_dbm);
if (!conn)
goto ms_power_default;
if (conn->ms_power_class == 0)
goto ms_power_default;
if ((max_pwr_dbm_pwclass = (int)ms_class_gmsk_dbm(bts->band, conn->ms_power_class)) < 0) {
LOG_LCHAN(lchan, LOGL_INFO,
"Failed getting max ms power for power class %" PRIu8
" on band %s, providing default max ms power\n",
conn->ms_power_class, gsm_band_name(bts->band));
goto ms_power_default;
}
/* Current configured max pwr is above maximum one allowed on
current band + ms power class, so use that one. */
if (ms_power_dbm > max_pwr_dbm_pwclass)
ms_power_dbm = max_pwr_dbm_pwclass;
ms_power_default:
if ((new_pwr = ms_pwr_ctl_lvl(bts->band, ms_power_dbm)) < 0) {
LOG_LCHAN(lchan, LOGL_INFO,
"Failed getting max ms power level %d on band %s,"
" providing default max ms power\n",
ms_power_dbm, gsm_band_name(bts->band));
return;
}
LOG_LCHAN(lchan, LOGL_DEBUG,
"MS Power level update (power class %" PRIu8 "): %" PRIu8 " -> %d\n",
conn ? conn->ms_power_class : 0, lchan->ms_power, new_pwr);
/* If chan was already activated and max ms_power changes (due to power
classmark received), send an MS Power Control message */
if (lchan->activate.activ_ack && new_pwr != lchan->ms_power)
send_pwr_ctrl_msg = true;
lchan->ms_power = new_pwr;
if (send_pwr_ctrl_msg)
rsl_chan_ms_power_ctrl(lchan);
}