aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/Makefile.am33
-rw-r--r--src/common/abis.c382
-rw-r--r--src/common/bts.c79
-rw-r--r--src/common/bts_shutdown_fsm.c50
-rw-r--r--src/common/bts_trx.c52
-rw-r--r--src/common/cbch.c28
-rw-r--r--src/common/gsm_data.c263
-rw-r--r--src/common/handover.c6
-rw-r--r--src/common/l1sap.c431
-rw-r--r--src/common/lchan.c491
-rw-r--r--src/common/load_indication.c10
-rw-r--r--src/common/main.c28
-rw-r--r--src/common/measurement.c267
-rw-r--r--src/common/nm_bb_transc_fsm.c75
-rw-r--r--src/common/nm_bts_fsm.c77
-rw-r--r--src/common/nm_bts_sm_fsm.c54
-rw-r--r--src/common/nm_channel_fsm.c57
-rw-r--r--src/common/nm_common_fsm.c4
-rw-r--r--src/common/nm_radio_carrier_fsm.c63
-rw-r--r--src/common/oml.c26
-rw-r--r--src/common/pcu_sock.c24
-rw-r--r--src/common/power_control.c499
-rw-r--r--src/common/probes.d2
-rw-r--r--src/common/rsl.c455
-rw-r--r--src/common/scheduler.c103
-rw-r--r--src/common/sysinfo.c16
-rw-r--r--src/common/ta_control.c88
-rw-r--r--src/common/tx_power.c8
-rw-r--r--src/common/vty.c161
-rw-r--r--src/osmo-bts-lc15/l1_if.c7
-rw-r--r--src/osmo-bts-lc15/lc15bts_vty.c2
-rw-r--r--src/osmo-bts-lc15/oml.c51
-rw-r--r--src/osmo-bts-oc2g/l1_if.c6
-rw-r--r--src/osmo-bts-oc2g/oc2gbts_vty.c2
-rw-r--r--src/osmo-bts-oc2g/oml.c51
-rw-r--r--src/osmo-bts-octphy/l1_if.c6
-rw-r--r--src/osmo-bts-octphy/l1_oml.c38
-rw-r--r--src/osmo-bts-omldummy/bts_model.c30
-rw-r--r--src/osmo-bts-omldummy/main.c12
-rw-r--r--src/osmo-bts-sysmo/oml.c47
-rw-r--r--src/osmo-bts-sysmo/sysmobts_vty.c2
-rw-r--r--src/osmo-bts-trx/Makefile.am13
-rw-r--r--src/osmo-bts-trx/l1_if.c96
-rw-r--r--src/osmo-bts-trx/main.c2
-rw-r--r--src/osmo-bts-trx/probes.d7
-rw-r--r--src/osmo-bts-trx/sched_lchan_pdtch.c29
-rw-r--r--src/osmo-bts-trx/sched_lchan_tchf.c52
-rw-r--r--src/osmo-bts-trx/sched_lchan_tchh.c44
-rw-r--r--src/osmo-bts-trx/sched_lchan_xcch.c4
-rw-r--r--src/osmo-bts-trx/sched_utils.h76
-rw-r--r--src/osmo-bts-trx/scheduler_trx.c118
-rw-r--r--src/osmo-bts-trx/trx_if.c59
-rw-r--r--src/osmo-bts-trx/trx_provision_fsm.c184
-rw-r--r--src/osmo-bts-trx/trx_vty.c41
-rw-r--r--src/osmo-bts-virtual/bts_model.c30
-rw-r--r--src/osmo-bts-virtual/l1_if.c10
56 files changed, 3360 insertions, 1491 deletions
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index 72438c67..35df73e6 100644
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -1,6 +1,21 @@
-AM_CPPFLAGS = $(all_includes) -I$(top_srcdir)/include
-AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOTRAU_CFLAGS) $(LIBOSMOCODEC_CFLAGS)
-LDADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOTRAU_LIBS) $(LIBOSMOCODEC_LIBS)
+AM_CPPFLAGS = \
+ $(all_includes) \
+ -I$(top_srcdir)/include \
+ -I$(top_builddir)/include \
+ $(NULL)
+
+AM_CFLAGS = \
+ -Wall \
+ $(LIBOSMOCORE_CFLAGS) \
+ $(LIBOSMOTRAU_CFLAGS) \
+ $(LIBOSMOCODEC_CFLAGS) \
+ $(NULL)
+
+LDADD = \
+ $(LIBOSMOCORE_LIBS) \
+ $(LIBOSMOTRAU_LIBS) \
+ $(LIBOSMOCODEC_LIBS) \
+ $(NULL)
if ENABLE_LC15BTS
AM_CFLAGS += -DENABLE_LC15BTS
@@ -44,6 +59,18 @@ libbts_a_SOURCES = \
nm_bb_transc_fsm.c \
nm_channel_fsm.c \
nm_radio_carrier_fsm.c \
+ probes.d \
$(NULL)
libl1sched_a_SOURCES = scheduler.c
+
+if ENABLE_SYSTEMTAP
+probes.h: probes.d
+ $(DTRACE) -C -h -s $< -o $@
+
+probes.lo: probes.d
+ $(LIBTOOL) --mode=compile $(AM_V_lt) --tag=CC env CFLAGS="$(CFLAGS)" $(DTRACE) -C -G -s $< -o $@
+
+BUILT_SOURCES = probes.h probes.lo
+libbts_la_LDADD = probes.lo
+endif
diff --git a/src/common/abis.c b/src/common/abis.c
index abef8264..4afe81af 100644
--- a/src/common/abis.c
+++ b/src/common/abis.c
@@ -38,11 +38,13 @@
#include <osmocom/core/msgb.h>
#include <osmocom/core/signal.h>
#include <osmocom/core/macaddr.h>
+#include <osmocom/core/fsm.h>
#include <osmocom/abis/abis.h>
#include <osmocom/abis/e1_input.h>
#include <osmocom/abis/ipaccess.h>
#include <osmocom/gsm/ipa.h>
+#include <osmo-bts/abis.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/bts.h>
@@ -51,35 +53,296 @@
#include <osmo-bts/abis_osmo.h>
#include <osmo-bts/bts_model.h>
#include <osmo-bts/bts_trx.h>
+#include <osmo-bts/bts_shutdown_fsm.h>
static struct gsm_bts *g_bts;
-int abis_oml_sendmsg(struct msgb *msg)
+static struct e1inp_line_ops line_ops;
+
+static struct ipaccess_unit bts_dev_info;
+
+#define S(x) (1 << (x))
+#define OML_RETRY_TIMER 5
+
+enum abis_link_fsm_state {
+ ABIS_LINK_ST_WAIT_RECONNECT, /* OML link has not yet been established */
+ ABIS_LINK_ST_CONNECTING, /* OML link in process of been established */
+ ABIS_LINK_ST_CONNECTED, /* OML link is established, RSL links may be established or not */
+ ABIS_LINK_ST_FAILED, /* There used to be an active OML connection but it became broken */
+};
+
+static const struct value_string abis_link_fsm_event_names[] = {
+ { ABIS_LINK_EV_SIGN_LINK_OML_UP, "SIGN_LINK_OML_UP", },
+ { ABIS_LINK_EV_SIGN_LINK_DOWN, "SIGN_LINK_DOWN" },
+ { ABIS_LINK_EV_VTY_RM_ADDR, "VTY_RM_ADDR" },
+ {}
+};
+
+struct abis_link_fsm_priv {
+ struct bsc_oml_host *current_bsc;
+ struct gsm_bts *bts;
+ char *model_name;
+ bool reconnect_to_current_bsc;
+};
+
+static void reset_oml_link(struct gsm_bts *bts)
{
- struct gsm_bts *bts = msg->trx->bts;
+ if (bts->oml_link) {
+ struct timespec now;
- if (!bts->oml_link) {
- llist_add_tail(&msg->list, &bts->oml_queue);
+ e1inp_sign_link_destroy(bts->oml_link);
+
+ /* Log a special notice if the OML connection was dropped relatively quickly. */
+ if (bts->oml_conn_established_timestamp.tv_sec != 0 && clock_gettime(CLOCK_MONOTONIC, &now) == 0 &&
+ bts->oml_conn_established_timestamp.tv_sec + OSMO_BTS_OML_CONN_EARLY_DISCONNECT >= now.tv_sec) {
+ LOGP(DABIS, LOGL_FATAL, "OML link was closed early within %" PRIu64 " seconds. "
+ "If this situation persists, please check your BTS and BSC configuration files for errors. "
+ "A common error is a mismatch between unit_id configuration parameters of BTS and BSC.\n",
+ (uint64_t) (now.tv_sec - bts->oml_conn_established_timestamp.tv_sec));
+ }
+ bts->oml_link = NULL;
+ }
+ memset(&bts->oml_conn_established_timestamp, 0, sizeof(bts->oml_conn_established_timestamp));
+
+ /* Same for IPAC_PROTO_OSMO on the same ipa connection: */
+ if (bts->osmo_link) {
+ e1inp_sign_link_destroy(bts->osmo_link);
+ bts->osmo_link = NULL;
+ }
+
+}
+
+static int pick_next_bsc(struct osmo_fsm_inst *fi)
+{
+ struct abis_link_fsm_priv *priv = fi->priv;
+ struct gsm_bts *bts = priv->bts;
+ struct bsc_oml_host *last;
+
+ if (llist_empty(&bts->bsc_oml_hosts)) {
+ LOGPFSML(fi, LOGL_ERROR, "List of BSCs to connect to is empty!\n");
+ return -1;
+ }
+
+ /* Keep current pointer to priv->current_bsc: */
+ if (priv->reconnect_to_current_bsc) {
+ OSMO_ASSERT(priv->current_bsc);
+ priv->reconnect_to_current_bsc = false;
return 0;
- } else {
- /* osmo-bts uses msg->trx internally, but libosmo-abis uses
- * the signalling link at msg->dst */
- msg->dst = bts->oml_link;
- return abis_sendmsg(msg);
+ }
+
+ last = (struct bsc_oml_host *)llist_last_entry(&bts->bsc_oml_hosts, struct bsc_oml_host, list);
+
+ if (!priv->current_bsc || priv->current_bsc == last) /* Pick first one (wrap around): */
+ priv->current_bsc = (struct bsc_oml_host *)llist_first_entry(&bts->bsc_oml_hosts, struct bsc_oml_host, list);
+ else
+ priv->current_bsc = (struct bsc_oml_host *)llist_entry(priv->current_bsc->list.next, struct bsc_oml_host, list);
+
+ return 0;
+}
+
+static void abis_link_connecting_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct e1inp_line *line;
+ struct abis_link_fsm_priv *priv = fi->priv;
+ struct gsm_bts *bts = priv->bts;
+
+ if (bts_shutdown_in_progress(bts)) {
+ LOGPFSML(fi, LOGL_NOTICE, "BTS is shutting down, delaying A-bis connection establishment to BSC\n");
+ osmo_fsm_inst_state_chg(fi, ABIS_LINK_ST_WAIT_RECONNECT, OML_RETRY_TIMER, 0);
+ return;
+ }
+
+ if (pick_next_bsc(fi) < 0) {
+ LOGPFSML(fi, LOGL_FATAL, "No BSC available, A-bis connection establishment failed\n");
+ osmo_fsm_inst_state_chg(fi, ABIS_LINK_ST_FAILED, 0, 0);
+ return;
+ }
+
+ LOGP(DABIS, LOGL_NOTICE, "A-bis connection establishment to BSC (%s) in progress...\n", priv->current_bsc->addr);
+
+ /* patch in various data from VTY and other sources */
+ line_ops.cfg.ipa.addr = priv->current_bsc->addr;
+ osmo_get_macaddr(bts_dev_info.mac_addr, "eth0");
+ bts_dev_info.site_id = bts->ip_access.site_id;
+ bts_dev_info.bts_id = bts->ip_access.bts_id;
+ bts_dev_info.unit_name = priv->model_name;
+ if (bts->description)
+ bts_dev_info.unit_name = bts->description;
+ bts_dev_info.location2 = priv->model_name;
+
+ line = e1inp_line_find(0);
+ if (!line)
+ line = e1inp_line_create(0, "ipa");
+ if (!line) {
+ osmo_fsm_inst_state_chg(fi, ABIS_LINK_ST_FAILED, 0, 0);
+ return;
+ }
+ /* Line always comes already with a "ctor" reference, enough to keep it alive forever. */
+
+ e1inp_line_bind_ops(line, &line_ops);
+ /* This will open the OML connection now */
+ if (e1inp_line_update(line) < 0) {
+ osmo_fsm_inst_state_chg(fi, ABIS_LINK_ST_FAILED, 0, 0);
+ return;
+ }
+
+ /* The TCP connection to the BSC is now in progress.
+ * Wait for OML Link UP to transition to CONNECTED. */
+}
+
+static void abis_link_connecting(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct abis_link_fsm_priv *priv = fi->priv;
+ struct gsm_bts *bts = priv->bts;
+
+ switch (event) {
+ case ABIS_LINK_EV_SIGN_LINK_OML_UP:
+ osmo_fsm_inst_state_chg(fi, ABIS_LINK_ST_CONNECTED, 0, 0);
+ break;
+ case ABIS_LINK_EV_SIGN_LINK_DOWN:
+ reset_oml_link(bts);
+ osmo_fsm_inst_state_chg(fi, ABIS_LINK_ST_WAIT_RECONNECT, OML_RETRY_TIMER, 0);
+ break;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
+static void abis_link_connected_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ bts_link_estab(g_bts);
+}
+
+static void abis_link_connected(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct abis_link_fsm_priv *priv = fi->priv;
+ struct gsm_bts *bts = priv->bts;
+ struct gsm_bts_trx *trx;
+ OSMO_ASSERT(event == ABIS_LINK_EV_SIGN_LINK_DOWN);
+
+ /* First remove the OML signalling link */
+ reset_oml_link(bts);
+
+ /* Then iterate over the RSL signalling links */
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ if (trx->rsl_link) {
+ e1inp_sign_link_destroy(trx->rsl_link);
+ trx->rsl_link = NULL;
+ if (trx == trx->bts->c0)
+ load_timer_stop(trx->bts);
+ }
+ /* Note: Here we could send NM_EV_RSL_DOWN to each
+ * trx->(bb_transc.)mo.fi, but we are starting shutdown of the
+ * entire BTS anyway through bts_model_abis_close(), so simply
+ * let bts_shutdown FSM take care of slowly powering down all
+ * the TRX. It would make sense to send NM_EV_RSL_DOWN only if a
+ * RSL link TRX!=C0 was going down, in order to selectively stop
+ * that TRX only. But libosmo-abis expects us to drop the entire
+ * line when something goes wrong... */
+ }
+ bts_model_abis_close(bts);
+
+ /* We want to try reconnecting to the current BSC at least once before switching to a new one: */
+ priv->reconnect_to_current_bsc = true;
+ osmo_fsm_inst_state_chg(fi, ABIS_LINK_ST_WAIT_RECONNECT, OML_RETRY_TIMER, 0);
+}
+
+static void abis_link_failed_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+ struct abis_link_fsm_priv *priv = fi->priv;
+ struct gsm_bts *bts = priv->bts;
+
+ /* None of the configured BSCs was reachable or there was an existing
+ * OML/RSL connection that broke. Initiate BTS process shut down now. */
+ bts_model_abis_close(bts);
+}
+
+static void abis_link_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct abis_link_fsm_priv *priv = fi->priv;
+ struct gsm_bts *bts = priv->bts;
+
+ OSMO_ASSERT(event == ABIS_LINK_EV_VTY_RM_ADDR);
+
+ if (priv->current_bsc == data) {
+ if (llist_count(&bts->bsc_oml_hosts) <= 1)
+ priv->current_bsc = NULL;
+ else
+ pick_next_bsc(fi);
}
}
-static void drain_oml_queue(struct gsm_bts *bts)
+int abis_link_fsm_timer_cb(struct osmo_fsm_inst *fi)
{
- struct msgb *msg, *msg2;
-
- llist_for_each_entry_safe(msg, msg2, &bts->oml_queue, list) {
- /* osmo-bts uses msg->trx internally, but libosmo-abis uses
- * the signalling link at msg->dst */
- llist_del(&msg->list);
- msg->dst = bts->oml_link;
- abis_sendmsg(msg);
+ switch (fi->state) {
+ case ABIS_LINK_ST_WAIT_RECONNECT:
+ osmo_fsm_inst_state_chg(fi, ABIS_LINK_ST_CONNECTING, 0, 0);
+ break;
+ default:
+ OSMO_ASSERT(0);
}
+ return 0;
+}
+
+
+static struct osmo_fsm_state abis_link_fsm_states[] = {
+ [ABIS_LINK_ST_WAIT_RECONNECT] = {
+ .name = "WAIT_RECONNECT",
+ .out_state_mask =
+ S(ABIS_LINK_ST_CONNECTING),
+ },
+ [ABIS_LINK_ST_CONNECTING] = {
+ .name = "CONNECTING",
+ .in_event_mask =
+ S(ABIS_LINK_EV_SIGN_LINK_OML_UP) |
+ S(ABIS_LINK_EV_SIGN_LINK_DOWN),
+ .out_state_mask =
+ S(ABIS_LINK_ST_WAIT_RECONNECT) |
+ S(ABIS_LINK_ST_CONNECTED) |
+ S(ABIS_LINK_ST_FAILED),
+ .onenter = abis_link_connecting_onenter,
+ .action = abis_link_connecting,
+ },
+ [ABIS_LINK_ST_CONNECTED] = {
+ .name = "CONNECTED",
+ .in_event_mask =
+ S(ABIS_LINK_EV_SIGN_LINK_DOWN),
+ .out_state_mask =
+ S(ABIS_LINK_ST_WAIT_RECONNECT),
+ .onenter = abis_link_connected_onenter,
+ .action = abis_link_connected,
+ },
+ [ABIS_LINK_ST_FAILED] = {
+ .name = "FAILED",
+ .onenter = abis_link_failed_onenter,
+ },
+};
+
+static struct osmo_fsm abis_link_fsm = {
+ .name = "abis_link",
+ .states = abis_link_fsm_states,
+ .num_states = ARRAY_SIZE(abis_link_fsm_states),
+ .log_subsys = DABIS,
+ .event_names = abis_link_fsm_event_names,
+ .allstate_action = abis_link_allstate,
+ .allstate_event_mask = S(ABIS_LINK_EV_VTY_RM_ADDR),
+ .timer_cb = abis_link_fsm_timer_cb,
+};
+
+int abis_oml_sendmsg(struct msgb *msg)
+{
+ struct gsm_bts *bts = msg->trx->bts;
+
+ if (!bts->oml_link) {
+ LOGP(DABIS, LOGL_INFO, "Drop Tx OML msg, OML link is down\n");
+ msgb_free(msg);
+ return 0;
+ }
+
+ /* osmo-bts uses msg->trx internally, but libosmo-abis uses
+ * the signalling link at msg->dst */
+ msg->dst = bts->oml_link;
+ return abis_sendmsg(msg);
}
int abis_bts_rsl_sendmsg(struct msgb *msg)
@@ -116,8 +379,7 @@ static struct e1inp_sign_link *sign_link_up(void *unit, struct e1inp_line *line,
sizeof(g_bts->oml_conn_established_timestamp));
g_bts->osmo_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OSMO,
g_bts->c0, IPAC_PROTO_OSMO, 0);
- drain_oml_queue(g_bts);
- bts_link_estab(g_bts);
+ osmo_fsm_inst_dispatch(g_bts->abis_link_fi, ABIS_LINK_EV_SIGN_LINK_OML_UP, NULL);
return g_bts->oml_link;
case E1INP_SIGN_RSL:
@@ -144,41 +406,8 @@ static struct e1inp_sign_link *sign_link_up(void *unit, struct e1inp_line *line,
static void sign_link_down(struct e1inp_line *line)
{
- struct gsm_bts_trx *trx;
LOGPIL(line, DABIS, LOGL_ERROR, "Signalling link down\n");
-
- /* First remove the OML signalling link */
- if (g_bts->oml_link) {
- struct timespec now;
-
- e1inp_sign_link_destroy(g_bts->oml_link);
-
- /* Log a special notice if the OML connection was dropped relatively quickly. */
- if (g_bts->oml_conn_established_timestamp.tv_sec != 0 && clock_gettime(CLOCK_MONOTONIC, &now) == 0 &&
- g_bts->oml_conn_established_timestamp.tv_sec + OSMO_BTS_OML_CONN_EARLY_DISCONNECT >= now.tv_sec) {
- LOGP(DABIS, LOGL_FATAL, "OML link was closed early within %" PRIu64 " seconds. "
- "If this situation persists, please check your BTS and BSC configuration files for errors. "
- "A common error is a mismatch between unit_id configuration parameters of BTS and BSC.\n",
- (uint64_t)(now.tv_sec - g_bts->oml_conn_established_timestamp.tv_sec));
- }
- g_bts->oml_link = NULL;
- }
- memset(&g_bts->oml_conn_established_timestamp, 0, sizeof(g_bts->oml_conn_established_timestamp));
-
- if (g_bts->osmo_link) {
- e1inp_sign_link_destroy(g_bts->osmo_link);
- g_bts->osmo_link = NULL;
- }
-
- /* Then iterate over the RSL signalling links */
- llist_for_each_entry(trx, &g_bts->trx_list, list) {
- if (trx->rsl_link) {
- e1inp_sign_link_destroy(trx->rsl_link);
- trx->rsl_link = NULL;
- }
- }
-
- bts_model_abis_close(g_bts);
+ osmo_fsm_inst_dispatch(g_bts->abis_link_fi, ABIS_LINK_EV_SIGN_LINK_DOWN, NULL);
}
@@ -277,33 +506,30 @@ void abis_init(struct gsm_bts *bts)
osmo_signal_register_handler(SS_L_INPUT, &inp_s_cbfn, bts);
}
-struct e1inp_line *abis_open(struct gsm_bts *bts, char *dst_host,
- char *model_name)
+int abis_open(struct gsm_bts *bts, char *model_name)
{
- struct e1inp_line *line;
+ struct abis_link_fsm_priv *abis_link_fsm_priv;
- /* patch in various data from VTY and other sources */
- line_ops.cfg.ipa.addr = dst_host;
- osmo_get_macaddr(bts_dev_info.mac_addr, "eth0");
- bts_dev_info.site_id = bts->ip_access.site_id;
- bts_dev_info.bts_id = bts->ip_access.bts_id;
- bts_dev_info.unit_name = model_name;
- if (bts->description)
- bts_dev_info.unit_name = bts->description;
- bts_dev_info.location2 = model_name;
+ if (llist_empty(&bts->bsc_oml_hosts)) {
+ LOGP(DABIS, LOGL_FATAL, "No BSC configured, cannot start BTS without knowing BSC OML IP\n");
+ return -EINVAL;
+ }
- line = e1inp_line_find(0);
- if (line) {
- e1inp_line_get2(line, __FILE__); /* We want a new reference for returned line */
- } else
- line = e1inp_line_create(0, "ipa"); /* already comes with a reference */
- if (!line)
- return NULL;
- e1inp_line_bind_ops(line, &line_ops);
+ bts->abis_link_fi = osmo_fsm_inst_alloc(&abis_link_fsm, bts, NULL, LOGL_DEBUG, "abis_link");
+ OSMO_ASSERT(bts->abis_link_fi);
- /* This will open the OML connection now */
- if (e1inp_line_update(line) < 0)
- return NULL;
+ abis_link_fsm_priv = talloc_zero(bts->abis_link_fi, struct abis_link_fsm_priv);
+ OSMO_ASSERT(abis_link_fsm_priv);
+ abis_link_fsm_priv->bts = bts;
+ abis_link_fsm_priv->model_name = model_name;
+ bts->abis_link_fi->priv = abis_link_fsm_priv;
+
+ osmo_fsm_inst_state_chg(bts->abis_link_fi, ABIS_LINK_ST_CONNECTING, 0, 0);
- return line;
+ return 0;
+}
+
+static __attribute__((constructor)) void abis_link_fsm_init(void)
+{
+ OSMO_ASSERT(osmo_fsm_register(&abis_link_fsm) == 0);
}
diff --git a/src/common/bts.c b/src/common/bts.c
index 27c7f74c..93fb4007 100644
--- a/src/common/bts.c
+++ b/src/common/bts.c
@@ -128,7 +128,7 @@ static const struct rate_ctr_group_desc cbch_ctrg_desc = {
cbch_ctr_desc
};
-static struct osmo_tdef bts_T_defs[] = {
+struct osmo_tdef bts_T_defs[] = {
/* T-1: FIXME: Ideally should be dynamically calculated per trx at
* shutdown start based on params below, and highest trx value taken:
* + VTY's power-ramp step-interval.
@@ -143,6 +143,11 @@ static struct osmo_tdef bts_T_defs[] = {
{}
};
+struct osmo_tdef abis_T_defs[] = {
+ { .T=-15, .default_val=0, .unit=OSMO_TDEF_MS, .desc="Time to wait between Channel Activation and dispatching a cached early Immediate Assignment" },
+ {}
+};
+
static const uint8_t bts_nse_timer_default[] = { 3, 3, 3, 3, 30, 3, 10 };
static const uint8_t bts_cell_timer_default[] =
{ 3, 3, 3, 3, 3, 10, 3, 10, 3, 10, 3 };
@@ -214,6 +219,10 @@ static int gsm_bts_talloc_destructor(struct gsm_bts *bts)
osmo_fsm_inst_free(bts->mo.fi);
bts->mo.fi = NULL;
}
+ if (bts->shutdown_fi) {
+ osmo_fsm_inst_free(bts->shutdown_fi);
+ bts->shutdown_fi = NULL;
+ }
return 0;
}
@@ -234,6 +243,7 @@ struct gsm_bts *gsm_bts_alloc(void *ctx, uint8_t bts_num)
bts->T_defs = bts_T_defs;
osmo_tdefs_reset(bts->T_defs);
+ osmo_tdefs_reset(abis_T_defs);
bts->shutdown_fi = osmo_fsm_inst_alloc(&bts_shutdown_fsm, bts, bts,
LOGL_INFO, NULL);
osmo_fsm_inst_update_id_f(bts->shutdown_fi, "bts%d", bts->nr);
@@ -334,12 +344,11 @@ int bts_init(struct gsm_bts *bts)
bts->rtp_priority = -1;
/* Default (fall-back) MS/BS Power control parameters */
- bts->bs_dpc_params = power_ctrl_params_def;
- bts->ms_dpc_params = power_ctrl_params_def;
+ power_ctrl_params_def_reset(&bts->bs_dpc_params, true);
+ power_ctrl_params_def_reset(&bts->ms_dpc_params, false);
/* configurable via OML */
bts->load.ccch.load_ind_period = 112;
- load_timer_start(bts);
bts->rtp_jitter_buf_ms = 100;
bts->max_ta = 63;
bts->ny1 = 4;
@@ -401,7 +410,7 @@ int bts_init(struct gsm_bts *bts)
bts->smscb_queue_tgt_len = 2;
bts->smscb_queue_hyst = 2;
- INIT_LLIST_HEAD(&bts->oml_queue);
+ INIT_LLIST_HEAD(&bts->bsc_oml_hosts);
/* register DTX DL FSM */
rc = osmo_fsm_register(&dtx_dl_amr_fsm);
@@ -449,66 +458,6 @@ int bts_link_estab(struct gsm_bts *bts)
return bts_model_oml_estab(bts);
}
-/* prepare the per-SAPI T200 arrays for a given lchan */
-static int t200_by_lchan(int *t200_ms_dcch, int *t200_ms_acch, struct gsm_lchan *lchan)
-{
- struct gsm_bts *bts = lchan->ts->trx->bts;
-
- /* we have to compensate for the "RTS advance" due to the asynchronous interface between
- * the BTS (LAPDm) and the PHY/L1 (OsmoTRX or DSP in case of osmo-bts-{sysmo,lc15,oc2g,octphy} */
- int32_t fn_advance = bts_get_avg_fn_advance(bts);
- int32_t fn_advance_us = fn_advance * 4615;
- int fn_advance_ms = fn_advance_us / 1000;
-
- t200_ms_acch[DL_SAPI0] = bts->t200_ms[T200_SACCH_SDCCH] + fn_advance_ms;
- t200_ms_acch[DL_SAPI3] = bts->t200_ms[T200_SACCH_SDCCH] + fn_advance_ms;
-
- if (lchan->repeated_acch_capability.dl_facch_all && (lchan->type == GSM_LCHAN_TCH_F || lchan->type == GSM_LCHAN_TCH_H)) {
- t200_ms_acch[DL_SAPI0] *= 2;
- t200_ms_acch[DL_SAPI3] *= 2;
- }
-
- switch (lchan->type) {
- case GSM_LCHAN_SDCCH:
- t200_ms_dcch[DL_SAPI0] = bts->t200_ms[T200_SDCCH] + fn_advance_ms;
- t200_ms_dcch[DL_SAPI3] = bts->t200_ms[T200_SDCCH_SAPI3] + fn_advance_ms;
- break;
- case GSM_LCHAN_TCH_F:
- t200_ms_dcch[DL_SAPI0] = bts->t200_ms[T200_FACCH_F] + fn_advance_ms;
- t200_ms_dcch[DL_SAPI3] = bts->t200_ms[T200_FACCH_F] + fn_advance_ms;
- break;
- case GSM_LCHAN_TCH_H:
- t200_ms_dcch[DL_SAPI0] = bts->t200_ms[T200_FACCH_H] + fn_advance_ms;
- t200_ms_dcch[DL_SAPI3] = bts->t200_ms[T200_FACCH_H] + fn_advance_ms;
- break;
- default:
- /* Channels such as CCCH don't use lapdm DL, and hence no T200 is needed */
- return -1;
- }
- return 0;
-}
-
-int lchan_init_lapdm(struct gsm_lchan *lchan)
-{
- struct lapdm_channel *lc = &lchan->lapdm_ch;
- int t200_ms_dcch[_NR_DL_SAPI], t200_ms_acch[_NR_DL_SAPI];
-
- if (t200_by_lchan(t200_ms_dcch, t200_ms_acch, lchan) == 0) {
- LOGPLCHAN(lchan, DLLAPD, LOGL_DEBUG,
- "Setting T200 D0=%u, D3=%u, S0=%u, S3=%u (all in ms)\n",
- t200_ms_dcch[DL_SAPI0], t200_ms_dcch[DL_SAPI3],
- t200_ms_acch[DL_SAPI0], t200_ms_acch[DL_SAPI3]);
- lapdm_channel_init3(lc, LAPDM_MODE_BTS, t200_ms_dcch, t200_ms_acch, lchan->type,
- gsm_lchan_name(lchan));
- lapdm_channel_set_flags(lc, LAPDM_ENT_F_POLLING_ONLY);
- lapdm_channel_set_l1(lc, NULL, lchan);
- }
- /* We still need to set Rx callback to receive RACH requests: */
- lapdm_channel_set_l3(lc, lapdm_rll_tx_cb, lchan);
-
- return 0;
-}
-
#define CCCH_RACH_RATIO_COMBINED256 (256*1/9)
#define CCCH_RACH_RATIO_SEPARATE256 (256*10/55)
diff --git a/src/common/bts_shutdown_fsm.c b/src/common/bts_shutdown_fsm.c
index 0ac30789..0c6d80c1 100644
--- a/src/common/bts_shutdown_fsm.c
+++ b/src/common/bts_shutdown_fsm.c
@@ -29,6 +29,7 @@
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/bts_model.h>
#include <osmo-bts/bts.h>
+#include <osmo-bts/nm_common_fsm.h>
#define X(s) (1 << (s))
@@ -58,6 +59,9 @@ static void st_none(struct osmo_fsm_inst *fi, uint32_t event, void *data)
unsigned int count;
switch(event) {
case BTS_SHUTDOWN_EV_START:
+ /* Firt announce to NM objects that we are starting a shutdown procedure: */
+ osmo_fsm_inst_dispatch(bts->site_mgr.mo.fi, NM_EV_SHUTDOWN_START, NULL);
+
count = count_trx_operational(bts);
if (count) {
bts_shutdown_fsm_state_chg(fi, BTS_SHUTDOWN_ST_WAIT_RAMP_DOWN_COMPL);
@@ -108,8 +112,15 @@ static void st_wait_ramp_down_compl(struct osmo_fsm_inst *fi, uint32_t event, vo
LOGPFSML(fi, LOGL_INFO, "%s Ramping down complete, %u TRX remaining\n",
gsm_trx_name(src_trx), remaining);
- if (remaining == 0)
+ if (remaining == 0) {
+ /* Make sure we end up any remaining ongoing power ramp
+ * down under target shutdown tx power level, then
+ * finally transit to next state:
+ */
+ llist_for_each_entry(trx, &bts->trx_list, list)
+ power_ramp_abort(trx);
bts_shutdown_fsm_state_chg(fi, BTS_SHUTDOWN_ST_WAIT_TRX_CLOSED);
+ }
break;
}
}
@@ -148,8 +159,15 @@ static void st_wait_trx_closed(struct osmo_fsm_inst *fi, uint32_t event, void *d
static void st_exit_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
- LOGPFSML(fi, LOGL_NOTICE, "Shutdown process completed successfuly, exiting process\n");
- exit(0);
+ struct gsm_bts *bts = (struct gsm_bts *)fi->priv;
+
+ osmo_fsm_inst_dispatch(bts->site_mgr.mo.fi, NM_EV_SHUTDOWN_FINISH, NULL);
+
+ if (bts->shutdown_fi_exit_proc) {
+ LOGPFSML(fi, LOGL_NOTICE, "Shutdown process completed successfully, exiting process\n");
+ exit(0);
+ }
+ bts_shutdown_fsm_state_chg(fi, BTS_SHUTDOWN_ST_NONE);
}
static struct osmo_fsm_state bts_shutdown_fsm_states[] = {
@@ -182,6 +200,8 @@ static struct osmo_fsm_state bts_shutdown_fsm_states[] = {
},
[BTS_SHUTDOWN_ST_EXIT] = {
.name = "EXIT",
+ .out_state_mask =
+ X(BTS_SHUTDOWN_ST_NONE),
.onenter = st_exit_on_enter,
}
};
@@ -189,7 +209,7 @@ static struct osmo_fsm_state bts_shutdown_fsm_states[] = {
const struct value_string bts_shutdown_fsm_event_names[] = {
OSMO_VALUE_STRING(BTS_SHUTDOWN_EV_START),
OSMO_VALUE_STRING(BTS_SHUTDOWN_EV_TRX_RAMP_COMPL),
- OSMO_VALUE_STRING(BTS_SHUTDOWN_ST_WAIT_TRX_CLOSED),
+ OSMO_VALUE_STRING(BTS_SHUTDOWN_EV_TRX_CLOSED),
{ 0, NULL }
};
@@ -224,18 +244,32 @@ static __attribute__((constructor)) void bts_shutdown_fsm_init(void)
OSMO_ASSERT(osmo_fsm_register(&bts_shutdown_fsm) == 0);
}
-void bts_shutdown(struct gsm_bts *bts, const char *reason)
+bool bts_shutdown_in_progress(const struct gsm_bts *bts)
+{
+ const struct osmo_fsm_inst *fi = bts->shutdown_fi;
+ return fi->state != BTS_SHUTDOWN_ST_NONE;
+}
+
+void bts_shutdown_ext(struct gsm_bts *bts, const char *reason, bool exit_proc)
{
struct osmo_fsm_inst *fi = bts->shutdown_fi;
- if (fi->state != BTS_SHUTDOWN_ST_NONE) {
+ if (bts_shutdown_in_progress(bts)) {
LOGPFSML(fi, LOGL_NOTICE, "BTS is already being shutdown.\n");
+ if (exit_proc)
+ bts->shutdown_fi_exit_proc = true;
return;
}
-
- LOGPFSML(fi, LOGL_NOTICE, "Shutting down BTS, reason: %s\n", reason);
+ bts->shutdown_fi_exit_proc = exit_proc;
+ LOGPFSML(fi, LOGL_NOTICE, "Shutting down BTS, exit %u, reason: %s\n",
+ exit_proc, reason);
osmo_fsm_inst_dispatch(fi, BTS_SHUTDOWN_EV_START, NULL);
}
+void bts_shutdown(struct gsm_bts *bts, const char *reason)
+{
+ bts_shutdown_ext(bts, reason, true);
+}
+
void bts_model_trx_close_cb(struct gsm_bts_trx *trx, int rc)
{
struct osmo_fsm_inst *fi = trx->bts->shutdown_fi;
diff --git a/src/common/bts_trx.c b/src/common/bts_trx.c
index a5d7ed3d..ff5c6181 100644
--- a/src/common/bts_trx.c
+++ b/src/common/bts_trx.c
@@ -59,14 +59,7 @@ static void gsm_bts_trx_ts_init_lchan(struct gsm_bts_trx_ts *ts)
for (ln = 0; ln < ARRAY_SIZE(ts->lchan); ln++) {
struct gsm_lchan *lchan = &ts->lchan[ln];
-
- lchan->ts = ts;
- lchan->nr = ln;
- lchan->type = GSM_LCHAN_NONE;
- gsm_lchan_name_update(lchan);
-
- INIT_LLIST_HEAD(&lchan->sapi_cmds);
- INIT_LLIST_HEAD(&lchan->dl_tch_queue);
+ gsm_lchan_init(lchan, ts, ln);
}
}
@@ -119,6 +112,27 @@ void gsm_bts_trx_init_shadow_ts(struct gsm_bts_trx *trx)
}
}
+void gsm_bts_trx_free_shadow_ts(struct gsm_bts_trx *trx)
+{
+ unsigned int tn;
+ unsigned int ln;
+
+ for (tn = 0; tn < ARRAY_SIZE(trx->ts); tn++) {
+ struct gsm_bts_trx_ts *shadow_ts = trx->ts[tn].vamos.peer;
+ if (!shadow_ts)
+ continue;
+
+ /* free lchan related mem allocated on the trx object: */
+ for (ln = 0; ln < ARRAY_SIZE(shadow_ts->lchan); ln++) {
+ struct gsm_lchan *lchan = &shadow_ts->lchan[ln];
+ TALLOC_FREE(lchan->name);
+ }
+
+ talloc_free(shadow_ts);
+ trx->ts[tn].vamos.peer = NULL;
+ }
+}
+
struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts)
{
struct gsm_bts_trx *trx = talloc_zero(bts, struct gsm_bts_trx);
@@ -206,25 +220,19 @@ const char *gsm_trx_unit_id(struct gsm_bts_trx *trx)
/* RSL link is established, send status report */
int trx_link_estab(struct gsm_bts_trx *trx)
{
- struct e1inp_sign_link *link = trx->rsl_link;
int rc;
- LOGPTRX(trx, DSUM, LOGL_INFO, "RSL link %s\n",
- link ? "up" : "down");
+ LOGPTRX(trx, DSUM, LOGL_INFO, "RSL link up\n");
- osmo_fsm_inst_dispatch(trx->mo.fi, link ? NM_EV_RSL_UP : NM_EV_RSL_DOWN, NULL);
- osmo_fsm_inst_dispatch(trx->bb_transc.mo.fi, link ? NM_EV_RSL_UP : NM_EV_RSL_DOWN, NULL);
+ osmo_fsm_inst_dispatch(trx->mo.fi, NM_EV_RSL_UP, NULL);
+ osmo_fsm_inst_dispatch(trx->bb_transc.mo.fi, NM_EV_RSL_UP, NULL);
- if (link)
- rc = rsl_tx_rf_res(trx);
- else
- rc = bts_model_trx_deact_rf(trx);
- if (rc < 0) {
+ if ((rc = rsl_tx_rf_res(trx)) < 0)
oml_tx_failure_event_rep(&trx->bb_transc.mo, NM_SEVER_MAJOR, OSMO_EVT_MAJ_RSL_FAIL,
- link ?
- "Failed to establish RSL link (%d)" :
- "Failed to deactivate RF (%d)", rc);
- }
+ "Failed to establish RSL link (%d)", rc);
+
+ if (trx == trx->bts->c0)
+ load_timer_start(trx->bts);
return 0;
}
diff --git a/src/common/cbch.c b/src/common/cbch.c
index ebea60be..f1f8b6a6 100644
--- a/src/common/cbch.c
+++ b/src/common/cbch.c
@@ -233,10 +233,10 @@ int bts_process_smscb_cmd(struct gsm_bts *bts, struct rsl_ie_cb_cmd_type cmd_typ
rate_ctr_inc2(bts_ss->ctrs, CBCH_CTR_RCVD_QUEUED);
break;
case RSL_CB_CMD_TYPE_DEFAULT:
- /* old default msg will be free'd in get_smscb_block() if it is currently in transit
- * and we set a new default_msg here */
+ /* clear the cur_msg pointer if it is the old default message */
if (bts_ss->cur_msg && bts_ss->cur_msg == bts_ss->default_msg)
- talloc_free(bts_ss->cur_msg);
+ bts_ss->cur_msg = NULL;
+ talloc_free(bts_ss->default_msg);
if (cmd_type.def_bcast == RSL_CB_CMD_DEFBCAST_NORMAL)
/* def_bcast == 0: normal message */
bts_ss->default_msg = scm;
@@ -322,3 +322,25 @@ int bts_cbch_get(struct gsm_bts *bts, uint8_t *outbuf, struct gsm_time *g_time)
return rc;
}
+
+static void bts_smscb_state_reset(struct bts_smscb_state *bts_ss)
+{
+ struct smscb_msg *scm, *tmp;
+ llist_for_each_entry_safe(scm, tmp, &bts_ss->queue, list) {
+ llist_del(&scm->list);
+ talloc_free(scm);
+ }
+ bts_ss->queue_len = 0;
+ rate_ctr_group_reset(bts_ss->ctrs);
+ /* avoid double-free of default_msg in case cur_msg == default_msg */
+ if (bts_ss->cur_msg && bts_ss->cur_msg != bts_ss->default_msg)
+ talloc_free(bts_ss->cur_msg);
+ bts_ss->cur_msg = NULL;
+ TALLOC_FREE(bts_ss->default_msg);
+}
+
+void bts_cbch_reset(struct gsm_bts *bts)
+{
+ bts_smscb_state_reset(&bts->smscb_basic);
+ bts_smscb_state_reset(&bts->smscb_extended);
+}
diff --git a/src/common/gsm_data.c b/src/common/gsm_data.c
index 36a57115..e5dbf105 100644
--- a/src/common/gsm_data.c
+++ b/src/common/gsm_data.c
@@ -40,6 +40,12 @@
#include <osmo-bts/bts_trx.h>
#include <osmo-bts/logging.h>
+struct osmo_tdef_group bts_tdef_groups[] = {
+ { .name = "bts", .tdefs = bts_T_defs, .desc = "BTS process timers" },
+ { .name = "abis", .tdefs = abis_T_defs, .desc = "Abis (RSL) related timers" },
+ {}
+};
+
const struct value_string gsm_pchant_names[13] = {
{ GSM_PCHAN_NONE, "NONE" },
{ GSM_PCHAN_CCCH, "CCCH" },
@@ -84,22 +90,6 @@ const char *gsm_lchant_name(enum gsm_chan_t c)
return get_value_string(gsm_chan_t_names, c);
}
-static const struct value_string lchan_s_names[] = {
- { LCHAN_S_NONE, "NONE" },
- { LCHAN_S_ACT_REQ, "ACTIVATION REQUESTED" },
- { LCHAN_S_ACTIVE, "ACTIVE" },
- { LCHAN_S_INACTIVE, "INACTIVE" },
- { LCHAN_S_REL_REQ, "RELEASE REQUESTED" },
- { LCHAN_S_REL_ERR, "RELEASE DUE ERROR" },
- { LCHAN_S_BROKEN, "BROKEN UNUSABLE" },
- { 0, NULL }
-};
-
-const char *gsm_lchans_name(enum gsm_lchan_state s)
-{
- return get_value_string(lchan_s_names, s);
-}
-
static char ts2str[255];
char *gsm_ts_name(const struct gsm_bts_trx_ts *ts)
@@ -157,172 +147,6 @@ char *gsm_ts_and_pchan_name(const struct gsm_bts_trx_ts *ts)
return ts2str;
}
-void gsm_lchan_name_update(struct gsm_lchan *lchan)
-{
- const struct gsm_bts_trx_ts *ts = lchan->ts;
- const struct gsm_bts_trx *trx = ts->trx;
- char *name;
-
- name = talloc_asprintf(trx, "(" GSM_TS_NAME_FMT ",ss=%u)",
- GSM_TS_NAME_ARGS(ts), lchan->nr);
- if (lchan->name != NULL)
- talloc_free(lchan->name);
- lchan->name = name;
-}
-
-/* See Table 10.5.25 of GSM04.08 */
-static uint8_t gsm_pchan2chan_nr(enum gsm_phys_chan_config pchan,
- uint8_t ts_nr, uint8_t lchan_nr)
-{
- uint8_t cbits, chan_nr;
-
- OSMO_ASSERT(pchan != GSM_PCHAN_OSMO_DYN);
- OSMO_ASSERT(pchan != GSM_PCHAN_TCH_F_PDCH);
-
- switch (pchan) {
- case GSM_PCHAN_TCH_F:
- OSMO_ASSERT(lchan_nr == 0);
- cbits = ABIS_RSL_CHAN_NR_CBITS_Bm_ACCHs;
- break;
- case GSM_PCHAN_PDCH:
- OSMO_ASSERT(lchan_nr == 0);
- cbits = ABIS_RSL_CHAN_NR_CBITS_OSMO_PDCH;
- break;
- case GSM_PCHAN_TCH_H:
- OSMO_ASSERT(lchan_nr < 2);
- cbits = ABIS_RSL_CHAN_NR_CBITS_Lm_ACCHs(lchan_nr);
- break;
- case GSM_PCHAN_CCCH_SDCCH4:
- case GSM_PCHAN_CCCH_SDCCH4_CBCH:
- /*
- * As a special hack for BCCH, lchan_nr == 4 may be passed
- * here. This should never be sent in an RSL message.
- * See osmo-bts-xxx/oml.c:opstart_compl().
- */
- if (lchan_nr == CCCH_LCHAN)
- cbits = ABIS_RSL_CHAN_NR_CBITS_BCCH;
- else {
- OSMO_ASSERT(lchan_nr < 4);
- cbits = ABIS_RSL_CHAN_NR_CBITS_SDCCH4_ACCH(lchan_nr);
- }
- break;
- case GSM_PCHAN_SDCCH8_SACCH8C:
- case GSM_PCHAN_SDCCH8_SACCH8C_CBCH:
- OSMO_ASSERT(lchan_nr < 8);
- cbits = ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(lchan_nr);
- break;
- case GSM_PCHAN_CCCH:
- cbits = ABIS_RSL_CHAN_NR_CBITS_BCCH;
- break;
- case GSM_PCHAN_NONE:
- LOGP(DRSL, LOGL_ERROR, "Physical channel %s not expected!\n",
- gsm_pchan_name(pchan));
- cbits = 0x00;
- break;
- default:
- LOGP(DRSL, LOGL_ERROR, "Physical channel %s (0x%02x) not expected!\n",
- gsm_pchan_name(pchan), (int)pchan);
- /* OSMO_ASSERT(lchan_nr == 0);
- * FIXME: On octphy and litecell, we hit above assertion (see
- * Max's comment at https://gerrit.osmocom.org/589 ); disabled
- * for BTS until this is clarified; remove the #ifdef when it
- * is fixed. Tracked in OS#2906.
- */
-#pragma message "fix caller that passes lchan_nr != 0"
- cbits = 0x10;
- break;
- }
-
- chan_nr = (cbits << 3) | (ts_nr & 0x7);
-
- return chan_nr;
-}
-
-uint8_t gsm_lchan2chan_nr(const struct gsm_lchan *lchan)
-{
- uint8_t chan_nr;
-
- switch (lchan->ts->pchan) {
- case GSM_PCHAN_OSMO_DYN:
- /* Return chan_nr reflecting the current TS pchan, either a standard TCH kind, or the
- * nonstandard value reflecting PDCH for Osmocom style dyn TS. */
- chan_nr = gsm_lchan_as_pchan2chan_nr(lchan, lchan->ts->dyn.pchan_is);
- break;
- case GSM_PCHAN_TCH_F_PDCH:
- /* For ip.access style dyn TS, we always want to use the chan_nr as if it was TCH/F.
- * We're using custom PDCH ACT and DEACT messages that use the usual chan_nr values. */
- chan_nr = gsm_lchan_as_pchan2chan_nr(lchan, GSM_PCHAN_TCH_F);
- break;
- default:
- chan_nr = gsm_pchan2chan_nr(lchan->ts->pchan, lchan->ts->nr, lchan->nr);
- break;
- }
-
- /* VAMOS: if this lchan belongs to a shadow timeslot, we must reflect
- * this in the channel number. Convert it to Osmocom specific value. */
- if (lchan->ts->vamos.is_shadow)
- chan_nr |= RSL_CHAN_OSMO_VAMOS_MASK;
-
- return chan_nr;
-}
-
-uint8_t gsm_lchan_as_pchan2chan_nr(const struct gsm_lchan *lchan,
- enum gsm_phys_chan_config as_pchan)
-{
- if (lchan->ts->pchan == GSM_PCHAN_OSMO_DYN
- && as_pchan == GSM_PCHAN_PDCH)
- return RSL_CHAN_OSMO_PDCH | (lchan->ts->nr & ~RSL_CHAN_NR_MASK);
- return gsm_pchan2chan_nr(as_pchan, lchan->ts->nr, lchan->nr);
-}
-
-/* Called by the model specific code every 104 TDMA frames (SACCH period) */
-void gsm_lchan_interf_meas_push(struct gsm_lchan *lchan, int dbm)
-{
- const uint8_t meas_num = lchan->meas.interf_meas_num;
-
- if (meas_num >= ARRAY_SIZE(lchan->meas.interf_meas_dbm)) {
- LOGPLCHAN(lchan, DL1C, LOGL_ERROR, "Not enough room "
- "to store interference report (%ddBm)\n", dbm);
- return;
- }
-
- lchan->meas.interf_meas_dbm[meas_num] = dbm;
- lchan->meas.interf_meas_num++;
-}
-
-/* Called by the higher layers every Intave * 104 TDMA frames */
-int gsm_lchan_interf_meas_calc_band(struct gsm_lchan *lchan)
-{
- const uint8_t meas_num = lchan->meas.interf_meas_num;
- const struct gsm_bts *bts = lchan->ts->trx->bts;
- int b, meas_avg, meas_sum = 0;
-
- /* There must be at least one sample */
- if (meas_num == 0)
- return -EAGAIN;
-
- /* Calculate the sum of all collected samples (in -x dBm) */
- while (lchan->meas.interf_meas_num) {
- uint8_t i = --lchan->meas.interf_meas_num;
- meas_sum += lchan->meas.interf_meas_dbm[i];
- }
-
- /* Calculate the average of all collected samples */
- meas_avg = meas_sum / (int) meas_num;
-
- /* Determine the band using interference boundaries from BSC */
- for (b = 0; b < ARRAY_SIZE(bts->interference.boundary); b++) {
- if (meas_avg >= bts->interference.boundary[b])
- break; /* Current 'b' is the band value */
- }
-
- LOGPLCHAN(lchan, DL1C, LOGL_DEBUG,
- "Interference AVG: %ddBm (band %d, samples %u)\n",
- meas_avg, b, meas_num);
-
- return b;
-}
-
/* determine logical channel based on TRX and channel number IE */
struct gsm_lchan *rsl_lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr,
int *rc)
@@ -466,61 +290,32 @@ bool ts_is_tch(const struct gsm_bts_trx_ts *ts)
return pchan_is_tch(ts_pchan(ts));
}
-const struct value_string lchan_ciph_state_names[] = {
- { LCHAN_CIPH_NONE, "NONE" },
- { LCHAN_CIPH_RX_REQ, "RX_REQ" },
- { LCHAN_CIPH_RX_CONF, "RX_CONF" },
- { LCHAN_CIPH_RXTX_REQ, "RXTX_REQ" },
- { LCHAN_CIPH_RX_CONF_TX_REQ, "RX_CONF_TX_REQ" },
- { LCHAN_CIPH_RXTX_CONF, "RXTX_CONF" },
- { 0, NULL }
-};
-
-/* determine the ECU codec constant for the codec used by given lchan */
-int lchan2ecu_codec(const struct gsm_lchan *lchan)
+bool ts_is_pdch(const struct gsm_bts_trx_ts *ts)
{
- const struct gsm_bts_trx_ts *ts = lchan->ts;
-
- switch (lchan->tch_mode) {
- case GSM48_CMODE_SPEECH_V1:
- if (ts_pchan(ts) == GSM_PCHAN_TCH_H)
- return OSMO_ECU_CODEC_HR;
- else
- return OSMO_ECU_CODEC_FR;
- break;
- case GSM48_CMODE_SPEECH_EFR:
- return OSMO_ECU_CODEC_EFR;
- case GSM48_CMODE_SPEECH_AMR:
- return OSMO_ECU_CODEC_AMR;
+ switch (ts->pchan) {
+ case GSM_PCHAN_PDCH:
+ return true;
+ case GSM_PCHAN_TCH_F_PDCH:
+ return (ts->flags & TS_F_PDCH_ACTIVE)
+ && !(ts->flags & TS_F_PDCH_PENDING_MASK);
+ case GSM_PCHAN_OSMO_DYN:
+ return ts->dyn.pchan_is == GSM_PCHAN_PDCH
+ && ts->dyn.pchan_want == ts->dyn.pchan_is;
default:
- return -1;
+ return false;
}
}
-/* Default MS/BS Power Control parameters (see 3GPP TS 45.008, table A.1) */
-const struct gsm_power_ctrl_params power_ctrl_params_def = {
- /* Power increasing/reducing step size (optimal defaults) */
- .inc_step_size_db = 4, /* quickly increase MS/BS power */
- .red_step_size_db = 2, /* slowly decrease MS/BS power */
-
- /* RxLev measurement parameters */
- .rxlev_meas = {
- /* Thresholds for RxLev (see 3GPP TS 45.008, A.3.2.1) */
- .lower_thresh = 32, /* L_RXLEV_XX_P (-78 dBm) */
- .upper_thresh = 38, /* U_RXLEV_XX_P (-72 dBm) */
-
- /* NOTE: only Osmocom specific EWMA is supported */
- .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_OSMO_EWMA,
- .ewma.alpha = 50, /* Smoothing factor 50% */
- },
-
- /* RxQual measurement parameters */
- .rxqual_meas = {
- /* Thresholds for RxQual (see 3GPP TS 45.008, A.3.2.1) */
- .lower_thresh = 3, /* L_RXQUAL_XX_P (0.8% <= BER < 1.6%) */
- .upper_thresh = 0, /* U_RXQUAL_XX_P (BER < 0.2%) */
+void gsm_ts_release(struct gsm_bts_trx_ts *ts)
+{
+ unsigned int ln;
- /* FIXME: RxQual averaging is not yet implemented */
- .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE,
- },
-};
+ for (ln = 0; ln < ARRAY_SIZE(ts->lchan); ln++) {
+ struct gsm_lchan *lchan = &ts->lchan[ln];
+ gsm_lchan_release(lchan, LCHAN_REL_ACT_OML);
+ }
+ ts->pchan = GSM_PCHAN_NONE;
+ /* Make sure pchan_is is reset, since PCU act_req to release it will be
+ * ignored as the lchan will already be released. */
+ ts->dyn.pchan_is = ts->dyn.pchan_want = GSM_PCHAN_NONE;
+}
diff --git a/src/common/handover.c b/src/common/handover.c
index 888649db..922c5140 100644
--- a/src/common/handover.c
+++ b/src/common/handover.c
@@ -51,7 +51,7 @@ static int ho_tx_phys_info(struct gsm_lchan *lchan)
gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
gh->proto_discr = GSM48_PDISC_RR;
gh->msg_type = GSM48_MT_RR_HANDO_INFO;
- msgb_put_u8(msg, lchan->rqd_ta);
+ msgb_put_u8(msg, lchan->ta_ctrl.current);
rsl_rll_push_l3(msg, RSL_MT_UNIT_DATA_REQ, gsm_lchan2chan_nr(lchan),
0x00, 0);
@@ -111,7 +111,7 @@ void handover_rach(struct gsm_lchan *lchan, uint8_t ra, uint8_t acc_delay)
"TA=%u, ref=%u\n", gsm_lchant_name(lchan->type), acc_delay, ra);
/* Set timing advance */
- lchan->rqd_ta = acc_delay;
+ lchan->ta_ctrl.current = acc_delay;
lchan->want_dl_sacch_active = true;
/* Stop handover detection, wait for valid frame */
@@ -123,7 +123,7 @@ void handover_rach(struct gsm_lchan *lchan, uint8_t ra, uint8_t acc_delay)
}
/* Send HANDover DETect to BSC */
- rsl_tx_hando_det(lchan, &lchan->rqd_ta);
+ rsl_tx_hando_det(lchan, &lchan->ta_ctrl.current);
/* Send PHYS INFO */
lchan->ho.phys_info_count = 1;
diff --git a/src/common/l1sap.c b/src/common/l1sap.c
index c028a2c7..502bcef5 100644
--- a/src/common/l1sap.c
+++ b/src/common/l1sap.c
@@ -33,7 +33,6 @@
#include <osmocom/gsm/l1sap.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/gsm/rsl.h>
-#include <osmocom/gsm/protocol/gsm_44_004.h>
#include <osmocom/core/gsmtap.h>
#include <osmocom/core/gsmtap_util.h>
#include <osmocom/core/utils.h>
@@ -52,7 +51,6 @@
#include <osmo-bts/abis.h>
#include <osmo-bts/bts_model.h>
#include <osmo-bts/handover.h>
-#include <osmo-bts/power_control.h>
#include <osmo-bts/msg_utils.h>
#include <osmo-bts/pcuif_proto.h>
#include <osmo-bts/cbch.h>
@@ -474,6 +472,9 @@ static bool is_fill_frame(uint8_t chan_type, const uint8_t *data, unsigned int l
switch (chan_type) {
case GSMTAP_CHANNEL_AGCH:
+ case GSMTAP_CHANNEL_SDCCH:
+ case GSMTAP_CHANNEL_TCH_F:
+ case GSMTAP_CHANNEL_TCH_H:
if (!memcmp(data, fill_frame, GSM_MACBLOCK_LEN))
return true;
break;
@@ -481,6 +482,7 @@ static bool is_fill_frame(uint8_t chan_type, const uint8_t *data, unsigned int l
if (!memcmp(data, paging_fill, GSM_MACBLOCK_LEN))
return true;
break;
+ /* FIXME: implement the same for GSMTAP_CHANNEL_PDTCH from/to PCU */
/* don't use 'default' case here as the above only conditionally return true */
}
return false;
@@ -588,6 +590,34 @@ static unsigned int calc_exprd_rach_frames(struct gsm_bts *bts, uint32_t fn)
return rach_frames_expired;
}
+static void l1sap_interf_meas_calc_avg(struct gsm_bts_trx *trx)
+{
+ unsigned int tn, ln;
+
+ for (tn = 0; tn < ARRAY_SIZE(trx->ts); tn++) {
+ struct gsm_bts_trx_ts *ts = &trx->ts[tn];
+
+ if (ts->mo.nm_state.operational != NM_OPSTATE_ENABLED)
+ continue;
+ if (ts->mo.nm_state.availability != NM_AVSTATE_OK)
+ continue;
+
+ for (ln = 0; ln < ARRAY_SIZE(ts->lchan); ln++) {
+ struct gsm_lchan *lchan = &ts->lchan[ln];
+
+ lchan->meas.interf_meas_avg_dbm = 0;
+ lchan->meas.interf_band = 0;
+
+ /* There must be at least one sample */
+ if (lchan->meas.interf_meas_num == 0)
+ continue;
+
+ /* Average all collected samples */
+ gsm_lchan_interf_meas_calc_avg(lchan);
+ }
+ }
+}
+
static void l1sap_interf_meas_report(struct gsm_bts *bts)
{
const uint32_t period = bts->interference.intave * 104;
@@ -598,8 +628,14 @@ static void l1sap_interf_meas_report(struct gsm_bts *bts)
if (bts->gsm_time.fn % period != 0)
return;
- llist_for_each_entry(trx, &bts->trx_list, list)
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ /* Calculate the average of all received samples */
+ l1sap_interf_meas_calc_avg(trx);
+ /* Report to the BSC over the A-bis/RSL */
rsl_tx_rf_res(trx);
+ /* Report to the PCU over the PCUIF */
+ pcu_tx_interf_ind(trx, bts->gsm_time.fn);
+ }
}
/* time information received from bts model */
@@ -635,7 +671,8 @@ static int l1sap_info_time_ind(struct gsm_bts *bts,
}
/* Report interference levels to the BSC */
- l1sap_interf_meas_report(bts);
+ if (bts_internal_flag_get(bts, BTS_INTERNAL_FLAG_INTERF_MEAS))
+ l1sap_interf_meas_report(bts);
return 0;
}
@@ -661,89 +698,70 @@ static inline void set_ms_to_data(struct gsm_lchan *lchan, int16_t data, bool se
}
/* measurement information received from bts model */
-static void process_l1sap_meas_data(struct gsm_bts_trx *trx,
- struct osmo_phsap_prim *l1sap,
+static void process_l1sap_meas_data(struct gsm_lchan *lchan,
+ const struct osmo_phsap_prim *l1sap,
enum osmo_ph_prim ind_type)
{
struct bts_ul_meas ulm;
- struct gsm_lchan *lchan;
- struct info_meas_ind_param *info_meas_ind;
- struct ph_data_param *ph_data_ind;
- struct ph_tch_param *ph_tch_ind;
- uint8_t chan_nr;
+ const struct info_meas_ind_param *info_meas_ind;
+ const struct ph_data_param *ph_data_ind;
+ const struct ph_tch_param *ph_tch_ind;
uint32_t fn;
- uint8_t inv_rssi;
- uint8_t is_sub;
- int16_t ta_offs_256bits;
- uint16_t ber10k;
const char *ind_name;
switch (ind_type) {
case PRIM_MPH_INFO:
/* (legacy way, see also OS#2977) */
info_meas_ind = &l1sap->u.info.u.meas_ind;
- chan_nr = info_meas_ind->chan_nr;
fn = info_meas_ind->fn;
- inv_rssi = info_meas_ind->inv_rssi;
- is_sub = info_meas_ind->is_sub;
- ta_offs_256bits = info_meas_ind->ta_offs_256bits;
- ber10k = info_meas_ind->ber10k;
ind_name = "MPH INFO";
+ ulm = (struct bts_ul_meas) {
+ .ta_offs_256bits = info_meas_ind->ta_offs_256bits,
+ .inv_rssi = info_meas_ind->inv_rssi,
+ .ber10k = info_meas_ind->ber10k,
+ .c_i = info_meas_ind->c_i_cb,
+ .is_sub = info_meas_ind->is_sub,
+ };
break;
case PRIM_TCH:
ph_tch_ind = &l1sap->u.tch;
if (ph_tch_ind->rssi == 0)
return;
- chan_nr = ph_tch_ind->chan_nr;
fn = ph_tch_ind->fn;
- inv_rssi = abs(ph_tch_ind->rssi);
- is_sub = ph_tch_ind->is_sub;
- ta_offs_256bits = ph_tch_ind->ta_offs_256bits;
- ber10k = ph_tch_ind->ber10k;
ind_name = "TCH";
+ ulm = (struct bts_ul_meas) {
+ .ta_offs_256bits = ph_tch_ind->ta_offs_256bits,
+ .inv_rssi = abs(ph_tch_ind->rssi),
+ .ber10k = ph_tch_ind->ber10k,
+ .c_i = ph_tch_ind->lqual_cb,
+ .is_sub = ph_tch_ind->is_sub,
+ };
break;
case PRIM_PH_DATA:
ph_data_ind = &l1sap->u.data;
if (ph_data_ind->rssi == 0)
return;
- chan_nr = ph_data_ind->chan_nr;
fn = ph_data_ind->fn;
- inv_rssi = abs(ph_data_ind->rssi);
- is_sub = ph_data_ind->is_sub;
- ta_offs_256bits = ph_data_ind->ta_offs_256bits;
- ber10k = ph_data_ind->ber10k;
ind_name = "DATA";
+ ulm = (struct bts_ul_meas) {
+ .ta_offs_256bits = ph_data_ind->ta_offs_256bits,
+ .inv_rssi = abs(ph_data_ind->rssi),
+ .ber10k = ph_data_ind->ber10k,
+ .c_i = ph_data_ind->lqual_cb,
+ .is_sub = ph_data_ind->is_sub,
+ };
break;
default:
OSMO_ASSERT(false);
}
- lchan = get_active_lchan_by_chan_nr(trx, chan_nr);
- if (!lchan) {
- LOGPFN(DL1P, LOGL_ERROR, fn,
- "No lchan for %s MEAS IND (chan_nr=%s)\n",
- ind_name, rsl_chan_nr_str(chan_nr));
- return;
- }
-
DEBUGPFN(DL1P, fn,
- "%s %s meas ind, ta_offs_256bits=%d, ber10k=%d, inv_rssi=%u\n",
- gsm_lchan_name(lchan), ind_name, ta_offs_256bits, ber10k,
- inv_rssi);
-
- /* in the GPRS case we are not interested in measurement
- * processing. The PCU will take care of it */
- if (lchan->type == GSM_LCHAN_PDTCH)
- return;
-
- memset(&ulm, 0, sizeof(ulm));
- ulm.ta_offs_256bits = ta_offs_256bits;
- ulm.ber10k = ber10k;
- ulm.inv_rssi = inv_rssi;
- ulm.is_sub = is_sub;
+ "%s %s meas ind, ta_offs_256bits=%d, ber10k=%d, inv_rssi=%u, C/I=%d cB\n",
+ gsm_lchan_name(lchan), ind_name, ulm.ta_offs_256bits,
+ ulm.ber10k, ulm.inv_rssi, ulm.c_i);
/* we assume that symbol period is 1 bit: */
- set_ms_to_data(lchan, ta_offs_256bits / 256, true);
+ set_ms_to_data(lchan, ulm.ta_offs_256bits / 256, true);
lchan_meas_process_measurement(lchan, &ulm, fn);
@@ -754,6 +772,8 @@ static void process_l1sap_meas_data(struct gsm_bts_trx *trx,
static int l1sap_mph_info_ind(struct gsm_bts_trx *trx,
struct osmo_phsap_prim *l1sap, struct mph_info_param *info)
{
+ const struct info_meas_ind_param *meas_ind;
+ struct gsm_lchan *lchan;
int rc = 0;
switch (info->type) {
@@ -773,7 +793,17 @@ static int l1sap_mph_info_ind(struct gsm_bts_trx *trx,
if (bts_internal_flag_get(trx->bts, BTS_INTERNAL_FLAG_MEAS_PAYLOAD_COMB))
OSMO_ASSERT(false);
- process_l1sap_meas_data(trx, l1sap, PRIM_MPH_INFO);
+ meas_ind = &l1sap->u.info.u.meas_ind;
+
+ lchan = get_active_lchan_by_chan_nr(trx, meas_ind->chan_nr);
+ if (!lchan) {
+ LOGPFN(DL1P, LOGL_ERROR, meas_ind->fn,
+ "No lchan for chan_nr=%s\n",
+ rsl_chan_nr_str(meas_ind->chan_nr));
+ return 0;
+ }
+
+ process_l1sap_meas_data(lchan, l1sap, PRIM_MPH_INFO);
break;
default:
LOGP(DL1P, LOGL_NOTICE, "unknown MPH_INFO ind type %d\n",
@@ -949,14 +979,14 @@ static inline struct msgb *lapdm_phsap_dequeue_msg_facch(struct gsm_lchan *lchan
/* Note: The repeated version of the FACCH block must be scheduled 8 or 9 bursts after the original
* transmission. see 3GPP TS 44.006, section 10.2 for a more detailed explaination. */
- if (lchan->tch.rep_facch[0].msg && GSM_TDMA_FN_SUB(fn, lchan->tch.rep_facch[0].fn) >= 8) {
+ if (lchan->rep_acch.dl_facch[0].msg && GSM_TDMA_FN_SUB(fn, lchan->rep_acch.dl_facch[0].fn) >= 8) {
/* Re-use stored FACCH message buffer from SLOT #0 for repetition. */
- msg = lchan->tch.rep_facch[0].msg;
- lchan->tch.rep_facch[0].msg = NULL;
- } else if (lchan->tch.rep_facch[1].msg && GSM_TDMA_FN_SUB(fn, lchan->tch.rep_facch[1].fn) >= 8) {
+ msg = lchan->rep_acch.dl_facch[0].msg;
+ lchan->rep_acch.dl_facch[0].msg = NULL;
+ } else if (lchan->rep_acch.dl_facch[1].msg && GSM_TDMA_FN_SUB(fn, lchan->rep_acch.dl_facch[1].fn) >= 8) {
/* Re-use stored FACCH message buffer from SLOT #1 for repetition. */
- msg = lchan->tch.rep_facch[1].msg;
- lchan->tch.rep_facch[1].msg = NULL;
+ msg = lchan->rep_acch.dl_facch[1].msg;
+ lchan->rep_acch.dl_facch[1].msg = NULL;
} else {
/* Fetch new FACCH from queue ... */
if (lapdm_phsap_dequeue_prim(le, &pp) < 0)
@@ -968,16 +998,16 @@ static inline struct msgb *lapdm_phsap_dequeue_msg_facch(struct gsm_lchan *lchan
* If the MS explicitly indicated that repeated ACCH is
* supported, than all FACCH frames may be repeated
* see also: 3GPP TS 44.006, section 10.3). */
- if (!(lchan->repeated_acch_capability.dl_facch_all || msg->data[0] & 0x02))
+ if (!(lchan->rep_acch_cap.dl_facch_all || msg->data[0] & 0x02))
return msg;
/* ... and store the message buffer for repetition. */
- if (lchan->tch.rep_facch[0].msg == NULL) {
- lchan->tch.rep_facch[0].msg = msgb_copy(msg, "rep_facch_0");
- lchan->tch.rep_facch[0].fn = fn;
- } else if (lchan->tch.rep_facch[1].msg == NULL) {
- lchan->tch.rep_facch[1].msg = msgb_copy(msg, "rep_facch_1");
- lchan->tch.rep_facch[1].fn = fn;
+ if (lchan->rep_acch.dl_facch[0].msg == NULL) {
+ lchan->rep_acch.dl_facch[0].msg = msgb_copy(msg, "rep_facch_0");
+ lchan->rep_acch.dl_facch[0].fn = fn;
+ } else if (lchan->rep_acch.dl_facch[1].msg == NULL) {
+ lchan->rep_acch.dl_facch[1].msg = msgb_copy(msg, "rep_facch_1");
+ lchan->rep_acch.dl_facch[1].fn = fn;
} else {
/* By definition 3GPP TS 05.02 does not allow more than two (for TCH/H only one) FACCH blocks
* to be transmitted simultaniously. */
@@ -988,80 +1018,6 @@ static inline struct msgb *lapdm_phsap_dequeue_msg_facch(struct gsm_lchan *lchan
return msg;
}
-/* Decide if repeated FACCH should be applied or not. If RXQUAL level, that the
- * MS reports is high enough, FACCH repetition is not needed. */
-void repeated_dl_facch_active_decision(struct gsm_lchan *lchan, const uint8_t *l3,
- size_t l3_len)
-{
- const struct gsm48_meas_res *meas_res;
- uint8_t upper;
- uint8_t lower;
- uint8_t rxqual;
- bool prev_repeated_dl_facch_active = lchan->repeated_dl_facch_active;
-
- /* This is an optimization so that we exit as quickly as possible if
- * there are no FACCH repetition capabilities present. However If the
- * repeated FACCH capabilities vanish for whatever reason, we must be
- * sure that FACCH repetition is disabled. */
- if (!lchan->repeated_acch_capability.dl_facch_cmd
- && !lchan->repeated_acch_capability.dl_facch_all) {
- lchan->repeated_dl_facch_active = false;
- goto out;
- }
-
- /* Threshold disabled (always on) */
- if (lchan->repeated_acch_capability.rxqual == 0) {
- lchan->repeated_dl_facch_active = true;
- goto out;
- }
-
- /* When the MS sets the SRR bit in the UL-SACCH L1 header
- * (repeated SACCH requested) then it makes sense to enable
- * FACCH repetition too. */
- if (lchan->meas.l1_info.srr_sro) {
- lchan->repeated_dl_facch_active = true;
- goto out;
- }
-
- /* Parse MS measurement results */
- if (l3_len <= sizeof(struct gsm48_meas_res *) + 2)
- goto out;
- if (l3[0] != GSM48_PDISC_RR)
- goto out;
- if (l3[1] != GSM48_MT_RR_MEAS_REP)
- goto out;
- l3 += 2;
- meas_res = (struct gsm48_meas_res *)l3;
-
- /* If the RXQUAL level at the MS drops under a certain threshold
- * we enable FACCH repetition. */
- upper = lchan->repeated_acch_capability.rxqual;
- if (upper > 2)
- lower = lchan->repeated_acch_capability.rxqual - 2;
- else
- lower = 0;
-
- /* When downlink DTX is applied, use RXQUAL-SUB, otherwise use
- * RXQUAL-FULL. */
- if (meas_res->dtx_used)
- rxqual = meas_res->rxqual_sub;
- else
- rxqual = meas_res->rxqual_full;
-
- if (rxqual >= upper)
- lchan->repeated_dl_facch_active = true;
- else if (rxqual <= lower)
- lchan->repeated_dl_facch_active = false;
-
-out:
- if (lchan->repeated_dl_facch_active == prev_repeated_dl_facch_active)
- return;
- if (lchan->repeated_dl_facch_active)
- LOGPLCHAN(lchan, DL1P, LOGL_DEBUG, "DL-FACCH repetition: inactive => active\n");
- else
- LOGPLCHAN(lchan, DL1P, LOGL_DEBUG, "DL-FACCH repetition: active => inactive\n");
-}
-
/* Special dequeueing function with SACCH repetition (3GPP TS 44.006, section 11) */
static inline struct msgb *lapdm_phsap_dequeue_msg_sacch(struct gsm_lchan *lchan, struct lapdm_entity *le)
{
@@ -1073,15 +1029,15 @@ static inline struct msgb *lapdm_phsap_dequeue_msg_sacch(struct gsm_lchan *lchan
* possible candidates in order to have one ready in case the MS enables
* SACCH repetition. */
- if (lchan->rep_sacch) {
+ if (lchan->rep_acch.dl_sacch_msg) {
if (lchan->meas.l1_info.srr_sro == 0) {
/* Toss previous repetition candidate */
- msgb_free(lchan->rep_sacch);
- lchan->rep_sacch = NULL;
+ msgb_free(lchan->rep_acch.dl_sacch_msg);
+ lchan->rep_acch.dl_sacch_msg = NULL;
} else {
/* Use previous repetition candidate */
- msg = lchan->rep_sacch;
- lchan->rep_sacch = NULL;
+ msg = lchan->rep_acch.dl_sacch_msg;
+ lchan->rep_acch.dl_sacch_msg = NULL;
return msg;
}
}
@@ -1095,7 +1051,7 @@ static inline struct msgb *lapdm_phsap_dequeue_msg_sacch(struct gsm_lchan *lchan
/* Only LAPDm frames for SAPI 0 may become a repetition
* candidate. */
if (sapi == 0)
- lchan->rep_sacch = msgb_copy(msg, "rep_sacch");
+ lchan->rep_acch.dl_sacch_msg = msgb_copy(msg, "rep_sacch");
return msg;
}
@@ -1175,24 +1131,31 @@ static int l1sap_ph_rts_ind(struct gsm_bts_trx *trx,
rsl_chan_nr_str(chan_nr));
return 0;
}
+ if (lchan->pending_rel_ind_msg) {
+ LOGPGT(DRSL, LOGL_INFO, &g_time,
+ "%s Forward RLL RELease INDication to the BSC\n",
+ gsm_lchan_name(lchan));
+ abis_bts_rsl_sendmsg(lchan->pending_rel_ind_msg);
+ lchan->pending_rel_ind_msg = NULL;
+ }
if (L1SAP_IS_LINK_SACCH(link_id)) {
p = msgb_put(msg, GSM_MACBLOCK_LEN);
/* L1-header, if not set/modified by layer 1 */
p[0] = lchan->ms_power_ctrl.current;
- if (lchan->repeated_ul_sacch_active)
+ if (lchan->rep_acch.ul_sacch_active)
p[0] |= 0x40; /* See also: 3GPP TS 44.004, section 7.1 */
- p[1] = lchan->rqd_ta;
+ p[1] = lchan->ta_ctrl.current;
le = &lchan->lapdm_ch.lapdm_acch;
- if (lchan->repeated_acch_capability.dl_sacch) {
+ if (lchan->rep_acch_cap.dl_sacch) {
/* Check if MS requests SACCH repetition and update state accordingly */
if (lchan->meas.l1_info.srr_sro) {
- if (lchan->repeated_dl_sacch_active == false)
+ if (lchan->rep_acch.dl_sacch_active == false)
LOGPLCHAN(lchan, DL1P, LOGL_DEBUG, "DL-SACCH repetition: inactive => active\n");
- lchan->repeated_dl_sacch_active = true;
+ lchan->rep_acch.dl_sacch_active = true;
} else {
- if (lchan->repeated_dl_sacch_active == true)
+ if (lchan->rep_acch.dl_sacch_active == true)
LOGPLCHAN(lchan, DL1P, LOGL_DEBUG, "DL-SACCH repetition: active => inactive\n");
- lchan->repeated_dl_sacch_active = false;
+ lchan->rep_acch.dl_sacch_active = false;
}
pp_msg = lapdm_phsap_dequeue_msg_sacch(lchan, le);
} else {
@@ -1202,7 +1165,7 @@ static int l1sap_ph_rts_ind(struct gsm_bts_trx *trx,
if (lchan->ts->trx->bts->dtxd)
dtxd_facch = true;
le = &lchan->lapdm_ch.lapdm_dcch;
- if (lchan->repeated_dl_facch_active && lchan->rsl_cmode != RSL_CMOD_SPD_SIGN)
+ if (lchan->rep_acch.dl_facch_active && lchan->rsl_cmode != RSL_CMOD_SPD_SIGN)
pp_msg = lapdm_phsap_dequeue_msg_facch(lchan, le, fn);
else
pp_msg = lapdm_phsap_dequeue_msg(le);
@@ -1310,7 +1273,6 @@ static int l1sap_tch_rts_ind(struct gsm_bts_trx *trx,
struct gsm_lchan *lchan;
uint8_t chan_nr, marker = 0;
uint32_t fn;
- int rc;
chan_nr = rts_ind->chan_nr;
fn = rts_ind->fn;
@@ -1354,16 +1316,6 @@ static int l1sap_tch_rts_ind(struct gsm_bts_trx *trx,
resp_l1sap = msgb_l1sap_prim(resp_msg);
}
- /* check for pending REL_IND */
- if (lchan->pending_rel_ind_msg) {
- LOGPGT(DRSL, LOGL_INFO, &g_time, "%s Forward REL_IND to L3\n", gsm_lchan_name(lchan));
- /* Forward it to L3 */
- rc = abis_bts_rsl_sendmsg(lchan->pending_rel_ind_msg);
- lchan->pending_rel_ind_msg = NULL;
- if (rc < 0)
- return rc;
- }
-
memset(resp_l1sap, 0, sizeof(*resp_l1sap));
osmo_prim_init(&resp_l1sap->oph, SAP_GSM_PH, PRIM_TCH, PRIM_OP_REQUEST,
resp_msg);
@@ -1463,20 +1415,20 @@ static void repeated_ul_sacch_active_decision(struct gsm_lchan *lchan,
{
uint16_t upper = 0;
uint16_t lower = 0;
- bool prev_repeated_ul_sacch_active = lchan->repeated_ul_sacch_active;
+ bool prev_repeated_ul_sacch_active = lchan->rep_acch.ul_sacch_active;
/* This is an optimization so that we exit as quickly as possible if
* there are no uplink SACCH repetition capabilities present.
* However If the repeated UL-SACCH capabilities vanish for whatever
* reason, we must be sure that UL-SACCH repetition is disabled. */
- if (!lchan->repeated_acch_capability.ul_sacch) {
- lchan->repeated_ul_sacch_active = false;
+ if (!lchan->rep_acch_cap.ul_sacch) {
+ lchan->rep_acch.ul_sacch_active = false;
goto out;
}
/* Threshold disabled (repetition is always on) */
- if (lchan->repeated_acch_capability.rxqual == 0) {
- lchan->repeated_ul_sacch_active = true;
+ if (lchan->rep_acch_cap.rxqual == 0) {
+ lchan->rep_acch.ul_sacch_active = true;
goto out;
}
@@ -1490,19 +1442,19 @@ static void repeated_ul_sacch_active_decision(struct gsm_lchan *lchan,
* of the table in GSM 05.08, section 8.2.4. The lower vector is just
* the upper vector shifted by 2. */
- upper = ber10k_by_rxqual_upper[lchan->repeated_acch_capability.rxqual];
- lower = ber10k_by_rxqual_lower[lchan->repeated_acch_capability.rxqual];
+ upper = ber10k_by_rxqual_upper[lchan->rep_acch_cap.rxqual];
+ lower = ber10k_by_rxqual_lower[lchan->rep_acch_cap.rxqual];
/* If upper/rxqual == 0, then repeated UL-SACCH is always on */
if (ber10k >= upper)
- lchan->repeated_ul_sacch_active = true;
+ lchan->rep_acch.ul_sacch_active = true;
else if (ber10k <= lower)
- lchan->repeated_ul_sacch_active = false;
+ lchan->rep_acch.ul_sacch_active = false;
out:
- if (lchan->repeated_ul_sacch_active == prev_repeated_ul_sacch_active)
+ if (lchan->rep_acch.ul_sacch_active == prev_repeated_ul_sacch_active)
return;
- if (lchan->repeated_ul_sacch_active)
+ if (lchan->rep_acch.ul_sacch_active)
LOGPLCHAN(lchan, DL1P, LOGL_DEBUG, "UL-SACCH repetition: inactive => active\n");
else
LOGPLCHAN(lchan, DL1P, LOGL_DEBUG, "UL-SACCH repetition: active => inactive\n");
@@ -1521,11 +1473,8 @@ static int l1sap_ph_data_ind(struct gsm_bts_trx *trx,
uint8_t chan_nr, link_id;
uint8_t tn;
uint32_t fn;
- int8_t rssi;
enum osmo_ph_pres_info_type pr_info = data_ind->pdch_presence_info;
- struct gsm_sacch_l1_hdr *l1_hdr;
- rssi = data_ind->rssi;
chan_nr = data_ind->chan_nr;
link_id = data_ind->link_id;
fn = data_ind->fn;
@@ -1536,21 +1485,6 @@ static int l1sap_ph_data_ind(struct gsm_bts_trx *trx,
DEBUGPGT(DL1P, &g_time, "Rx PH-DATA.ind chan_nr=%s link_id=0x%02x len=%d\n",
rsl_chan_nr_str(chan_nr), link_id, len);
- /* Actually, there can be no DATA.ind on PTCCH/U (rather RACH.ind instead),
- * but some BTS models with buggy implementation may still be sending them
- * to us. Let's keep this for backwards compatibility. */
- if (L1SAP_IS_CHAN_PDCH(chan_nr) && L1SAP_IS_PTCCH(fn)) {
- LOGPGT(DL1P, LOGL_NOTICE, &g_time, "There can be no DATA.ind on PTCCH/U. "
- "This is probably a bug of the BTS model you're using, please fix!\n");
- return -EINVAL;
- }
-
- /* The ph_data_param contained in the l1sap primitive may contain
- * measurement data. If this data is present, forward it for
- * processing */
- if (bts_internal_flag_get(trx->bts, BTS_INTERNAL_FLAG_MEAS_PAYLOAD_COMB))
- process_l1sap_meas_data(trx, l1sap, PRIM_PH_DATA);
-
if (ts_is_pdch(&trx->ts[tn])) {
lchan = get_lchan_by_chan_nr(trx, chan_nr);
if (!lchan)
@@ -1567,13 +1501,21 @@ static int l1sap_ph_data_ind(struct gsm_bts_trx *trx,
return 1;
}
+ /* There can be no DATA.ind on PTCCH/U (rather RACH.ind instead), but some
+ * BTS models with buggy implementation may still be sending them to us. */
+ if (L1SAP_IS_PTCCH(fn)) {
+ LOGPGT(DL1P, LOGL_NOTICE, &g_time, "There can be no DATA.ind on PTCCH/U. "
+ "This is probably a bug of the BTS model you're using, please fix!\n");
+ return -EINVAL;
+ }
+
/* Drop all data from incomplete UL block */
if (pr_info != PRES_INFO_BOTH)
len = 0;
/* PDTCH / PACCH frame handling */
pcu_tx_data_ind(&trx->ts[tn], PCU_IF_SAPI_PDTCH, fn, trx->arfcn,
- L1SAP_FN2MACBLOCK(fn), data, len, rssi, data_ind->ber10k,
+ L1SAP_FN2MACBLOCK(fn), data, len, data_ind->rssi, data_ind->ber10k,
data_ind->ta_offs_256bits/64, data_ind->lqual_cb);
return 0;
}
@@ -1584,53 +1526,39 @@ static int l1sap_ph_data_ind(struct gsm_bts_trx *trx,
return 0;
}
- if (L1SAP_IS_LINK_SACCH(link_id))
- repeated_ul_sacch_active_decision(lchan, data_ind->ber10k);
+ /* The ph_data_param contained in the l1sap primitive may contain
+ * measurement data. If this data is present, forward it for
+ * processing */
+ if (bts_internal_flag_get(trx->bts, BTS_INTERNAL_FLAG_MEAS_PAYLOAD_COMB))
+ process_l1sap_meas_data(lchan, l1sap, PRIM_PH_DATA);
- /* bad frame */
- if (len == 0) {
- if (L1SAP_IS_LINK_SACCH(link_id)) {
- /* In case we loose a SACCH block, we must take care
- * that the related measurement report is sent via RSL.
- * This is a fallback method. The report will also
- * lack the measurement report from the MS side. See
- * also rsl.c:lapdm_rll_tx_cb() */
- LOGPGT(DL1P, LOGL_INFO, &g_time, "Lost SACCH block, faking meas reports and ms pwr\n");
- le = &lchan->lapdm_ch.lapdm_acch;
- rsl_tx_meas_res(lchan, NULL, 0, le);
+ if (L1SAP_IS_LINK_SACCH(link_id)) {
+ repeated_ul_sacch_active_decision(lchan, data_ind->ber10k);
+ /* Radio Link Timeout counter */
+ if (len == 0) {
+ LOGPGT(DL1P, LOGL_INFO, &g_time, "%s Lost SACCH block\n",
+ gsm_lchan_name(lchan));
radio_link_timeout(lchan, true);
- lchan_ms_pwr_ctrl(lchan, lchan->ms_power_ctrl.current, data_ind->rssi);
+ } else {
+ radio_link_timeout(lchan, false);
}
- return -EINVAL;
+
+ /* Trigger the measurement reporting/processing logic */
+ lchan_meas_handle_sacch(lchan, msg);
}
+ /* bad frame */
+ if (len == 0)
+ return -EINVAL;
+
/* report first valid received frame to handover process */
if (lchan->ho.active == HANDOVER_WAIT_FRAME)
handover_frame(lchan);
- if (L1SAP_IS_LINK_SACCH(link_id)) {
- radio_link_timeout(lchan, false);
+ if (L1SAP_IS_LINK_SACCH(link_id))
le = &lchan->lapdm_ch.lapdm_acch;
- /* save the SACCH L1 header in the lchan struct for RSL MEAS RES */
- if (len != GSM_MACBLOCK_LEN) {
- LOGPGT(DL1P, LOGL_NOTICE, &g_time, "SACCH with odd len=%u!?!\n", len);
- return -EINVAL;
- }
- /* Some brilliant engineer decided that the ordering of
- * fields on the Um interface is different from the
- * order of fields in RSL. See 3GPP TS 44.004 (section 7.2)
- * vs. 3GPP TS 48.058 (section 9.3.10). */
- l1_hdr = (struct gsm_sacch_l1_hdr*)data;
- lchan->meas.l1_info.ms_pwr = l1_hdr->ms_pwr;
- lchan->meas.l1_info.fpc_epc = l1_hdr->fpc_epc;
- lchan->meas.l1_info.srr_sro = l1_hdr->srr_sro;
- lchan->meas.l1_info.ta = l1_hdr->ta;
- lchan->meas.flags |= LC_UL_M_F_L1_VALID;
-
- lchan_ms_pwr_ctrl(lchan, data[0] & 0x1f, data_ind->rssi);
- lchan_bs_pwr_ctrl(lchan, (const struct gsm48_hdr *) &data[5]);
- } else
+ else
le = &lchan->lapdm_ch.lapdm_dcch;
if (check_for_first_ciphrd(lchan, data, len))
@@ -1672,7 +1600,7 @@ static int l1sap_tch_ind(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap,
* measurement data. If this data is present, forward it for
* processing */
if (bts_internal_flag_get(trx->bts, BTS_INTERNAL_FLAG_MEAS_PAYLOAD_COMB))
- process_l1sap_meas_data(trx, l1sap, PRIM_TCH);
+ process_l1sap_meas_data(lchan, l1sap, PRIM_TCH);
msgb_pull_to_l2(msg);
@@ -1941,8 +1869,12 @@ int l1sap_pdch_req(struct gsm_bts_trx_ts *ts, int is_ptcch, uint32_t fn,
l1sap->u.data.chan_nr = RSL_CHAN_OSMO_PDCH | ts->nr;
l1sap->u.data.link_id = 0x00;
l1sap->u.data.fn = fn;
- msg->l2h = msgb_put(msg, len);
- memcpy(msg->l2h, data, len);
+ if (len) {
+ msg->l2h = msgb_put(msg, len);
+ memcpy(msg->l2h, data, len);
+ } else {
+ msg->l2h = NULL; /* Idle block */
+ }
return l1sap_down(ts->trx, l1sap);
}
@@ -2000,7 +1932,13 @@ int l1sap_chan_act(struct gsm_bts_trx *trx, uint8_t chan_nr, struct tlv_parsed *
struct gsm_lchan *lchan = get_lchan_by_chan_nr(trx, chan_nr);
int rc;
- LOGPLCHAN(lchan, DL1C, LOGL_INFO, "activating channel %s\n", rsl_chan_nr_str(chan_nr));
+ if (lchan->state == LCHAN_S_ACTIVE) {
+ LOGPLCHAN(lchan, DL1C, LOGL_ERROR, "Trying to activate already active channel %s\n",
+ rsl_chan_nr_str(chan_nr));
+ return -1;
+ }
+
+ LOGPLCHAN(lchan, DL1C, LOGL_INFO, "Activating channel %s\n", rsl_chan_nr_str(chan_nr));
lchan->s = trx->bts->radio_link_timeout.current;
@@ -2033,8 +1971,15 @@ int l1sap_chan_act(struct gsm_bts_trx *trx, uint8_t chan_nr, struct tlv_parsed *
int l1sap_chan_rel(struct gsm_bts_trx *trx, uint8_t chan_nr)
{
struct gsm_lchan *lchan = get_lchan_by_chan_nr(trx, chan_nr);
- LOGPLCHAN(lchan, DL1C, LOGL_INFO, "deactivating channel chan_nr=%s trx=%d\n",
- rsl_chan_nr_str(chan_nr), trx->nr);
+
+ if (lchan->state == LCHAN_S_NONE) {
+ LOGPLCHAN(lchan, DL1C, LOGL_ERROR, "Trying to deactivate already deactivated channel %s\n",
+ rsl_chan_nr_str(chan_nr));
+ return -1;
+ }
+
+ LOGPLCHAN(lchan, DL1C, LOGL_INFO, "Deactivating channel %s\n",
+ rsl_chan_nr_str(chan_nr));
if (lchan->tch.dtx.dl_amr_fsm) {
osmo_fsm_inst_free(lchan->tch.dtx.dl_amr_fsm);
@@ -2049,8 +1994,8 @@ int l1sap_chan_deact_sacch(struct gsm_bts_trx *trx, uint8_t chan_nr)
{
struct gsm_lchan *lchan = get_lchan_by_chan_nr(trx, chan_nr);
- LOGPLCHAN(lchan, DL1C, LOGL_INFO, "deactivating sacch chan_nr=%s trx=%d\n",
- rsl_chan_nr_str(chan_nr), trx->nr);
+ LOGPLCHAN(lchan, DL1C, LOGL_INFO, "Deactivating SACCH on channel %s\n",
+ rsl_chan_nr_str(chan_nr));
return l1sap_chan_act_dact_modify(trx, chan_nr, PRIM_INFO_DEACTIVATE,
1);
@@ -2058,8 +2003,10 @@ int l1sap_chan_deact_sacch(struct gsm_bts_trx *trx, uint8_t chan_nr)
int l1sap_chan_modify(struct gsm_bts_trx *trx, uint8_t chan_nr)
{
- LOGP(DL1C, LOGL_INFO, "modifying channel chan_nr=%s trx=%d\n",
- rsl_chan_nr_str(chan_nr), trx->nr);
+ struct gsm_lchan *lchan = get_lchan_by_chan_nr(trx, chan_nr);
+
+ LOGPLCHAN(lchan, DL1C, LOGL_INFO, "Modifying channel %s\n",
+ rsl_chan_nr_str(chan_nr));
return l1sap_chan_act_dact_modify(trx, chan_nr, PRIM_INFO_MODIFY, 0);
}
diff --git a/src/common/lchan.c b/src/common/lchan.c
index 5a3f539a..fe5efd57 100644
--- a/src/common/lchan.c
+++ b/src/common/lchan.c
@@ -20,30 +20,493 @@
*/
#include <osmocom/core/logging.h>
+
+#include <osmocom/trau/osmo_ortp.h>
+
#include <osmo-bts/logging.h>
-#include <osmo-bts/gsm_data.h>
+#include <osmo-bts/lchan.h>
+#include <osmo-bts/bts.h>
+#include <osmo-bts/rsl.h>
+#include <osmo-bts/pcu_if.h>
+#include <osmo-bts/handover.h>
+#include <osmo-bts/l1sap.h>
+#include <osmo-bts/bts_model.h>
+#include <errno.h>
+
+static const struct value_string lchan_s_names[] = {
+ { LCHAN_S_NONE, "NONE" },
+ { LCHAN_S_ACT_REQ, "ACTIVATION REQUESTED" },
+ { LCHAN_S_ACTIVE, "ACTIVE" },
+ { LCHAN_S_REL_REQ, "RELEASE REQUESTED" },
+ { LCHAN_S_REL_ERR, "RELEASE DUE ERROR" },
+ { LCHAN_S_BROKEN, "BROKEN UNUSABLE" },
+ { 0, NULL }
+};
+
+const struct value_string lchan_ciph_state_names[] = {
+ { LCHAN_CIPH_NONE, "NONE" },
+ { LCHAN_CIPH_RX_REQ, "RX_REQ" },
+ { LCHAN_CIPH_RX_CONF, "RX_CONF" },
+ { LCHAN_CIPH_RXTX_REQ, "RXTX_REQ" },
+ { LCHAN_CIPH_RX_CONF_TX_REQ, "RX_CONF_TX_REQ" },
+ { LCHAN_CIPH_RXTX_CONF, "RXTX_CONF" },
+ { 0, NULL }
+};
+
+/* prepare the per-SAPI T200 arrays for a given lchan */
+static int t200_by_lchan(int *t200_ms_dcch, int *t200_ms_acch, struct gsm_lchan *lchan)
+{
+ struct gsm_bts *bts = lchan->ts->trx->bts;
+
+ /* we have to compensate for the "RTS advance" due to the asynchronous interface between
+ * the BTS (LAPDm) and the PHY/L1 (OsmoTRX or DSP in case of osmo-bts-{sysmo,lc15,oc2g,octphy} */
+ int32_t fn_advance = bts_get_avg_fn_advance(bts);
+ int32_t fn_advance_us = fn_advance * 4615;
+ int fn_advance_ms = fn_advance_us / 1000;
+
+ t200_ms_acch[DL_SAPI0] = bts->t200_ms[T200_SACCH_SDCCH] + fn_advance_ms;
+ t200_ms_acch[DL_SAPI3] = bts->t200_ms[T200_SACCH_SDCCH] + fn_advance_ms;
+
+ if (lchan->rep_acch_cap.dl_facch_all && lchan_is_tch(lchan)) {
+ t200_ms_acch[DL_SAPI0] *= 2;
+ t200_ms_acch[DL_SAPI3] *= 2;
+ }
+
+ switch (lchan->type) {
+ case GSM_LCHAN_SDCCH:
+ t200_ms_dcch[DL_SAPI0] = bts->t200_ms[T200_SDCCH] + fn_advance_ms;
+ t200_ms_dcch[DL_SAPI3] = bts->t200_ms[T200_SDCCH_SAPI3] + fn_advance_ms;
+ break;
+ case GSM_LCHAN_TCH_F:
+ t200_ms_dcch[DL_SAPI0] = bts->t200_ms[T200_FACCH_F] + fn_advance_ms;
+ t200_ms_dcch[DL_SAPI3] = bts->t200_ms[T200_FACCH_F] + fn_advance_ms;
+ break;
+ case GSM_LCHAN_TCH_H:
+ t200_ms_dcch[DL_SAPI0] = bts->t200_ms[T200_FACCH_H] + fn_advance_ms;
+ t200_ms_dcch[DL_SAPI3] = bts->t200_ms[T200_FACCH_H] + fn_advance_ms;
+ break;
+ default:
+ /* Channels such as CCCH don't use lapdm DL, and hence no T200 is needed */
+ return -1;
+ }
+ return 0;
+}
+
+static void early_rr_ia_delay_cb(void *data)
+{
+ struct gsm_lchan *lchan = data;
+ struct gsm_bts *bts = lchan->ts->trx->bts;
+
+ if (!lchan->early_rr_ia) {
+ /* The IA message has disappeared since the timer was started. */
+ return;
+ }
+
+ if (lchan->state != LCHAN_S_ACTIVE) {
+ /* Release has happened since the timer was started. */
+ msgb_free(lchan->early_rr_ia);
+ lchan->early_rr_ia = NULL;
+ return;
+ }
+
+ /* Activation is done, send the RR IA now. Put RR IA msg into the AGCH queue of the BTS. */
+ if (bts_agch_enqueue(bts, lchan->early_rr_ia) < 0) {
+ /* if there is no space in the queue: send DELETE IND */
+ rsl_tx_delete_ind(bts, lchan->early_rr_ia->data, lchan->early_rr_ia->len);
+ rate_ctr_inc2(bts->ctrs, BTS_CTR_AGCH_DELETED);
+ msgb_free(lchan->early_rr_ia);
+ }
+ lchan->early_rr_ia = NULL;
+}
+
+void gsm_lchan_init(struct gsm_lchan *lchan, struct gsm_bts_trx_ts *ts, unsigned int lchan_nr)
+{
+ lchan->ts = ts;
+ lchan->nr = lchan_nr;
+ lchan->type = GSM_LCHAN_NONE;
+ gsm_lchan_name_update(lchan);
+
+ osmo_timer_setup(&lchan->early_rr_ia_delay, early_rr_ia_delay_cb, lchan);
+
+ INIT_LLIST_HEAD(&lchan->sapi_cmds);
+ INIT_LLIST_HEAD(&lchan->dl_tch_queue);
+}
+
+void gsm_lchan_name_update(struct gsm_lchan *lchan)
+{
+ const struct gsm_bts_trx_ts *ts = lchan->ts;
+ const struct gsm_bts_trx *trx = ts->trx;
+ char *name;
+
+ name = talloc_asprintf(trx, "(" GSM_TS_NAME_FMT ",ss=%u)",
+ GSM_TS_NAME_ARGS(ts), lchan->nr);
+ if (lchan->name != NULL)
+ talloc_free(lchan->name);
+ lchan->name = name;
+}
+
+int lchan_init_lapdm(struct gsm_lchan *lchan)
+{
+ struct lapdm_channel *lc = &lchan->lapdm_ch;
+ int t200_ms_dcch[_NR_DL_SAPI], t200_ms_acch[_NR_DL_SAPI];
+
+ if (t200_by_lchan(t200_ms_dcch, t200_ms_acch, lchan) == 0) {
+ LOGPLCHAN(lchan, DLLAPD, LOGL_DEBUG,
+ "Setting T200 D0=%u, D3=%u, S0=%u, S3=%u (all in ms)\n",
+ t200_ms_dcch[DL_SAPI0], t200_ms_dcch[DL_SAPI3],
+ t200_ms_acch[DL_SAPI0], t200_ms_acch[DL_SAPI3]);
+ lapdm_channel_init3(lc, LAPDM_MODE_BTS, t200_ms_dcch, t200_ms_acch, lchan->type,
+ gsm_lchan_name(lchan));
+ lapdm_channel_set_flags(lc, LAPDM_ENT_F_POLLING_ONLY);
+ lapdm_channel_set_l1(lc, NULL, lchan);
+ }
+ /* We still need to set Rx callback to receive RACH requests: */
+ lapdm_channel_set_l3(lc, lapdm_rll_tx_cb, lchan);
+
+ return 0;
+}
+
+static int dyn_ts_pdch_release(struct gsm_lchan *lchan)
+{
+ struct gsm_bts_trx_ts *ts = lchan->ts;
+
+ if (ts->dyn.pchan_is != ts->dyn.pchan_want) {
+ LOGP(DRSL, LOGL_ERROR, "%s: PDCH release requested but already"
+ " in switchover\n", gsm_ts_and_pchan_name(ts));
+ return -EINVAL;
+ }
+
+ /*
+ * Indicate PDCH Disconnect in dyn_pdch.want, let pcu_tx_info_ind()
+ * pick it up and wait for PCU to disable the channel.
+ */
+ ts->dyn.pchan_want = GSM_PCHAN_NONE;
+
+ if (!pcu_connected()) {
+ /* PCU not connected yet. Just record the new type and done,
+ * the PCU will pick it up once connected. */
+ ts->dyn.pchan_is = GSM_PCHAN_NONE;
+ return 1;
+ }
+
+ return pcu_tx_info_ind();
+}
+
+void gsm_lchan_release(struct gsm_lchan *lchan, enum lchan_rel_act_kind rel_kind)
+{
+ int rc;
+
+ if (lchan->abis_ip.rtp_socket) {
+ rsl_tx_ipac_dlcx_ind(lchan, RSL_ERR_NORMAL_UNSPEC);
+ osmo_rtp_socket_log_stats(lchan->abis_ip.rtp_socket, DRTP, LOGL_INFO,
+ "Closing RTP socket on Channel Release ");
+ osmo_rtp_socket_free(lchan->abis_ip.rtp_socket);
+ lchan->abis_ip.rtp_socket = NULL;
+ msgb_queue_flush(&lchan->dl_tch_queue);
+ }
+
+ /* FIXME: right now we allow creating the rtp_socket even if chan is not
+ * activated... Once we check for that, we can move this check at the
+ * start of the function */
+ if (lchan->state == LCHAN_S_NONE)
+ return;
+
+ /* release handover state */
+ handover_reset(lchan);
+
+ lchan->rel_act_kind = rel_kind;
+
+ /* Dynamic channel in PDCH mode is released via PCU */
+ if (lchan->ts->pchan == GSM_PCHAN_OSMO_DYN
+ && lchan->ts->dyn.pchan_is == GSM_PCHAN_PDCH) {
+ rc = dyn_ts_pdch_release(lchan);
+ if (rc == 1) {
+ /* If the PCU is not connected, continue to rel ack right away. */
+ lchan->rel_act_kind = LCHAN_REL_ACT_PCU;
+ rsl_tx_rf_rel_ack(lchan);
+ return;
+ }
+ /* Waiting for PDCH release */
+ return;
+ }
+
+ l1sap_chan_rel(lchan->ts->trx, gsm_lchan2chan_nr(lchan));
+}
+
+int lchan_deactivate(struct gsm_lchan *lchan)
+{
+ OSMO_ASSERT(lchan);
+
+ lchan->ciph_state = 0;
+ return bts_model_lchan_deactivate(lchan);
+}
+
+const char *gsm_lchans_name(enum gsm_lchan_state s)
+{
+ return get_value_string(lchan_s_names, s);
+}
+
+/* obtain the next to-be transmitted dowlink SACCH frame (L2 hdr + L3); returns pointer to lchan->si buffer */
+uint8_t *lchan_sacch_get(struct gsm_lchan *lchan)
+{
+ uint32_t tmp, i;
+
+ for (i = 0; i < _MAX_SYSINFO_TYPE; i++) {
+ tmp = (lchan->si.last + 1 + i) % _MAX_SYSINFO_TYPE;
+ if (!(lchan->si.valid & (1 << tmp)))
+ continue;
+ lchan->si.last = tmp;
+ return GSM_LCHAN_SI(lchan, tmp);
+ }
+ LOGPLCHAN(lchan, DL1P, LOGL_NOTICE, "SACCH no SI available\n");
+ return NULL;
+}
void lchan_set_state(struct gsm_lchan *lchan, enum gsm_lchan_state state)
{
- DEBUGP(DL1C, "%s state %s -> %s\n",
- gsm_lchan_name(lchan),
- gsm_lchans_name(lchan->state),
- gsm_lchans_name(state));
+ if (lchan->state == state)
+ return;
+ LOGPLCHAN(lchan, DL1C, LOGL_INFO, "state %s -> %s\n",
+ gsm_lchans_name(lchan->state), gsm_lchans_name(state));
lchan->state = state;
+
+ switch (lchan->state) {
+ case LCHAN_S_ACT_REQ:
+ /* Early Immediate Assignment: Activation is requested, keep the
+ * early IA until active. This allows the BSC to send the IA
+ * even before a dynamic timeslot is done switching to a
+ * different pchan kind (experimental). */
+ break;
+ case LCHAN_S_ACTIVE:
+ lchan_init_lapdm(lchan);
+ if (lchan->early_rr_ia) {
+ /* Early Immediate Assignment: Activation is done, send
+ * the RR IA now. Delay a bit more to give Um time to
+ * let the lchan light up for the MS */
+ osmo_timer_del(&lchan->early_rr_ia_delay);
+ osmo_timer_schedule(&lchan->early_rr_ia_delay, 0,
+ osmo_tdef_get(abis_T_defs, -15, OSMO_TDEF_US, -1));
+ }
+ break;
+ case LCHAN_S_NONE:
+ lapdm_channel_exit(&lchan->lapdm_ch);
+ /* Also ensure that there are no leftovers from repeated FACCH or
+ * repeated SACCH that might cause memory leakage. */
+ msgb_free(lchan->rep_acch.dl_facch[0].msg);
+ msgb_free(lchan->rep_acch.dl_facch[1].msg);
+ lchan->rep_acch.dl_facch[0].msg = NULL;
+ lchan->rep_acch.dl_facch[1].msg = NULL;
+ msgb_free(lchan->rep_acch.dl_sacch_msg);
+ lchan->rep_acch.dl_sacch_msg = NULL;
+ /* free() pending messages */
+ msgb_free(lchan->pending_rel_ind_msg);
+ lchan->pending_rel_ind_msg = NULL;
+ msgb_free(lchan->pending_chan_activ);
+ lchan->pending_chan_activ = NULL;
+ /* fall through */
+ default:
+ if (lchan->early_rr_ia) {
+ /* Early Immediate Assignment: Transition to any other
+ * state means whatever IA the BSC has sent shall now
+ * not be relevant anymore. */
+ osmo_timer_del(&lchan->early_rr_ia_delay);
+ msgb_free(lchan->early_rr_ia);
+ lchan->early_rr_ia = NULL;
+ }
+ break;
+ }
}
-bool ts_is_pdch(const struct gsm_bts_trx_ts *ts)
+/* See Table 10.5.25 of GSM04.08 */
+static uint8_t gsm_pchan2chan_nr(enum gsm_phys_chan_config pchan,
+ uint8_t ts_nr, uint8_t lchan_nr)
{
- switch (ts->pchan) {
+ uint8_t cbits, chan_nr;
+
+ OSMO_ASSERT(pchan != GSM_PCHAN_OSMO_DYN);
+ OSMO_ASSERT(pchan != GSM_PCHAN_TCH_F_PDCH);
+
+ switch (pchan) {
+ case GSM_PCHAN_TCH_F:
+ OSMO_ASSERT(lchan_nr == 0);
+ cbits = ABIS_RSL_CHAN_NR_CBITS_Bm_ACCHs;
+ break;
case GSM_PCHAN_PDCH:
- return true;
- case GSM_PCHAN_TCH_F_PDCH:
- return (ts->flags & TS_F_PDCH_ACTIVE)
- && !(ts->flags & TS_F_PDCH_PENDING_MASK);
+ OSMO_ASSERT(lchan_nr == 0);
+ cbits = ABIS_RSL_CHAN_NR_CBITS_OSMO_PDCH;
+ break;
+ case GSM_PCHAN_TCH_H:
+ OSMO_ASSERT(lchan_nr < 2);
+ cbits = ABIS_RSL_CHAN_NR_CBITS_Lm_ACCHs(lchan_nr);
+ break;
+ case GSM_PCHAN_CCCH_SDCCH4:
+ case GSM_PCHAN_CCCH_SDCCH4_CBCH:
+ /*
+ * As a special hack for BCCH, lchan_nr == 4 may be passed
+ * here. This should never be sent in an RSL message.
+ * See osmo-bts-xxx/oml.c:opstart_compl().
+ */
+ if (lchan_nr == CCCH_LCHAN)
+ cbits = ABIS_RSL_CHAN_NR_CBITS_BCCH;
+ else {
+ OSMO_ASSERT(lchan_nr < 4);
+ cbits = ABIS_RSL_CHAN_NR_CBITS_SDCCH4_ACCH(lchan_nr);
+ }
+ break;
+ case GSM_PCHAN_SDCCH8_SACCH8C:
+ case GSM_PCHAN_SDCCH8_SACCH8C_CBCH:
+ OSMO_ASSERT(lchan_nr < 8);
+ cbits = ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(lchan_nr);
+ break;
+ case GSM_PCHAN_CCCH:
+ cbits = ABIS_RSL_CHAN_NR_CBITS_BCCH;
+ break;
+ case GSM_PCHAN_NONE:
+ LOGP(DRSL, LOGL_ERROR, "Physical channel %s not expected!\n",
+ gsm_pchan_name(pchan));
+ cbits = 0x00;
+ break;
+ default:
+ LOGP(DRSL, LOGL_ERROR, "Physical channel %s (0x%02x) not expected!\n",
+ gsm_pchan_name(pchan), (int)pchan);
+ OSMO_ASSERT(0);
+ break;
+ }
+
+ chan_nr = (cbits << 3) | (ts_nr & 0x7);
+
+ return chan_nr;
+}
+
+static uint8_t _gsm_lchan2chan_nr(const struct gsm_lchan *lchan, bool rsl)
+{
+ uint8_t chan_nr;
+
+ switch (lchan->ts->pchan) {
case GSM_PCHAN_OSMO_DYN:
- return ts->dyn.pchan_is == GSM_PCHAN_PDCH
- && ts->dyn.pchan_want == ts->dyn.pchan_is;
+ /* Return chan_nr reflecting the current TS pchan, either a standard TCH kind, or the
+ * nonstandard value reflecting PDCH for Osmocom style dyn TS. */
+ chan_nr = gsm_lchan_as_pchan2chan_nr(lchan, lchan->ts->dyn.pchan_is);
+ break;
+ case GSM_PCHAN_TCH_F_PDCH:
+ /* For ip.access style dyn TS, on RSL we want to use the chan_nr as if it was TCH/F.
+ * We're using custom PDCH ACT and DEACT messages that use the usual chan_nr values. */
+ if (rsl)
+ chan_nr = gsm_lchan_as_pchan2chan_nr(lchan, GSM_PCHAN_TCH_F);
+ else if (~lchan->ts->flags & TS_F_PDCH_ACTIVE)
+ chan_nr = gsm_lchan_as_pchan2chan_nr(lchan, GSM_PCHAN_TCH_F);
+ else
+ chan_nr = gsm_lchan_as_pchan2chan_nr(lchan, GSM_PCHAN_PDCH);
+ break;
+ default:
+ chan_nr = gsm_pchan2chan_nr(lchan->ts->pchan, lchan->ts->nr, lchan->nr);
+ break;
+ }
+
+ /* VAMOS: if this lchan belongs to a shadow timeslot, we must reflect
+ * this in the channel number. Convert it to Osmocom specific value. */
+ if (lchan->ts->vamos.is_shadow)
+ chan_nr |= RSL_CHAN_OSMO_VAMOS_MASK;
+
+ return chan_nr;
+}
+
+uint8_t gsm_lchan2chan_nr(const struct gsm_lchan *lchan)
+{
+ return _gsm_lchan2chan_nr(lchan, false);
+}
+
+uint8_t gsm_lchan2chan_nr_rsl(const struct gsm_lchan *lchan)
+{
+ return _gsm_lchan2chan_nr(lchan, true);
+}
+
+uint8_t gsm_lchan_as_pchan2chan_nr(const struct gsm_lchan *lchan,
+ enum gsm_phys_chan_config as_pchan)
+{
+ if (lchan->ts->pchan == GSM_PCHAN_OSMO_DYN
+ && as_pchan == GSM_PCHAN_PDCH)
+ return RSL_CHAN_OSMO_PDCH | (lchan->ts->nr & ~RSL_CHAN_NR_MASK);
+ return gsm_pchan2chan_nr(as_pchan, lchan->ts->nr, lchan->nr);
+}
+
+/* Called by the model specific code every 104 TDMA frames (SACCH period) */
+void gsm_lchan_interf_meas_push(struct gsm_lchan *lchan, int dbm)
+{
+ const uint8_t meas_num = lchan->meas.interf_meas_num;
+
+ if (meas_num >= ARRAY_SIZE(lchan->meas.interf_meas_dbm)) {
+ LOGPLCHAN(lchan, DL1C, LOGL_ERROR, "Not enough room "
+ "to store interference report (%ddBm)\n", dbm);
+ return;
+ }
+
+ lchan->meas.interf_meas_dbm[meas_num] = dbm;
+ lchan->meas.interf_meas_num++;
+}
+
+/* Called by the higher layers every Intave * 104 TDMA frames */
+void gsm_lchan_interf_meas_calc_avg(struct gsm_lchan *lchan)
+{
+ const uint8_t meas_num = lchan->meas.interf_meas_num;
+ const struct gsm_bts *bts = lchan->ts->trx->bts;
+ int b, meas_avg, meas_sum = 0;
+
+ /* There must be at least one sample */
+ OSMO_ASSERT(meas_num > 0);
+
+ /* Calculate the sum of all collected samples (in -x dBm) */
+ while (lchan->meas.interf_meas_num) {
+ uint8_t i = --lchan->meas.interf_meas_num;
+ meas_sum += lchan->meas.interf_meas_dbm[i];
+ }
+
+ /* Calculate the average of all collected samples */
+ meas_avg = meas_sum / (int) meas_num;
+
+ /* 3GPP TS 48.008 defines 5 interference bands, and 6 interference level
+ * boundaries (0, X1, ... X5). It's not clear how to handle values
+ * exceeding the outer boundaries (0 or X5), because bands 0 and 6 do
+ * not exist (sigh). Let's map such values to closest bands 1 and 5. */
+ if (bts->interference.boundary[0] < bts->interference.boundary[5]) {
+ /* Ascending order (band=1 indicates lowest interference) */
+ for (b = 1; b < ARRAY_SIZE(bts->interference.boundary) - 1; b++) {
+ if (meas_avg < bts->interference.boundary[b])
+ break; /* Current 'b' is the band value */
+ }
+ } else {
+ /* Descending order (band=1 indicates highest interference) */
+ for (b = 1; b < ARRAY_SIZE(bts->interference.boundary) - 1; b++) {
+ if (meas_avg >= bts->interference.boundary[b])
+ break; /* Current 'b' is the band value */
+ }
+ }
+
+ LOGPLCHAN(lchan, DL1C, LOGL_DEBUG,
+ "Interference AVG: %ddBm (band %d, samples %u)\n",
+ meas_avg, b, meas_num);
+
+ lchan->meas.interf_meas_avg_dbm = meas_avg;
+ lchan->meas.interf_band = b;
+}
+
+/* determine the ECU codec constant for the codec used by given lchan */
+int lchan2ecu_codec(const struct gsm_lchan *lchan)
+{
+ const struct gsm_bts_trx_ts *ts = lchan->ts;
+
+ switch (lchan->tch_mode) {
+ case GSM48_CMODE_SPEECH_V1:
+ if (ts_pchan(ts) == GSM_PCHAN_TCH_H)
+ return OSMO_ECU_CODEC_HR;
+ else
+ return OSMO_ECU_CODEC_FR;
+ break;
+ case GSM48_CMODE_SPEECH_EFR:
+ return OSMO_ECU_CODEC_EFR;
+ case GSM48_CMODE_SPEECH_AMR:
+ return OSMO_ECU_CODEC_AMR;
default:
- return false;
+ return -1;
}
}
diff --git a/src/common/load_indication.c b/src/common/load_indication.c
index c9b26458..d5b76ea2 100644
--- a/src/common/load_indication.c
+++ b/src/common/load_indication.c
@@ -41,6 +41,10 @@ static void load_timer_cb(void *data)
struct gsm_bts *bts = data;
unsigned int pch_percent, rach_percent;
+ /* It makes no sense to send Load Indication if CCCH is still disabled...*/
+ if (bts->c0->ts[0].mo.nm_state.operational != NM_OPSTATE_ENABLED)
+ goto retry_later;
+
/* compute percentages */
if (bts->load.ccch.pch_total == 0)
pch_percent = 0;
@@ -73,6 +77,7 @@ static void load_timer_cb(void *data)
bts->load.rach.access);
}
+retry_later:
reset_load_counters(bts);
/* re-schedule the timer */
@@ -94,3 +99,8 @@ void load_timer_stop(struct gsm_bts *bts)
{
osmo_timer_del(&bts->load.ccch.timer);
}
+
+bool load_timer_is_running(const struct gsm_bts *bts)
+{
+ return osmo_timer_pending(&bts->load.ccch.timer);
+}
diff --git a/src/common/main.c b/src/common/main.c
index 0b7d3fb7..b5ba21b8 100644
--- a/src/common/main.c
+++ b/src/common/main.c
@@ -65,6 +65,7 @@ static int daemonize = 0;
static int rt_prio = -1;
static char *gsmtap_ip = 0;
extern int g_vty_port_num;
+static bool vty_test_mode = false;
static void print_help()
{
@@ -80,6 +81,8 @@ static void print_help()
"\nVTY reference generation:\n"
" --vty-ref-mode MODE VTY reference generation mode (e.g. 'expert').\n"
" --vty-ref-xml Generate the VTY reference XML output and exit.\n"
+ "\nRegression testing:\n"
+ " --vty-test VTY test mode. Do not connect to BSC, do not exit.\n"
);
bts_model_print_help();
}
@@ -103,6 +106,9 @@ static void handle_long_options(const char *prog_name, const int long_option)
get_value_string(vty_ref_gen_mode_desc, vty_ref_mode));
vty_dump_xml_ref_mode(stdout, (enum vty_ref_gen_mode) vty_ref_mode);
exit(0);
+ case 3:
+ vty_test_mode = true;
+ break;
default:
fprintf(stderr, "%s: error parsing cmdline options\n", prog_name);
exit(2);
@@ -140,6 +146,7 @@ static void handle_options(int argc, char **argv)
{ "realtime", 1, 0, 'r' },
{ "vty-ref-mode", 1, &long_option, 1 },
{ "vty-ref-xml", 0, &long_option, 2 },
+ { "vty-test", 0, &long_option, 3 },
{ 0, 0, 0, 0 }
};
@@ -272,7 +279,6 @@ static int write_pid_file(char *procname)
int bts_main(int argc, char **argv)
{
struct gsm_bts_trx *trx;
- struct e1inp_line *line;
int rc;
/* Track the use of talloc NULL memory contexts */
@@ -300,6 +306,8 @@ int bts_main(int argc, char **argv)
handle_options(argc, argv);
fprintf(stderr, "((*))\n |\n / \\ OsmoBTS\n");
+ if (vty_test_mode)
+ fprintf(stderr, "--- VTY test mode: not connecting to BSC, not exiting ---\n");
g_bts = gsm_bts_alloc(tall_bts_ctx, 0);
if (!g_bts) {
@@ -397,16 +405,18 @@ int bts_main(int argc, char **argv)
signal(SIGUSR2, &signal_handler);
osmo_init_ignore_signals();
- if (!g_bts->bsc_oml_host) {
- fprintf(stderr, "Cannot start BTS without knowing BSC OML IP\n");
- exit(1);
+ if (vty_test_mode) {
+ /* Just select-loop without connecting to the BSC, don't exit. This allows running tests on the VTY
+ * telnet port. */
+ while (!quit) {
+ log_reset_context();
+ osmo_select_main(0);
+ }
+ return EXIT_SUCCESS;
}
- line = abis_open(g_bts, g_bts->bsc_oml_host, "osmo-bts");
- if (!line) {
- fprintf(stderr, "unable to connect to BSC\n");
- exit(2);
- }
+ if (abis_open(g_bts, "osmo-bts") != 0)
+ exit(1);
rc = phy_links_open();
if (rc < 0) {
diff --git a/src/common/measurement.c b/src/common/measurement.c
index b5869872..1a5992b2 100644
--- a/src/common/measurement.c
+++ b/src/common/measurement.c
@@ -2,14 +2,18 @@
#include <stdint.h>
#include <errno.h>
-#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/core/utils.h>
+#include <osmocom/core/endian.h>
+
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/protocol/gsm_44_004.h>
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/measurement.h>
#include <osmo-bts/scheduler.h>
#include <osmo-bts/rsl.h>
+#include <osmo-bts/power_control.h>
#include <osmo-bts/ta_control.h>
/* Tables as per TS 45.008 Section 8.3 */
@@ -342,7 +346,7 @@ int lchan_new_ul_meas(struct gsm_lchan *lchan,
if (!ulm->is_sub)
dest->is_sub = ts45008_83_is_sub(lchan, fn);
- DEBUGPFN(DMEAS, fn, "%s adding measurement (ber10k=%u, ta_offs=%d, ci=%0.2f, is_sub=%u, rssi=-%u), num_ul_meas=%d, fn_mod=%u\n",
+ DEBUGPFN(DMEAS, fn, "%s adding measurement (ber10k=%u, ta_offs=%d, ci_cB=%d, is_sub=%u, rssi=-%u), num_ul_meas=%d, fn_mod=%u\n",
gsm_lchan_name(lchan), ulm->ber10k, ulm->ta_offs_256bits,
ulm->c_i, dest->is_sub, ulm->inv_rssi, lchan->meas.num_ul_meas,
fn_mod);
@@ -556,8 +560,10 @@ int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn)
struct gsm_meas_rep_unidir *mru;
uint32_t ber_full_sum = 0;
uint32_t irssi_full_sum = 0;
+ int32_t ci_full_sum = 0;
uint32_t ber_sub_sum = 0;
uint32_t irssi_sub_sum = 0;
+ int32_t ci_sub_sum = 0;
int32_t ta256b_sum = 0;
unsigned int num_meas_sub = 0;
unsigned int num_meas_sub_actual = 0;
@@ -589,7 +595,7 @@ int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn)
/* When AMR is used, we expect at least one SUB frame, since
* the SACCH will always be SUB frame. There may occur more
* SUB frames but since DTX periods in AMR are dynamic, we
- * can not know how much exactly. */
+ * can not know how many exactly. */
num_meas_sub_expect = 1;
}
@@ -625,11 +631,13 @@ int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn)
m = &lchan->meas.uplink[i + num_ul_meas_excess];
if (m->is_sub) {
irssi_sub_sum += m->inv_rssi;
+ ci_sub_sum += m->c_i;
num_meas_sub_actual++;
is_sub = true;
}
irssi_full_sum += m->inv_rssi;
ta256b_sum += m->ta_offs_256bits;
+ ci_full_sum += m->c_i;
num_ul_meas_actual++;
} else {
@@ -698,27 +706,32 @@ int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn)
else
irssi_full_sum = irssi_full_sum / num_ul_meas_actual;
- if (!num_ul_meas_actual)
+ if (!num_ul_meas_actual) {
ta256b_sum = lchan->meas.ms_toa256;
- else
+ ci_full_sum = lchan->meas.ul_ci_cb_full;
+ } else {
ta256b_sum = ta256b_sum / (signed)num_ul_meas_actual;
+ ci_full_sum = ci_full_sum / (signed)num_ul_meas_actual;
+ }
if (!num_meas_sub)
ber_sub_sum = MEASUREMENT_DUMMY_BER;
else
ber_sub_sum = ber_sub_sum / num_meas_sub;
- if (!num_meas_sub_actual)
+ if (!num_meas_sub_actual) {
irssi_sub_sum = MEASUREMENT_DUMMY_IRSSI;
- else
+ ci_sub_sum = lchan->meas.ul_ci_cb_sub;
+ } else {
irssi_sub_sum = irssi_sub_sum / num_meas_sub_actual;
+ ci_sub_sum = ci_sub_sum / (signed)num_meas_sub_actual;
+ }
LOGPLCHAN(lchan, DMEAS, LOGL_INFO,
- "Computed TA256(% 4d) BER-FULL(%2u.%02u%%), RSSI-FULL(-%3udBm), "
- "BER-SUB(%2u.%02u%%), RSSI-SUB(-%3udBm)\n",
- ta256b_sum, ber_full_sum / 100, ber_full_sum % 100,
- irssi_full_sum, ber_sub_sum / 100, ber_sub_sum % 100,
- irssi_sub_sum);
+ "Computed TA256(% 4d), BER-FULL(%2u.%02u%%), RSSI-FULL(-%3udBm), C/I-FULL(% 4d cB), "
+ "BER-SUB(%2u.%02u%%), RSSI-SUB(-%3udBm), C/I-SUB(% 4d cB)\n",
+ ta256b_sum, ber_full_sum / 100, ber_full_sum % 100, irssi_full_sum, ci_full_sum,
+ ber_sub_sum / 100, ber_sub_sum % 100, irssi_sub_sum, ci_sub_sum);
/* store results */
mru = &lchan->meas.ul_res;
@@ -727,6 +740,8 @@ int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn)
mru->full.rx_qual = ber10k_to_rxqual(ber_full_sum);
mru->sub.rx_qual = ber10k_to_rxqual(ber_sub_sum);
lchan->meas.ms_toa256 = ta256b_sum;
+ lchan->meas.ul_ci_cb_full = ci_full_sum;
+ lchan->meas.ul_ci_cb_sub = ci_sub_sum;
LOGPLCHAN(lchan, DMEAS, LOGL_INFO,
"UL MEAS RXLEV_FULL(%u), RXLEV_SUB(%u), RXQUAL_FULL(%u), RXQUAL_SUB(%u), "
@@ -739,11 +754,6 @@ int lchan_meas_check_compute(struct gsm_lchan *lchan, uint32_t fn)
lchan_meas_compute_extended(lchan);
- /* Compute new ta_req value. This has to be done here since the value
- * in lchan->meas.num_ul_meas together with lchan->meas.ms_toa256
- * is needed for the computation. */
- lchan_ms_ta_ctrl(lchan);
-
lchan->meas.num_ul_meas = 0;
/* return 1 to indicate that the computation has been done and the next
@@ -771,3 +781,226 @@ void lchan_meas_reset(struct gsm_lchan *lchan)
memset(&lchan->meas, 0, sizeof(lchan->meas));
lchan->meas.last_fn = LCHAN_FN_DUMMY;
}
+
+static inline uint8_t ms_to2rsl(const struct gsm_lchan *lchan, uint8_t ta)
+{
+ return (lchan->ms_t_offs >= 0) ? lchan->ms_t_offs : (lchan->p_offs - ta);
+}
+
+static inline bool ms_to_valid(const struct gsm_lchan *lchan)
+{
+ return (lchan->ms_t_offs >= 0) || (lchan->p_offs >= 0);
+}
+
+/* Decide if repeated FACCH should be applied or not. If RXQUAL level, that the
+ * MS reports is high enough, FACCH repetition is not needed. */
+static void repeated_dl_facch_active_decision(struct gsm_lchan *lchan,
+ const struct gsm48_meas_res *meas_res)
+{
+ uint8_t upper;
+ uint8_t lower;
+ uint8_t rxqual;
+ bool prev_repeated_dl_facch_active = lchan->rep_acch.dl_facch_active;
+
+ /* This is an optimization so that we exit as quickly as possible if
+ * there are no FACCH repetition capabilities present. However If the
+ * repeated FACCH capabilities vanish for whatever reason, we must be
+ * sure that FACCH repetition is disabled. */
+ if (!lchan->rep_acch_cap.dl_facch_cmd
+ && !lchan->rep_acch_cap.dl_facch_all) {
+ lchan->rep_acch.dl_facch_active = false;
+ goto out;
+ }
+
+ /* Threshold disabled (always on) */
+ if (lchan->rep_acch_cap.rxqual == 0) {
+ lchan->rep_acch.dl_facch_active = true;
+ goto out;
+ }
+
+ /* When the MS sets the SRR bit in the UL-SACCH L1 header
+ * (repeated SACCH requested) then it makes sense to enable
+ * FACCH repetition too. */
+ if (lchan->meas.l1_info.srr_sro) {
+ lchan->rep_acch.dl_facch_active = true;
+ goto out;
+ }
+
+ /* Parse MS measurement results */
+ if (meas_res == NULL)
+ goto out;
+ if (!gsm48_meas_res_is_valid(meas_res))
+ goto out;
+
+ /* If the RXQUAL level at the MS drops under a certain threshold
+ * we enable FACCH repetition. */
+ upper = lchan->rep_acch_cap.rxqual;
+ if (upper > 2)
+ lower = lchan->rep_acch_cap.rxqual - 2;
+ else
+ lower = 0;
+
+ /* When downlink DTX is applied, use RXQUAL-SUB, otherwise use
+ * RXQUAL-FULL. */
+ if (meas_res->dtx_used)
+ rxqual = meas_res->rxqual_sub;
+ else
+ rxqual = meas_res->rxqual_full;
+
+ if (rxqual >= upper)
+ lchan->rep_acch.dl_facch_active = true;
+ else if (rxqual <= lower)
+ lchan->rep_acch.dl_facch_active = false;
+
+out:
+ if (lchan->rep_acch.dl_facch_active == prev_repeated_dl_facch_active)
+ return;
+ if (lchan->rep_acch.dl_facch_active)
+ LOGPLCHAN(lchan, DL1P, LOGL_DEBUG, "DL-FACCH repetition: inactive => active\n");
+ else
+ LOGPLCHAN(lchan, DL1P, LOGL_DEBUG, "DL-FACCH repetition: active => inactive\n");
+}
+
+static void acch_overpower_active_decision(struct gsm_lchan *lchan,
+ const struct gsm48_meas_res *meas_res)
+{
+ const bool old = lchan->top_acch_active;
+ uint8_t upper, lower, rxqual;
+
+ /* ACCH overpower is not allowed => nothing to do */
+ if (lchan->top_acch_cap.overpower_db == 0)
+ return;
+ /* RxQual threshold is disabled => overpower is always on */
+ if (lchan->top_acch_cap.rxqual == 0)
+ return;
+
+ /* If DTx is active on Downlink, use the '-SUB' */
+ if (meas_res->dtx_used)
+ rxqual = meas_res->rxqual_sub;
+ else /* ... otherwise use the '-FULL' */
+ rxqual = meas_res->rxqual_full;
+
+ upper = lchan->top_acch_cap.rxqual;
+ if (upper > 2)
+ lower = upper - 2;
+ else
+ lower = 0;
+
+ if (rxqual >= upper)
+ lchan->top_acch_active = true;
+ else if (rxqual <= lower)
+ lchan->top_acch_active = false;
+
+ if (lchan->top_acch_active != old) {
+ LOGPLCHAN(lchan, DL1P, LOGL_DEBUG, "Temporary ACCH overpower: %s\n",
+ lchan->top_acch_active ? "inactive => active"
+ : "active => inactive");
+ }
+}
+
+static bool data_is_rr_meas_rep(const uint8_t *data)
+{
+ const struct gsm48_hdr *gh = (void *)(data + 5);
+ const uint8_t *lapdm_hdr = (void *)(data + 2);
+
+ /* LAPDm address field: SAPI=0, C/R=0, EA=1 */
+ if (lapdm_hdr[0] != 0x01)
+ return false;
+ /* LAPDm control field: U, func=UI */
+ if (lapdm_hdr[1] != 0x03)
+ return false;
+ /* Protocol discriminator: RR */
+ if (gh->proto_discr != GSM48_PDISC_RR)
+ return false;
+
+ switch (gh->msg_type) {
+ case GSM48_MT_RR_EXT_MEAS_REP:
+ case GSM48_MT_RR_MEAS_REP:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* Called every time a SACCH block is received from lower layers */
+void lchan_meas_handle_sacch(struct gsm_lchan *lchan, struct msgb *msg)
+{
+ const struct gsm48_meas_res *mr = NULL;
+ const struct gsm48_hdr *gh = NULL;
+ int timing_offset, rc;
+ bool dtxu_used = true; /* safe default assumption */
+ uint8_t ms_pwr;
+ uint8_t ms_ta;
+ int8_t ul_rssi;
+ int16_t ul_ci_cb;
+
+ if (msgb_l2len(msg) == GSM_MACBLOCK_LEN) {
+ /* Some brilliant engineer decided that the ordering of
+ * fields on the Um interface is different from the
+ * order of fields in RSL. See 3GPP TS 44.004 (section 7.2)
+ * vs. 3GPP TS 48.058 (section 9.3.10). */
+ const struct gsm_sacch_l1_hdr *l1h = msgb_l2(msg);
+ lchan->meas.l1_info.ms_pwr = l1h->ms_pwr;
+ lchan->meas.l1_info.fpc_epc = l1h->fpc_epc;
+ lchan->meas.l1_info.srr_sro = l1h->srr_sro;
+ lchan->meas.l1_info.ta = l1h->ta;
+ lchan->meas.flags |= LC_UL_M_F_L1_VALID;
+
+ /* Check if this is a Measurement Report */
+ if (data_is_rr_meas_rep(msgb_l2(msg))) {
+ /* Skip both L1 SACCH and LAPDm headers */
+ msg->l3h = (void *)(msg->l2h + 2 + 3);
+ gh = msgb_l3(msg);
+ }
+
+ ms_pwr = lchan->meas.l1_info.ms_pwr;
+ ms_ta = lchan->meas.l1_info.ta;
+ } else {
+ lchan->meas.flags &= ~LC_UL_M_F_L1_VALID;
+ ms_pwr = lchan->ms_power_ctrl.current;
+ ms_ta = lchan->ta_ctrl.current;
+ }
+
+ timing_offset = ms_to_valid(lchan) ? ms_to2rsl(lchan, ms_ta) : -1;
+ rc = rsl_tx_meas_res(lchan, msgb_l3(msg), msgb_l3len(msg), timing_offset);
+ if (rc == 0) /* Count successful transmissions */
+ lchan->meas.res_nr++;
+
+ /* Run control loops now that we have all the information: */
+ /* 3GPP TS 45.008 sec 4.2: UL L1 SACCH Header contains TA and
+ * MS_PWR used "for the last burst of the previous SACCH
+ * period". Since MS must use the values provided in DL SACCH
+ * starting at next meas period, the value of the "last burst"
+ * is actually the value used in the entire meas period. Since
+ * it contains info about the previous meas period, we want to
+ * feed the Control Loop with the measurements for the same
+ * period (the previous one), which is stored in lchan->meas(.ul_res):
+ */
+ if (gh && gh->msg_type == GSM48_MT_RR_MEAS_REP) {
+ mr = (const struct gsm48_meas_res *)gh->data;
+ if (gsm48_meas_res_is_valid(mr))
+ dtxu_used = mr->dtx_used;
+ }
+
+ if (dtxu_used) {
+ ul_rssi = rxlev2dbm(lchan->meas.ul_res.sub.rx_lev);
+ ul_ci_cb = lchan->meas.ul_ci_cb_sub;
+ } else {
+ ul_rssi = rxlev2dbm(lchan->meas.ul_res.full.rx_lev);
+ ul_ci_cb = lchan->meas.ul_ci_cb_full;
+ }
+ lchan_ms_ta_ctrl(lchan, ms_ta, lchan->meas.ms_toa256);
+ lchan_ms_pwr_ctrl(lchan, ms_pwr, ul_rssi, ul_ci_cb);
+ if (mr && gsm48_meas_res_is_valid(mr)) {
+ lchan_bs_pwr_ctrl(lchan, mr);
+ acch_overpower_active_decision(lchan, mr);
+ }
+
+ repeated_dl_facch_active_decision(lchan, mr);
+
+ /* Reset state for next iteration */
+ lchan->tch.dtx.dl_active = false;
+ lchan->meas.flags &= ~LC_UL_M_F_OSMO_EXT_VALID;
+ lchan->ms_t_offs = -1;
+ lchan->p_offs = -1;
+}
diff --git a/src/common/nm_bb_transc_fsm.c b/src/common/nm_bb_transc_fsm.c
index 936451ff..ca78256c 100644
--- a/src/common/nm_bb_transc_fsm.c
+++ b/src/common/nm_bb_transc_fsm.c
@@ -41,6 +41,17 @@
#define nm_bb_transc_fsm_state_chg(fi, NEXT_STATE) \
osmo_fsm_inst_state_chg(fi, NEXT_STATE, 0, 0)
+static void ev_dispatch_children(struct gsm_bts_bb_trx *bb_transc, uint32_t event)
+{
+ struct gsm_bts_trx *trx = gsm_bts_bb_trx_get_trx(bb_transc);
+ uint8_t tn;
+
+ for (tn = 0; tn < TRX_NR_TS; tn++) {
+ struct gsm_bts_trx_ts *ts = &trx->ts[tn];
+ osmo_fsm_inst_dispatch(ts->mo.fi, event, NULL);
+ }
+}
+
//////////////////////////
// FSM STATE ACTIONS
//////////////////////////
@@ -48,8 +59,12 @@
static void st_op_disabled_notinstalled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_bts_bb_trx *bb_transc = (struct gsm_bts_bb_trx *)fi->priv;
+ /* Reset state: */
+ TALLOC_FREE(bb_transc->mo.nm_attr);
+
+ bb_transc->mo.setattr_success = false;
bb_transc->mo.opstart_success = false;
- oml_mo_state_chg(&bb_transc->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_NOT_INSTALLED);
+ oml_mo_state_chg(&bb_transc->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_NOT_INSTALLED, NM_STATE_LOCKED);
}
static void st_op_disabled_notinstalled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -88,8 +103,9 @@ static void st_op_disabled_offline_on_enter(struct osmo_fsm_inst *fi, uint32_t p
struct gsm_bts_trx *trx = gsm_bts_bb_trx_get_trx(bb_transc);
int i;
+ bb_transc->mo.setattr_success = false;
bb_transc->mo.opstart_success = false;
- oml_mo_state_chg(&bb_transc->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE);
+ oml_mo_state_chg(&bb_transc->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE, -1);
if (prev_state == NM_BBTRANSC_ST_OP_ENABLED) {
for (i = 0; i < TRX_NR_TS; i++) {
@@ -103,10 +119,17 @@ static void st_op_disabled_offline(struct osmo_fsm_inst *fi, uint32_t event, voi
{
struct gsm_bts_bb_trx *bb_transc = (struct gsm_bts_bb_trx *)fi->priv;
struct gsm_bts_trx *trx = gsm_bts_bb_trx_get_trx(bb_transc);
+ struct nm_fsm_ev_setattr_data *setattr_data;
bool phy_state_connected;
bool rsl_link_connected;
switch (event) {
+ case NM_EV_SETATTR_ACK:
+ case NM_EV_SETATTR_NACK:
+ setattr_data = (struct nm_fsm_ev_setattr_data *)data;
+ bb_transc->mo.setattr_success = setattr_data->cause == 0;
+ oml_fom_ack_nack(setattr_data->msg, setattr_data->cause);
+ break;
case NM_EV_OPSTART_ACK:
bb_transc->mo.opstart_success = true;
oml_mo_opstart_ack(&bb_transc->mo);
@@ -139,14 +162,15 @@ static void st_op_disabled_offline(struct osmo_fsm_inst *fi, uint32_t event, voi
rsl_link_connected = true;
}
+ /* We so far don't expect any SetAttributes for this NM object */
if (rsl_link_connected && phy_state_connected &&
bb_transc->mo.opstart_success) {
nm_bb_transc_fsm_state_chg(fi, NM_BBTRANSC_ST_OP_ENABLED);
} else {
LOGPFSML(fi, LOGL_INFO, "Delay switch to operative state Enabled, wait for:%s%s%s\n",
- rsl_link_connected ? "": " rsl",
- phy_state_connected ? "": " phy",
- bb_transc->mo.opstart_success ? "": " opstart");
+ rsl_link_connected ? "" : " rsl",
+ phy_state_connected ? "" : " phy",
+ bb_transc->mo.opstart_success ? "" : " opstart");
}
}
@@ -157,7 +181,7 @@ static void st_op_enabled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state
struct gsm_bts_trx *trx = gsm_bts_bb_trx_get_trx(bb_transc);
uint8_t tn;
- oml_mo_state_chg(&bb_transc->mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK);
+ oml_mo_state_chg(&bb_transc->mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK, -1);
/* Mark Dependency TS as Offline (ready to be Opstarted) */
for (tn = 0; tn < TRX_NR_TS; tn++) {
struct gsm_bts_trx_ts *ts = &trx->ts[tn];
@@ -181,6 +205,28 @@ static void st_op_enabled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
nm_bb_transc_fsm_state_chg(fi, NM_BBTRANSC_ST_OP_DISABLED_OFFLINE);
}
+static void nm_bb_transc_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_bts_bb_trx *bb_transc = (struct gsm_bts_bb_trx *)fi->priv;
+
+ switch (event) {
+ case NM_EV_SHUTDOWN_START:
+ /* Announce we start shutting down */
+ oml_mo_state_chg(&bb_transc->mo, -1, -1, NM_STATE_SHUTDOWN);
+
+ /* Propagate event to children: */
+ ev_dispatch_children(bb_transc, event);
+ break;
+ case NM_EV_SHUTDOWN_FINISH:
+ /* Propagate event to children: */
+ ev_dispatch_children(bb_transc, event);
+ nm_bb_transc_fsm_state_chg(fi, NM_BBTRANSC_ST_OP_DISABLED_NOTINSTALLED);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
static struct osmo_fsm_state nm_bb_transc_fsm_states[] = {
[NM_BBTRANSC_ST_OP_DISABLED_NOTINSTALLED] = {
.in_event_mask =
@@ -188,8 +234,10 @@ static struct osmo_fsm_state nm_bb_transc_fsm_states[] = {
X(NM_EV_RSL_UP) |
X(NM_EV_RSL_DOWN) |
X(NM_EV_PHYLINK_UP) |
- X(NM_EV_PHYLINK_DOWN),
+ X(NM_EV_PHYLINK_DOWN) |
+ X(NM_EV_DISABLE),
.out_state_mask =
+ X(NM_BBTRANSC_ST_OP_DISABLED_NOTINSTALLED) |
X(NM_BBTRANSC_ST_OP_DISABLED_OFFLINE),
.name = "DISABLED_NOTINSTALLED",
.onenter = st_op_disabled_notinstalled_on_enter,
@@ -197,13 +245,17 @@ static struct osmo_fsm_state nm_bb_transc_fsm_states[] = {
},
[NM_BBTRANSC_ST_OP_DISABLED_OFFLINE] = {
.in_event_mask =
+ X(NM_EV_SETATTR_ACK) |
+ X(NM_EV_SETATTR_NACK) |
X(NM_EV_OPSTART_ACK) |
X(NM_EV_OPSTART_NACK) |
X(NM_EV_RSL_UP) |
X(NM_EV_RSL_DOWN) |
X(NM_EV_PHYLINK_UP) |
- X(NM_EV_PHYLINK_DOWN),
+ X(NM_EV_PHYLINK_DOWN) |
+ X(NM_EV_DISABLE),
.out_state_mask =
+ X(NM_BBTRANSC_ST_OP_DISABLED_NOTINSTALLED) |
X(NM_BBTRANSC_ST_OP_ENABLED),
.name = "DISABLED_OFFLINE",
.onenter = st_op_disabled_offline_on_enter,
@@ -212,8 +264,10 @@ static struct osmo_fsm_state nm_bb_transc_fsm_states[] = {
[NM_BBTRANSC_ST_OP_ENABLED] = {
.in_event_mask =
X(NM_EV_RSL_DOWN) |
- X(NM_EV_PHYLINK_DOWN),
+ X(NM_EV_PHYLINK_DOWN) |
+ X(NM_EV_DISABLE),
.out_state_mask =
+ X(NM_BBTRANSC_ST_OP_DISABLED_NOTINSTALLED) |
X(NM_BBTRANSC_ST_OP_DISABLED_OFFLINE),
.name = "ENABLED",
.onenter = st_op_enabled_on_enter,
@@ -226,6 +280,9 @@ struct osmo_fsm nm_bb_transc_fsm = {
.states = nm_bb_transc_fsm_states,
.num_states = ARRAY_SIZE(nm_bb_transc_fsm_states),
.event_names = nm_fsm_event_names,
+ .allstate_action = nm_bb_transc_allstate,
+ .allstate_event_mask = X(NM_EV_SHUTDOWN_START) |
+ X(NM_EV_SHUTDOWN_FINISH),
.log_subsys = DOML,
};
diff --git a/src/common/nm_bts_fsm.c b/src/common/nm_bts_fsm.c
index 12f1a612..063ffe84 100644
--- a/src/common/nm_bts_fsm.c
+++ b/src/common/nm_bts_fsm.c
@@ -35,12 +35,22 @@
#include <osmo-bts/rsl.h>
#include <osmo-bts/nm_common_fsm.h>
#include <osmo-bts/phy_link.h>
+#include <osmo-bts/cbch.h>
#define X(s) (1 << (s))
#define nm_bts_fsm_state_chg(fi, NEXT_STATE) \
osmo_fsm_inst_state_chg(fi, NEXT_STATE, 0, 0)
+static void ev_dispatch_children(struct gsm_bts *bts, uint32_t event)
+{
+ struct gsm_bts_trx *trx;
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ osmo_fsm_inst_dispatch(trx->mo.fi, event, NULL);
+ osmo_fsm_inst_dispatch(trx->bb_transc.mo.fi, event, NULL);
+ }
+}
+
//////////////////////////
// FSM STATE ACTIONS
//////////////////////////
@@ -48,17 +58,38 @@
static void st_op_disabled_notinstalled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_bts *bts = (struct gsm_bts *)fi->priv;
+ /* Reset state: */
+ bts->si_valid = 0;
+ TALLOC_FREE(bts->mo.nm_attr);
+ bts_cbch_reset(bts);
+
+ bts->mo.setattr_success = false;
bts->mo.opstart_success = false;
- oml_mo_state_chg(&bts->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_NOT_INSTALLED);
+ oml_mo_state_chg(&bts->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_NOT_INSTALLED, NM_STATE_LOCKED);
}
static void st_op_disabled_notinstalled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_bts *bts = (struct gsm_bts *)fi->priv;
+ struct gsm_bts_trx *trx;
switch (event) {
case NM_EV_SW_ACT:
oml_mo_tx_sw_act_rep(&bts->mo);
+
+ llist_for_each_entry(trx, &bts->trx_list, list) {
+ if (trx->bts->variant == BTS_OSMO_OMLDUMMY) /* In OMLDUMMY, phy=NULL */
+ continue;
+ /* During startup, phy_links are already opened, but if we are
+ * re-connecting, phy_link was closed when disconnected from
+ * previous BSC, so let's re-open it.
+ */
+ struct phy_instance *pinst = trx_phy_instance(trx);
+ struct phy_link *plink = pinst->phy_link;
+ if (phy_link_state_get(plink) == PHY_LINK_SHUTDOWN)
+ bts_model_phy_link_open(plink);
+ }
+
nm_bts_fsm_state_chg(fi, NM_BTS_ST_OP_DISABLED_OFFLINE);
return;
default:
@@ -69,15 +100,23 @@ static void st_op_disabled_notinstalled(struct osmo_fsm_inst *fi, uint32_t event
static void st_op_disabled_offline_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_bts *bts = (struct gsm_bts *)fi->priv;
+ bts->mo.setattr_success = false;
bts->mo.opstart_success = false;
- oml_mo_state_chg(&bts->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE);
+ oml_mo_state_chg(&bts->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE, -1);
}
static void st_op_disabled_offline(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_bts *bts = (struct gsm_bts *)fi->priv;
+ struct nm_fsm_ev_setattr_data *setattr_data;
switch (event) {
+ case NM_EV_SETATTR_ACK:
+ case NM_EV_SETATTR_NACK:
+ setattr_data = (struct nm_fsm_ev_setattr_data *)data;
+ bts->mo.setattr_success = setattr_data->cause == 0;
+ oml_fom_ack_nack(setattr_data->msg, setattr_data->cause);
+ break;
case NM_EV_OPSTART_ACK:
bts->mo.opstart_success = true;
oml_mo_opstart_ack(&bts->mo);
@@ -95,18 +134,41 @@ static void st_op_disabled_offline(struct osmo_fsm_inst *fi, uint32_t event, voi
static void st_op_enabled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_bts *bts = (struct gsm_bts *)fi->priv;
- oml_mo_state_chg(&bts->mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK);
+ oml_mo_state_chg(&bts->mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK, -1);
}
static void st_op_enabled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
}
+static void nm_bts_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_bts *bts = (struct gsm_bts *)fi->priv;
+
+ switch (event) {
+ case NM_EV_SHUTDOWN_START:
+ /* Announce we start shutting down */
+ oml_mo_state_chg(&bts->mo, -1, -1, NM_STATE_SHUTDOWN);
+
+ /* Propagate event to children: */
+ ev_dispatch_children(bts, event);
+ break;
+ case NM_EV_SHUTDOWN_FINISH:
+ /* Propagate event to children: */
+ ev_dispatch_children(bts, event);
+ nm_bts_fsm_state_chg(fi, NM_BTS_ST_OP_DISABLED_NOTINSTALLED);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
static struct osmo_fsm_state nm_bts_fsm_states[] = {
[NM_BTS_ST_OP_DISABLED_NOTINSTALLED] = {
.in_event_mask =
X(NM_EV_SW_ACT),
.out_state_mask =
+ X(NM_BTS_ST_OP_DISABLED_NOTINSTALLED) |
X(NM_BTS_ST_OP_DISABLED_OFFLINE),
.name = "DISABLED_NOTINSTALLED",
.onenter = st_op_disabled_notinstalled_on_enter,
@@ -114,9 +176,12 @@ static struct osmo_fsm_state nm_bts_fsm_states[] = {
},
[NM_BTS_ST_OP_DISABLED_OFFLINE] = {
.in_event_mask =
+ X(NM_EV_SETATTR_ACK) |
+ X(NM_EV_SETATTR_NACK) |
X(NM_EV_OPSTART_ACK) |
X(NM_EV_OPSTART_NACK),
.out_state_mask =
+ X(NM_BTS_ST_OP_DISABLED_NOTINSTALLED) |
X(NM_BTS_ST_OP_ENABLED),
.name = "DISABLED_OFFLINE",
.onenter = st_op_disabled_offline_on_enter,
@@ -124,7 +189,8 @@ static struct osmo_fsm_state nm_bts_fsm_states[] = {
},
[NM_BTS_ST_OP_ENABLED] = {
.in_event_mask = 0,
- .out_state_mask = 0,
+ .out_state_mask =
+ X(NM_BTS_ST_OP_DISABLED_NOTINSTALLED),
.name = "ENABLED",
.onenter = st_op_enabled_on_enter,
.action = st_op_enabled,
@@ -136,6 +202,9 @@ struct osmo_fsm nm_bts_fsm = {
.states = nm_bts_fsm_states,
.num_states = ARRAY_SIZE(nm_bts_fsm_states),
.event_names = nm_fsm_event_names,
+ .allstate_action = nm_bts_allstate,
+ .allstate_event_mask = X(NM_EV_SHUTDOWN_START) |
+ X(NM_EV_SHUTDOWN_FINISH),
.log_subsys = DOML,
};
diff --git a/src/common/nm_bts_sm_fsm.c b/src/common/nm_bts_sm_fsm.c
index dedbacbd..2d433153 100644
--- a/src/common/nm_bts_sm_fsm.c
+++ b/src/common/nm_bts_sm_fsm.c
@@ -41,6 +41,13 @@
#define nm_bts_sm_fsm_state_chg(fi, NEXT_STATE) \
osmo_fsm_inst_state_chg(fi, NEXT_STATE, 0, 0)
+
+static void ev_dispatch_children(struct gsm_bts_sm *site_mgr, uint32_t event)
+{
+ struct gsm_bts *bts = gsm_bts_sm_get_bts(site_mgr);
+ osmo_fsm_inst_dispatch(bts->mo.fi, event, NULL);
+}
+
//////////////////////////
// FSM STATE ACTIONS
//////////////////////////
@@ -48,8 +55,9 @@
static void st_op_disabled_notinstalled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_bts_sm *site_mgr = (struct gsm_bts_sm *)fi->priv;
+ site_mgr->mo.setattr_success = false;
site_mgr->mo.opstart_success = false;
- oml_mo_state_chg(&site_mgr->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_NOT_INSTALLED);
+ oml_mo_state_chg(&site_mgr->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_NOT_INSTALLED, NM_STATE_LOCKED);
}
static void st_op_disabled_notinstalled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -69,15 +77,23 @@ static void st_op_disabled_notinstalled(struct osmo_fsm_inst *fi, uint32_t event
static void st_op_disabled_offline_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_bts_sm *site_mgr = (struct gsm_bts_sm *)fi->priv;
+ site_mgr->mo.setattr_success = false;
site_mgr->mo.opstart_success = false;
- oml_mo_state_chg(&site_mgr->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE);
+ oml_mo_state_chg(&site_mgr->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE, -1);
}
static void st_op_disabled_offline(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_bts_sm *site_mgr = (struct gsm_bts_sm *)fi->priv;
+ struct nm_fsm_ev_setattr_data *setattr_data;
switch (event) {
+ case NM_EV_SETATTR_ACK:
+ case NM_EV_SETATTR_NACK:
+ setattr_data = (struct nm_fsm_ev_setattr_data *)data;
+ site_mgr->mo.setattr_success = setattr_data->cause == 0;
+ oml_fom_ack_nack(setattr_data->msg, setattr_data->cause);
+ break;
case NM_EV_OPSTART_ACK:
site_mgr->mo.opstart_success = true;
oml_mo_opstart_ack(&site_mgr->mo);
@@ -95,18 +111,41 @@ static void st_op_disabled_offline(struct osmo_fsm_inst *fi, uint32_t event, voi
static void st_op_enabled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_bts_sm *site_mgr = (struct gsm_bts_sm *)fi->priv;
- oml_mo_state_chg(&site_mgr->mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK);
+ oml_mo_state_chg(&site_mgr->mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK, -1);
}
static void st_op_enabled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
}
+static void nm_bts_sm_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_bts_sm *site_mgr = (struct gsm_bts_sm *)fi->priv;
+
+ switch (event) {
+ case NM_EV_SHUTDOWN_START:
+ /* Announce we start shutting down */
+ oml_mo_state_chg(&site_mgr->mo, -1, -1, NM_STATE_SHUTDOWN);
+
+ /* Propagate event to children: */
+ ev_dispatch_children(site_mgr, event);
+ break;
+ case NM_EV_SHUTDOWN_FINISH:
+ /* Propagate event to children: */
+ ev_dispatch_children(site_mgr, event);
+ nm_bts_sm_fsm_state_chg(fi, NM_BTS_SM_ST_OP_DISABLED_NOTINSTALLED);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
static struct osmo_fsm_state nm_bts_sm_fsm_states[] = {
[NM_BTS_SM_ST_OP_DISABLED_NOTINSTALLED] = {
.in_event_mask =
X(NM_EV_SW_ACT),
.out_state_mask =
+ X(NM_BTS_SM_ST_OP_DISABLED_NOTINSTALLED) |
X(NM_BTS_SM_ST_OP_DISABLED_OFFLINE),
.name = "DISABLED_NOTINSTALLED",
.onenter = st_op_disabled_notinstalled_on_enter,
@@ -114,9 +153,12 @@ static struct osmo_fsm_state nm_bts_sm_fsm_states[] = {
},
[NM_BTS_SM_ST_OP_DISABLED_OFFLINE] = {
.in_event_mask =
+ X(NM_EV_SETATTR_ACK) |
+ X(NM_EV_SETATTR_NACK) |
X(NM_EV_OPSTART_ACK) |
X(NM_EV_OPSTART_NACK),
.out_state_mask =
+ X(NM_BTS_SM_ST_OP_DISABLED_NOTINSTALLED) |
X(NM_BTS_SM_ST_OP_ENABLED),
.name = "DISABLED_OFFLINE",
.onenter = st_op_disabled_offline_on_enter,
@@ -124,7 +166,8 @@ static struct osmo_fsm_state nm_bts_sm_fsm_states[] = {
},
[NM_BTS_SM_ST_OP_ENABLED] = {
.in_event_mask = 0,
- .out_state_mask = 0,
+ .out_state_mask =
+ X(NM_BTS_SM_ST_OP_DISABLED_NOTINSTALLED),
.name = "ENABLED",
.onenter = st_op_enabled_on_enter,
.action = st_op_enabled,
@@ -136,6 +179,9 @@ struct osmo_fsm nm_bts_sm_fsm = {
.states = nm_bts_sm_fsm_states,
.num_states = ARRAY_SIZE(nm_bts_sm_fsm_states),
.event_names = nm_fsm_event_names,
+ .allstate_action = nm_bts_sm_allstate,
+ .allstate_event_mask = X(NM_EV_SHUTDOWN_START) |
+ X(NM_EV_SHUTDOWN_FINISH),
.log_subsys = DOML,
};
diff --git a/src/common/nm_channel_fsm.c b/src/common/nm_channel_fsm.c
index 15be6c6a..503ddfb9 100644
--- a/src/common/nm_channel_fsm.c
+++ b/src/common/nm_channel_fsm.c
@@ -55,8 +55,15 @@ static bool ts_can_be_enabled(const struct gsm_bts_trx_ts *ts)
static void st_op_disabled_notinstalled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_bts_trx_ts *ts = (struct gsm_bts_trx_ts *)fi->priv;
+ /* Reset state: */
+ gsm_ts_release(ts);
+ if (ts->vamos.peer)
+ gsm_ts_release(ts->vamos.peer);
+ TALLOC_FREE(ts->mo.nm_attr);
+
+ ts->mo.setattr_success = false;
ts->mo.opstart_success = false;
- oml_mo_state_chg(&ts->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_NOT_INSTALLED);
+ oml_mo_state_chg(&ts->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_NOT_INSTALLED, NM_STATE_LOCKED);
}
static void st_op_disabled_notinstalled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -80,14 +87,21 @@ static void st_op_disabled_dependency_on_enter(struct osmo_fsm_inst *fi, uint32_
{
struct gsm_bts_trx_ts *ts = (struct gsm_bts_trx_ts *)fi->priv;
ts->mo.opstart_success = false;
- oml_mo_state_chg(&ts->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_DEPENDENCY);
+ oml_mo_state_chg(&ts->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_DEPENDENCY, -1);
}
static void st_op_disabled_dependency(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_bts_trx_ts *ts = (struct gsm_bts_trx_ts *)fi->priv;
+ struct nm_fsm_ev_setattr_data *setattr_data;
switch (event) {
+ case NM_EV_SETATTR_ACK:
+ case NM_EV_SETATTR_NACK:
+ setattr_data = (struct nm_fsm_ev_setattr_data *)data;
+ ts->mo.setattr_success = setattr_data->cause == 0;
+ oml_fom_ack_nack(setattr_data->msg, setattr_data->cause);
+ break;
case NM_EV_OPSTART_ACK:
LOGPFSML(fi, LOGL_NOTICE, "BSC trying to activate TS while still in avail=dependency. "
"Allowing it to stay backward-compatible with older osmo-bts versions, but BSC is wrong.\n");
@@ -117,14 +131,21 @@ static void st_op_disabled_offline_on_enter(struct osmo_fsm_inst *fi, uint32_t p
{
struct gsm_bts_trx_ts *ts = (struct gsm_bts_trx_ts *)fi->priv;
ts->mo.opstart_success = false;
- oml_mo_state_chg(&ts->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE);
+ oml_mo_state_chg(&ts->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE, -1);
}
static void st_op_disabled_offline(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_bts_trx_ts *ts = (struct gsm_bts_trx_ts *)fi->priv;
+ struct nm_fsm_ev_setattr_data *setattr_data;
switch (event) {
+ case NM_EV_SETATTR_ACK:
+ case NM_EV_SETATTR_NACK:
+ setattr_data = (struct nm_fsm_ev_setattr_data *)data;
+ ts->mo.setattr_success = setattr_data->cause == 0;
+ oml_fom_ack_nack(setattr_data->msg, setattr_data->cause);
+ break;
case NM_EV_OPSTART_ACK:
ts->mo.opstart_success = true;
oml_mo_opstart_ack(&ts->mo);
@@ -147,7 +168,7 @@ static void st_op_disabled_offline(struct osmo_fsm_inst *fi, uint32_t event, voi
static void st_op_enabled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_bts_trx_ts *ts = (struct gsm_bts_trx_ts *)fi->priv;
- oml_mo_state_chg(&ts->mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK);
+ oml_mo_state_chg(&ts->mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK, -1);
}
static void st_op_enabled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -168,11 +189,29 @@ static void st_op_enabled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
}
}
+static void nm_chan_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_bts_trx_ts *ts = (struct gsm_bts_trx_ts *)fi->priv;
+
+ switch (event) {
+ case NM_EV_SHUTDOWN_START:
+ /* Announce we start shutting down */
+ oml_mo_state_chg(&ts->mo, -1, -1, NM_STATE_SHUTDOWN);
+ break;
+ case NM_EV_SHUTDOWN_FINISH:
+ nm_chan_fsm_state_chg(fi, NM_CHAN_ST_OP_DISABLED_NOTINSTALLED);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
static struct osmo_fsm_state nm_chan_fsm_states[] = {
[NM_CHAN_ST_OP_DISABLED_NOTINSTALLED] = {
.in_event_mask =
X(NM_EV_BBTRANSC_INSTALLED),
.out_state_mask =
+ X(NM_CHAN_ST_OP_DISABLED_NOTINSTALLED) |
X(NM_CHAN_ST_OP_DISABLED_OFFLINE) |
X(NM_CHAN_ST_OP_DISABLED_DEPENDENCY),
.name = "DISABLED_NOTINSTALLED",
@@ -181,6 +220,8 @@ static struct osmo_fsm_state nm_chan_fsm_states[] = {
},
[NM_CHAN_ST_OP_DISABLED_DEPENDENCY] = {
.in_event_mask =
+ X(NM_EV_SETATTR_ACK) |
+ X(NM_EV_SETATTR_NACK) |
X(NM_EV_OPSTART_ACK) | /* backward compatibility, buggy BSC */
X(NM_EV_OPSTART_NACK) |
X(NM_EV_BBTRANSC_ENABLED) |
@@ -188,6 +229,7 @@ static struct osmo_fsm_state nm_chan_fsm_states[] = {
X(NM_EV_BBTRANSC_DISABLED) |
X(NM_EV_RCARRIER_DISABLED),
.out_state_mask =
+ X(NM_CHAN_ST_OP_DISABLED_NOTINSTALLED) |
X(NM_CHAN_ST_OP_DISABLED_OFFLINE) |
X(NM_CHAN_ST_OP_ENABLED), /* backward compatibility, buggy BSC */
.name = "DISABLED_DEPENDENCY",
@@ -196,11 +238,14 @@ static struct osmo_fsm_state nm_chan_fsm_states[] = {
},
[NM_CHAN_ST_OP_DISABLED_OFFLINE] = {
.in_event_mask =
+ X(NM_EV_SETATTR_ACK) |
+ X(NM_EV_SETATTR_NACK) |
X(NM_EV_OPSTART_ACK) |
X(NM_EV_OPSTART_NACK) |
X(NM_EV_BBTRANSC_DISABLED) |
X(NM_EV_RCARRIER_DISABLED),
.out_state_mask =
+ X(NM_CHAN_ST_OP_DISABLED_NOTINSTALLED) |
X(NM_CHAN_ST_OP_ENABLED) |
X(NM_CHAN_ST_OP_DISABLED_DEPENDENCY),
.name = "DISABLED_OFFLINE",
@@ -213,6 +258,7 @@ static struct osmo_fsm_state nm_chan_fsm_states[] = {
X(NM_EV_RCARRIER_DISABLED) |
X(NM_EV_DISABLE),
.out_state_mask =
+ X(NM_CHAN_ST_OP_DISABLED_NOTINSTALLED) |
X(NM_CHAN_ST_OP_DISABLED_OFFLINE) |
X(NM_CHAN_ST_OP_DISABLED_DEPENDENCY),
.name = "ENABLED",
@@ -226,6 +272,9 @@ struct osmo_fsm nm_chan_fsm = {
.states = nm_chan_fsm_states,
.num_states = ARRAY_SIZE(nm_chan_fsm_states),
.event_names = nm_fsm_event_names,
+ .allstate_action = nm_chan_allstate,
+ .allstate_event_mask = X(NM_EV_SHUTDOWN_START) |
+ X(NM_EV_SHUTDOWN_FINISH),
.log_subsys = DOML,
};
diff --git a/src/common/nm_common_fsm.c b/src/common/nm_common_fsm.c
index 7273e22f..be11bef3 100644
--- a/src/common/nm_common_fsm.c
+++ b/src/common/nm_common_fsm.c
@@ -25,8 +25,12 @@
const struct value_string nm_fsm_event_names[] = {
{ NM_EV_SW_ACT, "SW_ACT" },
+ { NM_EV_SETATTR_ACK, "SETATTR_ACK" },
+ { NM_EV_SETATTR_NACK, "SETATTR_NACK" },
{ NM_EV_OPSTART_ACK, "OPSTART_ACK" },
{ NM_EV_OPSTART_NACK, "OPSTART_NACK" },
+ { NM_EV_SHUTDOWN_START, "SHUTDOWN_START" },
+ { NM_EV_SHUTDOWN_FINISH, "SHUTDOWN_FINISH" },
{ NM_EV_RSL_UP, "RSL_UP" },
{ NM_EV_RSL_DOWN, "RSL_DOWN" },
{ NM_EV_PHYLINK_UP, "PHYLINK_UP" },
diff --git a/src/common/nm_radio_carrier_fsm.c b/src/common/nm_radio_carrier_fsm.c
index 4cbdf682..88930dd7 100644
--- a/src/common/nm_radio_carrier_fsm.c
+++ b/src/common/nm_radio_carrier_fsm.c
@@ -48,8 +48,12 @@
static void st_op_disabled_notinstalled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct gsm_bts_trx *trx = (struct gsm_bts_trx *)fi->priv;
+ /* Reset state: */
+ TALLOC_FREE(trx->mo.nm_attr);
+
+ trx->mo.setattr_success = false;
trx->mo.opstart_success = false;
- oml_mo_state_chg(&trx->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_NOT_INSTALLED);
+ oml_mo_state_chg(&trx->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_NOT_INSTALLED, NM_STATE_LOCKED);
}
static void st_op_disabled_notinstalled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -81,8 +85,9 @@ static void st_op_disabled_offline_on_enter(struct osmo_fsm_inst *fi, uint32_t p
struct gsm_bts_trx *trx = (struct gsm_bts_trx *)fi->priv;
unsigned int i;
+ trx->mo.setattr_success = false;
trx->mo.opstart_success = false;
- oml_mo_state_chg(&trx->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE);
+ oml_mo_state_chg(&trx->mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE, -1);
if (prev_state == NM_RCARRIER_ST_OP_ENABLED) {
for (i = 0; i < TRX_NR_TS; i++) {
@@ -95,10 +100,17 @@ static void st_op_disabled_offline_on_enter(struct osmo_fsm_inst *fi, uint32_t p
static void st_op_disabled_offline(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct gsm_bts_trx *trx = (struct gsm_bts_trx *)fi->priv;
+ struct nm_fsm_ev_setattr_data *setattr_data;
bool phy_state_connected;
bool rsl_link_connected;
switch (event) {
+ case NM_EV_SETATTR_ACK:
+ case NM_EV_SETATTR_NACK:
+ setattr_data = (struct nm_fsm_ev_setattr_data *)data;
+ trx->mo.setattr_success = setattr_data->cause == 0;
+ oml_fom_ack_nack(setattr_data->msg, setattr_data->cause);
+ break;
case NM_EV_OPSTART_ACK:
trx->mo.opstart_success = true;
oml_mo_opstart_ack(&trx->mo);
@@ -131,13 +143,14 @@ static void st_op_disabled_offline(struct osmo_fsm_inst *fi, uint32_t event, voi
}
if (rsl_link_connected && phy_state_connected &&
- trx->mo.opstart_success) {
+ trx->mo.setattr_success && trx->mo.opstart_success) {
nm_rcarrier_fsm_state_chg(fi, NM_RCARRIER_ST_OP_ENABLED);
} else {
- LOGPFSML(fi, LOGL_INFO, "Delay switch to operative state Enabled, wait for:%s%s%s\n",
- rsl_link_connected ? "": " rsl",
- phy_state_connected ? "": " phy",
- trx->mo.opstart_success ? "": " opstart");
+ LOGPFSML(fi, LOGL_INFO, "Delay switch to operative state Enabled, wait for:%s%s%s%s\n",
+ rsl_link_connected ? "" : " rsl",
+ phy_state_connected ? "" : " phy",
+ trx->mo.setattr_success ? "" : " setattr",
+ trx->mo.opstart_success ? "" : " opstart");
}
}
@@ -147,7 +160,7 @@ static void st_op_enabled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state
struct gsm_bts_trx *trx = (struct gsm_bts_trx *)fi->priv;
unsigned int tn;
- oml_mo_state_chg(&trx->mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK);
+ oml_mo_state_chg(&trx->mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK, -1);
/* Mark Dependency TS as Offline (ready to be Opstarted) */
for (tn = 0; tn < TRX_NR_TS; tn++) {
struct gsm_bts_trx_ts *ts = &trx->ts[tn];
@@ -171,6 +184,23 @@ static void st_op_enabled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
nm_rcarrier_fsm_state_chg(fi, NM_RCARRIER_ST_OP_DISABLED_OFFLINE);
}
+static void nm_rcarrier_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+ struct gsm_bts_trx *trx = (struct gsm_bts_trx *)fi->priv;
+
+ switch (event) {
+ case NM_EV_SHUTDOWN_START:
+ /* Announce we start shutting down */
+ oml_mo_state_chg(&trx->mo, -1, -1, NM_STATE_SHUTDOWN);
+ break;
+ case NM_EV_SHUTDOWN_FINISH:
+ nm_rcarrier_fsm_state_chg(fi, NM_RCARRIER_ST_OP_DISABLED_NOTINSTALLED);
+ break;
+ default:
+ OSMO_ASSERT(false);
+ }
+}
+
static struct osmo_fsm_state nm_rcarrier_fsm_states[] = {
[NM_RCARRIER_ST_OP_DISABLED_NOTINSTALLED] = {
.in_event_mask =
@@ -178,8 +208,10 @@ static struct osmo_fsm_state nm_rcarrier_fsm_states[] = {
X(NM_EV_RSL_UP) |
X(NM_EV_RSL_DOWN) |
X(NM_EV_PHYLINK_UP) |
- X(NM_EV_PHYLINK_DOWN),
+ X(NM_EV_PHYLINK_DOWN) |
+ X(NM_EV_DISABLE),
.out_state_mask =
+ X(NM_RCARRIER_ST_OP_DISABLED_NOTINSTALLED) |
X(NM_RCARRIER_ST_OP_DISABLED_OFFLINE),
.name = "DISABLED_NOTINSTALLED",
.onenter = st_op_disabled_notinstalled_on_enter,
@@ -187,13 +219,17 @@ static struct osmo_fsm_state nm_rcarrier_fsm_states[] = {
},
[NM_RCARRIER_ST_OP_DISABLED_OFFLINE] = {
.in_event_mask =
+ X(NM_EV_SETATTR_ACK) |
+ X(NM_EV_SETATTR_NACK) |
X(NM_EV_OPSTART_ACK) |
X(NM_EV_OPSTART_NACK) |
X(NM_EV_RSL_UP) |
X(NM_EV_RSL_DOWN) |
X(NM_EV_PHYLINK_UP) |
- X(NM_EV_PHYLINK_DOWN),
+ X(NM_EV_PHYLINK_DOWN) |
+ X(NM_EV_DISABLE),
.out_state_mask =
+ X(NM_RCARRIER_ST_OP_DISABLED_NOTINSTALLED) |
X(NM_RCARRIER_ST_OP_ENABLED),
.name = "DISABLED_OFFLINE",
.onenter = st_op_disabled_offline_on_enter,
@@ -202,8 +238,10 @@ static struct osmo_fsm_state nm_rcarrier_fsm_states[] = {
[NM_RCARRIER_ST_OP_ENABLED] = {
.in_event_mask =
X(NM_EV_RSL_DOWN) |
- X(NM_EV_PHYLINK_DOWN),
+ X(NM_EV_PHYLINK_DOWN) |
+ X(NM_EV_DISABLE),
.out_state_mask =
+ X(NM_RCARRIER_ST_OP_DISABLED_NOTINSTALLED) |
X(NM_RCARRIER_ST_OP_DISABLED_OFFLINE),
.name = "ENABLED",
.onenter = st_op_enabled_on_enter,
@@ -216,6 +254,9 @@ struct osmo_fsm nm_rcarrier_fsm = {
.states = nm_rcarrier_fsm_states,
.num_states = ARRAY_SIZE(nm_rcarrier_fsm_states),
.event_names = nm_fsm_event_names,
+ .allstate_action = nm_rcarrier_allstate,
+ .allstate_event_mask = X(NM_EV_SHUTDOWN_START) |
+ X(NM_EV_SHUTDOWN_FINISH),
.log_subsys = DOML,
};
diff --git a/src/common/oml.c b/src/common/oml.c
index 819b033a..a434ad30 100644
--- a/src/common/oml.c
+++ b/src/common/oml.c
@@ -340,12 +340,13 @@ void oml_mo_state_init(struct gsm_abis_mo *mo, int op_state, int avail_state)
mo->nm_state.operational = op_state;
}
-int oml_mo_state_chg(struct gsm_abis_mo *mo, int op_state, int avail_state)
+int oml_mo_state_chg(struct gsm_abis_mo *mo, int op_state, int avail_state, int adm_state)
{
int rc = 0;
if ((op_state != -1 && mo->nm_state.operational != op_state) ||
- (avail_state != -1 && mo->nm_state.availability != avail_state)) {
+ (avail_state != -1 && mo->nm_state.availability != avail_state) ||
+ (adm_state != -1 && mo->nm_state.administrative != adm_state)) {
if (avail_state != -1) {
LOGP(DOML, LOGL_INFO, "%s AVAIL STATE %s -> %s\n",
gsm_abis_mo_name(mo),
@@ -354,14 +355,26 @@ int oml_mo_state_chg(struct gsm_abis_mo *mo, int op_state, int avail_state)
mo->nm_state.availability = avail_state;
}
if (op_state != -1) {
+ struct nm_statechg_signal_data nsd;
LOGP(DOML, LOGL_INFO, "%s OPER STATE %s -> %s\n",
gsm_abis_mo_name(mo),
abis_nm_opstate_name(mo->nm_state.operational),
abis_nm_opstate_name(op_state));
+ nsd.mo = mo;
+ nsd.old_state = mo->nm_state.operational;
+ nsd.new_state = op_state;
mo->nm_state.operational = op_state;
- osmo_signal_dispatch(SS_GLOBAL, S_NEW_OP_STATE, NULL);
+ osmo_signal_dispatch(SS_GLOBAL, S_NEW_OP_STATE, &nsd);
+ }
+ if (adm_state != -1) {
+ LOGP(DOML, LOGL_INFO, "%s ADMIN STATE %s -> %s\n",
+ gsm_abis_mo_name(mo),
+ abis_nm_admin_name(mo->nm_state.administrative),
+ abis_nm_admin_name(adm_state));
+ mo->nm_state.administrative = adm_state;
}
+
/* send state change report */
rc = oml_tx_state_changed(mo);
}
@@ -585,7 +598,7 @@ static int oml_rx_set_bts_attr(struct gsm_bts *bts, struct msgb *msg)
/* 9.4.25 Interference Level Boundaries */
if (TLVP_PRES_LEN(&tp, NM_ATT_INTERF_BOUND, 6)) {
payload = TLVP_VAL(&tp, NM_ATT_INTERF_BOUND);
- for (i = 0; i < 6; i++) {
+ for (i = 0; i < ARRAY_SIZE(bts->interference.boundary); i++) {
const int16_t boundary = payload[i];
bts->interference.boundary[i] = -1 * boundary;
}
@@ -661,7 +674,10 @@ static int oml_rx_set_bts_attr(struct gsm_bts *bts, struct msgb *msg)
/* 9.4.11 CCCH Load Indication Period */
if (TLVP_PRES_LEN(&tp, NM_ATT_CCCH_L_I_P, 1)) {
bts->load.ccch.load_ind_period = *TLVP_VAL(&tp, NM_ATT_CCCH_L_I_P);
- load_timer_start(bts);
+ if (load_timer_is_running(bts)) {
+ load_timer_stop(bts);
+ load_timer_start(bts);
+ }
}
/* 9.4.44 RACH Busy Threshold */
diff --git a/src/common/pcu_sock.c b/src/common/pcu_sock.c
index 03f1a05d..0018acb6 100644
--- a/src/common/pcu_sock.c
+++ b/src/common/pcu_sock.c
@@ -559,23 +559,35 @@ int pcu_tx_time_ind(uint32_t fn)
return pcu_sock_send(&bts_gsmnet, msg);
}
-int pcu_tx_interf_ind(uint8_t bts_nr, uint8_t trx_nr, uint32_t fn,
- const uint8_t *pdch_interf)
+int pcu_tx_interf_ind(const struct gsm_bts_trx *trx, uint32_t fn)
{
struct gsm_pcu_if_interf_ind *interf_ind;
struct gsm_pcu_if *pcu_prim;
struct msgb *msg;
+ unsigned int tn;
- msg = pcu_msgb_alloc(PCU_IF_MSG_INTERF_IND, bts_nr);
+ msg = pcu_msgb_alloc(PCU_IF_MSG_INTERF_IND, trx->bts->nr);
if (!msg)
return -ENOMEM;
pcu_prim = (struct gsm_pcu_if *) msg->data;
interf_ind = &pcu_prim->u.interf_ind;
- interf_ind->trx_nr = trx_nr;
+ interf_ind->trx_nr = trx->nr;
interf_ind->fn = fn;
- memcpy(&interf_ind->interf[0], &pdch_interf[0],
- sizeof(interf_ind->interf));
+
+ for (tn = 0; tn < ARRAY_SIZE(trx->ts); tn++) {
+ const struct gsm_bts_trx_ts *ts = &trx->ts[tn];
+ const struct gsm_lchan *lchan = &ts->lchan[0];
+
+ if (ts->mo.nm_state.operational != NM_OPSTATE_ENABLED)
+ continue;
+ if (ts->mo.nm_state.availability != NM_AVSTATE_OK)
+ continue;
+ if (ts_pchan(ts) != GSM_PCHAN_PDCH)
+ continue;
+
+ interf_ind->interf[tn] = -1 * lchan->meas.interf_meas_avg_dbm;
+ }
return pcu_sock_send(&bts_gsmnet, msg);
}
diff --git a/src/common/power_control.c b/src/common/power_control.c
index 4f5d15e3..42d9d07f 100644
--- a/src/common/power_control.c
+++ b/src/common/power_control.c
@@ -36,6 +36,8 @@
/* We don't want to deal with floating point, so we scale up */
#define EWMA_SCALE_FACTOR 100
+/* EWMA_SCALE_FACTOR/2 = +50: Round to nearest value when downscaling, otherwise floor() is applied. */
+#define EWMA_ROUND_FACTOR (EWMA_SCALE_FACTOR / 2)
/* Base Low-Pass Single-Pole IIR Filter (EWMA) formula:
*
@@ -84,49 +86,45 @@ static int do_pf_ewma(const struct gsm_power_ctrl_meas_params *mp,
return Val;
}
- *Avg100 += A * (Val - *Avg100 / EWMA_SCALE_FACTOR);
- return *Avg100 / EWMA_SCALE_FACTOR;
+ *Avg100 += A * (Val - (*Avg100 + EWMA_ROUND_FACTOR) / EWMA_SCALE_FACTOR);
+ return (*Avg100 + EWMA_ROUND_FACTOR) / EWMA_SCALE_FACTOR;
}
/* Calculate target RxLev value from lower/upper thresholds */
#define CALC_TARGET(mp) \
- (mp.lower_thresh + mp.upper_thresh) / 2
+ ((mp).lower_thresh + (mp).upper_thresh) / 2
-/* Calculate a 'delta' value (for the given MS/BS power control state and parameters)
- * to be applied to the current Tx power level to approach the target level. */
-static int calc_delta(const struct gsm_power_ctrl_params *params,
- struct lchan_power_ctrl_state *state,
- const int rxlev_dbm)
+static int do_avg_algo(const struct gsm_power_ctrl_meas_params *mp,
+ struct gsm_power_ctrl_meas_proc_state *mps,
+ const int val)
{
- int rxlev_dbm_avg;
- uint8_t rxlev_avg;
- int delta;
-
- /* Filter RxLev value to reduce unnecessary Tx power oscillations */
- switch (params->rxlev_meas.algo) {
+ int val_avg;
+ switch (mp->algo) {
case GSM_PWR_CTRL_MEAS_AVG_ALGO_OSMO_EWMA:
- rxlev_dbm_avg = do_pf_ewma(&params->rxlev_meas,
- &state->rxlev_meas_proc,
- rxlev_dbm);
+ val_avg = do_pf_ewma(mp, mps, val);
break;
/* TODO: implement other pre-processing methods */
case GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE:
default:
/* No filtering (pass through) */
- rxlev_dbm_avg = rxlev_dbm;
+ val_avg = val;
}
-
- /* FIXME: avoid this conversion, accept RxLev as-is */
- rxlev_avg = dbm2rxlev(rxlev_dbm_avg);
+ return val_avg;
+}
+/* Calculate a 'delta' value (for the given MS/BS power control parameters)
+ * to be applied to the current Tx power level to approach the target level. */
+static int calc_delta_rxlev(const struct gsm_power_ctrl_params *params, const uint8_t rxlev)
+{
+ int delta;
/* Check if RxLev is within the threshold window */
- if (rxlev_avg >= params->rxlev_meas.lower_thresh &&
- rxlev_avg <= params->rxlev_meas.upper_thresh)
+ if (rxlev >= params->rxlev_meas.lower_thresh &&
+ rxlev <= params->rxlev_meas.upper_thresh)
return 0;
/* How many dBs measured power should be increased (+) or decreased (-)
* to reach expected power. */
- delta = CALC_TARGET(params->rxlev_meas) - rxlev_avg;
+ delta = CALC_TARGET(params->rxlev_meas) - rxlev;
/* Don't ever change more than PWR_{LOWER,RAISE}_MAX_DBM during one loop
* iteration, i.e. reduce the speed at which the MS transmit power can
@@ -139,14 +137,57 @@ static int calc_delta(const struct gsm_power_ctrl_params *params,
return delta;
}
+/* Shall we skip current block based on configured interval? */
+static bool ctrl_interval_skip_block(const struct gsm_power_ctrl_params *params,
+ struct lchan_power_ctrl_state *state)
+{
+ /* Power control interval: how many blocks do we skip? */
+ if (state->skip_block_num-- > 0)
+ return true;
+
+ /* Reset the number of SACCH blocks to be skipped:
+ * ctrl_interval=0 => 0 blocks to skip,
+ * ctrl_interval=1 => 1 blocks to skip,
+ * ctrl_interval=2 => 3 blocks to skip,
+ * so basically ctrl_interval * 2 - 1. */
+ state->skip_block_num = params->ctrl_interval * 2 - 1;
+ return false;
+}
+
+static const struct gsm_power_ctrl_meas_params *lchan_get_ci_thresholds(const struct gsm_lchan *lchan)
+{
+ const struct gsm_power_ctrl_params *params = lchan->ms_power_ctrl.dpc_params;
+
+ switch (lchan->type) {
+ case GSM_LCHAN_SDCCH:
+ return &params->ci_sdcch_meas;
+ case GSM_LCHAN_PDTCH:
+ return &params->ci_gprs_meas;
+ case GSM_LCHAN_TCH_F:
+ if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR)
+ return &params->ci_amr_fr_meas;
+ else
+ return &params->ci_fr_meas;
+ case GSM_LCHAN_TCH_H:
+ if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR)
+ return &params->ci_amr_hr_meas;
+ else
+ return &params->ci_hr_meas;
+ default:
+ OSMO_ASSERT(0);
+ }
+}
+
/*! compute the new MS POWER LEVEL communicated to the MS and store it in lchan.
* \param lchan logical channel for which to compute (and in which to store) new power value.
* \param[in] ms_power_lvl MS Power Level received from Uplink L1 SACCH Header in SACCH block.
* \param[in] ul_rssi_dbm Signal level of the received SACCH block, in dBm.
+ * \param[in] ul_lqual_cb C/I of the received SACCH block, in dB.
*/
int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan,
const uint8_t ms_power_lvl,
- const int8_t ul_rssi_dbm)
+ const int8_t ul_rssi_dbm,
+ const int16_t ul_lqual_cb)
{
struct lchan_power_ctrl_state *state = &lchan->ms_power_ctrl;
const struct gsm_power_ctrl_params *params = state->dpc_params;
@@ -155,23 +196,20 @@ int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan,
enum gsm_band band = bts->band;
int8_t new_power_lvl; /* TS 05.05 power level */
int8_t ms_dbm, new_dbm, current_dbm, bsc_max_dbm;
+ uint8_t rxlev_avg;
+ int16_t ul_lqual_cb_avg;
+ const struct gsm_power_ctrl_meas_params *ci_meas;
+ bool ignore, ci_on;
if (!trx_ms_pwr_ctrl_is_osmo(trx))
return 0;
if (params == NULL)
return 0;
- /* Power control interval: how many blocks do we skip? */
- if (state->skip_block_num-- > 0)
+ /* Shall we skip current block based on configured interval? */
+ if (ctrl_interval_skip_block(params, state))
return 0;
- /* Reset the number of SACCH blocks to be skipped:
- * ctrl_interval=0 => 0 blocks to skip,
- * ctrl_interval=1 => 1 blocks to skip,
- * ctrl_interval=2 => 3 blocks to skip,
- * so basically ctrl_interval * 2 - 1. */
- state->skip_block_num = params->ctrl_interval * 2 - 1;
-
ms_dbm = ms_pwr_dbm(band, ms_power_lvl);
if (ms_dbm < 0) {
LOGPLCHAN(lchan, DLOOP, LOGL_NOTICE,
@@ -187,8 +225,24 @@ int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan,
return 0;
}
- /* Calculate the new Tx power value (in dBm) */
- new_dbm = ms_dbm + calc_delta(params, state, ul_rssi_dbm);
+ ci_meas = lchan_get_ci_thresholds(lchan);
+
+ /* Is C/I based algo enabled by config?
+ * FIXME: this can later be generalized when properly implementing P & N counting. */
+ ci_on = ci_meas->lower_cmp_n && ci_meas->upper_cmp_n;
+
+ ul_lqual_cb_avg = do_avg_algo(ci_meas, &state->ci_meas_proc, ul_lqual_cb);
+ rxlev_avg = do_avg_algo(&params->rxlev_meas, &state->rxlev_meas_proc, dbm2rxlev(ul_rssi_dbm));
+
+ /* If computed C/I is enabled and out of acceptable thresholds: */
+ if (ci_on && ul_lqual_cb_avg < ci_meas->lower_thresh * 10) {
+ new_dbm = ms_dbm + params->inc_step_size_db;
+ } else if (ci_on && ul_lqual_cb_avg > ci_meas->upper_thresh * 10) {
+ new_dbm = ms_dbm - params->red_step_size_db;
+ } else {
+ /* Calculate the new Tx power value (in dBm) */
+ new_dbm = ms_dbm + calc_delta_rxlev(params, rxlev_avg);
+ }
/* Make sure new_dbm is never negative. ms_pwr_ctl_lvl() can later on
cope with any unsigned dbm value, regardless of band minimal value. */
@@ -207,23 +261,46 @@ int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan,
return 0;
}
- /* FIXME: this is only needed for logging, print thresholds instead */
- int target_dbm = rxlev2dbm(CALC_TARGET(params->rxlev_meas));
+ current_dbm = ms_pwr_dbm(band, state->current);
- if (state->current == new_power_lvl) {
- LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "Keeping MS power at control level %d, %d dBm "
- "(rx-ms-pwr-lvl %" PRIu8 ", max-ms-pwr-lvl %" PRIu8 ", rx-current %d dBm, rx-target %d dBm)\n",
- new_power_lvl, new_dbm, ms_power_lvl, state->max,
- ul_rssi_dbm, target_dbm);
+ /* In this Power Control Loop, we infer a new good MS Power Level based
+ * on the previous MS Power Level announced by the MS (not the previous
+ * one we requested!) together with the related computed measurements.
+ * Hence, and since we allow for several good MS Power Levels falling into our
+ * thresholds, we could finally converge into an oscillation loop where
+ * the MS bounces between 2 different correct MS Power levels all the
+ * time, due to the fact that we "accept" and "request back" whatever
+ * good MS Power Level we received from the MS, but at that time the MS
+ * will be transmitting using the previous MS Power Level we
+ * requested, which we will later "accept" and "request back" on next loop
+ * iteration. As a result MS effectively bounces between those 2 MS
+ * Power Levels.
+ * In order to fix this permanent oscillation, if current MS_PWR used/announced
+ * by MS is good ("ms_dbm == new_dbm", hence within thresholds and no change
+ * required) but has higher Tx power than the one we last requested, we ignore
+ * it and keep requesting for one with lower Tx power. This way we converge to
+ * the lowest good Tx power avoiding oscillating over values within thresholds.
+ */
+ ignore = (ms_dbm == new_dbm && ms_dbm > current_dbm);
+
+ if (state->current == new_power_lvl || ignore) {
+ LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "Keeping MS power at control level %d (%d dBm): "
+ "ms-pwr-lvl[curr %" PRIu8 ", max %" PRIu8 "], RSSI[curr %d, avg %d, thresh %d..%d] dBm,"
+ " C/I[curr %d, avg %d, thresh %d..%d] dB\n",
+ new_power_lvl, new_dbm, ms_power_lvl, state->max, ul_rssi_dbm, rxlev2dbm(rxlev_avg),
+ rxlev2dbm(params->rxlev_meas.lower_thresh), rxlev2dbm(params->rxlev_meas.upper_thresh),
+ ul_lqual_cb/10, ul_lqual_cb_avg/10, ci_meas->lower_thresh, ci_meas->upper_thresh);
return 0;
}
- current_dbm = ms_pwr_dbm(band, state->current);
- LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "%s MS power from control level %d (%d dBm) to %d, %d dBm "
- "(rx-ms-pwr-lvl %" PRIu8 ", max-ms-pwr-lvl %" PRIu8 ", rx-current %d dBm, rx-target %d dBm)\n",
+ LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "%s MS power control level %d (%d dBm) => %d (%d dBm): "
+ "ms-pwr-lvl[curr %" PRIu8 ", max %" PRIu8 "], RSSI[curr %d, avg %d, thresh %d..%d] dBm,"
+ " C/I[curr %d, avg %d, thresh %d..%d] dB\n",
(new_dbm > current_dbm) ? "Raising" : "Lowering",
- state->current, current_dbm, new_power_lvl, new_dbm,
- ms_power_lvl, state->max, ul_rssi_dbm, target_dbm);
+ state->current, current_dbm, new_power_lvl, new_dbm, ms_power_lvl,
+ state->max, ul_rssi_dbm, rxlev2dbm(rxlev_avg),
+ rxlev2dbm(params->rxlev_meas.lower_thresh), rxlev2dbm(params->rxlev_meas.upper_thresh),
+ ul_lqual_cb/10, ul_lqual_cb_avg/10, ci_meas->lower_thresh, ci_meas->upper_thresh);
/* store the resulting new MS power level in the lchan */
state->current = new_power_lvl;
@@ -234,129 +311,265 @@ int lchan_ms_pwr_ctrl(struct gsm_lchan *lchan,
/*! compute the new Downlink attenuation value for the given logical channel.
* \param lchan logical channel for which to compute (and in which to store) new power value.
- * \param[in] gh pointer to the beginning of (presumably) a Measurement Report.
+ * \param[in] mr pointer to a *valid* Measurement Report.
*/
int lchan_bs_pwr_ctrl(struct gsm_lchan *lchan,
- const struct gsm48_hdr *gh)
+ const struct gsm48_meas_res *mr)
{
struct lchan_power_ctrl_state *state = &lchan->bs_power_ctrl;
const struct gsm_power_ctrl_params *params = state->dpc_params;
- uint8_t rxqual_full, rxqual_sub;
- uint8_t rxlev_full, rxlev_sub;
- uint8_t rxqual, rxlev;
- int delta, new;
+ uint8_t rxqual, rxqual_avg, rxlev, rxlev_avg;
+ int new_att;
/* Check if dynamic BS Power Control is enabled */
if (params == NULL)
return 0;
- /* Check if this is a Measurement Report */
- if (gh->proto_discr != GSM48_PDISC_RR)
- return 0;
- if (gh->msg_type != GSM48_MT_RR_MEAS_REP)
- return 0;
-
- /* Check if the measurement results are valid */
- if ((gh->data[1] & 0x40) == 0x40) {
- LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG,
- "The measurement results are not valid\n");
- return 0;
- }
-
- /* See 3GPP TS 44.018, section 10.5.2.20 */
- rxqual_full = (gh->data[2] >> 4) & 0x7;
- rxqual_sub = (gh->data[2] >> 1) & 0x7;
-
- rxlev_full = gh->data[0] & 0x3f;
- rxlev_sub = gh->data[1] & 0x3f;
LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG, "Rx DL Measurement Report: "
"RXLEV-FULL(%02u), RXQUAL-FULL(%u), "
"RXLEV-SUB(%02u), RXQUAL-SUB(%u), "
"DTx is %s => using %s\n",
- rxlev_full, rxqual_full, rxlev_sub, rxqual_sub,
+ mr->rxlev_full, mr->rxqual_full,
+ mr->rxlev_sub, mr->rxqual_sub,
lchan->tch.dtx.dl_active ? "enabled" : "disabled",
lchan->tch.dtx.dl_active ? "SUB" : "FULL");
- /* Power control interval: how many blocks do we skip? */
- if (state->skip_block_num-- > 0)
+ /* Shall we skip current block based on configured interval? */
+ if (ctrl_interval_skip_block(params, state))
return 0;
- /* Reset the number of SACCH blocks to be skipped:
- * ctrl_interval=0 => 0 blocks to skip,
- * ctrl_interval=1 => 1 blocks to skip,
- * ctrl_interval=2 => 3 blocks to skip,
- * so basically ctrl_interval * 2 - 1. */
- state->skip_block_num = params->ctrl_interval * 2 - 1;
-
/* If DTx is active on Downlink, use the '-SUB' */
if (lchan->tch.dtx.dl_active) {
- rxqual = rxqual_sub;
- rxlev = rxlev_sub;
+ rxqual = mr->rxqual_sub;
+ rxlev = mr->rxlev_sub;
} else { /* ... otherwise use the '-FULL' */
- rxqual = rxqual_full;
- rxlev = rxlev_full;
+ rxqual = mr->rxqual_full;
+ rxlev = mr->rxlev_full;
}
+ rxlev_avg = do_avg_algo(&params->rxlev_meas, &state->rxlev_meas_proc, rxlev);
+ rxqual_avg = do_avg_algo(&params->rxqual_meas, &state->rxqual_meas_proc, rxqual);
/* If RxQual > L_RXQUAL_XX_P, try to increase Tx power */
- if (rxqual > params->rxqual_meas.lower_thresh) {
- uint8_t old = state->current;
-
- /* Tx power has reached the maximum, nothing to do */
- if (state->current == 0)
- return 0;
-
+ if (rxqual_avg > params->rxqual_meas.lower_thresh) {
/* Increase Tx power by reducing Tx attenuation */
- if (state->current >= params->inc_step_size_db)
- state->current -= params->inc_step_size_db;
- else
- state->current = 0;
-
- LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "Reducing Downlink attenuation: "
- "%u -> %d dB due to RxQual %u worse than L_RXQUAL_XX_P %u\n",
- old, state->current, rxqual, params->rxqual_meas.lower_thresh);
- return 1;
+ new_att = state->current - params->inc_step_size_db;
+ } else if (rxqual_avg < params->rxqual_meas.upper_thresh) {
+ /* Increase Tx power by Increasing Tx attenuation */
+ new_att = state->current + params->red_step_size_db;
+ } else {
+ /* Basic signal transmission / reception formula:
+ *
+ * RxLev = TxPwr - (PathLoss + TxAtt)
+ *
+ * Here we want to change RxLev at the MS side, so:
+ *
+ * RxLev + Delta = TxPwr - (PathLoss + TxAtt) + Delta
+ *
+ * The only parameter we can change here is TxAtt, so:
+ *
+ * RxLev + Delta = TxPwr - PathLoss - TxAtt + Delta
+ * RxLev + Delta = TxPwr - PathLoss - (TxAtt - Delta)
+ */
+ new_att = state->current - calc_delta_rxlev(params, rxlev_avg);
}
- /* Calculate a 'delta' for the current attenuation level */
- delta = calc_delta(params, state, rxlev2dbm(rxlev));
-
- /* Basic signal transmission / reception formula:
- *
- * RxLev = TxPwr - (PathLoss + TxAtt)
- *
- * Here we want to change RxLev at the MS side, so:
- *
- * RxLev + Delta = TxPwr - (PathLoss + TxAtt) + Delta
- *
- * The only parameter we can change here is TxAtt, so:
- *
- * RxLev + Delta = TxPwr - PathLoss - TxAtt + Delta
- * RxLev + Delta = TxPwr - PathLoss - (TxAtt - Delta)
- */
- new = state->current - delta;
- if (new > state->max)
- new = state->max;
- if (new < 0)
- new = 0;
-
- if (state->current != new) {
- LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "Changing Downlink attenuation: "
- "%u -> %u dB (maximum %u dB, suggested delta %d dB, "
- "RxLev current %u (%d dBm), thresholds %u .. %u)\n",
- state->current, new, state->max,
- -delta, rxlev, rxlev2dbm(rxlev),
- params->rxlev_meas.lower_thresh,
- params->rxlev_meas.upper_thresh);
- state->current = new;
- return 1;
- } else {
- LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "Keeping Downlink attenuation "
- "at %u dB (maximum %u dB, suggested delta %d dB, "
- "RxLev current %u (%d dBm), thresholds %u .. %u)\n",
- state->current, state->max,
- -delta, rxlev, rxlev2dbm(rxlev),
- params->rxlev_meas.lower_thresh,
- params->rxlev_meas.upper_thresh);
+ /* Make sure new TxAtt is never negative: */
+ if (new_att < 0)
+ new_att = 0;
+
+ /* Don't ask for higher TxAtt than permitted: */
+ if (new_att > state->max)
+ new_att = state->max;
+
+ if (state->current == new_att) {
+ LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "Keeping DL attenuation at %u dB: "
+ "max %u dB, RSSI[curr %d, avg %d, thresh %d..%d] dBm, "
+ "RxQual[curr %d, avg %d, thresh %d..%d]\n",
+ state->current, state->max, rxlev2dbm(rxlev), rxlev2dbm(rxlev_avg),
+ rxlev2dbm(params->rxlev_meas.lower_thresh), rxlev2dbm(params->rxlev_meas.upper_thresh),
+ rxqual, rxqual_avg, params->rxqual_meas.lower_thresh, params->rxqual_meas.upper_thresh);
return 0;
}
+
+ LOGPLCHAN(lchan, DLOOP, LOGL_INFO, "%s DL attenuation %u dB => %u dB:"
+ "max %u dB, RSSI[curr %d, avg %d, thresh %d..%d] dBm, "
+ "RxQual[curr %d, avg %d, thresh %d..%d]\n",
+ (new_att > state->current) ? "Raising" : "Lowering",
+ state->current, new_att, state->max, rxlev2dbm(rxlev), rxlev2dbm(rxlev_avg),
+ rxlev2dbm(params->rxlev_meas.lower_thresh), rxlev2dbm(params->rxlev_meas.upper_thresh),
+ rxqual, rxqual_avg, params->rxqual_meas.lower_thresh, params->rxqual_meas.upper_thresh);
+ state->current = new_att;
+ return 1;
+}
+
+/* Default MS/BS Power Control parameters (see 3GPP TS 45.008, table A.1) */
+const struct gsm_power_ctrl_params power_ctrl_params_def = {
+
+ .ctrl_interval = 1, /* Trigger loop every second SACCH block. TS 45.008 sec 4.7.1 */
+
+ /* Power increasing/reducing step size (optimal defaults) */
+ .inc_step_size_db = 4, /* quickly increase MS/BS power */
+ .red_step_size_db = 2, /* slowly decrease MS/BS power */
+
+ /* RxLev measurement parameters */
+ .rxlev_meas = {
+ /* Thresholds for RxLev (see 3GPP TS 45.008, A.3.2.1) */
+ .lower_thresh = 32, /* L_RXLEV_XX_P (-78 dBm) */
+ .upper_thresh = 38, /* U_RXLEV_XX_P (-72 dBm) */
+
+ /* NOTE: only Osmocom specific EWMA is supported */
+ .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_OSMO_EWMA,
+ .ewma.alpha = 50, /* Smoothing factor 50% */
+ },
+
+ /* RxQual measurement parameters */
+ .rxqual_meas = {
+ /* Thresholds for RxQual (see 3GPP TS 45.008, A.3.2.1) */
+ .lower_thresh = 3, /* L_RXQUAL_XX_P (0.8% <= BER < 1.6%) */
+ .upper_thresh = 0, /* U_RXQUAL_XX_P (BER < 0.2%) */
+
+ /* No averaging (filtering) by default.
+ * NOTE: only Osmocom specific EWMA is supported */
+ .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE,
+ },
+
+ /* C/I measurement parameters.
+ * Target C/I retrieved from "GSM/EDGE: Evolution and Performance" Table 10.3.
+ * Set lower and upper so that (lower + upper) / 2 is equal or slightly
+ * above the target.
+ */
+ .ci_fr_meas = { /* FR: Target C/I = 15 dB, Soft blocking threshold = 10 dB */
+ .lower_thresh = 13,
+ .upper_thresh = 17,
+
+ /* Increase {UL,DL}_TXPWR if at least LOWER_CMP_P averages
+ * out of LOWER_CMP_N averages are lower than L_CI_FR_XX_P */
+ .lower_cmp_p = 5, /* P3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ .lower_cmp_n = 7, /* N3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ /* Decrease {UL,DL}_TXPWR if at least UPPER_CMP_P averages
+ * out of UPPER_CMP_N averages are greater than L_CI_FR_XX_P */
+ .upper_cmp_p = 15, /* P4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+ .upper_cmp_n = 18, /* N4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+
+ /* No averaging (filtering) by default */
+ .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE,
+
+ /* Hreqave: the period over which an average is produced */
+ .h_reqave = 4, /* TODO: investigate a reasonable default value */
+ /* Hreqt: the number of averaged results maintained */
+ .h_reqt = 6, /* TODO: investigate a reasonable default value */
+ },
+ .ci_hr_meas = { /* HR: Target C/I = 18 dB, Soft blocking threshold = 13 dB */
+ .lower_thresh = 16,
+ .upper_thresh = 21,
+
+ /* Increase {UL,DL}_TXPWR if at least LOWER_CMP_P averages
+ * out of LOWER_CMP_N averages are lower than L_CI_HR_XX_P */
+ .lower_cmp_p = 5, /* P3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ .lower_cmp_n = 7, /* N3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ /* Decrease {UL,DL}_TXPWR if at least UPPER_CMP_P averages
+ * out of UPPER_CMP_N averages are greater than L_CI_HR_XX_P */
+ .upper_cmp_p = 15, /* P4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+ .upper_cmp_n = 18, /* N4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+
+ /* No averaging (filtering) by default */
+ .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE,
+
+ /* Hreqave: the period over which an average is produced */
+ .h_reqave = 4, /* TODO: investigate a reasonable default value */
+ /* Hreqt: the number of averaged results maintained */
+ .h_reqt = 6, /* TODO: investigate a reasonable default value */
+ },
+ .ci_amr_fr_meas = { /* AMR-FR: Target C/I = 9 dB, Soft blocking threshold = 4 dB */
+ .lower_thresh = 7,
+ .upper_thresh = 11,
+
+ /* Increase {UL,DL}_TXPWR if at least LOWER_CMP_P averages
+ * out of LOWER_CMP_N averages are lower than L_CI_AMR_FR_XX_P */
+ .lower_cmp_p = 5, /* P3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ .lower_cmp_n = 7, /* N3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ /* Decrease {UL,DL}_TXPWR if at least UPPER_CMP_P averages
+ * out of UPPER_CMP_N averages are greater than L_CI_AMR_FR_XX_P */
+ .upper_cmp_p = 15, /* P4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+ .upper_cmp_n = 18, /* N4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+
+ /* No averaging (filtering) by default */
+ .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE,
+
+ /* Hreqave: the period over which an average is produced */
+ .h_reqave = 4, /* TODO: investigate a reasonable default value */
+ /* Hreqt: the number of averaged results maintained */
+ .h_reqt = 6, /* TODO: investigate a reasonable default value */
+ },
+ .ci_amr_hr_meas = { /* AMR-HR: Target C/I = 15 dB, Soft blocking threshold = 10 dB */
+ .lower_thresh = 13,
+ .upper_thresh = 17,
+
+ /* Increase {UL,DL}_TXPWR if at least LOWER_CMP_P averages
+ * out of LOWER_CMP_N averages are lower than L_CI_AMR_HR_XX_P */
+ .lower_cmp_p = 5, /* P3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ .lower_cmp_n = 7, /* N3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ /* Decrease {UL,DL}_TXPWR if at least UPPER_CMP_P averages
+ * out of UPPER_CMP_N averages are greater than L_CI_AMR_HR_XX_P */
+ .upper_cmp_p = 15, /* P4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+ .upper_cmp_n = 18, /* N4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+
+ /* No averaging (filtering) by default */
+ .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE,
+
+ /* Hreqave: the period over which an average is produced */
+ .h_reqave = 4, /* TODO: investigate a reasonable default value */
+ /* Hreqt: the number of averaged results maintained */
+ .h_reqt = 6, /* TODO: investigate a reasonable default value */
+ },
+ .ci_sdcch_meas = { /* SDCCH: Target C/I = 14 dB, Soft blocking threshold = 9 dB */
+ .lower_thresh = 12,
+ .upper_thresh = 16,
+
+ /* Increase {UL,DL}_TXPWR if at least LOWER_CMP_P averages
+ * out of LOWER_CMP_N averages are lower than L_CI_SDCCH_XX_P */
+ .lower_cmp_p = 5, /* P3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ .lower_cmp_n = 7, /* N3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ /* Decrease {UL,DL}_TXPWR if at least UPPER_CMP_P averages
+ * out of UPPER_CMP_N averages are greater than L_CI_SDCCH_XX_P */
+ .upper_cmp_p = 15, /* P4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+ .upper_cmp_n = 18, /* N4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+
+ /* No averaging (filtering) by default */
+ .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE,
+
+ /* Hreqave: the period over which an average is produced */
+ .h_reqave = 4, /* TODO: investigate a reasonable default value */
+ /* Hreqt: the number of averaged results maintained */
+ .h_reqt = 6, /* TODO: investigate a reasonable default value */
+ },
+ .ci_gprs_meas = { /* GPRS: Target C/I = 20 dB, Soft blocking threshold = 15 dB */
+ .lower_thresh = 18,
+ .upper_thresh = 24,
+
+ /* Increase {UL,DL}_TXPWR if at least LOWER_CMP_P averages
+ * out of LOWER_CMP_N averages are lower than L_CI_GPRS_XX_P */
+ .lower_cmp_p = 5, /* P3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ .lower_cmp_n = 7, /* N3 as in 3GPP TS 45.008, A.3.2.1 (case c) */
+ /* Decrease {UL,DL}_TXPWR if at least UPPER_CMP_P averages
+ * out of UPPER_CMP_N averages are greater than L_CI_GPRS_XX_P */
+ .upper_cmp_p = 15, /* P4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+ .upper_cmp_n = 18, /* N4 as in 3GPP TS 45.008, A.3.2.1 (case d) */
+
+ /* No averaging (filtering) by default */
+ .algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE,
+
+ /* Hreqave: the period over which an average is produced */
+ .h_reqave = 4, /* TODO: investigate a reasonable default value */
+ /* Hreqt: the number of averaged results maintained */
+ .h_reqt = 6, /* TODO: investigate a reasonable default value */
+ },
+};
+
+void power_ctrl_params_def_reset(struct gsm_power_ctrl_params *params, bool is_bs_pwr)
+{
+ *params = power_ctrl_params_def;
+ if (!is_bs_pwr)
+ /* Trigger loop every fourth SACCH block (1.92s). TS 45.008 sec 4.7.1: */
+ params->ctrl_interval = 2;
}
diff --git a/src/common/probes.d b/src/common/probes.d
new file mode 100644
index 00000000..aaf9030e
--- /dev/null
+++ b/src/common/probes.d
@@ -0,0 +1,2 @@
+provider osmo_bts {
+};
diff --git a/src/common/rsl.c b/src/common/rsl.c
index 18c03491..9b73869a 100644
--- a/src/common/rsl.c
+++ b/src/common/rsl.c
@@ -440,29 +440,37 @@ int rsl_tx_rf_res(struct gsm_bts_trx *trx)
uint8_t *len = msgb_tl_put(nmsg, RSL_IE_RESOURCE_INFO);
for (tn = 0; tn < ARRAY_SIZE(trx->ts); tn++) {
- struct gsm_bts_trx_ts *ts = &trx->ts[tn];
+ const struct gsm_bts_trx_ts *ts = &trx->ts[tn];
- for (ln = 0; ln < ARRAY_SIZE(ts->lchan); ln++) {
- struct gsm_lchan *lchan = &ts->lchan[ln];
+ if (ts->mo.nm_state.operational != NM_OPSTATE_ENABLED)
+ continue;
+ if (ts->mo.nm_state.availability != NM_AVSTATE_OK)
+ continue;
- /* We're not interested in active lchans */
- if (lchan->state == LCHAN_S_ACTIVE) {
- /* Avoid potential buffer overflow */
- lchan->meas.interf_meas_num = 0;
- continue;
- }
+ for (ln = 0; ln < ARRAY_SIZE(ts->lchan); ln++) {
+ const struct gsm_lchan *lchan = &ts->lchan[ln];
- /* Only for GSM_LCHAN_{SDCCH,TCH_F,TCH_H} */
- if (!lchan_is_dcch(lchan))
+ /* No average interference value => no band */
+ if (lchan->meas.interf_meas_avg_dbm == 0)
continue;
- /* Average all collected samples */
- int band = gsm_lchan_interf_meas_calc_band(lchan);
- if (band < 0)
+ /* Only for GSM_LCHAN_{SDCCH,TCH_F,TCH_H,PDTCH} */
+ switch (lchan->type) {
+ case GSM_LCHAN_SDCCH:
+ case GSM_LCHAN_TCH_F:
+ case GSM_LCHAN_TCH_H:
+ /* We're not interested in active CS lchans */
+ if (lchan->state == LCHAN_S_ACTIVE)
+ continue;
+ break;
+ case GSM_LCHAN_PDTCH:
+ break;
+ default:
continue;
+ }
- msgb_v_put(nmsg, gsm_lchan2chan_nr(lchan));
- msgb_v_put(nmsg, (band & 0x07) << 5);
+ msgb_v_put(nmsg, gsm_lchan2chan_nr_rsl(lchan));
+ msgb_v_put(nmsg, (lchan->meas.interf_band & 0x07) << 5);
}
}
@@ -909,7 +917,7 @@ static int parse_power_ctrl_params(struct gsm_power_ctrl_params *params,
const uint8_t *data, size_t data_len)
{
const struct tlv_p_entry *ie;
- struct tlv_parsed tp[2];
+ struct tlv_parsed tp[3];
unsigned int i;
int rc;
@@ -938,6 +946,30 @@ static int parse_power_ctrl_params(struct gsm_power_ctrl_params *params,
params->rxqual_meas.upper_thresh = thresh->u_rxqual;
}
+ /* Osmocom extension, C/I related thresholds: */
+ if (TLVP_PRES_LEN(&tp[0], RSL_IPAC_EIE_OSMO_MS_PWR_CTL, sizeof(struct osmo_preproc_pc_thresh))) {
+ const struct osmo_preproc_pc_thresh *osmo_thresh;
+ ie = TLVP_GET(&tp[0], RSL_IPAC_EIE_OSMO_MS_PWR_CTL);
+ osmo_thresh = (const struct osmo_preproc_pc_thresh *) ie->val;
+ params->ci_fr_meas.lower_thresh = osmo_thresh->l_ci_fr;
+ params->ci_fr_meas.upper_thresh = osmo_thresh->u_ci_fr;
+
+ params->ci_hr_meas.lower_thresh = osmo_thresh->l_ci_hr;
+ params->ci_hr_meas.upper_thresh = osmo_thresh->u_ci_hr;
+
+ params->ci_amr_fr_meas.lower_thresh = osmo_thresh->l_ci_amr_fr;
+ params->ci_amr_fr_meas.upper_thresh = osmo_thresh->u_ci_amr_fr;
+
+ params->ci_amr_hr_meas.lower_thresh = osmo_thresh->l_ci_amr_hr;
+ params->ci_amr_hr_meas.upper_thresh = osmo_thresh->u_ci_amr_hr;
+
+ params->ci_sdcch_meas.lower_thresh = osmo_thresh->l_ci_sdcch;
+ params->ci_sdcch_meas.upper_thresh = osmo_thresh->u_ci_sdcch;
+
+ params->ci_gprs_meas.lower_thresh = osmo_thresh->l_ci_gprs;
+ params->ci_gprs_meas.upper_thresh = osmo_thresh->u_ci_gprs;
+ }
+
/* (TV) PC Threshold Comparators */
if ((ie = TLVP_GET(&tp[0], RSL_IPAC_EIE_PC_THRESH_COMP)) != NULL) {
const struct ipac_preproc_pc_comp *thresh_comp;
@@ -964,12 +996,31 @@ static int parse_power_ctrl_params(struct gsm_power_ctrl_params *params,
params->red_step_size_db = thresh_comp->red_step_size;
}
+ /* Osmocom extension, C/I related thresholds: */
+ if (TLVP_PRES_LEN(&tp[0], RSL_IPAC_EIE_OSMO_PC_THRESH_COMP, sizeof(struct osmo_preproc_pc_thresh))) {
+ const struct osmo_preproc_pc_comp *osmo_thresh_comp;
+ ie = TLVP_GET(&tp[0], RSL_IPAC_EIE_OSMO_PC_THRESH_COMP);
+ osmo_thresh_comp = (const struct osmo_preproc_pc_comp *) ie->val;
+ #define SET_PREPROC_PC(PARAMS, FROM, TYPE) \
+ (PARAMS)->TYPE##_meas.lower_cmp_p = (FROM)->TYPE.lower_p; \
+ (PARAMS)->TYPE##_meas.lower_cmp_n = (FROM)->TYPE.lower_n; \
+ (PARAMS)->TYPE##_meas.upper_cmp_p = (FROM)->TYPE.upper_p; \
+ (PARAMS)->TYPE##_meas.upper_cmp_n = (FROM)->TYPE.upper_n
+ SET_PREPROC_PC(params, osmo_thresh_comp, ci_fr);
+ SET_PREPROC_PC(params, osmo_thresh_comp, ci_hr);
+ SET_PREPROC_PC(params, osmo_thresh_comp, ci_amr_fr);
+ SET_PREPROC_PC(params, osmo_thresh_comp, ci_amr_hr);
+ SET_PREPROC_PC(params, osmo_thresh_comp, ci_sdcch);
+ SET_PREPROC_PC(params, osmo_thresh_comp, ci_gprs);
+ #undef SET_PREPROC_PC
+ }
+
/* (TLV) Measurement Averaging parameters for RxLev/RxQual */
for (i = 0; i < ARRAY_SIZE(tp); i++) {
const struct ipac_preproc_ave_cfg *ave_cfg;
struct gsm_power_ctrl_meas_params *mp;
- ie = TLVP_GET(&tp[0], RSL_IPAC_EIE_MEAS_AVG_CFG);
+ ie = TLVP_GET(&tp[i], RSL_IPAC_EIE_MEAS_AVG_CFG);
if (ie == NULL)
break;
@@ -1008,6 +1059,42 @@ static int parse_power_ctrl_params(struct gsm_power_ctrl_params *params,
}
}
+ /* (TLV) Measurement Averaging parameters for C/I (Osmocom extension)*/
+ if (TLVP_PRES_LEN(&tp[0], RSL_IPAC_EIE_OSMO_MEAS_AVG_CFG, sizeof(struct osmo_preproc_ave_cfg))) {
+ ie = TLVP_GET(&tp[0], RSL_IPAC_EIE_OSMO_MEAS_AVG_CFG);
+ const struct osmo_preproc_ave_cfg *cfg = (const struct osmo_preproc_ave_cfg *) ie->val;
+ unsigned params_offset = 0;
+ #define SET_AVE_CFG(PARAMS, FROM, TYPE, PARAM_OFFSET) do {\
+ if ((FROM)->TYPE.ave_enabled) { \
+ (PARAMS)->TYPE##_meas.h_reqave = (FROM)->TYPE.h_reqave; \
+ (PARAMS)->TYPE##_meas.h_reqt = (FROM)->TYPE.h_reqt; \
+ (PARAMS)->TYPE##_meas.algo = (FROM)->TYPE.ave_method + 1; \
+ switch ((FROM)->TYPE.ave_method) { \
+ case IPAC_OSMO_EWMA_AVE: \
+ if (ie->len > sizeof(*cfg) + (PARAM_OFFSET)) { \
+ (PARAMS)->TYPE##_meas.ewma.alpha = (FROM)->params[PARAM_OFFSET]; \
+ (PARAM_OFFSET)++; \
+ } \
+ break; \
+ /* FIXME: not implemented */ \
+ case IPAC_UNWEIGHTED_AVE: \
+ case IPAC_WEIGHTED_AVE: \
+ case IPAC_MEDIAN_AVE: \
+ break; \
+ } \
+ } else { \
+ (PARAMS)->TYPE##_meas.algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE; \
+ } \
+ } while(0)
+ SET_AVE_CFG(params, cfg, ci_fr, params_offset);
+ SET_AVE_CFG(params, cfg, ci_hr, params_offset);
+ SET_AVE_CFG(params, cfg, ci_amr_fr, params_offset);
+ SET_AVE_CFG(params, cfg, ci_amr_hr, params_offset);
+ SET_AVE_CFG(params, cfg, ci_sdcch, params_offset);
+ SET_AVE_CFG(params, cfg, ci_gprs, params_offset);
+ #undef SET_AVE_CFG
+ }
+
return 0;
}
@@ -1032,7 +1119,8 @@ static int rsl_rx_meas_preproc_dft(struct gsm_bts_trx *trx, struct msgb *msg)
/* TLV (O) BS Power Parameters IE */
if ((ie = TLVP_GET(&tp, RSL_IE_BS_POWER_PARAM)) != NULL) {
/* Allocate a new chunk and initialize with default values */
- params = talloc_memdup(trx, &power_ctrl_params_def, sizeof(*params));
+ params = talloc(trx, struct gsm_power_ctrl_params);
+ power_ctrl_params_def_reset(params, true);
if (ie->len && parse_power_ctrl_params(params, ie->val, ie->len) == 0) {
/* Initially it points to the global defaults */
@@ -1049,7 +1137,8 @@ static int rsl_rx_meas_preproc_dft(struct gsm_bts_trx *trx, struct msgb *msg)
/* TLV (O) MS Power Parameters IE */
if ((ie = TLVP_GET(&tp, RSL_IE_MS_POWER_PARAM)) != NULL) {
/* Allocate a new chunk and initialize with default values */
- params = talloc_memdup(trx, &power_ctrl_params_def, sizeof(*params));
+ params = talloc(trx, struct gsm_power_ctrl_params);
+ power_ctrl_params_def_reset(params, false);
if (ie->len && parse_power_ctrl_params(params, ie->val, ie->len) == 0) {
/* Initially it points to the global defaults */
@@ -1085,6 +1174,30 @@ static int rsl_rx_imm_ass(struct gsm_bts_trx *trx, struct msgb *msg)
msg->l2h = NULL;
msg->len = TLVP_LEN(&tp, RSL_IE_FULL_IMM_ASS_INFO);
+ /* Early Immediate Assignment: when there is a lot of latency on Abis, the Abis roundtrip of Chan Activ -> Chan
+ * Activ ACK -> Immediate Assignment may take so long that each MS sends a second RACH for Chan Rqd, reserving
+ * two SDCCH for each request but using only one. To help with that, the Early IA feature in osmo-bsc sends the
+ * Immediate Assignment without waiting for the Channel Activation ACK. This may then be too early, and the MS
+ * may not be able to establish a channel. So to help with Early IA, look up whether the target lchan is already
+ * active. If not, then hold back the RR Immediate Assignment message, and send it once L1 has confirmed that
+ * the channel is active. Hence we still wait for the activation, but don't need the Abis roundtrip of Activ ACK
+ * -> Immediate Assignment via the BSC.
+ * If anything is wrong with the sizes or the lchan lookup, behave normally, i.e. do not do the RR IA caching,
+ * but just send the RR message to the MS as-is. */
+ if (msg->len >= sizeof(struct gsm48_imm_ass)) {
+ struct gsm48_imm_ass *rr_ia = (void*)msg->data;
+ struct gsm_lchan *ia_target_lchan = lchan_lookup(trx, rr_ia->chan_desc.chan_nr, "Early IA check: ");
+ if (ia_target_lchan && ia_target_lchan->state != LCHAN_S_ACTIVE) {
+ /* Target lchan is not yet active. Cache the IA.
+ * If a previous IA is still lingering, free it. */
+ msgb_free(ia_target_lchan->early_rr_ia);
+ ia_target_lchan->early_rr_ia = msg;
+
+ /* return 1 means: don't msgb_free() the msg */
+ return 1;
+ }
+ }
+
/* put into the AGCH queue of the BTS */
if (bts_agch_enqueue(trx->bts, msg) < 0) {
/* if there is no space in the queue: send DELETE IND */
@@ -1122,7 +1235,7 @@ static int tx_rf_rel_ack(struct gsm_lchan *lchan, uint8_t chan_nr)
/* 8.4.19 sending RF CHANnel RELease ACKnowledge */
int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan)
{
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
bool send_rel_ack;
switch (lchan->rel_act_kind) {
@@ -1188,21 +1301,6 @@ int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan)
gsm_ts_and_pchan_name(lchan->ts), lchan->nr,
gsm_lchant_name(lchan->type));
- /*
- * Free the LAPDm resources now that the BTS
- * has released all the resources.
- */
- lapdm_channel_exit(&lchan->lapdm_ch);
-
- /* Also ensure that there are no leftovers from repeated FACCH or
- * repeated SACCH that might cause memory leakage. */
- msgb_free(lchan->tch.rep_facch[0].msg);
- msgb_free(lchan->tch.rep_facch[1].msg);
- lchan->tch.rep_facch[0].msg = NULL;
- lchan->tch.rep_facch[1].msg = NULL;
- msgb_free(lchan->rep_sacch);
- lchan->rep_sacch = NULL;
-
return tx_rf_rel_ack(lchan, chan_nr);
}
@@ -1211,7 +1309,7 @@ static int rsl_tx_chan_act_ack(struct gsm_lchan *lchan)
{
struct gsm_time *gtime = get_time(lchan->ts->trx->bts);
struct msgb *msg;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
uint8_t ie[2];
LOGP(DRSL, LOGL_NOTICE, "%s (ss=%d) %s Tx CHAN ACT ACK\n",
@@ -1237,7 +1335,7 @@ static int rsl_tx_chan_act_ack(struct gsm_lchan *lchan)
int rsl_tx_hando_det(struct gsm_lchan *lchan, uint8_t *ho_delay)
{
struct msgb *msg;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
LOGPLCHAN(lchan, DRSL, LOGL_INFO, "Sending HANDOver DETect\n");
@@ -1279,7 +1377,7 @@ static int _rsl_tx_chan_act_nack(struct gsm_bts_trx *trx, uint8_t chan_nr, uint8
return abis_bts_rsl_sendmsg(msg);
}
static int rsl_tx_chan_act_nack(struct gsm_lchan *lchan, uint8_t cause) {
- return _rsl_tx_chan_act_nack(lchan->ts->trx, gsm_lchan2chan_nr(lchan), cause, lchan);
+ return _rsl_tx_chan_act_nack(lchan->ts->trx, gsm_lchan2chan_nr_rsl(lchan), cause, lchan);
}
/* Send an RSL Channel Activation Ack if cause is zero, a Nack otherwise. */
@@ -1300,7 +1398,7 @@ int rsl_tx_chan_act_acknack(struct gsm_lchan *lchan, uint8_t cause)
int rsl_tx_conn_fail(const struct gsm_lchan *lchan, uint8_t cause)
{
struct msgb *msg;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "Sending Connection Failure: cause = 0x%02x\n", cause);
@@ -1409,7 +1507,7 @@ static void clear_lchan_for_pdch_activ(struct gsm_lchan *lchan)
memset(&lchan->ho, 0, sizeof(lchan->ho));
memset(&lchan->ms_power_ctrl, 0, sizeof(lchan->ms_power_ctrl));
memset(&lchan->bs_power_ctrl, 0, sizeof(lchan->bs_power_ctrl));
- lchan->rqd_ta = 0;
+ lchan->ta_ctrl.current = 0;
copy_sacch_si_to_lchan(lchan);
memset(&lchan->tch, 0, sizeof(lchan->tch));
}
@@ -1418,7 +1516,7 @@ static void clear_lchan_for_pdch_activ(struct gsm_lchan *lchan)
* Store the CHAN_ACTIV msg, connect the L1 timeslot in the proper type and
* then invoke rsl_rx_chan_activ() with msg.
*/
-static int dyn_ts_l1_reconnect(struct gsm_bts_trx_ts *ts, struct msgb *msg)
+static int dyn_ts_l1_reconnect(struct gsm_bts_trx_ts *ts)
{
DEBUGP(DRSL, "%s dyn_ts_l1_reconnect\n", gsm_ts_and_pchan_name(ts));
@@ -1439,9 +1537,6 @@ static int dyn_ts_l1_reconnect(struct gsm_bts_trx_ts *ts, struct msgb *msg)
return -EINVAL;
}
- /* We will feed this back to rsl_rx_chan_activ() later */
- ts->dyn.pending_chan_activ = msg;
-
/* Disconnect, continue connecting from cb_ts_disconnected(). */
DEBUGP(DRSL, "%s Disconnect\n", gsm_ts_and_pchan_name(ts));
return bts_model_ts_disconnect(ts);
@@ -1476,7 +1571,7 @@ static enum gsm_phys_chan_config dyn_pchan_from_chan_nr(uint8_t chan_nr)
}
/* Parse RSL_IE_OSMO_REP_ACCH_CAP */
-static void parse_repeated_acch_capability(struct gsm_lchan *lchan, struct tlv_parsed *tp)
+static int parse_repeated_acch_capability(struct gsm_lchan *lchan, struct tlv_parsed *tp)
{
/* 3GPP TS 24.008, section 10.5.1.7 defines a Repeated ACCH Capability
* bit that indicates if REPEATED FACCH/SACCH is supported or not.
@@ -1484,15 +1579,42 @@ static void parse_repeated_acch_capability(struct gsm_lchan *lchan, struct tlv_p
* should be communicated in the RSL CHANNEL ACTIVATION. For osmo-bts
* we will use a propritary IE. */
- memset(&lchan->repeated_acch_capability, 0, sizeof(lchan->repeated_acch_capability));
+ memset(&lchan->rep_acch_cap, 0, sizeof(lchan->rep_acch_cap));
- if (!TLVP_PRESENT(tp, RSL_IE_OSMO_REP_ACCH_CAP))
- return;
- if (TLVP_LEN(tp, RSL_IE_OSMO_REP_ACCH_CAP) != sizeof(lchan->repeated_acch_capability))
- return;
+ if (!TLVP_PRES_LEN(tp, RSL_IE_OSMO_REP_ACCH_CAP, sizeof(lchan->rep_acch_cap)))
+ return 0;
+
+ if (!osmo_bts_has_feature(lchan->ts->trx->bts->features, BTS_FEAT_ACCH_REP))
+ return -RSL_ERR_OPT_IE_ERROR;
+
+ memcpy(&lchan->rep_acch_cap, TLVP_VAL(tp, RSL_IE_OSMO_REP_ACCH_CAP),
+ sizeof(lchan->rep_acch_cap));
- memcpy(&lchan->repeated_acch_capability, TLVP_VAL(tp, RSL_IE_OSMO_REP_ACCH_CAP),
- sizeof(lchan->repeated_acch_capability));
+ return 0;
+}
+
+/* Parse RSL_IE_OSMO_TOP_ACCH_CAP */
+static int parse_temporary_overpower_acch_capability(struct gsm_lchan *lchan,
+ const struct tlv_parsed *tp)
+{
+ memset(&lchan->top_acch_cap, 0, sizeof(lchan->top_acch_cap));
+
+ if (!TLVP_PRES_LEN(tp, RSL_IE_OSMO_TEMP_OVP_ACCH_CAP, sizeof(lchan->top_acch_cap)))
+ return 0;
+
+ if (!osmo_bts_has_feature(lchan->ts->trx->bts->features, BTS_FEAT_ACCH_TEMP_OVP))
+ return -RSL_ERR_OPT_IE_ERROR;
+
+ memcpy(&lchan->top_acch_cap,
+ TLVP_VAL(tp, RSL_IE_OSMO_TEMP_OVP_ACCH_CAP),
+ sizeof(lchan->top_acch_cap));
+
+ /* Simplify checking whether the overpower is enabled at all: allow
+ * testing just one parameter (overpower_db > 0) instead of all three. */
+ if (!lchan->top_acch_cap.sacch_enable && !lchan->top_acch_cap.facch_enable)
+ lchan->top_acch_cap.overpower_db = 0;
+
+ return 0;
}
/* 8.4.1 CHANnel ACTIVation is received */
@@ -1501,6 +1623,7 @@ static int rsl_rx_chan_activ(struct msgb *msg)
struct abis_rsl_dchan_hdr *dch = msgb_l2(msg);
struct gsm_lchan *lchan = msg->lchan;
struct gsm_bts_trx_ts *ts = lchan->ts;
+ struct gsm_bts_trx_ts *primary_ts;
struct tlv_parsed tp;
const struct tlv_p_entry *ie;
uint8_t type, cause;
@@ -1512,6 +1635,15 @@ static int rsl_rx_chan_activ(struct msgb *msg)
return rsl_tx_chan_act_nack(lchan, RSL_ERR_EQUIPMENT_FAIL);
}
+ /* We need to pick the real TS here to check NM state: */
+ primary_ts = ts->vamos.is_shadow ? ts->vamos.peer : ts;
+ if (primary_ts->mo.nm_state.operational != NM_OPSTATE_ENABLED ||
+ primary_ts->mo.nm_state.availability != NM_AVSTATE_OK) {
+ LOGP(DRSL, LOGL_ERROR, "%s rx chan activ but TS not in nm_state oper=ENABLED avail=OK, nack!\n",
+ gsm_ts_and_pchan_name(ts));
+ return rsl_tx_chan_act_nack(lchan, RSL_ERR_RR_UNAVAIL);
+ }
+
if (ts->pchan == GSM_PCHAN_OSMO_DYN) {
ts->dyn.pchan_want = dyn_pchan_from_chan_nr(dch->chan_nr);
DEBUGP(DRSL, "%s rx chan activ\n", gsm_ts_and_pchan_name(ts));
@@ -1522,17 +1654,17 @@ static int rsl_rx_chan_activ(struct msgb *msg)
* mode than this activation needs it to be.
* Re-connect, then come back to rsl_rx_chan_activ().
*/
- rc = dyn_ts_l1_reconnect(ts, msg);
+ rc = dyn_ts_l1_reconnect(ts);
if (rc)
return rsl_tx_chan_act_nack(lchan, RSL_ERR_NORMAL_UNSPEC);
+ /* will be fed back to rsl_rx_chan_activ() later */
+ OSMO_ASSERT(lchan->pending_chan_activ == NULL);
+ lchan->pending_chan_activ = msg;
/* indicate that the msgb should not be freed. */
return 1;
}
}
- LOGPLCHAN(lchan, DRSL, LOGL_DEBUG, "rx Channel Activation in state: %s.\n",
- gsm_lchans_name(lchan->state));
-
/* Initialize MS Power Control defaults */
lchan->ms_power_ctrl = (struct lchan_power_ctrl_state) {
.max = ms_pwr_ctl_lvl(lchan->ts->trx->bts->band, 0),
@@ -1618,7 +1750,7 @@ static int rsl_rx_chan_activ(struct msgb *msg)
}
/* 9.3.24 Timing Advance */
if (TLVP_PRES_LEN(&tp, RSL_IE_TIMING_ADVANCE, 1))
- lchan->rqd_ta = *TLVP_VAL(&tp, RSL_IE_TIMING_ADVANCE);
+ lchan->ta_ctrl.current = *TLVP_VAL(&tp, RSL_IE_TIMING_ADVANCE);
/* 9.3.31 (TLV) MS Power Parameters IE (vendor specific) */
if ((ie = TLVP_GET(&tp, RSL_IE_MS_POWER_PARAM)) != NULL) {
@@ -1794,7 +1926,19 @@ static int rsl_rx_chan_activ(struct msgb *msg)
/* Remember to send an RSL ACK once the lchan is active */
lchan->rel_act_kind = LCHAN_REL_ACT_RSL;
- parse_repeated_acch_capability(lchan, &tp);
+ rc = parse_repeated_acch_capability(lchan, &tp);
+ if (rc < 0)
+ return rsl_tx_chan_act_acknack(lchan, -rc);
+ rc = parse_temporary_overpower_acch_capability(lchan, &tp);
+ if (rc < 0)
+ return rsl_tx_chan_act_acknack(lchan, -rc);
+
+ /* Take the first ACCH overpower decision (if allowed): it can be
+ * enabled immediately if the RxQual threshold is disabled (0). */
+ if (lchan->top_acch_cap.overpower_db > 0)
+ lchan->top_acch_active = !lchan->top_acch_cap.rxqual;
+ else
+ lchan->top_acch_active = false;
/* actually activate the channel in the BTS */
rc = l1sap_chan_act(lchan->ts->trx, dch->chan_nr, &tp);
@@ -1804,37 +1948,9 @@ static int rsl_rx_chan_activ(struct msgb *msg)
return 0;
}
-static int dyn_ts_pdch_release(struct gsm_lchan *lchan)
-{
- struct gsm_bts_trx_ts *ts = lchan->ts;
-
- if (ts->dyn.pchan_is != ts->dyn.pchan_want) {
- LOGP(DRSL, LOGL_ERROR, "%s: PDCH release requested but already"
- " in switchover\n", gsm_ts_and_pchan_name(ts));
- return -EINVAL;
- }
-
- /*
- * Indicate PDCH Disconnect in dyn_pdch.want, let pcu_tx_info_ind()
- * pick it up and wait for PCU to disable the channel.
- */
- ts->dyn.pchan_want = GSM_PCHAN_NONE;
-
- if (!pcu_connected()) {
- /* PCU not connected yet. Just record the new type and done,
- * the PCU will pick it up once connected. */
- ts->dyn.pchan_is = GSM_PCHAN_NONE;
- return 1;
- }
-
- return pcu_tx_info_ind();
-}
-
/* 8.4.14 RF CHANnel RELease is received */
static int rsl_rx_rf_chan_rel(struct gsm_lchan *lchan, uint8_t chan_nr)
{
- int rc;
-
if (lchan->state == LCHAN_S_NONE) {
LOGP(DRSL, LOGL_ERROR,
"%s ss=%d state=%s Rx RSL RF Channel Release, but is already inactive;"
@@ -1845,38 +1961,7 @@ static int rsl_rx_rf_chan_rel(struct gsm_lchan *lchan, uint8_t chan_nr)
* not necessarily reflecting the current lchan state. */
return tx_rf_rel_ack(lchan, chan_nr);
}
-
- if (lchan->abis_ip.rtp_socket) {
- rsl_tx_ipac_dlcx_ind(lchan, RSL_ERR_NORMAL_UNSPEC);
- osmo_rtp_socket_log_stats(lchan->abis_ip.rtp_socket, DRTP, LOGL_INFO,
- "Closing RTP socket on Channel Release ");
- osmo_rtp_socket_free(lchan->abis_ip.rtp_socket);
- lchan->abis_ip.rtp_socket = NULL;
- msgb_queue_flush(&lchan->dl_tch_queue);
- }
-
- /* release handover state */
- handover_reset(lchan);
-
- lchan->rel_act_kind = LCHAN_REL_ACT_RSL;
-
- /* Dynamic channel in PDCH mode is released via PCU */
- if (lchan->ts->pchan == GSM_PCHAN_OSMO_DYN
- && lchan->ts->dyn.pchan_is == GSM_PCHAN_PDCH) {
- rc = dyn_ts_pdch_release(lchan);
- if (rc == 1) {
- /* If the PCU is not connected, continue to rel ack right away. */
- lchan->rel_act_kind = LCHAN_REL_ACT_PCU;
- return rsl_tx_rf_rel_ack(lchan);
- }
- /* Waiting for PDCH release */
- return rc;
- }
-
- l1sap_chan_rel(lchan->ts->trx, chan_nr);
-
- lapdm_channel_exit(&lchan->lapdm_ch);
-
+ gsm_lchan_release(lchan, LCHAN_REL_ACT_RSL);
return 0;
}
@@ -1910,7 +1995,7 @@ static int tx_ciph_mod_compl_hack(struct gsm_lchan *lchan, uint8_t link_id,
}
}
- rsl_rll_push_l3(fake_msg, RSL_MT_DATA_IND, gsm_lchan2chan_nr(lchan),
+ rsl_rll_push_l3(fake_msg, RSL_MT_DATA_IND, gsm_lchan2chan_nr_rsl(lchan),
link_id, 1);
fake_msg->lchan = lchan;
@@ -2045,7 +2130,7 @@ static int _rsl_tx_mode_modif_nack(struct gsm_bts_trx *trx, uint8_t chan_nr, uin
}
static int rsl_tx_mode_modif_nack(struct gsm_lchan *lchan, uint8_t cause)
{
- return _rsl_tx_mode_modif_nack(lchan->ts->trx, gsm_lchan2chan_nr(lchan), cause, lchan);
+ return _rsl_tx_mode_modif_nack(lchan->ts->trx, gsm_lchan2chan_nr_rsl(lchan), cause, lchan);
}
@@ -2053,7 +2138,7 @@ static int rsl_tx_mode_modif_nack(struct gsm_lchan *lchan, uint8_t cause)
static int rsl_tx_mode_modif_ack(struct gsm_lchan *lchan)
{
struct msgb *msg;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
LOGPLCHAN(lchan, DRSL, LOGL_INFO, "Tx MODE MODIF ACK\n");
@@ -2118,7 +2203,19 @@ static int rsl_rx_mode_modif(struct msgb *msg)
/* 9.3.53 MultiRate Control */
/* 9.3.54 Supported Codec Types */
- parse_repeated_acch_capability(lchan, &tp);
+ rc = parse_repeated_acch_capability(lchan, &tp);
+ if (rc < 0)
+ return rsl_tx_mode_modif_nack(lchan, -rc);
+ rc = parse_temporary_overpower_acch_capability(lchan, &tp);
+ if (rc < 0)
+ return rsl_tx_mode_modif_nack(lchan, -rc);
+
+ /* Immediately disable ACCH overpower if the value is 0 dB,
+ * or enable if the RxQual threshold becomes disabled (0). */
+ if (lchan->top_acch_cap.overpower_db == 0)
+ lchan->top_acch_active = false;
+ else if (lchan->top_acch_cap.rxqual == 0)
+ lchan->top_acch_active = true;
l1sap_chan_modify(lchan->ts->trx, dch->chan_nr);
@@ -2331,7 +2428,7 @@ int rsl_tx_cbch_load_indication(struct gsm_bts *bts, bool ext_cbch, bool overflo
return -ENOMEM;
/* 9.3.1 Channel Number */
- rsl_cch_push_hdr(msg, RSL_MT_CBCH_LOAD_IND, gsm_lchan2chan_nr(lchan));
+ rsl_cch_push_hdr(msg, RSL_MT_CBCH_LOAD_IND, gsm_lchan2chan_nr_rsl(lchan));
/* 9.3.43 CBCH Load Information */
load_info = ((overflow & 1) << 7) | (amount & 0x0F);
@@ -2393,7 +2490,7 @@ int rsl_tx_ipac_dlcx_ind(struct gsm_lchan *lchan, uint8_t cause)
msgb_tv16_put(nmsg, RSL_IE_IPAC_CONN_ID, htons(lchan->abis_ip.conn_id));
rsl_add_rtp_stats(lchan, nmsg);
msgb_tlv_put(nmsg, RSL_IE_CAUSE, 1, &cause);
- rsl_ipa_push_hdr(nmsg, RSL_MT_IPAC_DLCX_IND, gsm_lchan2chan_nr(lchan));
+ rsl_ipa_push_hdr(nmsg, RSL_MT_IPAC_DLCX_IND, gsm_lchan2chan_nr_rsl(lchan));
nmsg->trx = lchan->ts->trx;
@@ -2405,7 +2502,7 @@ static int rsl_tx_ipac_XXcx_ack(struct gsm_lchan *lchan, int inc_pt2,
uint8_t orig_msgt)
{
struct msgb *msg;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
const char *name;
struct in_addr ia;
@@ -2453,7 +2550,7 @@ static int rsl_tx_ipac_XXcx_ack(struct gsm_lchan *lchan, int inc_pt2,
static int rsl_tx_ipac_dlcx_ack(struct gsm_lchan *lchan, int inc_conn_id)
{
struct msgb *msg;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
LOGPLCHAN(lchan, DRSL, LOGL_INFO, "RSL Tx IPAC_DLCX_ACK\n");
@@ -2476,7 +2573,7 @@ static int rsl_tx_ipac_dlcx_nack(struct gsm_lchan *lchan, int inc_conn_id,
uint8_t cause)
{
struct msgb *msg;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
LOGPLCHAN(lchan, DRSL, LOGL_INFO, "RSL Tx IPAC_DLCX_NACK\n");
@@ -2502,7 +2599,7 @@ static int tx_ipac_XXcx_nack(struct gsm_lchan *lchan, uint8_t cause,
int inc_ipport, uint8_t orig_msgtype)
{
struct msgb *msg;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
/* FIXME: allocate new msgb and copy old over */
LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "RSL Tx IPAC_BIND_NACK\n");
@@ -2817,7 +2914,7 @@ static int rsl_rx_ipac_dlcx(struct msgb *msg)
static int rsl_tx_dyn_pdch_ack(struct gsm_lchan *lchan, bool pdch_act)
{
struct gsm_time *gtime = get_time(lchan->ts->trx->bts);
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
struct msgb *msg;
uint8_t ie[2];
@@ -2846,7 +2943,7 @@ static int rsl_tx_dyn_pdch_nack(struct gsm_lchan *lchan, bool pdch_act,
uint8_t cause)
{
struct msgb *msg;
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "Tx PDCH %s NACK (cause = 0x%02x)\n",
pdch_act ? "ACT" : "DEACT", cause);
@@ -3107,8 +3204,7 @@ static void ipacc_dyn_pdch_ts_connected(struct gsm_bts_trx_ts *ts, int rc)
static void osmo_dyn_ts_connected(struct gsm_bts_trx_ts *ts, int rc)
{
- struct msgb *msg = ts->dyn.pending_chan_activ;
- ts->dyn.pending_chan_activ = NULL;
+ unsigned int ln;
if (rc) {
LOGP(DRSL, LOGL_NOTICE, "%s PDCH ACT OSMO operation failed (%d) in bts model\n",
@@ -3117,20 +3213,23 @@ static void osmo_dyn_ts_connected(struct gsm_bts_trx_ts *ts, int rc)
return;
}
- if (!msg) {
- LOGP(DRSL, LOGL_ERROR,
- "%s TS re-connected, but no chan activ msg pending\n",
- gsm_ts_and_pchan_name(ts));
- return;
- }
-
ts->dyn.pchan_is = ts->dyn.pchan_want;
DEBUGP(DRSL, "%s Connected\n", gsm_ts_and_pchan_name(ts));
- /* continue where we left off before re-connecting the TS. */
- rc = rsl_rx_chan_activ(msg);
- if (rc != 1)
- msgb_free(msg);
+ /* Handle postponed RSL CHANnel ACTIVation messages (if any) */
+ for (ln = 0; ln < ARRAY_SIZE(ts->lchan); ln++) {
+ struct gsm_lchan *lchan = &ts->lchan[ln];
+
+ if (lchan->pending_chan_activ == NULL)
+ continue;
+
+ struct msgb *msg = lchan->pending_chan_activ;
+ lchan->pending_chan_activ = NULL;
+
+ /* Continue where we left off before re-connecting the TS */
+ if (rsl_rx_chan_activ(msg) != 1)
+ msgb_free(msg);
+ }
}
void cb_ts_connected(struct gsm_bts_trx_ts *ts, int rc)
@@ -3374,6 +3473,7 @@ static int handle_gprs_susp_req(struct msgb *msg)
if (!gh || msgb_l3len(msg) < sizeof(*gh)+sizeof(*gsr)) {
LOGP(DRSL, LOGL_NOTICE, "%s Short GPRS SUSPEND REQ received, ignoring\n", gsm_lchan_name(msg->lchan));
+ msgb_free(msg);
return -EINVAL;
}
@@ -3389,16 +3489,6 @@ static int handle_gprs_susp_req(struct msgb *msg)
return rc;
}
-static inline uint8_t ms_to2rsl(const struct gsm_lchan *lchan, const struct lapdm_entity *le)
-{
- return (lchan->ms_t_offs >= 0) ? lchan->ms_t_offs : (lchan->p_offs - le->ta);
-}
-
-static inline bool ms_to_valid(const struct gsm_lchan *lchan)
-{
- return (lchan->ms_t_offs >= 0) || (lchan->p_offs >= 0);
-}
-
struct osmo_bts_supp_meas_info {
int16_t toa256_mean;
int16_t toa256_min;
@@ -3406,12 +3496,12 @@ struct osmo_bts_supp_meas_info {
uint16_t toa256_std_dev;
} __attribute__((packed));
-/* Compose and send 8.4.8 MEASUREMENT RESult via RSL */
-int rsl_tx_meas_res(struct gsm_lchan *lchan, uint8_t *l3, int l3_len, const struct lapdm_entity *le)
+/* Compose and send 8.4.8 MEASUREMENT RESult via RSL. (timing_offset=-1 -> not present) */
+int rsl_tx_meas_res(struct gsm_lchan *lchan, const uint8_t *l3, int l3_len, int timing_offset)
{
struct msgb *msg;
uint8_t meas_res[16];
- uint8_t chan_nr = gsm_lchan2chan_nr(lchan);
+ uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan);
int res_valid = lchan->meas.flags & LC_UL_M_F_RES_VALID;
struct gsm_bts *bts = lchan->ts->trx->bts;
@@ -3433,13 +3523,12 @@ int rsl_tx_meas_res(struct gsm_lchan *lchan, uint8_t *l3, int l3_len, const stru
lchan->meas.ul_res.full.rx_qual,
lchan->meas.ul_res.sub.rx_qual,
lchan->meas.l1_info.ms_pwr,
- lchan->meas.l1_info.ta, l3_len, ms_to2rsl(lchan, le) - MEAS_MAX_TIMING_ADVANCE);
+ lchan->meas.l1_info.ta, l3_len, timing_offset - MEAS_MAX_TIMING_ADVANCE);
- msgb_tv_put(msg, RSL_IE_MEAS_RES_NR, lchan->meas.res_nr++);
+ msgb_tv_put(msg, RSL_IE_MEAS_RES_NR, lchan->meas.res_nr);
size_t ie_len = gsm0858_rsl_ul_meas_enc(&lchan->meas.ul_res,
lchan->tch.dtx.dl_active,
meas_res);
- lchan->tch.dtx.dl_active = false;
if (ie_len >= 3) {
if (bts->supp_meas_toa256 && lchan->meas.flags & LC_UL_M_F_OSMO_EXT_VALID) {
struct osmo_bts_supp_meas_info *smi;
@@ -3457,24 +3546,18 @@ int rsl_tx_meas_res(struct gsm_lchan *lchan, uint8_t *l3, int l3_len, const stru
smi->toa256_min = htons(ta256 + lchan->meas.ext.toa256_min);
smi->toa256_max = htons(ta256 + lchan->meas.ext.toa256_max);
smi->toa256_std_dev = htons(lchan->meas.ext.toa256_std_dev);
- lchan->meas.flags &= ~LC_UL_M_F_OSMO_EXT_VALID;
}
msgb_tlv_put(msg, RSL_IE_UPLINK_MEAS, ie_len, meas_res);
- lchan->meas.flags &= ~LC_UL_M_F_RES_VALID;
}
msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power_ctrl.current / 2);
if (lchan->meas.flags & LC_UL_M_F_L1_VALID) {
msgb_tv_fixed_put(msg, RSL_IE_L1_INFO, sizeof(lchan->meas.l1_info), (uint8_t*)&lchan->meas.l1_info);
- lchan->meas.flags &= ~LC_UL_M_F_L1_VALID;
}
- if (l3 && l3_len > 0)
+ if (l3 && l3_len > 0) {
msgb_tl16v_put(msg, RSL_IE_L3_INFO, l3_len, l3);
- if (ms_to_valid(lchan)) {
- if (l3 && l3_len > 0)
- msgb_tv_put(msg, RSL_IE_MS_TIMING_OFFSET, ms_to2rsl(lchan, le));
- lchan->ms_t_offs = -1;
- lchan->p_offs = -1;
+ if (timing_offset != -1)
+ msgb_tv_put(msg, RSL_IE_MS_TIMING_OFFSET, timing_offset);
}
rsl_dch_push_hdr(msg, RSL_MT_MEAS_RES, chan_nr);
@@ -3502,17 +3585,19 @@ int lapdm_rll_tx_cb(struct msgb *msg, struct lapdm_entity *le, void *ctx)
msg->trx = lchan->ts->trx;
msg->lchan = lchan;
- /* check if this is a measurement report from SACCH which needs special
- * processing before forwarding */
+ /* If this is a Measurement Report, then we simply ignore it,
+ * because it has already been processed in l1sap_ph_data_ind(). */
if (rslms_is_meas_rep(msg)) {
- int rc;
-
- LOGPLCHAN(lchan, DRSL, LOGL_INFO, "Handing RLL msg %s from LAPDm to MEAS REP\n",
+ msgb_free(msg);
+ return 0;
+ } else if (rslms_is_gprs_susp_req(msg)) {
+ return handle_gprs_susp_req(msg);
+ } else {
+ LOGPLCHAN(lchan, DRSL, LOGL_INFO, "Fwd RLL msg %s from LAPDm to A-bis\n",
rsl_msg_name(rh->msg_type));
/* REL_IND handling */
- if (rh->msg_type == RSL_MT_REL_IND &&
- (lchan->type == GSM_LCHAN_TCH_F || lchan->type == GSM_LCHAN_TCH_H)) {
+ if (rh->msg_type == RSL_MT_REL_IND && lchan_is_tch(lchan)) {
LOGPLCHAN(lchan, DRSL, LOGL_INFO,
"Scheduling %s to L3 in next associated TCH-RTS.ind\n",
rsl_msg_name(rh->msg_type));
@@ -3527,16 +3612,6 @@ int lapdm_rll_tx_cb(struct msgb *msg, struct lapdm_entity *le, void *ctx)
return 0;
}
- repeated_dl_facch_active_decision(lchan, msgb_l3(msg), msgb_l3len(msg));
- rc = rsl_tx_meas_res(lchan, msgb_l3(msg), msgb_l3len(msg), le);
- msgb_free(msg);
- return rc;
- } else if (rslms_is_gprs_susp_req(msg)) {
- return handle_gprs_susp_req(msg);
- } else {
- LOGPLCHAN(lchan, DRSL, LOGL_INFO, "Fwd RLL msg %s from LAPDm to A-bis\n",
- rsl_msg_name(rh->msg_type));
-
return abis_bts_rsl_sendmsg(msg);
}
}
@@ -3767,14 +3842,6 @@ static int rsl_rx_ipaccess(struct gsm_bts_trx *trx, struct msgb *msg)
return ret;
}
-int lchan_deactivate(struct gsm_lchan *lchan)
-{
- OSMO_ASSERT(lchan);
-
- lchan->ciph_state = 0;
- return bts_model_lchan_deactivate(lchan);
-}
-
int down_rsl(struct gsm_bts_trx *trx, struct msgb *msg)
{
struct abis_rsl_common_hdr *rslh;
diff --git a/src/common/scheduler.c b/src/common/scheduler.c
index 6e1e4fb1..d8048b50 100644
--- a/src/common/scheduler.c
+++ b/src/common/scheduler.c
@@ -221,13 +221,14 @@ const struct trx_chan_desc trx_chan_desc[_TRX_CHAN_MAX] = {
/* Rx and Tx, multiple convolutional coding types (3GPP TS 05.03,
* chapter 3), block diagonal interleaving (3GPP TS 05.02, clause 7):
*
- * - a traffic frame is interleaved over 6 consecutive bursts
+ * - a traffic frame is interleaved over 4 consecutive bursts
* using the even numbered bits of the first 2 bursts,
- * all bits of the middle two 2 bursts,
* and odd numbered bits of the last 2 bursts;
* - a FACCH/H frame 'steals' (replaces) two traffic frames,
- * interleaving is done over 4 consecutive bursts,
- * the same as given for a TCH/FS. */
+ * interleaving is done over 6 consecutive bursts,
+ * using the even numbered bits of the first 2 bursts,
+ * all bits of the middle two 2 bursts,
+ * and odd numbered bits of the last 2 bursts. */
.rts_fn = rts_tchh_fn,
.dl_fn = tx_tchh_fn,
.ul_fn = rx_tchh_fn,
@@ -653,35 +654,50 @@ void trx_sched_init(struct gsm_bts_trx *trx)
}
}
+static void trx_sched_clean_ts(struct gsm_bts_trx_ts *ts)
+{
+ struct l1sched_ts *l1ts = ts->priv;
+ unsigned int i;
+
+ msgb_queue_flush(&l1ts->dl_prims);
+ rate_ctr_group_free(l1ts->ctrs);
+ l1ts->ctrs = NULL;
+ for (i = 0; i < _TRX_CHAN_MAX; i++) {
+ struct l1sched_chan_state *chan_state;
+ chan_state = &l1ts->chan_state[i];
+ if (chan_state->dl_bursts) {
+ talloc_free(chan_state->dl_bursts);
+ chan_state->dl_bursts = NULL;
+ }
+ if (chan_state->ul_bursts) {
+ talloc_free(chan_state->ul_bursts);
+ chan_state->ul_bursts = NULL;
+ }
+ }
+ /* clear lchan channel states */
+ for (i = 0; i < ARRAY_SIZE(ts->lchan); i++)
+ lchan_set_state(&ts->lchan[i], LCHAN_S_NONE);
+
+ talloc_free(l1ts);
+ ts->priv = NULL;
+}
+
void trx_sched_clean(struct gsm_bts_trx *trx)
{
- unsigned int tn, i;
+ unsigned int tn;
LOGPTRX(trx, DL1C, LOGL_DEBUG, "Clean scheduler structures\n");
for (tn = 0; tn < ARRAY_SIZE(trx->ts); tn++) {
struct gsm_bts_trx_ts *ts = &trx->ts[tn];
- struct l1sched_ts *l1ts = ts->priv;
-
- msgb_queue_flush(&l1ts->dl_prims);
- rate_ctr_group_free(l1ts->ctrs);
- l1ts->ctrs = NULL;
- for (i = 0; i < _TRX_CHAN_MAX; i++) {
- struct l1sched_chan_state *chan_state;
- chan_state = &l1ts->chan_state[i];
- if (chan_state->dl_bursts) {
- talloc_free(chan_state->dl_bursts);
- chan_state->dl_bursts = NULL;
- }
- if (chan_state->ul_bursts) {
- talloc_free(chan_state->ul_bursts);
- chan_state->ul_bursts = NULL;
- }
- }
- /* clear lchan channel states */
- for (i = 0; i < ARRAY_SIZE(ts->lchan); i++)
- lchan_set_state(&ts->lchan[i], LCHAN_S_NONE);
+
+ /* Clean primary and shadow timeslots */
+ trx_sched_clean_ts(ts);
+ trx_sched_clean_ts(ts->vamos.peer);
}
+
+ /* Free previously allocated shadow timeslots */
+ gsm_bts_trx_free_shadow_ts(trx);
}
struct msgb *_sched_dequeue_prim(struct l1sched_ts *l1ts, const struct trx_dl_burst_req *br)
@@ -795,7 +811,7 @@ int _sched_compose_ph_data_ind(struct l1sched_ts *l1ts, uint32_t fn,
int _sched_compose_tch_ind(struct l1sched_ts *l1ts, uint32_t fn,
enum trx_chan_type chan, uint8_t *tch, uint8_t tch_len,
int16_t ta_offs_256bits, uint16_t ber10k, float rssi,
- uint8_t is_sub)
+ int16_t link_qual_cb, uint8_t is_sub)
{
struct msgb *msg;
struct osmo_phsap_prim *l1sap;
@@ -816,6 +832,7 @@ int _sched_compose_tch_ind(struct l1sched_ts *l1ts, uint32_t fn,
l1sap->u.tch.rssi = (int8_t) (rssi);
l1sap->u.tch.ber10k = ber10k;
l1sap->u.tch.ta_offs_256bits = ta_offs_256bits;
+ l1sap->u.tch.lqual_cb = link_qual_cb;
l1sap->u.tch.is_sub = is_sub & 1;
msg->l2h = msgb_put(msg, tch_len);
@@ -852,7 +869,7 @@ int trx_sched_ph_data_req(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap
OSMO_ASSERT(l1sap->oph.msg);
/* ignore empty frame */
- if (!msgb_l2len(l1sap->oph.msg)) {
+ if (!l1sap->oph.msg->l2h || msgb_l2len(l1sap->oph.msg) == 0) {
msgb_free(l1sap->oph.msg);
return 0;
}
@@ -1068,6 +1085,12 @@ int trx_sched_set_lchan(struct gsm_lchan *lchan, uint8_t chan_nr, uint8_t link_i
bool found = false;
int i;
+ if (!l1ts) {
+ LOGPLCHAN(lchan, DL1C, LOGL_ERROR, "%s lchan with uninitialized scheduler structure\n",
+ (active) ? "Activating" : "Deactivating");
+ return -EINVAL;
+ }
+
/* VAMOS: convert Osmocom specific channel number to a generic one,
* otherwise we won't match anything in trx_chan_desc[]. */
if (lchan->ts->vamos.is_shadow)
@@ -1085,7 +1108,7 @@ int trx_sched_set_lchan(struct gsm_lchan *lchan, uint8_t chan_nr, uint8_t link_i
continue;
found = true;
- LOGPLCHAN(lchan, DL1C, LOGL_NOTICE, "%s %s\n",
+ LOGPLCHAN(lchan, DL1C, LOGL_INFO, "%s %s\n",
(active) ? "Activating" : "Deactivating",
trx_chan_desc[i].name);
/* free burst memory, to cleanly start with burst 0 */
@@ -1278,6 +1301,26 @@ int _sched_rts(const struct l1sched_ts *l1ts, uint32_t fn)
return func(l1ts, &dbr);
}
+static void trx_sched_apply_att(const struct gsm_lchan *lchan,
+ struct trx_dl_burst_req *br)
+{
+ const struct trx_chan_desc *desc = &trx_chan_desc[br->chan];
+
+ /* Current BS power reduction value in dB */
+ br->att = lchan->bs_power_ctrl.current;
+
+ /* Temporary Overpower for SACCH/FACCH bursts */
+ if (!lchan->top_acch_active)
+ return;
+ if ((lchan->top_acch_cap.sacch_enable && desc->link_id == LID_SACCH) ||
+ (lchan->top_acch_cap.facch_enable && br->flags & TRX_BR_F_FACCH)) {
+ if (br->att > lchan->top_acch_cap.overpower_db)
+ br->att -= lchan->top_acch_cap.overpower_db;
+ else
+ br->att = 0;
+ }
+}
+
/* process downlink burst */
void _sched_dl_burst(struct l1sched_ts *l1ts, struct trx_dl_burst_req *br)
{
@@ -1317,9 +1360,7 @@ void _sched_dl_burst(struct l1sched_ts *l1ts, struct trx_dl_burst_req *br)
/* BS Power reduction (in dB) per logical channel */
if (l1cs->lchan != NULL)
- br->att = l1cs->lchan->bs_power_ctrl.current;
- else /* Ensure no attenuation in the absence of lchan (e.g. on PDCH) */
- br->att = 0;
+ trx_sched_apply_att(l1cs->lchan, br);
/* encrypt */
if (br->burst_len && l1cs->dl_encr_algo) {
diff --git a/src/common/sysinfo.c b/src/common/sysinfo.c
index 5c5af253..b0f1ebfd 100644
--- a/src/common/sysinfo.c
+++ b/src/common/sysinfo.c
@@ -164,22 +164,6 @@ uint8_t num_agch(const struct gsm_bts_trx *trx, const char * arg)
return 1;
}
-/* obtain the next to-be transmitted dowlink SACCH frame (L2 hdr + L3); returns pointer to lchan->si buffer */
-uint8_t *lchan_sacch_get(struct gsm_lchan *lchan)
-{
- uint32_t tmp, i;
-
- for (i = 0; i < _MAX_SYSINFO_TYPE; i++) {
- tmp = (lchan->si.last + 1 + i) % _MAX_SYSINFO_TYPE;
- if (!(lchan->si.valid & (1 << tmp)))
- continue;
- lchan->si.last = tmp;
- return GSM_LCHAN_SI(lchan, tmp);
- }
- LOGPLCHAN(lchan, DL1P, LOGL_NOTICE, "SACCH no SI available\n");
- return NULL;
-}
-
/* re-generate SI3 restoctets with GPRS indicator depending on the PCU socket connection state */
void regenerate_si3_restoctets(struct gsm_bts *bts)
{
diff --git a/src/common/ta_control.c b/src/common/ta_control.c
index ccb60e2b..025699ce 100644
--- a/src/common/ta_control.c
+++ b/src/common/ta_control.c
@@ -1,6 +1,7 @@
/* Loop control for Timing Advance */
/* (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
+ * (C) 2021 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
*
* All Rights Reserved
*
@@ -19,32 +20,83 @@
*
*/
+/* Related specs: 3GPP TS 45.010 sections 5.5, 5.6 */
+
#include <osmo-bts/gsm_data.h>
+#include <osmo-bts/bts_trx.h>
#include <osmo-bts/logging.h>
-/* 90% of one bit duration in 1/256 symbols: 256*0.9 */
-#define TOA256_9OPERCENT 230
+/* 3GPP TS 45.010 sec 5.6.3 Delay assessment error:
+ * 75% of one bit duration in 1/256 symbols: 256*0.75 */
+#define TOA256_THRESH 192
/* rqd_ta value range */
#define TA_MIN 0
#define TA_MAX 63
-void lchan_ms_ta_ctrl(struct gsm_lchan *lchan)
+/* TODO: make configurable over osmo-bts VTY? Pass it BSC->BTS? */
+#define TA_MAX_INC_STEP 2
+#define TA_MAX_DEC_STEP 2
+
+
+/*! compute the new "Ordered Timing Advance" communicated to the MS and store it in lchan.
+ * \param lchan logical channel for which to compute (and in which to store) new power value.
+ * \param[in] ms_tx_ta The TA used by the MS and reported in L1SACCH, see struct gsm_sacch_l1_hdr field "ta".
+ * \param[in] toa256 Time of Arrival (in 1/256th bits) computed at Rx side
+ */
+void lchan_ms_ta_ctrl(struct gsm_lchan *lchan, uint8_t ms_tx_ta, int16_t toa256)
{
- int16_t toa256 = lchan->meas.ms_toa256;
-
- if (toa256 < -TOA256_9OPERCENT && lchan->rqd_ta > TA_MIN) {
- LOGPLCHAN(lchan, DLOOP, LOGL_INFO,
- "TOA is too early (%d), now lowering TA from %d to %d\n",
- toa256, lchan->rqd_ta, lchan->rqd_ta - 1);
- lchan->rqd_ta--;
- } else if (toa256 > TOA256_9OPERCENT && lchan->rqd_ta < TA_MAX) {
- LOGPLCHAN(lchan, DLOOP, LOGL_INFO,
- "TOA is too late (%d), now raising TA from %d to %d\n",
- toa256, lchan->rqd_ta, lchan->rqd_ta + 1);
- lchan->rqd_ta++;
- } else
+ int16_t new_ta;
+ /* Shall we skip current block based on configured interval? */
+
+ /* TA control interval: how many blocks do we skip? */
+ if (lchan->ta_ctrl.skip_block_num-- > 0)
+ return;
+
+ /* Reset the number of SACCH blocks to be skipped:
+ * ctrl_interval=0 => 0 blocks to skip,
+ * ctrl_interval=1 => 1 blocks to skip,
+ * ctrl_interval=2 => 3 blocks to skip,
+ * so basically ctrl_interval * 2 - 1. */
+ lchan->ta_ctrl.skip_block_num = lchan->ts->trx->ta_ctrl_interval * 2 - 1;
+
+ int16_t delta_ta = toa256/256;
+ if (toa256 >= 0) {
+ if ((toa256 - (256 * delta_ta)) > TOA256_THRESH)
+ delta_ta++;
+ if (delta_ta > TA_MAX_INC_STEP)
+ delta_ta = TA_MAX_INC_STEP;
+ } else {
+ if ((toa256 - (256 * delta_ta)) < -TOA256_THRESH)
+ delta_ta--;
+ if (delta_ta < -TA_MAX_DEC_STEP)
+ delta_ta = -TA_MAX_DEC_STEP;
+ }
+
+ new_ta = ms_tx_ta + delta_ta;
+
+ /* Make sure new_ta is never negative: */
+ if (new_ta < TA_MIN)
+ new_ta = TA_MIN;
+
+ /* Don't ask for out of range TA: */
+ if (new_ta > TA_MAX)
+ new_ta = TA_MAX;
+
+ if (lchan->ta_ctrl.current == (uint8_t)new_ta) {
LOGPLCHAN(lchan, DLOOP, LOGL_DEBUG,
- "TOA is correct (%d), keeping current TA of %d\n",
- toa256, lchan->rqd_ta);
+ "Keeping current TA at %u: TOA was %d\n",
+ lchan->ta_ctrl.current, toa256);
+ return;
+ }
+
+ LOGPLCHAN(lchan, DLOOP, LOGL_INFO,
+ "%s TA %u => %u: TOA was too %s (%d)\n",
+ (uint8_t)new_ta > lchan->ta_ctrl.current ? "Raising" : "Lowering",
+ lchan->ta_ctrl.current, (uint8_t)new_ta,
+ (uint8_t)new_ta > lchan->ta_ctrl.current ? "late" : "early",
+ toa256);
+
+ /* store the resulting new TA in the lchan */
+ lchan->ta_ctrl.current = (uint8_t)new_ta;
}
diff --git a/src/common/tx_power.c b/src/common/tx_power.c
index 348aba5c..03074221 100644
--- a/src/common/tx_power.c
+++ b/src/common/tx_power.c
@@ -259,7 +259,7 @@ int power_ramp_start(struct gsm_bts_trx *trx, int p_total_tgt_mdBm, int bypass,
}
/* Cancel any pending request */
- osmo_timer_del(&tpp->ramp.step_timer);
+ power_ramp_abort(trx);
/* set the new target */
tpp->p_total_tgt_mdBm = p_total_tgt_mdBm;
@@ -297,6 +297,12 @@ int power_ramp_start(struct gsm_bts_trx *trx, int p_total_tgt_mdBm, int bypass,
return 0;
}
+/* Cancel any pending request */
+void power_ramp_abort(struct gsm_bts_trx *trx)
+{
+ osmo_timer_del(&trx->power_params.ramp.step_timer);
+}
+
/* determine the initial transceiver output power at start-up time */
int power_ramp_initial_power_mdBm(const struct gsm_bts_trx *trx)
{
diff --git a/src/common/vty.c b/src/common/vty.c
index a7d1e845..97bd6589 100644
--- a/src/common/vty.c
+++ b/src/common/vty.c
@@ -37,11 +37,12 @@
#include <osmocom/vty/logging.h>
#include <osmocom/vty/misc.h>
#include <osmocom/vty/ports.h>
+#include <osmocom/vty/tdef_vty.h>
#include <osmocom/core/gsmtap.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/sockaddr_str.h>
#include <osmocom/trau/osmo_ortp.h>
-
+#include <osmocom/core/fsm.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/gsm_data.h>
@@ -282,6 +283,7 @@ static void config_write_bts_single(struct vty *vty, const struct gsm_bts *bts)
const struct gsm_bts_trx *trx;
const char *sapi_buf;
int i;
+ struct bsc_oml_host *bsc_oml_host;
vty_out(vty, "bts %u%s", bts->nr, VTY_NEWLINE);
if (bts->description)
@@ -291,7 +293,8 @@ static void config_write_bts_single(struct vty *vty, const struct gsm_bts *bts)
vty_out(vty, " auto-band%s", VTY_NEWLINE);
vty_out(vty, " ipa unit-id %u %u%s",
bts->ip_access.site_id, bts->ip_access.bts_id, VTY_NEWLINE);
- vty_out(vty, " oml remote-ip %s%s", bts->bsc_oml_host, VTY_NEWLINE);
+ llist_for_each_entry(bsc_oml_host, &bts->bsc_oml_hosts, list)
+ vty_out(vty, " oml remote-ip %s%s", bsc_oml_host->addr, VTY_NEWLINE);
vty_out(vty, " rtp jitter-buffer %u", bts->rtp_jitter_buf_ms);
if (bts->rtp_jitter_adaptive)
vty_out(vty, " adaptive");
@@ -367,6 +370,8 @@ static void config_write_bts_single(struct vty *vty, const struct gsm_bts *bts)
vty_out(vty, " ms-power-control %s%s",
trx->ms_pwr_ctl_soft ? "osmo" : "dsp",
VTY_NEWLINE);
+ vty_out(vty, " ta-control interval %u%s",
+ trx->ta_ctrl_interval, VTY_NEWLINE);
vty_out(vty, " phy %u instance %u%s", pinst->phy_link->num,
pinst->num, VTY_NEWLINE);
@@ -382,6 +387,7 @@ static int config_write_bts(struct vty *vty)
llist_for_each_entry(bts, &net->bts_list, list)
config_write_bts_single(vty, bts);
+ osmo_tdef_vty_groups_write(vty, "");
return CMD_SUCCESS;
}
@@ -507,11 +513,51 @@ DEFUN(cfg_bts_oml_ip,
"OML Parameters\n" "OML IP Address\n" "OML IP Address\n")
{
struct gsm_bts *bts = vty->index;
+ struct bsc_oml_host *bsc_oml_host;
- if (bts->bsc_oml_host)
- talloc_free(bts->bsc_oml_host);
+ /* stop when the address is already in the list */
+ llist_for_each_entry(bsc_oml_host, &bts->bsc_oml_hosts, list) {
+ if (strcmp(argv[0], bsc_oml_host->addr) == 0) {
+ vty_out(vty, "%% duplicate BSC (A-Bis/OML) ip address configured ('%s')%s",
+ argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
+ }
+
+ bsc_oml_host = talloc_zero(bts, struct bsc_oml_host);
+ OSMO_ASSERT(bsc_oml_host);
+ bsc_oml_host->addr = talloc_strdup(bsc_oml_host, argv[0]);
+ OSMO_ASSERT(bsc_oml_host->addr);
+ llist_add_tail(&bsc_oml_host->list, &bts->bsc_oml_hosts);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(cfg_bts_no_oml_ip,
+ cfg_bts_no_oml_ip_cmd,
+ "no oml remote-ip A.B.C.D",
+ NO_STR "OML Parameters\n" "OML IP Address\n" "OML IP Address\n")
+{
+ struct gsm_bts *bts = vty->index;
+ struct bsc_oml_host *bsc_oml_host;
+ struct bsc_oml_host *bsc_oml_host_del = NULL;
+
+ llist_for_each_entry(bsc_oml_host, &bts->bsc_oml_hosts, list) {
+ if (strcmp(argv[0], bsc_oml_host->addr) == 0)
+ bsc_oml_host_del = bsc_oml_host;
+ }
- bts->bsc_oml_host = talloc_strdup(bts, argv[0]);
+ if (bsc_oml_host_del) {
+ if (bts->abis_link_fi) {
+ osmo_fsm_inst_dispatch(bts->abis_link_fi, ABIS_LINK_EV_VTY_RM_ADDR, bsc_oml_host_del);
+ llist_del(&bsc_oml_host_del->list);
+ talloc_free(bsc_oml_host_del);
+ }
+ } else {
+ vty_out(vty, "%% no such BSC (A-Bis/OML) ip address configured ('%s')%s",
+ argv[0], VTY_NEWLINE);
+ return CMD_WARNING;
+ }
return CMD_SUCCESS;
}
@@ -965,6 +1011,19 @@ DEFUN(cfg_trx_ms_power_control, cfg_trx_ms_power_control_cmd,
return CMD_SUCCESS;
}
+DEFUN(cfg_ta_ctrl_interval, cfg_ta_ctrl_interval_cmd,
+ "ta-control interval <0-31>",
+ "Timing Advance Control Parameters\n"
+ "Set TA control loop interval\n"
+ "As in P_CON_INTERVAL, in units of 2 SACCH periods (0.96 seconds) (default=0, every SACCH block)\n")
+{
+ struct gsm_bts_trx *trx = vty->index;
+
+ trx->ta_ctrl_interval = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_trx_phy, cfg_trx_phy_cmd,
"phy <0-255> instance <0-255>",
"Configure PHY Link+Instance for this TRX\n"
@@ -1347,7 +1406,7 @@ static void dump_dpc_meas_params(struct vty *vty, const unsigned int indent,
}
static void dump_dpc_params(struct vty *vty, const unsigned int indent,
- const struct gsm_power_ctrl_params *cp)
+ const struct gsm_power_ctrl_params *cp, bool uplink)
{
cfg_out(vty, "Power control interval: %u ms (every %u SACCH block(s))%s",
cp->ctrl_interval ? cp->ctrl_interval * 2 * 480 : 480,
@@ -1364,6 +1423,26 @@ static void dump_dpc_params(struct vty *vty, const unsigned int indent,
cfg_out(vty, "RxQual measurement processing:%s", VTY_NEWLINE);
dump_dpc_meas_params(vty, indent + 2, &cp->rxqual_meas, "RXQUAL", 3);
+
+ if (uplink) {
+ cfg_out(vty, "C/I measurement processing (FR/EFR):%s", VTY_NEWLINE);
+ dump_dpc_meas_params(vty, indent + 2, &cp->ci_fr_meas, "CI_FR", 0);
+
+ cfg_out(vty, "C/I measurement processing (HR):%s", VTY_NEWLINE);
+ dump_dpc_meas_params(vty, indent + 2, &cp->ci_hr_meas, "CI_HR", 0);
+
+ cfg_out(vty, "C/I measurement processing (AMR-FR):%s", VTY_NEWLINE);
+ dump_dpc_meas_params(vty, indent + 2, &cp->ci_amr_fr_meas, "CI_AMR_FR", 0);
+
+ cfg_out(vty, "C/I measurement processing (AMR-HR):%s", VTY_NEWLINE);
+ dump_dpc_meas_params(vty, indent + 2, &cp->ci_amr_hr_meas, "CI_AMR_HR", 0);
+
+ cfg_out(vty, "C/I measurement processing (SDCCH):%s", VTY_NEWLINE);
+ dump_dpc_meas_params(vty, indent + 2, &cp->ci_sdcch_meas, "CI_SDCCH", 0);
+
+ cfg_out(vty, "C/I measurement processing (GPRS):%s", VTY_NEWLINE);
+ dump_dpc_meas_params(vty, indent + 2, &cp->ci_gprs_meas, "CI_GPRS", 0);
+ }
}
static void trx_dump_vty(struct vty *vty, const struct gsm_bts_trx *trx)
@@ -1381,13 +1460,13 @@ static void trx_dump_vty(struct vty *vty, const struct gsm_bts_trx *trx)
trx->bs_dpc_params == &trx->bts->bs_dpc_params ?
"fall-back" : "from BSC",
VTY_NEWLINE);
- dump_dpc_params(vty, 4, trx->bs_dpc_params);
+ dump_dpc_params(vty, 4, trx->bs_dpc_params, false);
vty_out(vty, " MS Power control parameters (%s):%s",
trx->ms_dpc_params == &trx->bts->ms_dpc_params ?
"fall-back" : "from BSC",
VTY_NEWLINE);
- dump_dpc_params(vty, 4, trx->ms_dpc_params);
+ dump_dpc_params(vty, 4, trx->ms_dpc_params, true);
vty_out(vty, " NM State: ");
net_dump_nmstate(vty, &trx->mo.nm_state);
@@ -1594,7 +1673,7 @@ static void lchan_bs_power_ctrl_state_dump(struct vty *vty, unsigned int indent,
return;
cfg_out(vty, "Power Control parameters:%s", VTY_NEWLINE);
- dump_dpc_params(vty, indent + 2, st->dpc_params);
+ dump_dpc_params(vty, indent + 2, st->dpc_params, false);
}
static void lchan_ms_power_ctrl_state_dump(struct vty *vty, unsigned int indent,
@@ -1620,7 +1699,7 @@ static void lchan_ms_power_ctrl_state_dump(struct vty *vty, unsigned int indent,
return;
cfg_out(vty, "Power Control parameters:%s", VTY_NEWLINE);
- dump_dpc_params(vty, indent + 2, st->dpc_params);
+ dump_dpc_params(vty, indent + 2, st->dpc_params, true);
}
static void lchan_acch_rep_state_dump(struct vty *vty, unsigned int indent,
@@ -1628,23 +1707,23 @@ static void lchan_acch_rep_state_dump(struct vty *vty, unsigned int indent,
{
cfg_out(vty, "ACCH repetition:%s", VTY_NEWLINE);
indent += 2;
- if (lchan->repeated_acch_capability.rxqual)
+ if (lchan->rep_acch_cap.rxqual)
cfg_out(vty, "Enable RXQUAL threshold: %u%s",
- lchan->repeated_acch_capability.rxqual, VTY_NEWLINE);
+ lchan->rep_acch_cap.rxqual, VTY_NEWLINE);
else
cfg_out(vty, "Enable RXQUAL threshold: (none, alway on)%s",
VTY_NEWLINE);
cfg_out(vty, "DL-FACCH:%s", VTY_NEWLINE);
indent += 2;
- if (lchan->repeated_acch_capability.dl_facch_all)
+ if (lchan->rep_acch_cap.dl_facch_all)
cfg_out(vty, "retramsit all LAPDM block types%s", VTY_NEWLINE);
- else if (lchan->repeated_acch_capability.dl_facch_cmd)
+ else if (lchan->rep_acch_cap.dl_facch_cmd)
cfg_out(vty, "retramsit only LAPDM command blocks%s",
VTY_NEWLINE);
else
cfg_out(vty, "no retransmission (disabled)%s", VTY_NEWLINE);
- if (lchan->repeated_dl_facch_active)
+ if (lchan->rep_acch.dl_facch_active)
cfg_out(vty, "retransmission currently active%s", VTY_NEWLINE);
else
cfg_out(vty, "retransmission currently inactive%s",
@@ -1653,12 +1732,12 @@ static void lchan_acch_rep_state_dump(struct vty *vty, unsigned int indent,
cfg_out(vty, "DL-SACCH:%s", VTY_NEWLINE);
indent += 2;
- if (lchan->repeated_acch_capability.ul_sacch)
+ if (lchan->rep_acch_cap.ul_sacch)
cfg_out(vty, "retramsit all SACCH blocks for SAPI=0%s",
VTY_NEWLINE);
else
cfg_out(vty, "no retransmission (disabled)%s", VTY_NEWLINE);
- if (lchan->repeated_dl_sacch_active)
+ if (lchan->rep_acch.dl_sacch_active)
cfg_out(vty, "retransmission currently active%s", VTY_NEWLINE);
else
cfg_out(vty, "retransmission currently inactive%s",
@@ -1667,12 +1746,12 @@ static void lchan_acch_rep_state_dump(struct vty *vty, unsigned int indent,
cfg_out(vty, "UL-SACCH:%s", VTY_NEWLINE);
indent += 2;
- if (lchan->repeated_acch_capability.dl_sacch)
+ if (lchan->rep_acch_cap.dl_sacch)
cfg_out(vty, "retramsit all SACCH blocks for SAPI=0%s",
VTY_NEWLINE);
else
cfg_out(vty, "no retransmission (disabled)%s", VTY_NEWLINE);
- if (lchan->repeated_ul_sacch_active)
+ if (lchan->rep_acch.ul_sacch_active)
cfg_out(vty, "retransmission currently active%s", VTY_NEWLINE);
else
cfg_out(vty, "retransmission currently inactive%s",
@@ -1680,6 +1759,34 @@ static void lchan_acch_rep_state_dump(struct vty *vty, unsigned int indent,
indent -= 2;
}
+static void lchan_acch_top_state_dump(struct vty *vty, unsigned int indent,
+ const struct gsm_lchan *lchan)
+{
+ if (lchan->top_acch_cap.overpower_db == 0)
+ return;
+
+ cfg_out(vty, "Temporary ACCH overpower:%s", VTY_NEWLINE);
+ indent += 2;
+
+ cfg_out(vty, "Overpower value: %u dB%s",
+ lchan->top_acch_cap.overpower_db, VTY_NEWLINE);
+
+ cfg_out(vty, "SACCH overpower: %sabled%s",
+ lchan->top_acch_cap.sacch_enable ? "en" : "dis",
+ VTY_NEWLINE);
+ cfg_out(vty, "FACCH overpower: %sabled%s",
+ lchan->top_acch_cap.facch_enable ? "en" : "dis",
+ VTY_NEWLINE);
+
+ if (lchan->top_acch_cap.rxqual == 0) {
+ cfg_out(vty, "RxQual threshold: disabled "
+ "(overpower is always on)%s", VTY_NEWLINE);
+ } else {
+ cfg_out(vty, "RxQual threshold: %u%s",
+ lchan->top_acch_cap.rxqual, VTY_NEWLINE);
+ }
+}
+
static void lchan_dump_full_vty(struct vty *vty, const struct gsm_lchan *lchan)
{
struct in_addr ia;
@@ -1751,10 +1858,21 @@ static void lchan_dump_full_vty(struct vty *vty, const struct gsm_lchan *lchan)
vty_out(vty, " RTP/PDCH Loopback Enabled%s", VTY_NEWLINE);
vty_out(vty, " Radio Link Failure Counter 'S': %d%s", lchan->s, VTY_NEWLINE);
+ /* Interference levels */
+ if (lchan->meas.interf_meas_avg_dbm != 0) {
+ vty_out(vty, " Interference: %d dBm (band %d)%s",
+ lchan->meas.interf_meas_avg_dbm,
+ lchan->meas.interf_band,
+ VTY_NEWLINE);
+ }
+
/* BS/MS Power Control state and parameters */
lchan_bs_power_ctrl_state_dump(vty, 2, lchan);
lchan_ms_power_ctrl_state_dump(vty, 2, lchan);
+
+ /* ACCH repetition / overpower state */
lchan_acch_rep_state_dump(vty, 2, lchan);
+ lchan_acch_top_state_dump(vty, 2, lchan);
}
static void lchan_dump_short_vty(struct vty *vty, const struct gsm_lchan *lchan)
@@ -2376,8 +2494,12 @@ int bts_vty_init(void *ctx)
install_node(&bts_node, config_write_bts);
install_element(CONFIG_NODE, &cfg_bts_cmd);
install_element(CONFIG_NODE, &cfg_vty_telnet_port_cmd);
+
+ osmo_tdef_vty_groups_init(CONFIG_NODE, bts_tdef_groups);
+
install_element(BTS_NODE, &cfg_bts_unit_id_cmd);
install_element(BTS_NODE, &cfg_bts_oml_ip_cmd);
+ install_element(BTS_NODE, &cfg_bts_no_oml_ip_cmd);
install_element(BTS_NODE, &cfg_bts_rtp_bind_ip_cmd);
install_element(BTS_NODE, &cfg_bts_rtp_jitbuf_cmd);
install_element(BTS_NODE, &cfg_bts_rtp_port_range_cmd);
@@ -2419,6 +2541,7 @@ int bts_vty_init(void *ctx)
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(TRX_NODE, &cfg_ta_ctrl_interval_cmd);
install_element(TRX_NODE, &cfg_trx_phy_cmd);
install_element(ENABLE_NODE, &bts_t_t_l_jitter_buf_cmd);
diff --git a/src/osmo-bts-lc15/l1_if.c b/src/osmo-bts-lc15/l1_if.c
index 02c8646e..ac165b88 100644
--- a/src/osmo-bts-lc15/l1_if.c
+++ b/src/osmo-bts-lc15/l1_if.c
@@ -553,7 +553,12 @@ static int ph_tch_req(struct gsm_bts_trx *trx, struct msgb *msg,
empty_req_from_l1sap(l1p, fl1, u8Tn, u32Fn, sapi, subCh, u8BlockNbr);
}
/* send message to DSP's queue */
- osmo_wqueue_enqueue(&fl1->write_q[MQ_L1_WRITE], nmsg);
+ if (osmo_wqueue_enqueue(&fl1->write_q[MQ_L1_WRITE], nmsg) < 0) {
+ LOGPFN(DL1P, LOGL_ERROR, u32Fn, "MQ_L1_WRITE queue full. Dropping msg.\n");
+ msgb_free(nmsg);
+ return -ENOBUFS;
+ }
+
if (dtx_is_first_p1(lchan))
dtx_dispatch(lchan, E_FIRST);
else
diff --git a/src/osmo-bts-lc15/lc15bts_vty.c b/src/osmo-bts-lc15/lc15bts_vty.c
index be6a4f8b..5efbfcc5 100644
--- a/src/osmo-bts-lc15/lc15bts_vty.c
+++ b/src/osmo-bts-lc15/lc15bts_vty.c
@@ -140,7 +140,7 @@ DEFUN(cfg_phy_no_dsp_trace_f, cfg_phy_no_dsp_trace_f_cmd,
/* runtime */
DEFUN(show_dsp_trace_f, show_dsp_trace_f_cmd,
- "show trx <0-0> dsp-trace-flags",
+ "show dsp-trace-flags trx <0-0>",
SHOW_TRX_STR "Display the current setting of the DSP trace flags")
{
int trx_nr = atoi(argv[0]);
diff --git a/src/osmo-bts-lc15/oml.c b/src/osmo-bts-lc15/oml.c
index b4945be0..675e3fed 100644
--- a/src/osmo-bts-lc15/oml.c
+++ b/src/osmo-bts-lc15/oml.c
@@ -1197,10 +1197,6 @@ int lchan_activate(struct gsm_lchan *lchan)
enqueue_sapi_act_cmd(lchan, sapi, dir);
}
-
-#warning "FIXME: Should this be in sapi_activate_cb?"
- lchan_init_lapdm(lchan);
-
return 0;
}
@@ -1834,11 +1830,31 @@ int bts_model_check_oml(struct gsm_bts *bts, uint8_t msg_type,
int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg,
struct tlv_parsed *new_attr, int kind, void *obj)
{
- if (kind == NM_OC_RADIO_CARRIER) {
- struct gsm_bts_trx *trx = obj;
- struct lc15l1_hdl *fl1h = trx_lc15l1_hdl(trx);
+ struct abis_om_fom_hdr *foh = msgb_l3(msg);
+ struct gsm_abis_mo *mo = gsm_objclass2mo(bts, foh->obj_class, &foh->obj_inst);
+ struct nm_fsm_ev_setattr_data ev_data = {
+ .msg = msg,
+ .cause = 0,
+ };
+ int rc;
+ struct gsm_bts_trx *trx;
+ struct lc15l1_hdl *fl1h;
+ uint8_t cell_size;
+
+ /* TODO: NM Object without FSM: */
+ switch (foh->obj_class) {
+ case NM_OC_GPRS_NSE:
+ case NM_OC_GPRS_CELL:
+ case NM_OC_GPRS_NSVC:
+ return oml_fom_ack_nack(ev_data.msg, ev_data.cause);
+ }
+
+ switch (foh->msg_type) {
+ case NM_MT_SET_RADIO_ATTR:
+ trx = obj;
+ fl1h = trx_lc15l1_hdl(trx);
/* convert max TA to max cell size in qbits */
- uint8_t cell_size = bts->max_ta << 2;
+ cell_size = bts->max_ta << 2;
#if LITECELL15_API_VERSION >= LITECELL15_API(2,1,7)
/* We do not need to check for L1 handle
@@ -1870,9 +1886,14 @@ int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg,
}
#endif
}
+ break;
}
- /* FIXME: we actually need to send a ACK or NACK for the OML message */
- return oml_fom_ack_nack(msg, 0);
+
+ rc = osmo_fsm_inst_dispatch(mo->fi,
+ ev_data.cause == 0 ? NM_EV_SETATTR_ACK : NM_EV_SETATTR_NACK,
+ &ev_data);
+ /* msgb ownership is transferred to FSM if it received ev: */
+ return rc == 0 ? 1 : 0;
}
/* callback from OML */
@@ -1906,13 +1927,13 @@ int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo,
case NM_OC_GPRS_NSE:
case NM_OC_GPRS_CELL:
case NM_OC_GPRS_NSVC:
- oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, -1);
+ oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, -1, -1);
rc = oml_mo_opstart_ack(mo);
if (mo->obj_class == NM_OC_BTS) {
- oml_mo_state_chg(&bts->mo, -1, NM_AVSTATE_OK);
- oml_mo_state_chg(&bts->gprs.nse.mo, -1, NM_AVSTATE_OK);
- oml_mo_state_chg(&bts->gprs.cell.mo, -1, NM_AVSTATE_OK);
- oml_mo_state_chg(&bts->gprs.nsvc[0].mo, -1, NM_AVSTATE_OK);
+ oml_mo_state_chg(&bts->mo, -1, NM_AVSTATE_OK, -1);
+ oml_mo_state_chg(&bts->gprs.nse.mo, -1, NM_AVSTATE_OK, -1);
+ oml_mo_state_chg(&bts->gprs.cell.mo, -1, NM_AVSTATE_OK, -1);
+ oml_mo_state_chg(&bts->gprs.nsvc[0].mo, -1, NM_AVSTATE_OK, -1);
}
break;
default:
diff --git a/src/osmo-bts-oc2g/l1_if.c b/src/osmo-bts-oc2g/l1_if.c
index 2cefc3b0..194f82a4 100644
--- a/src/osmo-bts-oc2g/l1_if.c
+++ b/src/osmo-bts-oc2g/l1_if.c
@@ -606,7 +606,11 @@ static int ph_tch_req(struct gsm_bts_trx *trx, struct msgb *msg,
empty_req_from_l1sap(l1p, fl1, u8Tn, u32Fn, sapi, subCh, u8BlockNbr);
}
/* send message to DSP's queue */
- osmo_wqueue_enqueue(&fl1->write_q[MQ_L1_WRITE], nmsg);
+ if (osmo_wqueue_enqueue(&fl1->write_q[MQ_L1_WRITE], nmsg) < 0) {
+ LOGPFN(DL1P, LOGL_ERROR, u32Fn, "MQ_L1_WRITE queue full. Dropping msg.\n");
+ msgb_free(nmsg);
+ return -ENOBUFS;
+ }
if (dtx_is_first_p1(lchan))
dtx_dispatch(lchan, E_FIRST);
else
diff --git a/src/osmo-bts-oc2g/oc2gbts_vty.c b/src/osmo-bts-oc2g/oc2gbts_vty.c
index d69225ac..ae7cd12d 100644
--- a/src/osmo-bts-oc2g/oc2gbts_vty.c
+++ b/src/osmo-bts-oc2g/oc2gbts_vty.c
@@ -133,7 +133,7 @@ DEFUN(cfg_phy_no_dsp_trace_f, cfg_phy_no_dsp_trace_f_cmd,
/* runtime */
DEFUN(show_dsp_trace_f, show_dsp_trace_f_cmd,
- "show trx <0-0> dsp-trace-flags",
+ "show dsp-trace-flags trx <0-0>",
SHOW_TRX_STR "Display the current setting of the DSP trace flags")
{
int trx_nr = atoi(argv[0]);
diff --git a/src/osmo-bts-oc2g/oml.c b/src/osmo-bts-oc2g/oml.c
index 9791ab8b..d12571b7 100644
--- a/src/osmo-bts-oc2g/oml.c
+++ b/src/osmo-bts-oc2g/oml.c
@@ -1212,10 +1212,6 @@ int lchan_activate(struct gsm_lchan *lchan)
enqueue_sapi_act_cmd(lchan, sapi, dir);
}
-
-#warning "FIXME: Should this be in sapi_activate_cb?"
- lchan_init_lapdm(lchan);
-
return 0;
}
@@ -1843,11 +1839,31 @@ int bts_model_check_oml(struct gsm_bts *bts, uint8_t msg_type,
int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg,
struct tlv_parsed *new_attr, int kind, void *obj)
{
- if (kind == NM_OC_RADIO_CARRIER) {
- struct gsm_bts_trx *trx = obj;
- struct oc2gl1_hdl *fl1h = trx_oc2gl1_hdl(trx);
+ struct abis_om_fom_hdr *foh = msgb_l3(msg);
+ struct gsm_abis_mo *mo = gsm_objclass2mo(bts, foh->obj_class, &foh->obj_inst);
+ struct nm_fsm_ev_setattr_data ev_data = {
+ .msg = msg,
+ .cause = 0,
+ };
+ int rc;
+ struct gsm_bts_trx *trx;
+ struct oc2gl1_hdl *fl1h;
+ uint8_t cell_size;
+
+ /* TODO: NM Object without FSM: */
+ switch (foh->obj_class) {
+ case NM_OC_GPRS_NSE:
+ case NM_OC_GPRS_CELL:
+ case NM_OC_GPRS_NSVC:
+ return oml_fom_ack_nack(ev_data.msg, ev_data.cause);
+ }
+
+ switch (foh->msg_type) {
+ case NM_MT_SET_RADIO_ATTR:
+ trx = obj;
+ fl1h = trx_oc2gl1_hdl(trx);
/* convert max TA to max cell size in qbits */
- uint8_t cell_size = bts->max_ta << 2;
+ cell_size = bts->max_ta << 2;
/* We do not need to check for L1 handle
* because the max cell size parameter can receive before MphInit */
@@ -1876,11 +1892,14 @@ int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg,
l1if_set_txpower_c0_idle_pwr_red(fl1h, fl1h->phy_inst->u.oc2g.tx_c0_idle_pwr_red);
}
}
-
+ break;
}
- /* FIXME: we actually need to send a ACK or NACK for the OML message */
- return oml_fom_ack_nack(msg, 0);
+ rc = osmo_fsm_inst_dispatch(mo->fi,
+ ev_data.cause == 0 ? NM_EV_SETATTR_ACK : NM_EV_SETATTR_NACK,
+ &ev_data);
+ /* msgb ownsership is transferred to FSM if it received ev: */
+ return rc == 0 ? 1 : 0;
}
/* callback from OML */
@@ -1898,10 +1917,10 @@ int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo,
break;
case NM_OC_BTS:
rc = osmo_fsm_inst_dispatch(bts->mo.fi, NM_EV_OPSTART_ACK, NULL);
- oml_mo_state_chg(&bts->mo, -1, NM_AVSTATE_OK);
- oml_mo_state_chg(&bts->gprs.nse.mo, -1, NM_AVSTATE_OK);
- oml_mo_state_chg(&bts->gprs.cell.mo, -1, NM_AVSTATE_OK);
- oml_mo_state_chg(&bts->gprs.nsvc[0].mo, -1, NM_AVSTATE_OK);
+ oml_mo_state_chg(&bts->mo, -1, NM_AVSTATE_OK, -1);
+ oml_mo_state_chg(&bts->gprs.nse.mo, -1, NM_AVSTATE_OK, -1);
+ oml_mo_state_chg(&bts->gprs.cell.mo, -1, NM_AVSTATE_OK, -1);
+ oml_mo_state_chg(&bts->gprs.nsvc[0].mo, -1, NM_AVSTATE_OK, -1);
break;
case NM_OC_RADIO_CARRIER:
trx = (struct gsm_bts_trx *) obj;
@@ -1918,7 +1937,7 @@ int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo,
case NM_OC_GPRS_NSE:
case NM_OC_GPRS_CELL:
case NM_OC_GPRS_NSVC:
- oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, -1);
+ oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, -1, -1);
rc = oml_mo_opstart_ack(mo);
break;
default:
diff --git a/src/osmo-bts-octphy/l1_if.c b/src/osmo-bts-octphy/l1_if.c
index 294a65ec..ebd960e3 100644
--- a/src/osmo-bts-octphy/l1_if.c
+++ b/src/osmo-bts-octphy/l1_if.c
@@ -1302,7 +1302,11 @@ static int retransmit_wlc_upto(struct octphy_hdl *fl1h, uint32_t trans_id)
wlc->num_retrans++;
msg = msgb_copy(wlc->cmd_msg, "PHY CMD Retrans");
msg_set_retrans_flag(msg);
- osmo_wqueue_enqueue(&fl1h->phy_wq, msg);
+ if (osmo_wqueue_enqueue(&fl1h->phy_wq, msg) < 0) {
+ LOGP(DL1C, LOGL_ERROR, "Queue full on wlc retransmit\n");
+ msgb_free(msg);
+ return 0;
+ }
osmo_timer_schedule(&wlc->timer, CMD_TIMEOUT, 0);
count++;
LOGP(DL1C, LOGL_INFO, "Re-transmitting %s "
diff --git a/src/osmo-bts-octphy/l1_oml.c b/src/osmo-bts-octphy/l1_oml.c
index 5b48b2e6..5894c210 100644
--- a/src/osmo-bts-octphy/l1_oml.c
+++ b/src/osmo-bts-octphy/l1_oml.c
@@ -459,7 +459,7 @@ static int mph_send_activate_req(struct gsm_lchan *lchan, struct sapi_cmd *cmd)
lac->LchId.bySAPI = cmd->sapi;
lac->LchId.byDirection = cmd->dir;
- lac->Config.byTimingAdvance = lchan->rqd_ta;
+ lac->Config.byTimingAdvance = lchan->ta_ctrl.current;
lac->Config.byBSIC = lchan->ts->trx->bts->bsic;
if ((rc = lchan2lch_par(lchan, &lac->Config)) != 0) {
talloc_free(msg);
@@ -1107,9 +1107,6 @@ int lchan_activate(struct gsm_lchan *lchan)
}
enqueue_sapi_act_cmd(lchan, sapi, dir);
}
-
- lchan_init_lapdm(lchan);
-
return 0;
}
@@ -1748,13 +1745,36 @@ int bts_model_check_oml(struct gsm_bts *bts, uint8_t msg_type,
int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg,
struct tlv_parsed *new_attr, int kind, void *obj)
{
- if (kind == NM_OC_RADIO_CARRIER) {
- struct gsm_bts_trx *trx = obj;
- /*struct octphy_hdl *fl1h = trx_octphy_hdl(trx); */
+ struct abis_om_fom_hdr *foh = msgb_l3(msg);
+ struct gsm_abis_mo *mo = gsm_objclass2mo(bts, foh->obj_class, &foh->obj_inst);
+ struct nm_fsm_ev_setattr_data ev_data = {
+ .msg = msg,
+ .cause = 0,
+ };
+ int rc;
+ struct gsm_bts_trx *trx;
+ /* TODO: NM Object without FSM: */
+ switch (foh->obj_class) {
+ case NM_OC_GPRS_NSE:
+ case NM_OC_GPRS_CELL:
+ case NM_OC_GPRS_NSVC:
+ return oml_fom_ack_nack(ev_data.msg, ev_data.cause);
+ }
+
+ switch (foh->msg_type) {
+ case NM_MT_SET_RADIO_ATTR:
+ trx = obj;
+ /*struct octphy_hdl *fl1h = trx_octphy_hdl(trx); */
power_ramp_start(trx, get_p_target_mdBm(trx, 0), 0, NULL);
+ break;
}
- return oml_fom_ack_nack(msg, 0);
+
+ rc = osmo_fsm_inst_dispatch(mo->fi,
+ ev_data.cause == 0 ? NM_EV_SETATTR_ACK : NM_EV_SETATTR_NACK,
+ &ev_data);
+ /* msgb ownsership is transferred to FSM if it received ev: */
+ return rc == 0 ? 1 : 0;
}
@@ -1788,7 +1808,7 @@ int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo, void *obj)
case NM_OC_GPRS_NSE:
case NM_OC_GPRS_CELL:
case NM_OC_GPRS_NSVC:
- oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, -1);
+ oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, -1, -1);
rc = oml_mo_opstart_ack(mo);
break;
default:
diff --git a/src/osmo-bts-omldummy/bts_model.c b/src/osmo-bts-omldummy/bts_model.c
index 46558a1c..f5d59a30 100644
--- a/src/osmo-bts-omldummy/bts_model.c
+++ b/src/osmo-bts-omldummy/bts_model.c
@@ -97,20 +97,38 @@ int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg,
struct tlv_parsed *new_attr, int kind, void *obj)
{
struct abis_om_fom_hdr *foh = msgb_l3(msg);
- int cause = 0;
+ struct gsm_abis_mo *mo = gsm_objclass2mo(bts, foh->obj_class, &foh->obj_inst);
+ struct nm_fsm_ev_setattr_data ev_data = {
+ .msg = msg,
+ .cause = 0,
+ };
+ int rc;
+
+ /* TODO: NM Object without FSM: */
+ switch (foh->obj_class) {
+ case NM_OC_GPRS_NSE:
+ case NM_OC_GPRS_CELL:
+ case NM_OC_GPRS_NSVC:
+ return oml_fom_ack_nack(ev_data.msg, ev_data.cause);
+ }
switch (foh->msg_type) {
case NM_MT_SET_BTS_ATTR:
- cause = vbts_set_bts(obj);
+ ev_data.cause = vbts_set_bts(obj);
break;
case NM_MT_SET_RADIO_ATTR:
- cause = vbts_set_trx(obj);
+ ev_data.cause = vbts_set_trx(obj);
break;
case NM_MT_SET_CHAN_ATTR:
- cause = vbts_set_ts(obj);
+ ev_data.cause = vbts_set_ts(obj);
break;
}
- return oml_fom_ack_nack(msg, cause);
+
+ rc = osmo_fsm_inst_dispatch(mo->fi,
+ ev_data.cause == 0 ? NM_EV_SETATTR_ACK : NM_EV_SETATTR_NACK,
+ &ev_data);
+ /* msgb ownsership is transferred to FSM if it received ev: */
+ return rc == 0 ? 1 : 0;
}
/* MO: TS 12.21 Managed Object */
@@ -143,7 +161,7 @@ int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo, void *obj)
case NM_OC_GPRS_NSE:
case NM_OC_GPRS_CELL:
case NM_OC_GPRS_NSVC:
- oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK);
+ oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK, -1);
rc = oml_mo_opstart_ack(mo);
break;
default:
diff --git a/src/osmo-bts-omldummy/main.c b/src/osmo-bts-omldummy/main.c
index a36e0db5..c74af745 100644
--- a/src/osmo-bts-omldummy/main.c
+++ b/src/osmo-bts-omldummy/main.c
@@ -107,7 +107,7 @@ int main(int argc, char **argv)
{
struct gsm_bts *bts;
struct gsm_bts_trx *trx;
- struct e1inp_line *line;
+ struct bsc_oml_host *bsc_oml_host;
int i;
parse_cmdline(argc, argv);
@@ -144,9 +144,13 @@ int main(int argc, char **argv)
//btsb = bts_role_bts(bts);
abis_init(bts);
- line = abis_open(bts, cmdline.dst_host, "OMLdummy");
- if (!line)
- exit(2);
+ bsc_oml_host = talloc_zero(bts, struct bsc_oml_host);
+ OSMO_ASSERT(bsc_oml_host);
+ bsc_oml_host->addr = talloc_strdup(bsc_oml_host, cmdline.dst_host);
+ OSMO_ASSERT(bsc_oml_host->addr);
+ llist_add_tail(&bsc_oml_host->list, &bts->bsc_oml_hosts);
+ if (abis_open(bts, "OMLdummy") != 0)
+ exit(1);
while (1) {
osmo_select_main(0);
diff --git a/src/osmo-bts-sysmo/oml.c b/src/osmo-bts-sysmo/oml.c
index 93ecf902..ba60b76e 100644
--- a/src/osmo-bts-sysmo/oml.c
+++ b/src/osmo-bts-sysmo/oml.c
@@ -1204,10 +1204,6 @@ int lchan_activate(struct gsm_lchan *lchan)
continue;
enqueue_sapi_act_cmd(lchan, sapi, dir);
}
-
-#warning "FIXME: Should this be in sapi_activate_cb?"
- lchan_init_lapdm(lchan);
-
return 0;
}
@@ -1748,17 +1744,40 @@ int bts_model_check_oml(struct gsm_bts *bts, uint8_t msg_type,
int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg,
struct tlv_parsed *new_attr, int kind, void *obj)
{
- if (kind == NM_OC_RADIO_CARRIER) {
- struct gsm_bts_trx *trx = obj;
- struct femtol1_hdl *fl1h = trx_femtol1_hdl(trx);
+ struct abis_om_fom_hdr *foh = msgb_l3(msg);
+ struct gsm_abis_mo *mo = gsm_objclass2mo(bts, foh->obj_class, &foh->obj_inst);
+ struct nm_fsm_ev_setattr_data ev_data = {
+ .msg = msg,
+ .cause = 0,
+ };
+ int rc;
+ struct gsm_bts_trx *trx;
+ struct femtol1_hdl *fl1h;
+
+ /* TODO: NM Object without FSM: */
+ switch (foh->obj_class) {
+ case NM_OC_GPRS_NSE:
+ case NM_OC_GPRS_CELL:
+ case NM_OC_GPRS_NSVC:
+ return oml_fom_ack_nack(ev_data.msg, ev_data.cause);
+ }
+
+ switch (foh->msg_type) {
+ case NM_MT_SET_RADIO_ATTR:
+ trx = obj;
+ fl1h = trx_femtol1_hdl(trx);
/* Did we go through MphInit yet? If yes fire and forget */
if (fl1h->hLayer1)
power_ramp_start(trx, get_p_target_mdBm(trx, 0), 0, NULL);
+ break;
}
- /* FIXME: we actually need to send a ACK or NACK for the OML message */
- return oml_fom_ack_nack(msg, 0);
+ rc = osmo_fsm_inst_dispatch(mo->fi,
+ ev_data.cause == 0 ? NM_EV_SETATTR_ACK : NM_EV_SETATTR_NACK,
+ &ev_data);
+ /* msgb ownsership is transferred to FSM if it received ev: */
+ return rc == 0 ? 1 : 0;
}
/* callback from OML */
@@ -1776,10 +1795,10 @@ int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo,
break;
case NM_OC_BTS:
rc = osmo_fsm_inst_dispatch(bts->mo.fi, NM_EV_OPSTART_ACK, NULL);
- oml_mo_state_chg(&bts->mo, -1, NM_AVSTATE_OK);
- oml_mo_state_chg(&bts->gprs.nse.mo, -1, NM_AVSTATE_OK);
- oml_mo_state_chg(&bts->gprs.cell.mo, -1, NM_AVSTATE_OK);
- oml_mo_state_chg(&bts->gprs.nsvc[0].mo, -1, NM_AVSTATE_OK);
+ oml_mo_state_chg(&bts->mo, -1, NM_AVSTATE_OK, -1);
+ oml_mo_state_chg(&bts->gprs.nse.mo, -1, NM_AVSTATE_OK, -1);
+ oml_mo_state_chg(&bts->gprs.cell.mo, -1, NM_AVSTATE_OK, -1);
+ oml_mo_state_chg(&bts->gprs.nsvc[0].mo, -1, NM_AVSTATE_OK, -1);
break;
case NM_OC_RADIO_CARRIER:
trx = (struct gsm_bts_trx *) obj;
@@ -1796,7 +1815,7 @@ int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo,
case NM_OC_GPRS_NSE:
case NM_OC_GPRS_CELL:
case NM_OC_GPRS_NSVC:
- oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, -1);
+ oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, -1, -1);
rc = oml_mo_opstart_ack(mo);
break;
default:
diff --git a/src/osmo-bts-sysmo/sysmobts_vty.c b/src/osmo-bts-sysmo/sysmobts_vty.c
index a8e7401f..2e853356 100644
--- a/src/osmo-bts-sysmo/sysmobts_vty.c
+++ b/src/osmo-bts-sysmo/sysmobts_vty.c
@@ -226,7 +226,7 @@ DEFUN(show_phy_clksrc, show_trx_clksrc_cmd,
}
DEFUN(show_dsp_trace_f, show_dsp_trace_f_cmd,
- "show trx <0-0> dsp-trace-flags",
+ "show dsp-trace-flags trx <0-0>",
SHOW_TRX_STR "Display the current setting of the DSP trace flags")
{
int trx_nr = atoi(argv[0]);
diff --git a/src/osmo-bts-trx/Makefile.am b/src/osmo-bts-trx/Makefile.am
index 54d1af9e..afa54141 100644
--- a/src/osmo-bts-trx/Makefile.am
+++ b/src/osmo-bts-trx/Makefile.am
@@ -1,6 +1,7 @@
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_srcdir)/include \
+ -I$(top_builddir)/include \
$(NULL)
AM_CFLAGS = \
@@ -51,6 +52,7 @@ osmo_bts_trx_SOURCES = \
trx_provision_fsm.c \
trx_vty.c \
loops.c \
+ probes.d \
$(NULL)
osmo_bts_trx_LDADD = \
@@ -58,3 +60,14 @@ osmo_bts_trx_LDADD = \
$(top_builddir)/src/common/libbts.a \
$(LDADD) \
$(NULL)
+
+if ENABLE_SYSTEMTAP
+probes.h: probes.d
+ $(DTRACE) -C -h -s $< -o $@
+
+probes.lo: probes.d
+ $(LIBTOOL) --mode=compile $(AM_V_lt) --tag=CC env CFLAGS="$(CFLAGS)" $(DTRACE) -C -G -s $< -o $@
+
+BUILT_SOURCES = probes.h probes.lo
+osmo_bts_trx_LDADD += probes.lo
+endif
diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c
index 754e9d7a..5ac3e314 100644
--- a/src/osmo-bts-trx/l1_if.c
+++ b/src/osmo-bts-trx/l1_if.c
@@ -87,26 +87,6 @@ struct trx_l1h *trx_l1h_alloc(void *tall_ctx, struct phy_instance *pinst)
return l1h;
}
-static void check_transceiver_availability_trx(struct trx_l1h *l1h, int avail)
-{
- struct phy_instance *pinst = l1h->phy_inst;
- struct gsm_bts_trx *trx = pinst->trx;
-
- /* HACK, we should change state when we receive first clock from
- * transceiver */
- if (avail) {
- /* signal availability */
- if (!pinst->u.osmotrx.sw_act_reported) {
- osmo_fsm_inst_dispatch(trx->mo.fi, NM_EV_SW_ACT, NULL);
- osmo_fsm_inst_dispatch(trx->bb_transc.mo.fi, NM_EV_SW_ACT, NULL);
- pinst->u.osmotrx.sw_act_reported = true;
- }
- } else {
- osmo_fsm_inst_dispatch(trx->mo.fi, NM_EV_DISABLE, NULL);
- osmo_fsm_inst_dispatch(trx->bb_transc.mo.fi, NM_EV_DISABLE, NULL);
- }
-}
-
int bts_model_lchan_deactivate(struct gsm_lchan *lchan)
{
if (lchan->rel_act_kind == LCHAN_REL_ACT_REACT) {
@@ -185,10 +165,6 @@ static int trx_init(struct gsm_bts_trx *trx)
if (rc != 0)
return osmo_fsm_inst_dispatch(trx->mo.fi, NM_EV_OPSTART_NACK,
(void*)(intptr_t)NM_NACK_CANT_PERFORM);
-
- if (trx == trx->bts->c0)
- lchan_init_lapdm(&trx->ts[0].lchan[CCCH_LCHAN]);
-
/* Send OPSTART ack */
return osmo_fsm_inst_dispatch(trx->mo.fi, NM_EV_OPSTART_ACK, NULL);
}
@@ -211,13 +187,15 @@ void bts_model_trx_close(struct gsm_bts_trx *trx)
osmo_fsm_inst_dispatch(l1h->provision_fi, TRX_PROV_EV_CLOSE, NULL);
/* Set to Operational State: Disabled */
- check_transceiver_availability_trx(l1h, 0);
+ osmo_fsm_inst_dispatch(trx->mo.fi, NM_EV_DISABLE, NULL);
+ osmo_fsm_inst_dispatch(trx->bb_transc.mo.fi, NM_EV_DISABLE, NULL);
}
-/* on RSL failure, deactivate transceiver */
void bts_model_abis_close(struct gsm_bts *bts)
{
- bts_shutdown(bts, "Abis close");
+ /* Go into shutdown state deactivating transceivers until Abis link
+ * becomes up again */
+ bts_shutdown_ext(bts, "Abis close", false);
}
int bts_model_adjst_ms_pwr(struct gsm_lchan *lchan)
@@ -230,15 +208,19 @@ int bts_model_adjst_ms_pwr(struct gsm_lchan *lchan)
/* set bts attributes */
static uint8_t trx_set_bts(struct gsm_bts *bts, struct tlv_parsed *new_attr)
{
- struct gsm_bts_trx *trx;
+ struct phy_instance *pinst = trx_phy_instance(bts->c0);
+ struct trx_l1h *l1h = pinst->u.osmotrx.hdl;
uint8_t bsic = bts->bsic;
+ struct gsm_bts_trx *trx;
+
+ /* ARFCN for C0 is assigned during Set BTS Attr, see oml.c */
+ osmo_fsm_inst_dispatch(l1h->provision_fi, TRX_PROV_EV_CFG_ARFCN, (void *)(intptr_t)pinst->trx->arfcn);
llist_for_each_entry(trx, &bts->trx_list, list) {
- struct phy_instance *pinst = trx_phy_instance(trx);
- struct phy_link *plink = pinst->phy_link;
- struct trx_l1h *l1h = pinst->u.osmotrx.hdl;
+ pinst = trx_phy_instance(trx);
+ l1h = pinst->u.osmotrx.hdl;
+
osmo_fsm_inst_dispatch(l1h->provision_fi, TRX_PROV_EV_CFG_BSIC, (void*)(intptr_t)bsic);
- check_transceiver_availability_trx(l1h, phy_link_state_get(plink) != PHY_LINK_SHUTDOWN);
}
return 0;
@@ -252,7 +234,9 @@ static uint8_t trx_set_trx(struct gsm_bts_trx *trx)
struct phy_link *plink = pinst->phy_link;
uint16_t arfcn = trx->arfcn;
- osmo_fsm_inst_dispatch(l1h->provision_fi, TRX_PROV_EV_CFG_ARFCN, (void*)(intptr_t)arfcn);
+ /* ARFCN for C0 is assigned during Set BTS Attr, see oml.c */
+ if (trx != trx->bts->c0)
+ osmo_fsm_inst_dispatch(l1h->provision_fi, TRX_PROV_EV_CFG_ARFCN, (void *)(intptr_t)arfcn);
/* Begin to ramp up the power if power reduction is set by OML and TRX
is already running. Otherwise skip, power ramping will be started
@@ -297,7 +281,7 @@ static uint8_t trx_set_ts_as_pchan(struct gsm_bts_trx_ts *ts,
struct trx_prov_ev_cfg_ts_data data = { .tn = tn, .slottype = slottype };
- if (ts->tsc_set != 0 || ts->tsc != BTS_TSC(ts->trx->bts)) {
+ if (ts->tsc_set != 0) {
/* On TRXC we use 3GPP compliant numbering, so +1 */
data.tsc_set = ts->tsc_set + 1;
data.tsc_val = ts->tsc;
@@ -436,19 +420,6 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
/* attempt to allocate an Error Concealment Unit instance, if available */
lchan->ecu_state = osmo_ecu_init(trx, lchan2ecu_codec(lchan));
- /* trx_chan_desc[] in scheduler.c uses the RSL_CHAN_OSMO_PDCH cbits
- * (0xc0) to indicate the need for PDTCH and PTCCH SAPI activation.
- * However, 0xc0 is a cbits pattern exclusively used for Osmocom style
- * dyn TS (a non-standard RSL Chan Activ mod); hence, for IPA style dyn
- * TS, the chan_nr will never reflect 0xc0 and we would omit the
- * PDTCH,PTTCH SAPIs. To properly de-/activate the PDTCH SAPIs in
- * scheduler.c, make sure the 0xc0 cbits are set for de-/activating PDTCH
- * lchans, i.e. both Osmocom and IPA style dyn TS. (For Osmocom style dyn
- * TS, the chan_nr typically already reflects 0xc0, while it doesn't for
- * IPA style.) */
- if (lchan->type == GSM_LCHAN_PDTCH)
- chan_nr = RSL_CHAN_OSMO_PDCH | (chan_nr & ~RSL_CHAN_NR_MASK);
-
/* activate dedicated channel */
trx_sched_set_lchan(lchan, chan_nr, LID_DEDIC, true);
/* activate associated channel */
@@ -463,8 +434,6 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
lchan->tch.amr_mr.bts_mode[3].mode,
amr_get_initial_mode(lchan),
(lchan->ho.active == 1));
- /* init lapdm */
- lchan_init_lapdm(lchan);
/* set lchan active */
lchan_set_state(lchan, LCHAN_S_ACTIVE);
/* set initial ciphering */
@@ -555,21 +524,38 @@ int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg,
struct tlv_parsed *new_attr, int kind, void *obj)
{
struct abis_om_fom_hdr *foh = msgb_l3(msg);
- int cause = 0;
+ struct gsm_abis_mo *mo = gsm_objclass2mo(bts, foh->obj_class, &foh->obj_inst);
+ struct nm_fsm_ev_setattr_data ev_data = {
+ .msg = msg,
+ .cause = 0,
+ };
+ int rc;
+
+ /* TODO: NM Object without FSM: */
+ switch (foh->obj_class) {
+ case NM_OC_GPRS_NSE:
+ case NM_OC_GPRS_CELL:
+ case NM_OC_GPRS_NSVC:
+ return oml_fom_ack_nack(ev_data.msg, ev_data.cause);
+ }
switch (foh->msg_type) {
case NM_MT_SET_BTS_ATTR:
- cause = trx_set_bts(obj, new_attr);
+ ev_data.cause = trx_set_bts(obj, new_attr);
break;
case NM_MT_SET_RADIO_ATTR:
- cause = trx_set_trx(obj);
+ ev_data.cause = trx_set_trx(obj);
break;
case NM_MT_SET_CHAN_ATTR:
- cause = trx_set_ts(obj);
+ ev_data.cause = trx_set_ts(obj);
break;
}
- return oml_fom_ack_nack(msg, cause);
+ rc = osmo_fsm_inst_dispatch(mo->fi,
+ ev_data.cause == 0 ? NM_EV_SETATTR_ACK : NM_EV_SETATTR_NACK,
+ &ev_data);
+ /* msgb ownsership is transferred to FSM if it received ev: */
+ return rc == 0 ? 1 : 0;
}
/* callback from OML */
@@ -604,7 +590,7 @@ int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo,
case NM_OC_GPRS_NSE:
case NM_OC_GPRS_CELL:
case NM_OC_GPRS_NSVC:
- oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK);
+ oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK, -1);
rc = oml_mo_opstart_ack(mo);
break;
default:
diff --git a/src/osmo-bts-trx/main.c b/src/osmo-bts-trx/main.c
index 9720b035..5d680f05 100644
--- a/src/osmo-bts-trx/main.c
+++ b/src/osmo-bts-trx/main.c
@@ -148,8 +148,10 @@ int bts_model_init(struct gsm_bts *bts)
osmo_bts_set_feature(bts->features, BTS_FEAT_MULTI_TSC);
osmo_bts_set_feature(bts->features, BTS_FEAT_VAMOS);
osmo_bts_set_feature(bts->features, BTS_FEAT_BCCH_POWER_RED);
+ osmo_bts_set_feature(bts->features, BTS_FEAT_ACCH_TEMP_OVP);
bts_internal_flag_set(bts, BTS_INTERNAL_FLAG_MEAS_PAYLOAD_COMB);
+ bts_internal_flag_set(bts, BTS_INTERNAL_FLAG_INTERF_MEAS);
return 0;
}
diff --git a/src/osmo-bts-trx/probes.d b/src/osmo-bts-trx/probes.d
new file mode 100644
index 00000000..2f905bd1
--- /dev/null
+++ b/src/osmo-bts-trx/probes.d
@@ -0,0 +1,7 @@
+provider osmo_bts_trx {
+ probe ul_data_start(int, int, int); /* trx_nr, ts_nr, fn */
+ probe ul_data_done(int, int, int); /* trx_nr, ts_nr, fn */
+
+ probe dl_rts_start(int, int, int); /* trx_nr, ts_nr, fn */
+ probe dl_rts_done(int, int, int); /* trx_nr, ts_nr, fn */
+};
diff --git a/src/osmo-bts-trx/sched_lchan_pdtch.c b/src/osmo-bts-trx/sched_lchan_pdtch.c
index 335ba6fe..66be1164 100644
--- a/src/osmo-bts-trx/sched_lchan_pdtch.c
+++ b/src/osmo-bts-trx/sched_lchan_pdtch.c
@@ -155,26 +155,21 @@ int tx_pdtch_fn(struct l1sched_ts *l1ts, struct trx_dl_burst_req *br)
/* send burst, if we already got a frame */
if (br->bid > 0) {
if (!*bursts_p)
- return 0;
+ return -ENODEV;
goto send_burst;
}
/* get mac block from queue */
msg = _sched_dequeue_prim(l1ts, br);
- if (msg)
- goto got_msg;
-
- LOGL1SB(DL1P, LOGL_INFO, l1ts, br, "No prim for transmit.\n");
-
-no_msg:
- /* free burst memory */
- if (*bursts_p) {
- talloc_free(*bursts_p);
- *bursts_p = NULL;
+ if (!msg) {
+ /* It's totally fine to get no block here, since PCU may submit
+ * empty blocks when there's no MS listening. The scheduler will
+ * take care of filling C0 with dummy bursts to keep expected
+ * power transmit levels
+ */
+ goto no_msg;
}
- return -ENODEV;
-got_msg:
/* BURST BYPASS */
/* allocate burst memory, if not already */
@@ -229,4 +224,12 @@ send_burst:
LOGL1SB(DL1P, LOGL_DEBUG, l1ts, br, "Transmitting burst=%u.\n", br->bid);
return 0;
+
+no_msg:
+ /* free burst memory */
+ if (*bursts_p) {
+ talloc_free(*bursts_p);
+ *bursts_p = NULL;
+ }
+ return -ENODEV;
}
diff --git a/src/osmo-bts-trx/sched_lchan_tchf.c b/src/osmo-bts-trx/sched_lchan_tchf.c
index 689925fb..85ab3217 100644
--- a/src/osmo-bts-trx/sched_lchan_tchf.c
+++ b/src/osmo-bts-trx/sched_lchan_tchf.c
@@ -65,6 +65,7 @@ int rx_tchf_fn(struct l1sched_ts *l1ts, const struct trx_ul_burst_ind *bi)
uint16_t ber10k;
uint8_t is_sub = 0;
uint8_t ft;
+ bool amr_is_cmr;
/* If handover RACH detection is turned on, treat this burst as an Access Burst.
* Handle NOPE.ind as usually to ensure proper Uplink measurement reporting. */
@@ -129,6 +130,8 @@ int rx_tchf_fn(struct l1sched_ts *l1ts, const struct trx_ul_burst_ind *bi)
* the first FN 4,13,21 defines that CMR is included in frame.
* NOTE: A frame ends 7 FN after start.
*/
+ fn_begin = gsm0502_fn_remap(bi->fn, FN_REMAP_TCH_F);
+ amr_is_cmr = !ul_amr_fn_is_cmi(fn_begin);
/* The AFS_ONSET frame itself does not result into an RTP frame
* since it only contains a recognition pattern that marks the
@@ -144,8 +147,7 @@ int rx_tchf_fn(struct l1sched_ts *l1ts, const struct trx_ul_burst_ind *bi)
* know this before we actually decode the frame) */
amr = 2;
rc = gsm0503_tch_afs_decode_dtx(tch_data + amr, *bursts_p,
- (((bi->fn + 26 - 7) % 26) >> 2) & 1, chan_state->codec,
- chan_state->codecs, &chan_state->ul_ft,
+ amr_is_cmr, chan_state->codec, chan_state->codecs, &chan_state->ul_ft,
&chan_state->ul_cmr, &n_errors, &n_bits_total, &chan_state->amr_last_dtx);
/* Tag all frames that are not regular AMR voice frames as
@@ -186,7 +188,7 @@ int rx_tchf_fn(struct l1sched_ts *l1ts, const struct trx_ul_burst_ind *bi)
/* only good speech frames get rtp header */
if (rc != GSM_MACBLOCK_LEN && rc >= 4) {
if (chan_state->amr_last_dtx == AMR_OTHER) {
- ft = chan_state->codec[chan_state->ul_cmr];
+ ft = chan_state->codec[chan_state->ul_ft];
} else {
/* SID frames will always get Frame Type Index 8 (AMR_SID) */
ft = AMR_SID;
@@ -274,8 +276,8 @@ bfi:
break;
case GSM48_CMODE_SPEECH_AMR: /* AMR */
rc = osmo_amr_rtp_enc(tch_data,
- chan_state->codec[chan_state->dl_cmr],
- chan_state->codec[chan_state->dl_ft],
+ chan_state->codec[chan_state->ul_cmr],
+ chan_state->codec[chan_state->ul_ft],
AMR_BAD);
if (rc < 2) {
LOGL1SB(DL1P, LOGL_ERROR, l1ts, bi,
@@ -302,7 +304,9 @@ compose_l1sap:
return _sched_compose_tch_ind(l1ts, fn_begin, bi->chan, tch_data, rc,
/* FIXME: what should we use for BFI here? */
bfi_flag ? bi->toa256 : meas_avg.toa256, ber10k,
- bfi_flag ? bi->rssi : meas_avg.rssi, is_sub);
+ bfi_flag ? bi->rssi : meas_avg.rssi,
+ bfi_flag ? bi->ci_cb : meas_avg.ci_cb,
+ is_sub);
}
/* common section for generation of TCH bursts (TCH/H and TCH/F).
@@ -364,7 +368,7 @@ inval_mode1:
/* Note: RSSI/ToA256 is set to 0 to indicate to the higher
* layers that this is a faked tch_ind */
_sched_compose_tch_ind(l1ts, br->fn, br->chan,
- tch_data, len, 0, 10000, 0, 0);
+ tch_data, len, 0, 10000, 0, 0, 0);
}
}
@@ -394,12 +398,6 @@ inval_mode1:
msg_tch = msg2;
}
}
- } else if (msg2) {
- l1sap = msgb_l1sap_prim(msg2);
- if (l1sap->oph.primitive == PRIM_TCH)
- msg_tch = msg2;
- else
- msg_facch = msg2;
}
/* check validity of message */
@@ -419,6 +417,7 @@ inval_mode1:
enum osmo_amr_type ft_codec;
enum osmo_amr_quality bfi;
int8_t sti, cmi;
+ bool amr_is_cmr = !dl_amr_fn_is_cmi(br->fn);
if (rsl_cmode != RSL_CMOD_SPD_SPEECH) {
LOGL1SB(DL1P, LOGL_NOTICE, l1ts, br, "Dropping speech frame, "
@@ -463,7 +462,7 @@ inval_mode1:
"Codec (FT = %d) of RTP frame not in list\n", ft_codec);
goto free_bad_msg;
}
- if (fn_is_codec_mode_request(br->fn) && chan_state->dl_ft != ft) {
+ if (amr_is_cmr && chan_state->dl_ft != ft) {
LOGL1SB(DL1P, LOGL_NOTICE, l1ts, br, "Codec (FT = %d) "
" of RTP cannot be changed now, but in next frame\n", ft_codec);
goto free_bad_msg;
@@ -511,7 +510,7 @@ int tx_tchf_fn(struct l1sched_ts *l1ts, struct trx_dl_burst_req *br)
/* send burst, if we already got a frame */
if (br->bid > 0) {
if (!*bursts_p)
- return 0;
+ return -ENODEV;
goto send_burst;
}
@@ -530,22 +529,30 @@ int tx_tchf_fn(struct l1sched_ts *l1ts, struct trx_dl_burst_req *br)
memset(*bursts_p + 464, 0, 464);
}
- /* no message at all */
+ /* no message at all, send a dummy L2 frame on FACCH */
if (!msg_tch && !msg_facch) {
- LOGL1SB(DL1P, LOGL_INFO, l1ts, br, "No TCH or FACCH prim for transmit.\n");
+ static const uint8_t dummy[GSM_MACBLOCK_LEN] = {
+ 0x03, 0x03, 0x01, /* TODO: use randomized padding */
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ };
+
+ LOGL1SB(DL1P, LOGL_DEBUG, l1ts, br, "No TCH or FACCH prim for transmit.\n");
+ gsm0503_tch_fr_encode(*bursts_p, dummy, sizeof(dummy), 1);
goto send_burst;
}
/* encode bursts (prioritize FACCH) */
- if (msg_facch)
+ if (msg_facch) {
gsm0503_tch_fr_encode(*bursts_p, msg_facch->l2h, msgb_l2len(msg_facch),
1);
- else if (tch_mode == GSM48_CMODE_SPEECH_AMR)
+ chan_state->dl_facch_bursts = 8;
+ } else if (tch_mode == GSM48_CMODE_SPEECH_AMR)
/* the first FN 4,13,21 defines that CMI is included in frame,
* the first FN 0,8,17 defines that CMR is included in frame.
*/
gsm0503_tch_afs_encode(*bursts_p, msg_tch->l2h + 2,
- msgb_l2len(msg_tch) - 2, fn_is_codec_mode_request(br->fn),
+ msgb_l2len(msg_tch) - 2, !dl_amr_fn_is_cmi(br->fn),
chan_state->codec, chan_state->codecs,
chan_state->dl_ft,
chan_state->dl_cmr);
@@ -567,6 +574,11 @@ send_burst:
br->burst_len = GSM_BURST_LEN;
+ if (chan_state->dl_facch_bursts > 0) {
+ chan_state->dl_facch_bursts--;
+ br->flags |= TRX_BR_F_FACCH;
+ }
+
LOGL1SB(DL1P, LOGL_DEBUG, l1ts, br, "Transmitting burst=%u.\n", br->bid);
return 0;
diff --git a/src/osmo-bts-trx/sched_lchan_tchh.c b/src/osmo-bts-trx/sched_lchan_tchh.c
index 4f03bd12..366c625c 100644
--- a/src/osmo-bts-trx/sched_lchan_tchh.c
+++ b/src/osmo-bts-trx/sched_lchan_tchh.c
@@ -72,6 +72,7 @@ int rx_tchh_fn(struct l1sched_ts *l1ts, const struct trx_ul_burst_ind *bi)
uint8_t is_sub = 0;
uint8_t ft;
bool mask_stolen_tch_block = false;
+ bool fn_is_cmi;
/* If handover RACH detection is turned on, treat this burst as an Access Burst.
* Handle NOPE.ind as usually to ensure proper Uplink measurement reporting. */
@@ -164,10 +165,21 @@ int rx_tchh_fn(struct l1sched_ts *l1ts, const struct trx_ul_burst_ind *bi)
break;
}
+ /* Calculate the frame number where the block begins */
+ if (bi->fn % 13 < 4)
+ fn_tch_end = GSM_TDMA_FN_SUB(bi->fn, 5);
+ else
+ fn_tch_end = GSM_TDMA_FN_SUB(bi->fn, 4);
+ if (lchan->nr == 0)
+ fn_begin = gsm0502_fn_remap(fn_tch_end, FN_REMAP_TCH_H0);
+ else
+ fn_begin = gsm0502_fn_remap(fn_tch_end, FN_REMAP_TCH_H1);
+ fn_is_cmi = ul_amr_fn_is_cmi(fn_begin);
+
/* See comment in function rx_tchf_fn() */
amr = 2;
rc = gsm0503_tch_ahs_decode_dtx(tch_data + amr, *bursts_p,
- fn_is_odd, fn_is_odd, chan_state->codec,
+ fn_is_odd, !fn_is_cmi, chan_state->codec,
chan_state->codecs, &chan_state->ul_ft,
&chan_state->ul_cmr, &n_errors, &n_bits_total, &chan_state->amr_last_dtx);
@@ -210,7 +222,7 @@ int rx_tchh_fn(struct l1sched_ts *l1ts, const struct trx_ul_burst_ind *bi)
/* only good speech frames get rtp header */
if (rc != GSM_MACBLOCK_LEN && rc >= 4) {
if (chan_state->amr_last_dtx == AMR_OTHER) {
- ft = chan_state->codec[chan_state->ul_cmr];
+ ft = chan_state->codec[chan_state->ul_ft];
} else {
/* SID frames will always get Frame Type Index 8 (AMR_SID) */
ft = AMR_SID;
@@ -305,8 +317,8 @@ bfi:
break;
case GSM48_CMODE_SPEECH_AMR: /* AMR */
rc = osmo_amr_rtp_enc(tch_data,
- chan_state->codec[chan_state->dl_cmr],
- chan_state->codec[chan_state->dl_ft],
+ chan_state->codec[chan_state->ul_cmr],
+ chan_state->codec[chan_state->ul_ft],
AMR_BAD);
if (rc < 2) {
LOGL1SB(DL1P, LOGL_ERROR, l1ts, bi,
@@ -343,7 +355,6 @@ compose_l1sap:
fn_tch_end = GSM_TDMA_FN_SUB(bi->fn, 5);
else
fn_tch_end = GSM_TDMA_FN_SUB(bi->fn, 4);
-
if (lchan->nr == 0)
fn_begin = gsm0502_fn_remap(fn_tch_end, FN_REMAP_TCH_H0);
else
@@ -364,7 +375,9 @@ compose_l1sap:
return _sched_compose_tch_ind(l1ts, fn_begin, bi->chan, tch_data, rc,
/* FIXME: what should we use for BFI here? */
bfi_flag ? bi->toa256 : meas_avg.toa256, ber10k,
- bfi_flag ? bi->rssi : meas_avg.rssi, is_sub);
+ bfi_flag ? bi->rssi : meas_avg.rssi,
+ bfi_flag ? bi->ci_cb : meas_avg.ci_cb,
+ is_sub);
}
/* common section for generation of TCH bursts (TCH/H and TCH/F).
@@ -384,7 +397,7 @@ int tx_tchh_fn(struct l1sched_ts *l1ts, struct trx_dl_burst_req *br)
/* send burst, if we already got a frame */
if (br->bid > 0) {
if (!*bursts_p)
- return 0;
+ return -ENODEV;
goto send_burst;
}
@@ -417,9 +430,16 @@ int tx_tchh_fn(struct l1sched_ts *l1ts, struct trx_dl_burst_req *br)
}
}
- /* no message at all */
+ /* no message at all, send a dummy L2 frame on FACCH */
if (!msg_tch && !msg_facch && !chan_state->dl_ongoing_facch) {
+ static const uint8_t dummy[GSM_MACBLOCK_LEN] = {
+ 0x03, 0x03, 0x01, /* TODO: use randomized padding */
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+ };
+
LOGL1SB(DL1P, LOGL_INFO, l1ts, br, "No TCH or FACCH prim for transmit.\n");
+ gsm0503_tch_hr_encode(*bursts_p, dummy, sizeof(dummy));
goto send_burst;
}
@@ -427,6 +447,7 @@ int tx_tchh_fn(struct l1sched_ts *l1ts, struct trx_dl_burst_req *br)
if (msg_facch) {
gsm0503_tch_hr_encode(*bursts_p, msg_facch->l2h, msgb_l2len(msg_facch));
chan_state->dl_ongoing_facch = 1; /* first of two TCH frames */
+ chan_state->dl_facch_bursts = 6;
} else if (chan_state->dl_ongoing_facch) /* second of two TCH frames */
chan_state->dl_ongoing_facch = 0; /* we are done with FACCH */
else if (tch_mode == GSM48_CMODE_SPEECH_AMR)
@@ -434,7 +455,7 @@ int tx_tchh_fn(struct l1sched_ts *l1ts, struct trx_dl_burst_req *br)
* in frame, the first FN 0,8,17 or 1,9,18 defines that CMR is
* included in frame. */
gsm0503_tch_ahs_encode(*bursts_p, msg_tch->l2h + 2,
- msgb_l2len(msg_tch) - 2, fn_is_codec_mode_request(br->fn),
+ msgb_l2len(msg_tch) - 2, !dl_amr_fn_is_cmi(br->fn),
chan_state->codec, chan_state->codecs,
chan_state->dl_ft,
chan_state->dl_cmr);
@@ -456,6 +477,11 @@ send_burst:
br->burst_len = GSM_BURST_LEN;
+ if (chan_state->dl_facch_bursts > 0) {
+ chan_state->dl_facch_bursts--;
+ br->flags |= TRX_BR_F_FACCH;
+ }
+
LOGL1SB(DL1P, LOGL_DEBUG, l1ts, br, "Transmitting burst=%u.\n", br->bid);
return 0;
diff --git a/src/osmo-bts-trx/sched_lchan_xcch.c b/src/osmo-bts-trx/sched_lchan_xcch.c
index 4bfc101a..e8f46c1a 100644
--- a/src/osmo-bts-trx/sched_lchan_xcch.c
+++ b/src/osmo-bts-trx/sched_lchan_xcch.c
@@ -60,7 +60,7 @@ int rx_data_fn(struct l1sched_ts *l1ts, const struct trx_ul_burst_ind *bi)
uint16_t ber10k;
int rc;
struct gsm_lchan *lchan = chan_state->lchan;
- bool rep_sacch = L1SAP_IS_LINK_SACCH(trx_chan_desc[bi->chan].link_id) && lchan->repeated_ul_sacch_active;
+ bool rep_sacch = L1SAP_IS_LINK_SACCH(trx_chan_desc[bi->chan].link_id) && lchan->rep_acch.ul_sacch_active;
/* If handover RACH detection is turned on, treat this burst as an Access Burst.
* Handle NOPE.ind as usually to ensure proper Uplink measurement reporting. */
@@ -176,7 +176,7 @@ int tx_data_fn(struct l1sched_ts *l1ts, struct trx_dl_burst_req *br)
/* send burst, if we already got a frame */
if (br->bid > 0) {
if (!*bursts_p)
- return 0;
+ return -ENODEV;
goto send_burst;
}
diff --git a/src/osmo-bts-trx/sched_utils.h b/src/osmo-bts-trx/sched_utils.h
index 4a1aaf5f..f76e49bb 100644
--- a/src/osmo-bts-trx/sched_utils.h
+++ b/src/osmo-bts-trx/sched_utils.h
@@ -23,6 +23,8 @@
#include <stdint.h>
#include <errno.h>
+#include <stdbool.h>
+#include <osmo-bts/scheduler.h>
extern void *tall_bts_ctx;
@@ -35,8 +37,76 @@ static inline uint16_t compute_ber10k(int n_bits_total, int n_errors)
return 10000 * n_errors / n_bits_total;
}
-/* determine if the FN is transmitting a CMR (1) or not (0) */
-static inline int fn_is_codec_mode_request(uint32_t fn)
+/*! determine whether an uplink AMR block is CMI according to 3GPP TS 45.009.
+ * \param[in] fn_begin frame number of the beginning of the block.
+ * \returns true in case of CMI; false otherwise. */
+static inline bool ul_amr_fn_is_cmi(uint32_t fn_begin)
{
- return (((fn + 4) % 26) >> 2) & 1;
+ switch (fn_begin % 26) {
+ /*! See also: 3GPP TS 45.009, section 3.2.1.3 Transmitter/Receiver Synchronisation */
+ /* valid for AHS subslot 0 and AFS: */
+ case 0:
+ case 8:
+ case 17:
+ /* valid for AHS subslot 1: */
+ case 1:
+ case 9:
+ case 18:
+ return true;
+ break;
+ /* Complementary values for sanity check */
+ /* valid for AHS subslot 0 and AFS: */
+ case 4:
+ case 13:
+ case 21:
+ /* valid for AHS subslot 1: */
+ case 5:
+ case 14:
+ case 22:
+ return false;
+ break;
+ default:
+ LOGP(DL1P, LOGL_DEBUG,
+ "uplink frame number fn_begin=%u does not mark the beginning of a voice block!\n", fn_begin);
+ OSMO_ASSERT(false);
+ return false;
+ break;
+ }
+}
+
+/*! determine the whether a downlink AMR block is CMI according to 3GPP TS 45.009.
+ * \param[in] fn_begin frame number of the beginning of the block.
+ * \returns true in case of CMI; false otherwise. */
+static inline bool dl_amr_fn_is_cmi(uint32_t fn_begin)
+{
+ switch (fn_begin % 26) {
+ /*! See also: 3GPP TS 45.009, section 3.2.1.3 Transmitter/Receiver Synchronisation */
+ /* valid for AHS subslot 0 and AFS: */
+ case 4:
+ case 13:
+ case 21:
+ /* valid for AHS subslot 1: */
+ case 5:
+ case 14:
+ case 22:
+ return true;
+ break;
+ /* Complementary values for sanity check */
+ /* valid for AHS subslot 0 and AFS: */
+ case 0:
+ case 8:
+ case 17:
+ /* valid for AHS subslot 1: */
+ case 1:
+ case 9:
+ case 18:
+ return false;
+ break;
+ default:
+ LOGP(DL1P, LOGL_DEBUG,
+ "downlink frame number fn_begin=%u does not mark the beginning of a voice block!\n", fn_begin);
+ OSMO_ASSERT(false);
+ return false;
+ break;
+ }
}
diff --git a/src/osmo-bts-trx/scheduler_trx.c b/src/osmo-bts-trx/scheduler_trx.c
index 0a907fba..1159e594 100644
--- a/src/osmo-bts-trx/scheduler_trx.c
+++ b/src/osmo-bts-trx/scheduler_trx.c
@@ -50,88 +50,86 @@
#include "l1_if.h"
#include "trx_if.h"
+#include "btsconfig.h"
+
+#ifdef HAVE_SYSTEMTAP
+/* include the generated probes header and put markers in code */
+#include "probes.h"
+#define TRACE(probe) probe
+#define TRACE_ENABLED(probe) probe ## _ENABLED()
+#else
+/* Wrap the probe to allow it to be removed when no systemtap available */
+#define TRACE(probe)
+#define TRACE_ENABLED(probe) (0)
+#endif /* HAVE_SYSTEMTAP */
+
#define SCHED_FH_PARAMS_FMT "hsn=%u, maio=%u, ma_len=%u"
#define SCHED_FH_PARAMS_VALS(ts) \
(ts)->hopping.hsn, (ts)->hopping.maio, (ts)->hopping.arfcn_num
-static void ts_report_interf_meas(const struct gsm_bts_trx_ts *ts)
+static void lchan_report_interf_meas(const struct gsm_lchan *lchan)
{
+ const struct gsm_bts_trx_ts *ts = lchan->ts;
const struct l1sched_ts *l1ts = ts->priv;
- unsigned int ln;
-
- for (ln = 0; ln < ARRAY_SIZE(ts->lchan); ln++) {
- const struct gsm_lchan *lchan = &ts->lchan[ln];
- enum trx_chan_type dcch, acch;
- int interf_avg;
+ enum trx_chan_type dcch, acch;
+ int interf_avg;
- /* We're not interested in active channels */
- if (lchan->state == LCHAN_S_ACTIVE)
- continue;
+ /* We're not interested in active CS channels */
+ if (lchan->state == LCHAN_S_ACTIVE) {
+ if (lchan->type != GSM_LCHAN_PDTCH)
+ return;
+ }
- switch (lchan->type) {
- case GSM_LCHAN_SDCCH:
- if (ts->pchan == GSM_PCHAN_CCCH_SDCCH4 ||
- ts->pchan == GSM_PCHAN_CCCH_SDCCH4_CBCH) {
- dcch = TRXC_SDCCH4_0 + ln;
- acch = TRXC_SACCH4_0 + ln;
- } else { /* SDCCH/8 otherwise */
- dcch = TRXC_SDCCH8_0 + ln;
- acch = TRXC_SACCH8_0 + ln;
- }
- break;
- case GSM_LCHAN_TCH_F:
- dcch = TRXC_TCHF;
- acch = TRXC_SACCHTF;
- break;
- case GSM_LCHAN_TCH_H:
- dcch = TRXC_TCHH_0 + ln;
- acch = TRXC_SACCHTH_0 + ln;
- break;
- default:
- /* Skip other lchan types */
- continue;
+ switch (lchan->type) {
+ case GSM_LCHAN_SDCCH:
+ if (ts->pchan == GSM_PCHAN_CCCH_SDCCH4 ||
+ ts->pchan == GSM_PCHAN_CCCH_SDCCH4_CBCH) {
+ dcch = TRXC_SDCCH4_0 + lchan->nr;
+ acch = TRXC_SACCH4_0 + lchan->nr;
+ } else { /* SDCCH/8 otherwise */
+ dcch = TRXC_SDCCH8_0 + lchan->nr;
+ acch = TRXC_SACCH8_0 + lchan->nr;
}
+ break;
+ case GSM_LCHAN_TCH_F:
+ dcch = TRXC_TCHF;
+ acch = TRXC_SACCHTF;
+ break;
+ case GSM_LCHAN_TCH_H:
+ dcch = TRXC_TCHH_0 + lchan->nr;
+ acch = TRXC_SACCHTH_0 + lchan->nr;
+ break;
+ case GSM_LCHAN_PDTCH:
+ /* We use idle TDMA frames on PDCH */
+ dcch = TRXC_IDLE;
+ acch = TRXC_IDLE;
+ break;
+ default:
+ /* Skip other lchan types */
+ return;
+ }
- OSMO_ASSERT(dcch < ARRAY_SIZE(l1ts->chan_state));
- OSMO_ASSERT(acch < ARRAY_SIZE(l1ts->chan_state));
+ OSMO_ASSERT(dcch < ARRAY_SIZE(l1ts->chan_state));
+ OSMO_ASSERT(acch < ARRAY_SIZE(l1ts->chan_state));
- interf_avg = (l1ts->chan_state[dcch].meas.interf_avg +
- l1ts->chan_state[acch].meas.interf_avg) / 2;
+ interf_avg = (l1ts->chan_state[dcch].meas.interf_avg +
+ l1ts->chan_state[acch].meas.interf_avg) / 2;
- gsm_lchan_interf_meas_push((struct gsm_lchan *) lchan, interf_avg);
- }
+ gsm_lchan_interf_meas_push((struct gsm_lchan *) lchan, interf_avg);
}
static void bts_report_interf_meas(const struct gsm_bts *bts,
const uint32_t fn)
{
const struct gsm_bts_trx *trx;
+ unsigned int tn, ln;
llist_for_each_entry(trx, &bts->trx_list, list) {
- uint8_t pdch_interf[8] = { 0 };
- unsigned int tn, pdch_num = 0;
-
for (tn = 0; tn < ARRAY_SIZE(trx->ts); tn++) {
const struct gsm_bts_trx_ts *ts = &trx->ts[tn];
- const struct l1sched_ts *l1ts = ts->priv;
- const struct l1sched_chan_state *l1cs;
-
- /* PS interference reports for the PCU */
- if (ts_pchan(ts) == GSM_PCHAN_PDCH) {
- l1cs = &l1ts->chan_state[TRXC_IDLE];
- /* Interference value is encoded as -x dBm */
- pdch_interf[tn] = -1 * l1cs->meas.interf_avg;
- pdch_num++;
- continue;
- }
-
- /* CS interference reports for the BSC */
- ts_report_interf_meas(ts);
+ for (ln = 0; ln < ARRAY_SIZE(ts->lchan); ln++)
+ lchan_report_interf_meas(&ts->lchan[ln]);
}
-
- /* Report interference levels on PDCH to the PCU */
- if (pdch_num > 0)
- pcu_tx_interf_ind(bts->nr, trx->nr, fn, pdch_interf);
}
}
@@ -295,8 +293,10 @@ static void bts_sched_fn(struct gsm_bts *bts, const uint32_t fn)
struct trx_dl_burst_req *br;
/* ready-to-send */
+ TRACE(OSMO_BTS_TRX_DL_RTS_START(trx->nr, tn, fn));
_sched_rts(l1ts, GSM_TDMA_FN_SUM(fn, plink->u.osmotrx.clock_advance
+ plink->u.osmotrx.rts_advance));
+ TRACE(OSMO_BTS_TRX_DL_RTS_DONE(trx->nr, tn, fn));
/* pre-initialized buffer for the Downlink burst */
br = &pinst->u.osmotrx.br[tn];
diff --git a/src/osmo-bts-trx/trx_if.c b/src/osmo-bts-trx/trx_if.c
index 968c335e..9232e64f 100644
--- a/src/osmo-bts-trx/trx_if.c
+++ b/src/osmo-bts-trx/trx_if.c
@@ -51,6 +51,19 @@
#include "trx_if.h"
#include "trx_provision_fsm.h"
+#include "btsconfig.h"
+
+#ifdef HAVE_SYSTEMTAP
+/* include the generated probes header and put markers in code */
+#include "probes.h"
+#define TRACE(probe) probe
+#define TRACE_ENABLED(probe) probe ## _ENABLED()
+#else
+/* Wrap the probe to allow it to be removed when no systemtap available */
+#define TRACE(probe)
+#define TRACE_ENABLED(probe) (0)
+#endif /* HAVE_SYSTEMTAP */
+
/*
* socket helper functions
*/
@@ -332,8 +345,10 @@ int trx_if_cmd_setslot(struct trx_l1h *l1h, uint8_t tn,
trx_if_cmd_setslot_cb *cb)
{
const struct trx_config *cfg = &l1h->config;
+ const struct phy_instance *pinst = l1h->phy_inst;
- if (cfg->setslot[tn].tsc_valid) { /* PHY is instructed to use a custom TSC */
+ if (cfg->setslot[tn].tsc_valid && cfg->setslot[tn].tsc_val != BTS_TSC(pinst->trx->bts)) {
+ /* PHY is instructed to use a custom TSC */
return trx_ctrl_cmd_cb(l1h, 1, cb, "SETSLOT", "%u %u C%u/S%u",
tn, cfg->setslot[tn].slottype,
cfg->setslot[tn].tsc_val,
@@ -707,6 +722,13 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what)
rsp.cb = tcm->cb;
+ /* Remove command from list, save it to last_acked and remove previous
+ * last_acked. Do it before calling callback to avoid user freeing tcm
+ * pointer if flushing/closing the iface. */
+ llist_del(&tcm->list);
+ talloc_free(l1h->last_acked);
+ l1h->last_acked = tcm;
+
/* check for response code */
rc = trx_ctrl_rx_rsp(l1h, &rsp, tcm);
if (rc == -EINVAL)
@@ -714,15 +736,11 @@ static int trx_ctrl_read_cb(struct osmo_fd *ofd, unsigned int what)
/* re-schedule last cmd in rc seconds time */
if (rc > 0) {
- osmo_timer_schedule(&l1h->trx_ctrl_timer, rc, 0);
+ if (!llist_empty(&l1h->trx_ctrl_list))
+ osmo_timer_schedule(&l1h->trx_ctrl_timer, rc, 0);
return 0;
}
- /* remove command from list, save it to last_acked and removed previous last_acked */
- llist_del(&tcm->list);
- talloc_free(l1h->last_acked);
- l1h->last_acked = tcm;
-
trx_ctrl_send(l1h);
return 0;
@@ -1078,7 +1096,9 @@ static int trx_data_read_cb(struct osmo_fd *ofd, unsigned int what)
bi._num_pdus++;
/* feed received burst into scheduler code */
+ TRACE(OSMO_BTS_TRX_UL_DATA_START(l1h->phy_inst->trx->nr, bi.tn, bi.fn));
trx_sched_route_burst_ind(l1h->phy_inst->trx, &bi);
+ TRACE(OSMO_BTS_TRX_UL_DATA_DONE(l1h->phy_inst->trx->nr, bi.tn, bi.fn));
} while (bi.flags & TRX_BI_F_BATCH_IND);
return 0;
@@ -1199,6 +1219,11 @@ void trx_if_flush(struct trx_l1h *l1h)
talloc_free(tcm);
}
talloc_free(l1h->last_acked);
+ l1h->last_acked = NULL;
+
+ /* Tx queue is now empty, so there's no point in keeping the retrans timer armed: */
+ if (osmo_timer_pending(&l1h->trx_ctrl_timer))
+ osmo_timer_del(&l1h->trx_ctrl_timer);
}
/*! close the TRX for given handle (data + control socket) */
@@ -1262,7 +1287,8 @@ static void trx_phy_inst_close(struct phy_instance *pinst)
struct trx_l1h *l1h = pinst->u.osmotrx.hdl;
trx_if_close(l1h);
- trx_sched_clean(pinst->trx);
+ if (pinst->trx)
+ trx_sched_clean(pinst->trx);
}
/*! open the control + burst data sockets for one phy_instance */
@@ -1331,6 +1357,23 @@ cleanup:
return -1;
}
+/*! close the PHY link using TRX protocol */
+int bts_model_phy_link_close(struct phy_link *plink)
+{
+ bool clock_stopped = false;
+ struct phy_instance *pinst;
+ llist_for_each_entry(pinst, &plink->instances, list) {
+ if (!clock_stopped) {
+ clock_stopped = true;
+ trx_sched_clock_stopped(pinst->trx->bts);
+ }
+ trx_phy_inst_close(pinst);
+ }
+ trx_udp_close(&plink->u.osmotrx.trx_ofd_clk);
+ phy_link_state_set(plink, PHY_LINK_SHUTDOWN);
+ return 0;
+}
+
/*! determine if the TRX for given handle is powered up */
int trx_if_powered(struct trx_l1h *l1h)
{
diff --git a/src/osmo-bts-trx/trx_provision_fsm.c b/src/osmo-bts-trx/trx_provision_fsm.c
index 5beca2ac..5deecd78 100644
--- a/src/osmo-bts-trx/trx_provision_fsm.c
+++ b/src/osmo-bts-trx/trx_provision_fsm.c
@@ -33,6 +33,8 @@
#include <osmo-bts/bts_model.h>
#include <osmo-bts/bts.h>
#include <osmo-bts/rsl.h>
+#include <osmo-bts/nm_common_fsm.h>
+#include <osmo-bts/signal.h>
#include "l1_if.h"
#include "trx_provision_fsm.h"
@@ -48,12 +50,14 @@ static void l1if_poweronoff_cb(struct trx_l1h *l1h, bool poweronoff, int rc)
struct phy_link *plink = pinst->phy_link;
plink->u.osmotrx.powered = poweronoff;
- plink->u.osmotrx.poweronoff_sent = false;
- if (poweronoff)
+ if (poweronoff) {
+ plink->u.osmotrx.poweron_sent = false;
osmo_fsm_inst_dispatch(l1h->provision_fi, TRX_PROV_EV_POWERON_CNF, (void*)(intptr_t)rc);
- else
+ } else {
+ plink->u.osmotrx.poweroff_sent = false;
osmo_fsm_inst_dispatch(l1h->provision_fi, TRX_PROV_EV_POWEROFF_CNF, (void*)(intptr_t)rc);
+ }
}
@@ -92,6 +96,53 @@ void l1if_setformat_cb(struct trx_l1h *l1h, int rc)
/*
* transceiver provisioning
*/
+
+static void trx_provision_reset(struct trx_l1h *l1h)
+{
+ struct phy_instance *pinst = l1h->phy_inst;
+ uint8_t tn;
+
+ l1h->config.trxd_pdu_ver_req = pinst->phy_link->u.osmotrx.trxd_pdu_ver_max;
+ l1h->config.trxd_pdu_ver_use = 0;
+ l1h->config.setformat_sent = false;
+ l1h->config.setformat_acked = false;
+
+ l1h->config.enabled = false;
+ l1h->config.arfcn_valid = false;
+ l1h->config.arfcn = 0;
+ l1h->config.rxtune_sent = false;
+ l1h->config.rxtune_acked = false;
+ l1h->config.txtune_sent = false;
+ l1h->config.txtune_acked = false;
+
+ l1h->config.tsc_valid = false;
+ l1h->config.tsc = 0;
+ l1h->config.tsc_sent = false;
+ l1h->config.tsc_acked = false;
+
+ l1h->config.bsic_valid = false;
+ l1h->config.bsic = 0;
+ l1h->config.bsic_sent = false;
+ l1h->config.bsic_acked = false;
+
+ l1h->config.rxgain_sent = false;
+
+ l1h->config.nomtxpower_sent = false;
+ l1h->config.nomtxpower_acked = false;
+
+ l1h->config.maxdly_sent = false;
+
+ l1h->config.maxdlynb_sent = false;
+
+ for (tn = 0; tn < TRX_NR_TS; tn++) {
+ l1h->config.setslot_valid[tn] = false;
+ l1h->config.setslot_sent[tn] = false;
+ l1h->config.setslot[tn].slottype = 0;
+ l1h->config.setslot[tn].tsc_set = 0;
+ l1h->config.setslot[tn].tsc_val = 0;
+ l1h->config.setslot[tn].tsc_valid = 0;
+ }
+}
int l1if_provision_transceiver_trx(struct trx_l1h *l1h)
{
struct phy_instance *pinst = l1h->phy_inst;
@@ -248,6 +299,48 @@ static bool trx_other_trx0_ready(struct trx_l1h *l1h)
return true;
}
+/* Closes a phy_link and all its associated TRX */
+static void trx_prov_fsm_apply_close(struct phy_link *plink, int rc)
+{
+ struct trx_l1h *l1h;
+ struct phy_instance *pinst;
+
+ if (plink->state == PHY_LINK_SHUTDOWN)
+ return;
+
+ bts_model_phy_link_close(plink);
+ /* Notify TRX close on all TRX associated with this phy */
+ llist_for_each_entry(pinst, &plink->instances, list) {
+ l1h = pinst->u.osmotrx.hdl;
+ trx_prov_fsm_state_chg(l1h->provision_fi, TRX_PROV_ST_CLOSED);
+ bts_model_trx_close_cb(pinst->trx, rc);
+ }
+}
+
+static int trx_prov_fsm_signal_cb(unsigned int subsys, unsigned int signal,
+ void *hdlr_data, void *signal_data)
+{
+ struct nm_statechg_signal_data *nsd;
+ struct gsm_bts_trx *trx;
+
+ if (subsys != SS_GLOBAL)
+ return -EINVAL;
+
+ if (signal != S_NEW_OP_STATE)
+ return 0;
+
+ nsd = (struct nm_statechg_signal_data *)signal_data;
+
+ if (nsd->mo->obj_class != NM_OC_RADIO_CARRIER)
+ return 0;
+
+ if (nsd->old_state != NM_OPSTATE_ENABLED && nsd->new_state == NM_OPSTATE_ENABLED) {
+ trx = gsm_objclass2obj(nsd->mo->bts, nsd->mo->obj_class, &nsd->mo->obj_inst);
+ l1if_trx_start_power_ramp(trx, NULL);
+ }
+ return 0;
+}
+
//////////////////////////
// FSM STATE ACTIONS
//////////////////////////
@@ -274,13 +367,18 @@ static void st_open_poweroff_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_st
struct trx_l1h *l1h = (struct trx_l1h *)fi->priv;
struct phy_instance *pinst = l1h->phy_inst;
- l1h->config.trxd_pdu_ver_req = pinst->phy_link->u.osmotrx.trxd_pdu_ver_max;
+ trx_provision_reset(l1h);
- /* Apply initial RFMUTE state */
- if (pinst->trx != NULL)
- trx_if_cmd_rfmute(l1h, pinst->trx->mo.nm_state.administrative != NM_STATE_UNLOCKED);
- else
+ if (pinst->trx == NULL) {
trx_if_cmd_rfmute(l1h, true);
+ return;
+ }
+
+ /* Apply initial RFMUTE state */
+ trx_if_cmd_rfmute(l1h, pinst->trx->mo.nm_state.administrative != NM_STATE_UNLOCKED);
+
+ osmo_fsm_inst_dispatch(pinst->trx->mo.fi, NM_EV_SW_ACT, NULL);
+ osmo_fsm_inst_dispatch(pinst->trx->bb_transc.mo.fi, NM_EV_SW_ACT, NULL);
}
static void st_open_poweroff(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -294,6 +392,13 @@ static void st_open_poweroff(struct osmo_fsm_inst *fi, uint32_t event, void *dat
bool others_ready;
switch (event) {
+ case TRX_PROV_EV_CLOSE:
+ /* In this state, we didn't for sure send a POWERON yet, hence we
+ are save directly applying the close as if we received a
+ POWEROFF RSP: */
+ if (pinst->num == 0)
+ trx_prov_fsm_apply_close(pinst->phy_link, 0);
+ return;
case TRX_PROV_EV_CFG_ENABLE:
l1h->config.enabled =(bool)data;
break;
@@ -419,7 +524,7 @@ static void st_open_wait_power_cnf_on_enter(struct osmo_fsm_inst *fi, uint32_t p
struct phy_instance *pinst = l1h->phy_inst;
trx_if_cmd_poweron(l1h, l1if_poweronoff_cb);
- pinst->phy_link->u.osmotrx.poweronoff_sent = true;
+ pinst->phy_link->u.osmotrx.poweron_sent = true;
}
static void st_open_wait_power_cnf(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -435,13 +540,6 @@ static void st_open_wait_power_cnf(struct osmo_fsm_inst *fi, uint32_t event, voi
if (rc == 0 && plink->state != PHY_LINK_CONNECTED) {
trx_sched_clock_started(pinst->trx->bts);
phy_link_state_set(plink, PHY_LINK_CONNECTED);
-
- /* Begin to ramp up the power on all TRX associated with this phy */
- llist_for_each_entry(pinst, &plink->instances, list) {
- if (pinst->trx->mo.nm_state.administrative == NM_STATE_UNLOCKED)
- l1if_trx_start_power_ramp(pinst->trx, NULL);
- }
-
trx_prov_fsm_state_chg(fi, TRX_PROV_ST_OPEN_POWERON);
} else if (rc != 0 && plink->state != PHY_LINK_SHUTDOWN) {
trx_sched_clock_stopped(pinst->trx->bts);
@@ -451,6 +549,14 @@ static void st_open_wait_power_cnf(struct osmo_fsm_inst *fi, uint32_t event, voi
case TRX_PROV_EV_CFG_TS:
update_ts_data(l1h, (struct trx_prov_ev_cfg_ts_data*)data);
break;
+ case TRX_PROV_EV_CLOSE:
+ /* power off transceiver, if not already */
+ if (pinst->num == 0 && !plink->u.osmotrx.poweroff_sent) {
+ trx_if_cmd_poweroff(l1h, l1if_poweronoff_cb);
+ plink->u.osmotrx.poweroff_sent = true;
+ }
+ trx_prov_fsm_state_chg(fi, TRX_PROV_ST_OPEN_WAIT_POWEROFF_CNF);
+ break;
default:
OSMO_ASSERT(0);
}
@@ -490,29 +596,15 @@ static void st_open_poweron(struct osmo_fsm_inst *fi, uint32_t event, void *data
struct phy_instance *pinst = l1h->phy_inst;
struct phy_link *plink = pinst->phy_link;
struct trx_prov_ev_cfg_ts_data* ts_data;
- uint8_t tn;
switch (event) {
case TRX_PROV_EV_CLOSE:
/* power off transceiver, if not already */
- if (l1h->config.enabled) {
- if (pinst->num == 0 && plink->u.osmotrx.powered && !plink->u.osmotrx.poweronoff_sent) {
- trx_if_cmd_poweroff(l1h, l1if_poweronoff_cb);
- plink->u.osmotrx.poweronoff_sent = true;
- }
- l1h->config.rxgain_sent = false;
- l1h->config.maxdly_sent = false;
- l1h->config.maxdlynb_sent = false;
- for (tn = 0; tn < TRX_NR_TS; tn++)
- l1h->config.setslot_sent[tn] = false;
- } else if (!pinst->phy_link->u.osmotrx.poweronoff_sent) {
- bts_model_trx_close_cb(pinst->trx, 0);
- } /* else: poweroff in progress, cb will be called upon TRXC RSP */
-
- if (pinst->num == 0)
- trx_prov_fsm_state_chg(fi, TRX_PROV_ST_OPEN_WAIT_POWEROFF_CNF);
- else
- trx_prov_fsm_state_chg(fi, TRX_PROV_ST_OPEN_POWEROFF);
+ if (pinst->num == 0 && plink->u.osmotrx.powered && !plink->u.osmotrx.poweroff_sent) {
+ trx_if_cmd_poweroff(l1h, l1if_poweronoff_cb);
+ plink->u.osmotrx.poweroff_sent = true;
+ }
+ trx_prov_fsm_state_chg(fi, TRX_PROV_ST_OPEN_WAIT_POWEROFF_CNF);
break;
case TRX_PROV_EV_CFG_TS:
ts_data = (struct trx_prov_ev_cfg_ts_data*)data;
@@ -536,15 +628,7 @@ static void st_open_wait_poweroff_cnf(struct osmo_fsm_inst *fi, uint32_t event,
switch (event) {
case TRX_PROV_EV_POWEROFF_CNF:
rc = (uint16_t)(intptr_t)data;
- if (plink->state != PHY_LINK_SHUTDOWN) {
- trx_sched_clock_stopped(pinst->trx->bts);
- phy_link_state_set(plink, PHY_LINK_SHUTDOWN);
-
- /* Notify TRX close on all TRX associated with this phy */
- llist_for_each_entry(pinst, &plink->instances, list) {
- bts_model_trx_close_cb(pinst->trx, rc);
- }
- }
+ trx_prov_fsm_apply_close(plink, rc);
break;
default:
OSMO_ASSERT(0);
@@ -562,6 +646,7 @@ static struct osmo_fsm_state trx_prov_fsm_states[] = {
},
[TRX_PROV_ST_OPEN_POWEROFF] = {
.in_event_mask =
+ X(TRX_PROV_EV_CLOSE) |
X(TRX_PROV_EV_OTHER_TRX_READY) |
X(TRX_PROV_EV_CFG_ENABLE) |
X(TRX_PROV_EV_CFG_BSIC) |
@@ -574,6 +659,7 @@ static struct osmo_fsm_state trx_prov_fsm_states[] = {
X(TRX_PROV_EV_SETTSC_CNF) |
X(TRX_PROV_EV_SETFORMAT_CNF),
.out_state_mask =
+ X(TRX_PROV_ST_CLOSED) |
X(TRX_PROV_ST_OPEN_WAIT_POWERON_CNF) |
X(TRX_PROV_ST_OPEN_POWERON),
.name = "OPEN_POWEROFF",
@@ -582,10 +668,12 @@ static struct osmo_fsm_state trx_prov_fsm_states[] = {
},
[TRX_PROV_ST_OPEN_WAIT_POWERON_CNF] = {
.in_event_mask =
+ X(TRX_PROV_EV_CLOSE) |
X(TRX_PROV_EV_POWERON_CNF) |
X(TRX_PROV_EV_CFG_TS),
.out_state_mask =
- X(TRX_PROV_ST_OPEN_POWERON),
+ X(TRX_PROV_ST_OPEN_POWERON) |
+ X(TRX_PROV_ST_OPEN_WAIT_POWEROFF_CNF),
.name = "OPEN_WAIT_POWERON_CNF",
.onenter = st_open_wait_power_cnf_on_enter,
.action = st_open_wait_power_cnf,
@@ -595,8 +683,7 @@ static struct osmo_fsm_state trx_prov_fsm_states[] = {
X(TRX_PROV_EV_CLOSE) |
X(TRX_PROV_EV_CFG_TS),
.out_state_mask =
- X(TRX_PROV_ST_OPEN_WAIT_POWEROFF_CNF) |
- X(TRX_PROV_ST_OPEN_POWEROFF),
+ X(TRX_PROV_ST_OPEN_WAIT_POWEROFF_CNF),
.name = "OPEN_POWERON",
.onenter = st_open_poweron_on_enter,
.action = st_open_poweron,
@@ -605,7 +692,7 @@ static struct osmo_fsm_state trx_prov_fsm_states[] = {
.in_event_mask =
X(TRX_PROV_EV_POWEROFF_CNF),
.out_state_mask =
- X(TRX_PROV_ST_OPEN_POWEROFF),
+ X(TRX_PROV_ST_CLOSED),
.name = "OPEN_WAIT_POWEROFF_CNF",
.action = st_open_wait_poweroff_cnf,
},
@@ -643,4 +730,5 @@ struct osmo_fsm trx_prov_fsm = {
static __attribute__((constructor)) void trx_prov_fsm_init(void)
{
OSMO_ASSERT(osmo_fsm_register(&trx_prov_fsm) == 0);
+ OSMO_ASSERT(osmo_signal_register_handler(SS_GLOBAL, trx_prov_fsm_signal_cb, NULL) == 0);
}
diff --git a/src/osmo-bts-trx/trx_vty.c b/src/osmo-bts-trx/trx_vty.c
index bb9d1584..2b8bc24b 100644
--- a/src/osmo-bts-trx/trx_vty.c
+++ b/src/osmo-bts-trx/trx_vty.c
@@ -265,18 +265,12 @@ DEFUN_DEPRECATED(cfg_phy_no_timing_advance_loop, cfg_phy_no_timing_advance_loop_
return CMD_SUCCESS;
}
-DEFUN_ATTR(cfg_phyinst_maxdly, cfg_phyinst_maxdly_cmd,
- "osmotrx maxdly <0-31>",
- OSMOTRX_STR
- "Set the maximum acceptable delay of an Access Burst (in GSM symbols)."
- " Access Burst is the first burst a mobile transmits in order to establish"
- " a connection and it is used to estimate Timing Advance (TA) which is"
- " then applied to Normal Bursts to compensate for signal delay due to"
- " distance. So changing this setting effectively changes maximum range of"
- " the cell, because if we receive an Access Burst with a delay higher than"
- " this value, it will be ignored and connection is dropped.\n"
- "GSM symbols (approx. 1.1km per symbol)\n",
- CMD_ATTR_IMMEDIATE)
+DEFUN_USRATTR(cfg_phyinst_maxdly, cfg_phyinst_maxdly_cmd,
+ X(BTS_VTY_TRX_POWERCYCLE),
+ "osmotrx maxdly <0-63>",
+ OSMOTRX_STR
+ "Set the maximum acceptable delay of an Access Burst\n"
+ "Delay in GSMK symbol periods (approx. 550m per symbol)\n")
{
struct phy_instance *pinst = vty->index;
struct trx_l1h *l1h = pinst->u.osmotrx.hdl;
@@ -284,25 +278,17 @@ DEFUN_ATTR(cfg_phyinst_maxdly, cfg_phyinst_maxdly_cmd,
l1h->config.maxdly = atoi(argv[0]);
l1h->config.maxdly_valid = 1;
l1h->config.maxdly_sent = false;
- l1if_provision_transceiver_trx(l1h);
return CMD_SUCCESS;
}
-DEFUN_ATTR(cfg_phyinst_maxdlynb, cfg_phyinst_maxdlynb_cmd,
- "osmotrx maxdlynb <0-31>",
- OSMOTRX_STR
- "Set the maximum acceptable delay of a Normal Burst (in GSM symbols)."
- " USE FOR TESTING ONLY, DON'T CHANGE IN PRODUCTION USE!"
- " During normal operation, Normal Bursts delay are controlled by a Timing"
- " Advance control loop and thus Normal Bursts arrive to a BTS with no more"
- " than a couple GSM symbols, which is already taken into account in osmo-trx."
- " So changing this setting will have no effect in production installations"
- " except increasing osmo-trx CPU load. This setting is only useful when"
- " testing with a transmitter which can't precisely synchronize to the BTS"
- " downlink signal, like e.g. R&S CMD57.\n"
- "GSM symbols (approx. 1.1km per symbol)\n",
- CMD_ATTR_IMMEDIATE)
+DEFUN_ATTR_USRATTR(cfg_phyinst_maxdlynb, cfg_phyinst_maxdlynb_cmd,
+ CMD_ATTR_HIDDEN, /* expert mode command */
+ X(BTS_VTY_TRX_POWERCYCLE),
+ "osmotrx maxdlynb <0-63>",
+ OSMOTRX_STR
+ "Set the maximum acceptable delay of a Normal Burst\n"
+ "Delay in GMSK symbol periods (approx. 550m per symbol)\n")
{
struct phy_instance *pinst = vty->index;
struct trx_l1h *l1h = pinst->u.osmotrx.hdl;
@@ -310,7 +296,6 @@ DEFUN_ATTR(cfg_phyinst_maxdlynb, cfg_phyinst_maxdlynb_cmd,
l1h->config.maxdlynb = atoi(argv[0]);
l1h->config.maxdlynb_valid = 1;
l1h->config.maxdlynb_sent = false;
- l1if_provision_transceiver_trx(l1h);
return CMD_SUCCESS;
}
diff --git a/src/osmo-bts-virtual/bts_model.c b/src/osmo-bts-virtual/bts_model.c
index af8a6f2a..87040566 100644
--- a/src/osmo-bts-virtual/bts_model.c
+++ b/src/osmo-bts-virtual/bts_model.c
@@ -110,20 +110,38 @@ int bts_model_apply_oml(struct gsm_bts *bts, struct msgb *msg,
struct tlv_parsed *new_attr, int kind, void *obj)
{
struct abis_om_fom_hdr *foh = msgb_l3(msg);
- int cause = 0;
+ struct gsm_abis_mo *mo = gsm_objclass2mo(bts, foh->obj_class, &foh->obj_inst);
+ struct nm_fsm_ev_setattr_data ev_data = {
+ .msg = msg,
+ .cause = 0,
+ };
+ int rc;
+
+ /* TODO: NM Object without FSM: */
+ switch (foh->obj_class) {
+ case NM_OC_GPRS_NSE:
+ case NM_OC_GPRS_CELL:
+ case NM_OC_GPRS_NSVC:
+ return oml_fom_ack_nack(ev_data.msg, ev_data.cause);
+ }
switch (foh->msg_type) {
case NM_MT_SET_BTS_ATTR:
- cause = vbts_set_bts(obj);
+ ev_data.cause = vbts_set_bts(obj);
break;
case NM_MT_SET_RADIO_ATTR:
- cause = vbts_set_trx(obj);
+ ev_data.cause = vbts_set_trx(obj);
break;
case NM_MT_SET_CHAN_ATTR:
- cause = vbts_set_ts(obj);
+ ev_data.cause = vbts_set_ts(obj);
break;
}
- return oml_fom_ack_nack(msg, cause);
+
+ rc = osmo_fsm_inst_dispatch(mo->fi,
+ ev_data.cause == 0 ? NM_EV_SETATTR_ACK : NM_EV_SETATTR_NACK,
+ &ev_data);
+ /* msgb ownsership is transferred to FSM if it received ev: */
+ return rc == 0 ? 1 : 0;
}
/* MO: TS 12.21 Managed Object */
@@ -156,7 +174,7 @@ int bts_model_opstart(struct gsm_bts *bts, struct gsm_abis_mo *mo, void *obj)
case NM_OC_GPRS_NSE:
case NM_OC_GPRS_CELL:
case NM_OC_GPRS_NSVC:
- oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK);
+ oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK, -1);
rc = oml_mo_opstart_ack(mo);
break;
default:
diff --git a/src/osmo-bts-virtual/l1_if.c b/src/osmo-bts-virtual/l1_if.c
index 444d6ad2..0d36db18 100644
--- a/src/osmo-bts-virtual/l1_if.c
+++ b/src/osmo-bts-virtual/l1_if.c
@@ -224,8 +224,6 @@ int bts_model_phy_link_open(struct phy_link *plink)
* / PRIM_INFO_ACTIVATE */
if (pinst->trx == pinst->trx->bts->c0) {
vbts_sched_start(pinst->trx->bts);
- /* init lapdm layer 3 callback for the trx on timeslot 0 == BCCH */
- lchan_init_lapdm(&pinst->trx->ts[0].lchan[CCCH_LCHAN]);
/* FIXME: This is probably the wrong location to set the CCCH to active... the OML link def. needs to be reworked and fixed. */
pinst->trx->ts[0].lchan[CCCH_LCHAN].rel_act_kind = LCHAN_REL_ACT_OML;
lchan_set_state(&pinst->trx->ts[0].lchan[CCCH_LCHAN], LCHAN_S_ACTIVE);
@@ -320,11 +318,11 @@ static int l1if_process_meas_res(struct gsm_bts_trx *trx, uint8_t tn, uint32_t f
float ber = n_bits_total==0 ? 1.0 : (float)n_errors / (float)n_bits_total;
DEBUGPFN(DMEAS, fn, "RX L1 frame %s chan_nr=0x%02x MS pwr=%ddBm rssi=%.1f dBFS "
- "ber=%.2f%% (%d/%d bits) L1_ta=%d rqd_ta=%d toa=%.2f\n",
+ "ber=%.2f%% (%d/%d bits) L1_ta=%d ta_ctrl.current=%d toa=%.2f\n",
gsm_lchan_name(lchan), chan_nr, ms_pwr_dbm(lchan->ts->trx->bts->band, lchan->ms_power_ctrl.max),
- rssi, ber*100, n_errors, n_bits_total, lchan->meas.l1_info.ta, lchan->rqd_ta, toa);
+ rssi, ber*100, n_errors, n_bits_total, lchan->meas.l1_info.ta, lchan->ta_ctrl.current, toa);
- l1if_fill_meas_res(&l1sap, chan_nr, lchan->rqd_ta + toa, ber, rssi, fn);
+ l1if_fill_meas_res(&l1sap, chan_nr, lchan->ta_ctrl.current + toa, ber, rssi, fn);
return l1sap_up(trx, &l1sap);
}
@@ -392,8 +390,6 @@ int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
lchan->tch.amr_mr.bts_mode[3].mode,
amr_get_initial_mode(lchan),
(lchan->ho.active == 1));
- /* init lapdm */
- lchan_init_lapdm(lchan);
/* set lchan active */
lchan_set_state(lchan, LCHAN_S_ACTIVE);
/* set initial ciphering */