aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/osmocom/bsc/Makefile.am1
-rw-r--r--include/osmocom/bsc/gsm_data.h41
-rw-r--r--include/osmocom/bsc/gsm_timers.h55
-rw-r--r--src/ipaccess/Makefile.am2
-rw-r--r--src/osmo-bsc/Makefile.am2
-rw-r--r--src/osmo-bsc/abis_om2000.c10
-rw-r--r--src/osmo-bsc/abis_rsl.c20
-rw-r--r--src/osmo-bsc/bsc_subscr_conn_fsm.c60
-rw-r--r--src/osmo-bsc/bsc_vty.c102
-rw-r--r--src/osmo-bsc/bts_ipaccess_nanobts_omlattr.c3
-rw-r--r--src/osmo-bsc/bts_siemens_bs11.c3
-rw-r--r--src/osmo-bsc/gsm_data.c3
-rw-r--r--src/osmo-bsc/gsm_timers.c206
-rw-r--r--src/osmo-bsc/gsm_timers_vty.c117
-rw-r--r--src/osmo-bsc/net_init.c49
-rw-r--r--src/osmo-bsc/paging.c3
-rw-r--r--src/utils/Makefile.am2
-rw-r--r--tests/abis/Makefile.am1
-rw-r--r--tests/bsc/Makefile.am1
-rw-r--r--tests/gsm0408/Makefile.am1
-rw-r--r--tests/handover/Makefile.am1
-rw-r--r--tests/nanobts_omlattr/Makefile.am1
-rw-r--r--tests/nanobts_omlattr/nanobts_omlattr_test.c9
23 files changed, 513 insertions, 180 deletions
diff --git a/include/osmocom/bsc/Makefile.am b/include/osmocom/bsc/Makefile.am
index 1e26c3403..0f134c850 100644
--- a/include/osmocom/bsc/Makefile.am
+++ b/include/osmocom/bsc/Makefile.am
@@ -19,6 +19,7 @@ noinst_HEADERS = \
gsm_04_08_rr.h \
gsm_04_80.h \
gsm_data.h \
+ gsm_timers.h \
handover.h \
handover_cfg.h \
handover_decision.h \
diff --git a/include/osmocom/bsc/gsm_data.h b/include/osmocom/bsc/gsm_data.h
index 287249382..272b192ce 100644
--- a/include/osmocom/bsc/gsm_data.h
+++ b/include/osmocom/bsc/gsm_data.h
@@ -32,6 +32,8 @@
#include <osmocom/bsc/bsc_msg_filter.h>
#include <osmocom/bsc/acc_ramp.h>
+#define GSM_T3122_DEFAULT 10
+
struct mgcp_client_conf;
struct mgcp_client;
struct mgcp_ctx;
@@ -1192,23 +1194,6 @@ static const struct rate_ctr_group_desc bsc_ctrg_desc = {
bsc_ctr_description,
};
-#define GSM_T3101_DEFAULT 3 /* s */
-#define GSM_T3103_DEFAULT 5 /* s */
-#define GSM_T3105_DEFAULT 100 /* ms */
-#define GSM_T3107_DEFAULT 5 /* s */
-#define GSM_T3109_DEFAULT 5 /* s, must be 2s + radio_link_timeout*0.48 */
-#define GSM_T3111_DEFAULT 2 /* s */
-#define GSM_T3113_DEFAULT 10 /* s */
-#define GSM_T3115_DEFAULT 10
-#define GSM_T3117_DEFAULT 10
-#define GSM_T3119_DEFAULT 10
-#define GSM_T3122_DEFAULT 10
-#define GSM_T3141_DEFAULT 10
-#define GSM_T10_DEFAULT 6 /* RR Assignment timeout, in seconds */
-#define GSM_T7_DEFAULT 10 /* inter-BSC MO Handover first timeout, in seconds */
-#define GSM_T8_DEFAULT 10 /* inter-BSC MO Handover second timeout, in seconds */
-#define GSM_T101_DEFAULT 10 /* inter-BSC MT Handover timeout, in seconds */
-
struct gsm_tz {
int override; /* if 0, use system's time zone instead. */
int hr; /* hour */
@@ -1240,23 +1225,8 @@ struct gsm_network {
unsigned int num_bts;
struct llist_head bts_list;
- /* timer values */
- int T3101;
- int T3103; /*< Handover timeout */
- int T3105;
- int T3107;
- int T3109;
- int T3111;
- int T3113;
- int T3115;
- int T3117;
- int T3119;
- int T3122;
- int T3141;
- int T10; /*< RR Assignment timeout, in seconds */
- int T7; /*< inter-BSC handover MO timeout from Handover Required to Handover Command */
- int T8; /*< inter-BSC handover MO timeout from Handover Command to final Clear*/
- int T101; /*< inter-BSC handover MT timeout from Handover Request to Handover Accept */
+ /* shall reference gsm_network_T[] */
+ struct T_def *T_defs;
enum gsm_chan_t ctype_by_chreq[_NUM_CHREQ_T];
@@ -1290,9 +1260,6 @@ struct gsm_network {
* pointer is NULL to indicate absence of a bsc_subscribers list. */
struct llist_head *bsc_subscribers;
- /* Periodic location update default value */
- uint8_t t3212;
-
/* Timer for periodic channel load measurements to maintain each BTS's T3122. */
struct osmo_timer_list t3122_chan_load_timer;
diff --git a/include/osmocom/bsc/gsm_timers.h b/include/osmocom/bsc/gsm_timers.h
new file mode 100644
index 000000000..fde8c93c0
--- /dev/null
+++ b/include/osmocom/bsc/gsm_timers.h
@@ -0,0 +1,55 @@
+/* API to define Tnnn timers globally, configure in VTY and use for FSM state changes. */
+#pragma once
+
+#include <stdint.h>
+#include <osmocom/core/utils.h>
+
+struct osmo_fsm_inst;
+struct vty;
+
+enum T_unit {
+ T_S = 0, /*< most T are in seconds, keep 0 as default. */
+ T_MS, /*< milliseconds */
+ T_M, /*< minutes */
+ T_CUSTOM,
+};
+
+extern const struct value_string T_unit_names[];
+static inline const char *T_unit_name(enum T_unit val)
+{ return get_value_string(T_unit_names, val); }
+
+/* Define a GSM timer of the form Tnnn, with unit, default value and doc string. */
+struct T_def {
+ const int T; /*< T1234 number */
+ const int default_val; /*< timeout duration (according to unit), default value. */
+ const enum T_unit unit;
+ const char *desc;
+ int val; /*< currently active value, e.g. set by user config. */
+};
+
+/* Iterate an array of struct T_def, the last item should be fully zero, i.e. "{}" */
+#define for_each_T_def(d, T_defs) \
+ for (d = T_defs; d && (d->T || d->default_val || d->desc); d++)
+
+int T_def_get(struct T_def *T_defs, int T, enum T_unit as_unit, int val_if_not_present);
+void T_defs_reset(struct T_def *T_defs);
+struct T_def *T_def_get_entry(struct T_def *T_defs, int T);
+
+void T_defs_vty_init(struct T_def *T_defs, int cfg_parent_node);
+void T_defs_vty_write(struct vty *vty, const char *indent);
+
+
+struct state_timeout {
+ int T;
+ bool keep_timer;
+};
+
+struct state_timeout *get_state_timeout(uint32_t state, struct state_timeout *timeouts_array);
+
+#define fsm_inst_state_chg_T(fi, state, timeouts_array, T_defs, default_timeout) \
+ _fsm_inst_state_chg_T(fi, state, timeouts_array, T_defs, default_timeout, \
+ __FILE__, __LINE__)
+int _fsm_inst_state_chg_T(struct osmo_fsm_inst *fi, uint32_t state,
+ struct state_timeout *timeouts_array,
+ struct T_def *T_defs, int default_timeout,
+ const char *file, int line);
diff --git a/src/ipaccess/Makefile.am b/src/ipaccess/Makefile.am
index 2c6282d11..ec3e02705 100644
--- a/src/ipaccess/Makefile.am
+++ b/src/ipaccess/Makefile.am
@@ -55,6 +55,7 @@ ipaccess_config_LDADD = \
$(top_builddir)/src/osmo-bsc/bts_ipaccess_nanobts_omlattr.o \
$(top_builddir)/src/osmo-bsc/gsm_data.o \
$(top_builddir)/src/osmo-bsc/net_init.o \
+ $(top_builddir)/src/osmo-bsc/gsm_timers.o \
$(OSMO_LIBS) \
$(NULL)
@@ -65,5 +66,6 @@ ipaccess_proxy_SOURCES = \
$(NULL)
ipaccess_proxy_LDADD = \
+ $(top_builddir)/src/osmo-bsc/gsm_timers.o \
$(OSMO_LIBS) \
$(NULL)
diff --git a/src/osmo-bsc/Makefile.am b/src/osmo-bsc/Makefile.am
index 0bc78d51c..9183791bb 100644
--- a/src/osmo-bsc/Makefile.am
+++ b/src/osmo-bsc/Makefile.am
@@ -58,6 +58,8 @@ osmo_bsc_SOURCES = \
gsm_04_08_rr.c \
gsm_04_80_utils.c \
gsm_data.c \
+ gsm_timers.c \
+ gsm_timers_vty.c \
handover_cfg.c \
handover_decision.c \
handover_decision_2.c \
diff --git a/src/osmo-bsc/abis_om2000.c b/src/osmo-bsc/abis_om2000.c
index d533ea198..293459087 100644
--- a/src/osmo-bsc/abis_om2000.c
+++ b/src/osmo-bsc/abis_om2000.c
@@ -42,6 +42,7 @@
#include <osmocom/bsc/abis_rsl.h>
#include <osmocom/bsc/abis_om2000.h>
#include <osmocom/bsc/signal.h>
+#include <osmocom/bsc/gsm_timers.h>
#include <osmocom/abis/e1_input.h>
/* FIXME: move to libosmocore */
@@ -1382,7 +1383,8 @@ int abis_om2k_tx_ts_conf_req(struct gsm_bts_trx_ts *ts)
msgb_tv_put(msg, OM2K_DEI_CCCH_OPTIONS, 0x01);
break;
case GSM_PCHAN_CCCH_SDCCH4:
- msgb_tv_put(msg, OM2K_DEI_T3105, ts->trx->bts->network->T3105 / 10);
+ msgb_tv_put(msg, OM2K_DEI_T3105,
+ T_def_get(ts->trx->bts->network->T_defs, 3105, T_MS, -1) / 10);
msgb_tv_put(msg, OM2K_DEI_NY1, 35);
msgb_tv_put(msg, OM2K_DEI_BA_PA_MFRMS, 0x06);
msgb_tv_put(msg, OM2K_DEI_CBCH_INDICATOR, 0);
@@ -1396,7 +1398,8 @@ int abis_om2k_tx_ts_conf_req(struct gsm_bts_trx_ts *ts)
sizeof(icm_bound_params), icm_bound_params);
break;
case GSM_PCHAN_SDCCH8_SACCH8C:
- msgb_tv_put(msg, OM2K_DEI_T3105, ts->trx->bts->network->T3105 / 10);
+ msgb_tv_put(msg, OM2K_DEI_T3105,
+ T_def_get(ts->trx->bts->network->T_defs, 3105, T_MS, -1) / 10);
msgb_tv_put(msg, OM2K_DEI_NY1, 35);
msgb_tv_put(msg, OM2K_DEI_CBCH_INDICATOR, 0);
msgb_tv_put(msg, OM2K_DEI_TSC, gsm_ts_tsc(ts));
@@ -1406,7 +1409,8 @@ int abis_om2k_tx_ts_conf_req(struct gsm_bts_trx_ts *ts)
sizeof(icm_bound_params), icm_bound_params);
break;
default:
- msgb_tv_put(msg, OM2K_DEI_T3105, ts->trx->bts->network->T3105 / 10);
+ msgb_tv_put(msg, OM2K_DEI_T3105,
+ T_def_get(ts->trx->bts->network->T_defs, 3105, T_MS, -1) / 10);
msgb_tv_put(msg, OM2K_DEI_NY1, 35);
msgb_tv_put(msg, OM2K_DEI_TSC, gsm_ts_tsc(ts));
/* Disable RF RESOURCE INDICATION on idle channels */
diff --git a/src/osmo-bsc/abis_rsl.c b/src/osmo-bsc/abis_rsl.c
index bf8dfa5c7..97d2aa256 100644
--- a/src/osmo-bsc/abis_rsl.c
+++ b/src/osmo-bsc/abis_rsl.c
@@ -47,6 +47,7 @@
#include <osmocom/bsc/bsc_api.h>
#include <osmocom/bsc/bsc_subscr_conn_fsm.h>
#include <osmocom/netif/rtp.h>
+#include <osmocom/bsc/gsm_timers.h>
#define RSL_ALLOC_SIZE 1024
#define RSL_ALLOC_HEADROOM 128
@@ -77,7 +78,9 @@ static void do_lchan_free(struct gsm_lchan *lchan)
if (lchan->state == LCHAN_S_REL_ERR) {
osmo_timer_setup(&lchan->error_timer, error_timeout_cb, lchan);
osmo_timer_schedule(&lchan->error_timer,
- lchan->ts->trx->bts->network->T3111 + 2, 0);
+ T_def_get(lchan->ts->trx->bts->network->T_defs,
+ 993111, T_S, -1),
+ 0);
} else {
rsl_lchan_set_state(lchan, LCHAN_S_NONE);
}
@@ -1911,11 +1914,10 @@ static int rsl_rx_chan_rqd(struct msgb *msg)
LOGP(DRSL, LOGL_NOTICE, "(bts=%d) CHAN RQD: no resources for %s 0x%x\n",
msg->lchan->ts->trx->bts->nr, gsm_lchant_name(lctype), rqd_ref->ra);
rate_ctr_inc(&bts->bts_ctrs->ctr[BTS_CTR_CHREQ_NO_CHANNEL]);
- if (bts->T3122)
- wait_ind = bts->T3122;
- else if (bts->network->T3122)
- wait_ind = bts->network->T3122 & 0xff;
- else
+ wait_ind = bts->T3122;
+ if (!wait_ind)
+ wait_ind = T_def_get(bts->network->T_defs, 3122, T_S, -1);
+ if (!wait_ind)
wait_ind = GSM_T3122_DEFAULT;
/* The BTS will gather multiple CHAN RQD and reject up to 4 MS at the same time. */
rsl_send_imm_ass_rej(bts, rqd_ref, wait_ind);
@@ -1997,7 +1999,7 @@ static int rsl_send_imm_assignment(struct gsm_lchan *lchan)
/* Start timer T3101 to wait for GSM48_MT_RR_PAG_RESP */
osmo_timer_setup(&lchan->T3101, t3101_expired, lchan);
- osmo_timer_schedule(&lchan->T3101, bts->network->T3101, 0);
+ osmo_timer_schedule(&lchan->T3101, T_def_get(bts->network->T_defs, 3101, T_S, -1), 0);
/* send IMMEDIATE ASSIGN CMD on RSL to BTS (to send on CCCH to MS) */
return rsl_imm_assign_cmd(bts, sizeof(*ia)+ia->mob_alloc_len, (uint8_t *) ia);
@@ -2157,7 +2159,7 @@ static void rsl_handle_release(struct gsm_lchan *lchan)
osmo_timer_del(&lchan->T3109);
osmo_timer_setup(&lchan->T3111, t3111_expired, lchan);
bts = lchan->ts->trx->bts;
- osmo_timer_schedule(&lchan->T3111, bts->network->T3111, 0);
+ osmo_timer_schedule(&lchan->T3111, T_def_get(bts->network->T_defs, 3111, T_S, -1), 0);
}
/* ESTABLISH INDICATION, LOCATION AREA UPDATE REQUEST
@@ -2996,7 +2998,7 @@ int rsl_start_t3109(struct gsm_lchan *lchan)
struct gsm_bts *bts = lchan->ts->trx->bts;
osmo_timer_setup(&lchan->T3109, t3109_expired, lchan);
- osmo_timer_schedule(&lchan->T3109, bts->network->T3109, 0);
+ osmo_timer_schedule(&lchan->T3109, T_def_get(bts->network->T_defs, 3109, T_S, -1), 0);
return 0;
}
diff --git a/src/osmo-bsc/bsc_subscr_conn_fsm.c b/src/osmo-bsc/bsc_subscr_conn_fsm.c
index 32492707c..f97b7781c 100644
--- a/src/osmo-bsc/bsc_subscr_conn_fsm.c
+++ b/src/osmo-bsc/bsc_subscr_conn_fsm.c
@@ -36,6 +36,7 @@
#include <osmocom/bsc/penalty_timers.h>
#include <osmocom/bsc/bsc_rll.h>
#include <osmocom/bsc/abis_rsl.h>
+#include <osmocom/bsc/gsm_timers.h>
#include <osmocom/mgcp_client/mgcp_client_fsm.h>
#include <osmocom/core/byteswap.h>
@@ -147,6 +148,25 @@ void bsc_subscr_pick_codec(struct mgcp_conn_peer *conn_peer, struct gsm_subscrib
}
}
+struct state_timeout conn_fsm_timeouts[32] = {
+ [ST_WAIT_ASS_CMPL] = { .T = 10 },
+ [ST_WAIT_CRCX_BTS] = { .T = 992427 },
+ [ST_WAIT_MDCX_BTS] = { .T = 992427 },
+ [ST_WAIT_CRCX_MSC] = { .T = 992427 },
+ [ST_WAIT_MDCX_BTS_HO] = { .T = 992427 },
+ [ST_WAIT_CC] = { .T = 993210 },
+ [ST_CLEARING] = { .T = 999 },
+};
+
+/* Transition to a state, using the T timer defined in conn_fsm_timeouts.
+ * The actual timeout value is in turn obtained from network->T_defs.
+ * Assumes local variable 'conn' exists. */
+#define conn_fsm_state_chg(state) \
+ fsm_inst_state_chg_T(conn->fi, state, \
+ conn_fsm_timeouts, \
+ conn->network->T_defs, \
+ -1)
+
/* forward MT DTAP from BSSAP side to RSL side */
static inline void submit_dtap(struct gsm_subscriber_connection *conn, struct msgb *msg,
struct osmo_fsm_inst *fi)
@@ -324,7 +344,7 @@ static void gscon_fsm_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
} else {
/* SCCP T(conn est) is 1-2 minutes, way too long. The MS will timeout
* using T3210 (20s), T3220 (5s) or T3230 (10s) */
- osmo_fsm_inst_state_chg(fi, ST_WAIT_CC, 20, 993210);
+ conn_fsm_state_chg(ST_WAIT_CC);
}
break;
case GSCON_EV_A_CONN_IND:
@@ -353,11 +373,12 @@ static void gscon_fsm_init(struct osmo_fsm_inst *fi, uint32_t event, void *data)
/* We've sent the CONNECTION.req to the SCCP provider and are waiting for CC from MSC */
static void gscon_fsm_wait_cc(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
+ struct gsm_subscriber_connection *conn = fi->priv;
switch (event) {
case GSCON_EV_A_CONN_CFM:
/* MSC has confirmed the connection, we now change into the
* active state and wait there for further operations */
- osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0);
+ conn_fsm_state_chg(ST_ACTIVE);
/* if there's user payload, forward it just like EV_MT_DTAP */
/* FIXME: Question: if there's user payload attached to the CC, forward it like EV_MT_DTAP? */
break;
@@ -399,7 +420,7 @@ static void _assignment_failed(struct osmo_fsm_inst *fi, enum gsm0808_cause caus
resp = gsm0808_create_assignment_failure(cause, NULL);
sigtran_send(conn, resp, fi);
if (fi->state != ST_ACTIVE)
- osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0);
+ conn_fsm_state_chg(ST_ACTIVE);
}
/* We're on an active subscriber connection, passing DTAP back and forth */
@@ -437,7 +458,7 @@ static void gscon_fsm_active(struct osmo_fsm_inst *fi, uint32_t event, void *dat
osmo_strlcpy(conn_peer.endpoint, get_mgw_ep_name(conn), sizeof(conn_peer.endpoint));
/* (Pre)Change state and create the connection */
- osmo_fsm_inst_state_chg(fi, ST_WAIT_CRCX_BTS, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
+ conn_fsm_state_chg(ST_WAIT_CRCX_BTS);
conn->user_plane.fi_bts =
mgcp_conn_create(conn->network->mgw.client, fi, GSCON_EV_MGW_FAIL_BTS,
GSCON_EV_MGW_CRCX_RESP_BTS, &conn_peer);
@@ -464,7 +485,7 @@ static void gscon_fsm_active(struct osmo_fsm_inst *fi, uint32_t event, void *dat
return;
}
- osmo_fsm_inst_state_chg(fi, ST_WAIT_ASS_CMPL, conn->network->T10, 10);
+ conn_fsm_state_chg(ST_WAIT_ASS_CMPL);
break;
default:
/* An unsupported channel is requested, so we have to
@@ -484,7 +505,7 @@ static void gscon_fsm_active(struct osmo_fsm_inst *fi, uint32_t event, void *dat
if (rc) {
resp = gsm0808_create_clear_rqst(GSM0808_CAUSE_EQUIPMENT_FAILURE);
sigtran_send(conn, resp, fi);
- osmo_fsm_inst_state_chg(fi, ST_CLEARING, 0, 0);
+ conn_fsm_state_chg(ST_CLEARING);
return;
}
@@ -493,7 +514,7 @@ static void gscon_fsm_active(struct osmo_fsm_inst *fi, uint32_t event, void *dat
* handover time out, so we do not need another timeout
* here (maybe its worth to think about giving GSCON
* more power over the actual handover process). */
- osmo_fsm_inst_state_chg(fi, ST_WAIT_HO_COMPL, 0, 0);
+ conn_fsm_state_chg(ST_WAIT_HO_COMPL);
break;
case GSCON_EV_A_HO_REQ:
/* FIXME: reject any handover requests with HO FAIL until implemented */
@@ -551,7 +572,7 @@ static void gscon_fsm_wait_crcx_bts(struct osmo_fsm_inst *fi, uint32_t event, vo
return;
}
- osmo_fsm_inst_state_chg(fi, ST_WAIT_ASS_CMPL, conn->network->T10, 10);
+ conn_fsm_state_chg(ST_WAIT_ASS_CMPL);
break;
case GSCON_EV_MO_DTAP:
forward_dtap(conn, (struct msgb *)data, fi);
@@ -598,7 +619,7 @@ static void gscon_fsm_wait_ass_cmpl(struct osmo_fsm_inst *fi, uint32_t event, vo
conn_peer.ptime = 20;
/* (Pre)Change state and modify the connection */
- osmo_fsm_inst_state_chg(fi, ST_WAIT_MDCX_BTS, MGCP_MGW_TIMEOUT, MGCP_MGW_TIMEOUT_TIMER_NR);
+ conn_fsm_state_chg(ST_WAIT_MDCX_BTS);
rc = mgcp_conn_modify(conn->user_plane.fi_bts, GSCON_EV_MGW_MDCX_RESP_BTS, &conn_peer);
if (rc != 0) {
assignment_failed(fi, GSM0808_CAUSE_EQUIPMENT_FAILURE);
@@ -609,7 +630,7 @@ static void gscon_fsm_wait_ass_cmpl(struct osmo_fsm_inst *fi, uint32_t event, vo
/* Confirm the successful assignment on BSSMAP and
* change back into active state */
send_ass_compl(lchan, fi, false);
- osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0);
+ conn_fsm_state_chg(ST_ACTIVE);
break;
default:
/* Unsupported modes should have been already filtered
@@ -673,12 +694,11 @@ static void gscon_fsm_wait_mdcx_bts(struct osmo_fsm_inst *fi, uint32_t event, vo
case OSMO_SS7_ASP_PROT_IPA:
/* Send assignment complete message to the MSC */
send_ass_compl(conn->lchan, fi, true);
- osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0);
+ conn_fsm_state_chg(ST_ACTIVE);
break;
default:
/* (Pre)Change state and create the connection */
- osmo_fsm_inst_state_chg(fi, ST_WAIT_CRCX_MSC, MGCP_MGW_TIMEOUT,
- MGCP_MGW_TIMEOUT_TIMER_NR);
+ conn_fsm_state_chg(ST_WAIT_CRCX_MSC);
conn->user_plane.fi_msc = mgcp_conn_create(conn->network->mgw.client, fi,
GSCON_EV_MGW_FAIL_MSC,
GSCON_EV_MGW_CRCX_RESP_MSC, &conn_peer);
@@ -725,7 +745,7 @@ static void gscon_fsm_wait_crcx_msc(struct osmo_fsm_inst *fi, uint32_t event, vo
/* Send assignment complete message to the MSC */
send_ass_compl(lchan, fi, true);
- osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0);
+ conn_fsm_state_chg(ST_ACTIVE);
break;
case GSCON_EV_MO_DTAP:
@@ -790,12 +810,12 @@ static void gscon_fsm_wait_ho_compl(struct osmo_fsm_inst *fi, uint32_t event, vo
conn_peer.ptime = 20;
/* (Pre)Change state and modify the connection */
- osmo_fsm_inst_state_chg(fi, ST_WAIT_MDCX_BTS_HO, MGCP_MGW_TIMEOUT, MGCP_MGW_HO_TIMEOUT_TIMER_NR);
+ conn_fsm_state_chg(ST_WAIT_MDCX_BTS_HO);
rc = mgcp_conn_modify(conn->user_plane.fi_bts, GSCON_EV_MGW_MDCX_RESP_BTS, &conn_peer);
if (rc != 0) {
resp = gsm0808_create_clear_rqst(GSM0808_CAUSE_EQUIPMENT_FAILURE);
sigtran_send(conn, resp, fi);
- osmo_fsm_inst_state_chg(fi, ST_CLEARING, 0, 0);
+ conn_fsm_state_chg(ST_CLEARING);
return;
}
break;
@@ -805,7 +825,7 @@ static void gscon_fsm_wait_ho_compl(struct osmo_fsm_inst *fi, uint32_t event, vo
* some reason. This means the phone stays on the TS/BTS on
* which it currently is. We will change back to the active
* state again as there are no further operations needed */
- osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0);
+ conn_fsm_state_chg(ST_ACTIVE);
break;
default:
OSMO_ASSERT(false);
@@ -824,7 +844,7 @@ static void gscon_fsm_wait_mdcx_bts_ho(struct osmo_fsm_inst *fi, uint32_t event,
/* The MGW has confirmed the handover MDCX, and the handover
* is now also done on the RTP side. We may now change back
* to the active state. */
- osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0);
+ conn_fsm_state_chg(ST_ACTIVE);
break;
case GSCON_EV_MO_DTAP:
forward_dtap(conn, (struct msgb *)data, fi);
@@ -955,7 +975,7 @@ static void gscon_fsm_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *d
break;
case GSCON_EV_A_CLEAR_CMD:
/* MSC tells us to cleanly shut down */
- osmo_fsm_inst_state_chg(fi, ST_CLEARING, 0, 0);
+ conn_fsm_state_chg(ST_CLEARING);
gsm0808_clear(conn);
/* FIXME: Release all terestrial resources in ST_CLEARING */
/* According to 3GPP 48.008 3.1.9.1. "The BSS need not wait for the radio channel
@@ -1080,7 +1100,7 @@ static int gscon_timer_cb(struct osmo_fsm_inst *fi)
assignment_failed(fi, GSM0808_CAUSE_EQUIPMENT_FAILURE);
break;
case MGCP_MGW_HO_TIMEOUT_TIMER_NR: /* Handover failed (no response from MGW) */
- osmo_fsm_inst_state_chg(fi, ST_ACTIVE, 0, 0);
+ conn_fsm_state_chg(ST_ACTIVE);
break;
default:
OSMO_ASSERT(false);
diff --git a/src/osmo-bsc/bsc_vty.c b/src/osmo-bsc/bsc_vty.c
index b187704ee..a551fb14a 100644
--- a/src/osmo-bsc/bsc_vty.c
+++ b/src/osmo-bsc/bsc_vty.c
@@ -64,6 +64,7 @@
#include <osmocom/bsc/meas_feed.h>
#include <osmocom/bsc/neighbor_ident.h>
#include <osmocom/bsc/handover.h>
+#include <osmocom/bsc/gsm_timers.h>
#include <inttypes.h>
@@ -968,11 +969,6 @@ static int config_write_bts(struct vty *v)
return CMD_SUCCESS;
}
-/* small helper macro for conditional dumping of timer */
-#define VTY_OUT_TIMER(number) \
- if (gsmnet->T##number != GSM_T##number##_DEFAULT) \
- vty_out(vty, " timer t"#number" %u%s", gsmnet->T##number, VTY_NEWLINE)
-
static int config_write_net(struct vty *vty)
{
struct gsm_network *gsmnet = gsmnet_from_vty(vty);
@@ -993,22 +989,7 @@ static int config_write_net(struct vty *vty)
ho_vty_write_net(vty, gsmnet);
- VTY_OUT_TIMER(3101);
- VTY_OUT_TIMER(3103);
- VTY_OUT_TIMER(3105);
- VTY_OUT_TIMER(3107);
- VTY_OUT_TIMER(3109);
- VTY_OUT_TIMER(3111);
- VTY_OUT_TIMER(3113);
- VTY_OUT_TIMER(3115);
- VTY_OUT_TIMER(3117);
- VTY_OUT_TIMER(3119);
- VTY_OUT_TIMER(3122);
- VTY_OUT_TIMER(3141);
- VTY_OUT_TIMER(10);
- VTY_OUT_TIMER(7);
- VTY_OUT_TIMER(8);
- VTY_OUT_TIMER(101);
+ T_defs_vty_write(vty, " ");
if (!gsmnet->dyn_ts_allow_tch_f)
vty_out(vty, " dyn_ts_allow_tch_f 0%s", VTY_NEWLINE);
@@ -1021,11 +1002,8 @@ static int config_write_net(struct vty *vty)
vty_out(vty, " timezone %d %d%s",
gsmnet->tz.hr, gsmnet->tz.mn, VTY_NEWLINE);
}
- if (gsmnet->t3212 == 0)
- vty_out(vty, " no periodic location update%s", VTY_NEWLINE);
- else
- vty_out(vty, " periodic location update %u%s",
- gsmnet->t3212 * 6, VTY_NEWLINE);
+
+ /* writing T3212 from the common T_defs_vty_write() instead. */
{
uint16_t meas_port;
@@ -1889,48 +1867,6 @@ DEFUN(cfg_net_pag_any_tch,
return CMD_SUCCESS;
}
-#define DEFAULT_TIMER(number) GSM_T##number##_DEFAULT
-/* Add another expansion so that DEFAULT_TIMER() becomes its value */
-#define EXPAND_AND_STRINGIFY(x) OSMO_STRINGIFY(x)
-
-#define DECLARE_TIMER(number, doc) \
- DEFUN(cfg_net_T##number, \
- cfg_net_T##number##_cmd, \
- "timer t" #number " (default|<1-65535>)", \
- "Configure GSM Timers\n" \
- doc " (default: " EXPAND_AND_STRINGIFY(DEFAULT_TIMER(number)) " seconds)\n" \
- "Set to default timer value" \
- " (" EXPAND_AND_STRINGIFY(DEFAULT_TIMER(number)) " seconds)\n" \
- "Timer Value in seconds\n") \
-{ \
- struct gsm_network *gsmnet = gsmnet_from_vty(vty); \
- int value; \
- if (strcmp(argv[0], "default") == 0) \
- value = DEFAULT_TIMER(number); \
- else \
- value = atoi(argv[0]); \
- \
- gsmnet->T##number = value; \
- return CMD_SUCCESS; \
-}
-
-DECLARE_TIMER(3101, "Set the timeout value for IMMEDIATE ASSIGNMENT")
-DECLARE_TIMER(3103, "Set the timeout value for HANDOVER")
-DECLARE_TIMER(3105, "Set the timer for repetition of PHYSICAL INFORMATION")
-DECLARE_TIMER(3107, "Currently not used")
-DECLARE_TIMER(3109, "Set the RSL SACCH deactivation timeout")
-DECLARE_TIMER(3111, "Set the RSL timeout to wait before releasing the RF Channel")
-DECLARE_TIMER(3113, "Set the time to try paging a subscriber")
-DECLARE_TIMER(3115, "Currently not used")
-DECLARE_TIMER(3117, "Currently not used")
-DECLARE_TIMER(3119, "Currently not used")
-DECLARE_TIMER(3122, "Default waiting time (seconds) after IMM ASS REJECT")
-DECLARE_TIMER(3141, "Currently not used")
-DECLARE_TIMER(10, "Assignment Command timeout in seconds")
-DECLARE_TIMER(7, "Set the outgoing inter-BSC Handover timeout, from Handover Required to Handover Command")
-DECLARE_TIMER(8, "Set the outgoing inter-BSC Handover timeout, from Handover Command to final Clear")
-DECLARE_TIMER(101, "Set the incoming inter-BSC Handover timeout, from Handover Request to Accept")
-
DEFUN_DEPRECATED(cfg_net_dtx,
cfg_net_dtx_cmd,
"dtx-used (0|1)",
@@ -4736,9 +4672,11 @@ DEFUN(cfg_net_per_loc_upd, cfg_net_per_loc_upd_cmd,
"Periodic Location Updating Interval in Minutes\n")
{
struct gsm_network *net = vty->index;
+ struct T_def *d = T_def_get_entry(net->T_defs, 3212);
- net->t3212 = atoi(argv[0]) / 6;
-
+ OSMO_ASSERT(d);
+ d->val = atoi(argv[0]) / 6;
+ vty_out(vty, "T%d = %u %s (%s)%s", d->T, d->val, "* 6min", d->desc, VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -4750,9 +4688,11 @@ DEFUN(cfg_net_no_per_loc_upd, cfg_net_no_per_loc_upd_cmd,
"Periodic Location Updating Interval\n")
{
struct gsm_network *net = vty->index;
+ struct T_def *d = T_def_get_entry(net->T_defs, 3212);
- net->t3212 = 0;
-
+ OSMO_ASSERT(d);
+ d->val = 0;
+ vty_out(vty, "T%d = %u %s (%s)%s", d->T, d->val, "* 6min", d->desc, VTY_NEWLINE);
return CMD_SUCCESS;
}
@@ -4844,23 +4784,9 @@ int bsc_vty_init(struct gsm_network *network)
logging_vty_add_cmds(NULL);
osmo_talloc_vty_add_cmds();
+ T_defs_vty_init(network->T_defs, GSMNET_NODE);
+
install_element(GSMNET_NODE, &cfg_net_neci_cmd);
- install_element(GSMNET_NODE, &cfg_net_T3101_cmd);
- install_element(GSMNET_NODE, &cfg_net_T3103_cmd);
- install_element(GSMNET_NODE, &cfg_net_T3105_cmd);
- install_element(GSMNET_NODE, &cfg_net_T3107_cmd);
- install_element(GSMNET_NODE, &cfg_net_T3109_cmd);
- install_element(GSMNET_NODE, &cfg_net_T3111_cmd);
- install_element(GSMNET_NODE, &cfg_net_T3113_cmd);
- install_element(GSMNET_NODE, &cfg_net_T3115_cmd);
- install_element(GSMNET_NODE, &cfg_net_T3117_cmd);
- install_element(GSMNET_NODE, &cfg_net_T3119_cmd);
- install_element(GSMNET_NODE, &cfg_net_T3122_cmd);
- install_element(GSMNET_NODE, &cfg_net_T3141_cmd);
- install_element(GSMNET_NODE, &cfg_net_T10_cmd);
- install_element(GSMNET_NODE, &cfg_net_T7_cmd);
- install_element(GSMNET_NODE, &cfg_net_T8_cmd);
- install_element(GSMNET_NODE, &cfg_net_T101_cmd);
install_element(GSMNET_NODE, &cfg_net_dtx_cmd);
install_element(GSMNET_NODE, &cfg_net_pag_any_tch_cmd);
/* See also handover commands added on net level from handover_vty.c */
diff --git a/src/osmo-bsc/bts_ipaccess_nanobts_omlattr.c b/src/osmo-bsc/bts_ipaccess_nanobts_omlattr.c
index 1a8d9b07b..d674c1891 100644
--- a/src/osmo-bsc/bts_ipaccess_nanobts_omlattr.c
+++ b/src/osmo-bsc/bts_ipaccess_nanobts_omlattr.c
@@ -23,6 +23,7 @@
#include <osmocom/core/msgb.h>
#include <osmocom/bsc/gsm_data.h>
#include <osmocom/bsc/abis_nm.h>
+#include <osmocom/bsc/gsm_timers.h>
static void patch_16(uint8_t *data, const uint16_t val)
{
@@ -90,7 +91,7 @@ struct msgb *nanobts_attr_bts_get(struct gsm_bts *bts)
msgb_tv_fixed_put(msgb, NM_ATT_LDAVG_SLOTS, 2, buf);
/* 10 milliseconds */
- msgb_tv_put(msgb, NM_ATT_BTS_AIR_TIMER, bts->network->T3105 > 0? bts->network->T3105 : 13);
+ msgb_tv_put(msgb, NM_ATT_BTS_AIR_TIMER, T_def_get(bts->network->T_defs, 3105, T_MS, -1));
/* 10 retransmissions of physical config */
msgb_tv_put(msgb, NM_ATT_NY1, 10);
diff --git a/src/osmo-bsc/bts_siemens_bs11.c b/src/osmo-bsc/bts_siemens_bs11.c
index 2d2351702..1da189fb9 100644
--- a/src/osmo-bsc/bts_siemens_bs11.c
+++ b/src/osmo-bsc/bts_siemens_bs11.c
@@ -27,6 +27,7 @@
#include <osmocom/bsc/abis_nm.h>
#include <osmocom/abis/e1_input.h>
#include <osmocom/bsc/signal.h>
+#include <osmocom/bsc/gsm_timers.h>
static int bts_model_bs11_start(struct gsm_network *net);
@@ -358,7 +359,7 @@ static void patch_nm_tables(struct gsm_bts *bts)
uint8_t arfcn_high = (bts->c0->arfcn >> 8) & 0x0f;
/* T3105 attribute in units of 10ms */
- bs11_attr_bts[2] = bts->network->T3105 / 10;
+ bs11_attr_bts[2] = T_def_get(bts->network->T_defs, 3105, T_MS, -1) / 10;
/* patch ARFCN into BTS Attributes */
bs11_attr_bts[69] &= 0xf0;
diff --git a/src/osmo-bsc/gsm_data.c b/src/osmo-bsc/gsm_data.c
index fc12d0f71..99438bc3c 100644
--- a/src/osmo-bsc/gsm_data.c
+++ b/src/osmo-bsc/gsm_data.c
@@ -39,6 +39,7 @@
#include <osmocom/bsc/bsc_msc_data.h>
#include <osmocom/bsc/abis_nm.h>
#include <osmocom/bsc/handover_cfg.h>
+#include <osmocom/bsc/gsm_timers.h>
void *tall_bsc_ctx = NULL;
@@ -864,7 +865,7 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, uint8_t bts_num)
bts->si_common.chan_desc.att = 1; /* attachment required */
bts->si_common.chan_desc.bs_pa_mfrms = RSL_BS_PA_MFRMS_5; /* paging frames */
bts->si_common.chan_desc.bs_ag_blks_res = 1; /* reserved AGCH blocks */
- bts->si_common.chan_desc.t3212 = net->t3212; /* Use network's current value */
+ bts->si_common.chan_desc.t3212 = T_def_get(net->T_defs, 3212, T_CUSTOM, -1);
gsm_bts_set_radio_link_timeout(bts, 32); /* Use RADIO LINK TIMEOUT of 32 */
INIT_LLIST_HEAD(&bts->abis_queue);
diff --git a/src/osmo-bsc/gsm_timers.c b/src/osmo-bsc/gsm_timers.c
new file mode 100644
index 000000000..cc9bdd906
--- /dev/null
+++ b/src/osmo-bsc/gsm_timers.c
@@ -0,0 +1,206 @@
+/* Implementation to define Tnnn timers globally and use for FSM state changes. */
+/* (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * Author: Neels Hofmeyr <neels@hofmeyr.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <osmocom/core/fsm.h>
+
+#include <osmocom/bsc/gsm_timers.h>
+
+/* a = return_val * b. Return 0 if factor is below 1. */
+static int T_factor(enum T_unit a, enum T_unit b)
+{
+ if (b == a
+ || b == T_CUSTOM || a == T_CUSTOM)
+ return 1;
+
+ switch (b) {
+ case T_MS:
+ switch (a) {
+ case T_S:
+ return 1000;
+ case T_M:
+ return 60*1000;
+ default:
+ return 0;
+ }
+ case T_S:
+ switch (a) {
+ case T_M:
+ return 60;
+ default:
+ return 0;
+ }
+ default:
+ return 0;
+ }
+}
+
+static int T_round(int val, enum T_unit from_unit, enum T_unit to_unit)
+{
+ int f;
+ if (!val)
+ return 0;
+
+ f = T_factor(from_unit, to_unit);
+ if (f < 1) {
+ f = T_factor(to_unit, from_unit);
+ return (val / f) + (val % f? 1 : 0);
+ }
+ return val * f;
+}
+
+/* Return the value of a T timer from a list of T_defs.
+ * Any value is rounded up to match as_unit: 1100 ms as T_S becomes 2 seconds, as T_M becomes one minute.
+ * If no such timer is defined, return the default value passed, or abort the program if default < 0.
+ *
+ * Usage examples:
+ *
+ * - Initialization:
+ *
+ * struct T_def global_T_defs[] = {
+ * { .T=7, .default_val=50, .desc="Water Boiling Timeout" }, // default is .unit=T_S == 0
+ * { .T=8, .default_val=300, .desc="Tea brewing" },
+ * { .T=9, .default_val=5, .unit=T_M, .desc="Let tea cool down before drinking" },
+ * { .T=10, .default_val=20, .unit=T_M, .desc="Forgot to drink tea while it's warm" },
+ * {} // <-- important! last entry shall be zero
+ * };
+ * T_defs_reset(global_T_defs); // make all values the default
+ * T_defs_vty_init(global_T_defs, CONFIG_NODE);
+ *
+ * val = T_def_get(global_T_defs, 7, T_S, -1); // -> 50
+ * sleep(val);
+ *
+ * val = T_def_get(global_T_defs, 7, T_M, -1); // 50 seconds becomes 1 minute -> 1
+ * sleep_minutes(val);
+ *
+ * val = T_def_get(global_T_defs, 99, T_S, -1); // not defined, program aborts!
+ *
+ * val = T_def_get(global_T_defs, 99, T_S, 3); // not defined, returns 3
+ */
+int T_def_get(struct T_def *T_defs, int T, enum T_unit as_unit, int val_if_not_present)
+{
+ struct T_def *d = T_def_get_entry(T_defs, T);
+ if (!d) {
+ OSMO_ASSERT(val_if_not_present >= 0);
+ return val_if_not_present;
+ }
+ return T_round(d->val, d->unit, as_unit);
+}
+
+/* Set all T_def values to the default_val. */
+void T_defs_reset(struct T_def *T_defs)
+{
+ struct T_def *d;
+ for_each_T_def(d, T_defs)
+ d->val = d->default_val;
+}
+
+/* Return a pointer to a T_def from an array, or NULL. */
+struct T_def *T_def_get_entry(struct T_def *T_defs, int T)
+{
+ struct T_def *d;
+ for_each_T_def(d, T_defs) {
+ if (d->T == T)
+ return d;
+ }
+ return NULL;
+}
+
+/* Return a state_timeout entry from an array, or return NULL if the entry is zero.
+ *
+ * The timeouts_array shall contain exactly 32 elements, which corresponds to the number of states
+ * allowed by osmo_fsm_*. Lookup is by array index.
+ *
+ * For example:
+ * struct state_timeout my_fsm_timeouts[32] = {
+ * [MY_FSM_STATE_3] = { .T = 423 },
+ * [MY_FSM_STATE_7] = { .T = 235 },
+ * [MY_FSM_STATE_8] = { .keep_timer = true },
+ * // any state that is omitted will remain zero == no timeout
+ * };
+ * get_state_timeout(MY_FSM_STATE_0, &my_fsm_timeouts) -> NULL,
+ * get_state_timeout(MY_FSM_STATE_7, &my_fsm_timeouts) -> { .T = 235 }
+ *
+ * The intention is then to obtain the timer like T_def_get(global_T_defs, T=235); see also
+ * fsm_inst_state_chg_T() below.
+ */
+struct state_timeout *get_state_timeout(uint32_t state, struct state_timeout *timeouts_array)
+{
+ struct state_timeout *t;
+ OSMO_ASSERT(state < 32);
+ t = &timeouts_array[state];
+ if (!t->keep_timer && !t->T)
+ return NULL;
+ return t;
+}
+
+/* Call osmo_fsm_inst_state_chg() or osmo_fsm_inst_state_chg_keep_timer(), depending on the T value
+ * defined for this state in the timeouts_array, and obtaining the actual timeout value from T_defs.
+ * A T timer configured in sub-second precision is rounded up to the next full second.
+ *
+ * See get_state_timeout() and T_def_get().
+ *
+ * Should a T number be defined in timeouts_array that is not defined in T_defs, use default_timeout.
+ * This is best used by wrapping this function call in a macro suitable for a specific FSM
+ * implementation, which can become as short as: my_fsm_state_chg(fi, NEXT_STATE):
+ *
+ * #define my_fsm_state_chg(fi, NEXT_STATE) \
+ * fsm_inst_state_chg_T(fi, NEXT_STATE, my_fsm_timeouts, global_T_defs, 5)
+ *
+ * my_fsm_state_chg(fi, MY_FSM_STATE_1);
+ * // -> No timeout configured, will enter state without timeout.
+ *
+ * my_fsm_state_chg(fi, MY_FSM_STATE_3);
+ * // T423 configured for this state, will look up T423 in T_defs, or use 5 seconds if unset.
+ *
+ * my_fsm_state_chg(fi, MY_FSM_STATE_8);
+ * // keep_timer configured for this state, will invoke osmo_fsm_inst_state_chg_keep_timer().
+ *
+ */
+int _fsm_inst_state_chg_T(struct osmo_fsm_inst *fi, uint32_t state,
+ struct state_timeout *timeouts_array,
+ struct T_def *T_defs, int default_timeout,
+ const char *file, int line)
+{
+ struct state_timeout *t = get_state_timeout(state, timeouts_array);
+ int val;
+
+ /* No timeout defined for this state? */
+ if (!t)
+ return _osmo_fsm_inst_state_chg(fi, state, 0, 0, file, line);
+
+ if (t->keep_timer) {
+ int rc = _osmo_fsm_inst_state_chg_keep_timer(fi, state, file, line);
+ if (t->T && !rc)
+ fi->T = t->T;
+ return rc;
+ }
+
+ val = T_def_get(T_defs, t->T, T_S, default_timeout);
+ return _osmo_fsm_inst_state_chg(fi, state, val, t->T, file, line);
+}
+
+const struct value_string T_unit_names[] = {
+ { T_S, "s" },
+ { T_MS, "ms" },
+ { T_CUSTOM, "(custom)" },
+ { 0, NULL }
+};
diff --git a/src/osmo-bsc/gsm_timers_vty.c b/src/osmo-bsc/gsm_timers_vty.c
new file mode 100644
index 000000000..8a13259ff
--- /dev/null
+++ b/src/osmo-bsc/gsm_timers_vty.c
@@ -0,0 +1,117 @@
+/* Implementation to configure Tnnn timers in VTY */
+/* (C) 2018 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * Author: Neels Hofmeyr <neels@hofmeyr.de>
+ *
+ * All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string.h>
+
+#include <osmocom/vty/vty.h>
+#include <osmocom/vty/command.h>
+
+#include <osmocom/bsc/gsm_timers.h>
+
+/* Global singleton list used for the VTY configuration. See T_defs_vty_init(). */
+static struct T_def *g_vty_T_defs = NULL;
+
+/* Parse an argument like "T1234", "t1234" or "1234" and return the corresponding T_def entry from
+ * g_vty_T_defs, if any. */
+static struct T_def *parse_T_arg(struct vty *vty, const char *T_str)
+{
+ int T;
+ struct T_def *d;
+
+ if (T_str[0] == 't' || T_str[0] == 'T')
+ T_str++;
+ T = atoi(T_str);
+
+ d = T_def_get_entry(g_vty_T_defs, T);
+ if (!d)
+ vty_out(vty, "No such timer: T%d%s", T, VTY_NEWLINE);
+ return d;
+}
+
+/* Installed in the VTY on T_defs_vty_init(). */
+DEFUN(cfg_timer, cfg_timer_cmd,
+ "timer TNNNN (default|<1-65535>)",
+ "Configure GSM Timers\n"
+ "T-number, optionally preceded by 't' or 'T'."
+ "See also 'show timer' for a list of available timers.\n"
+ "Set to default timer value\n" "Timer value\n")
+{
+ const char *val_str = argv[1];
+ struct T_def *d;
+
+ d = parse_T_arg(vty, argv[0]);
+ if (!d)
+ return CMD_WARNING;
+
+ if (!strcmp(val_str, "default"))
+ d->val = d->default_val;
+ else
+ d->val = atoi(val_str);
+ vty_out(vty, "T%d = %u %s (%s)%s", d->T, d->val, T_unit_name(d->unit), d->desc, VTY_NEWLINE);
+ return CMD_SUCCESS;
+}
+
+/* Print a T_def to the VTY. */
+static void show_one_timer(struct vty *vty, struct T_def *d)
+{
+ vty_out(vty, "T%d = %u %s (default = %u %s) \t%s%s",
+ d->T, d->val, T_unit_name(d->unit),
+ d->default_val, T_unit_name(d->unit), d->desc, VTY_NEWLINE);
+}
+
+/* Installed in the VTY on T_defs_vty_init(). */
+DEFUN(show_timer, show_timer_cmd,
+ "show timer [TNNNN]",
+ SHOW_STR "GSM Timers\n"
+ "Specific timer to show, or all timers if omitted.\n")
+{
+ struct T_def *d;
+
+ if (argc) {
+ d = parse_T_arg(vty, argv[0]);
+ if (!d)
+ return CMD_WARNING;
+ show_one_timer(vty, d);
+ return CMD_SUCCESS;
+ }
+
+ for_each_T_def(d, g_vty_T_defs)
+ show_one_timer(vty, d);
+ return CMD_SUCCESS;
+}
+
+/* Install GSM timer configuration commands in the VTY. */
+void T_defs_vty_init(struct T_def *T_defs, int cfg_parent_node)
+{
+ install_element_ve(&show_timer_cmd);
+ install_element(cfg_parent_node, &cfg_timer_cmd);
+}
+
+/* Write GSM timer configuration to the vty. */
+void T_defs_vty_write(struct vty *vty, const char *indent)
+{
+ struct T_def *d;
+ for_each_T_def(d, g_vty_T_defs) {
+ if (d->val != d->default_val)
+ vty_out(vty, "%stimer t%d %u%s", indent, d->T, d->val, VTY_NEWLINE);
+ }
+}
diff --git a/src/osmo-bsc/net_init.c b/src/osmo-bsc/net_init.c
index 208b4efe2..1199bdc82 100644
--- a/src/osmo-bsc/net_init.c
+++ b/src/osmo-bsc/net_init.c
@@ -23,6 +23,33 @@
#include <osmocom/bsc/handover_cfg.h>
#include <osmocom/bsc/chan_alloc.h>
#include <osmocom/bsc/neighbor_ident.h>
+#include <osmocom/bsc/gsm_timers.h>
+
+static struct T_def gsm_network_T_defs[] = {
+ { .T=7, .default_val=10, .desc="inter-BSC Handover MO, HO Required to HO Command" },
+ { .T=8, .default_val=10, .desc="inter-BSC Handover MO, HO Command to final Clear" },
+ { .T=10, .default_val=6, .desc="RR Assignment" },
+ { .T=101, .default_val=10, .desc="inter-BSC Handover MT, HO Request to HO Accept" },
+ { .T=3101, .default_val=3, .desc="RR Immediate Assignment" },
+ { .T=3103, .default_val=5, .desc="Handover" },
+ { .T=3105, .default_val=100, .unit=T_MS, .desc="Physical Information" },
+ { .T=3107, .default_val=5, .desc="(unused)" },
+ { .T=3109, .default_val=5, .desc="RSL SACCH deactivation" },
+ { .T=3111, .default_val=2, .desc="Wait time before RSL RF Channel Release" },
+ { .T=993111, .default_val=4, .desc="Wait time after lchan was released in error (should be T3111 + 2s)" },
+ { .T=3113, .default_val=10, .desc="Paging"},
+ { .T=3115, .default_val=10, .desc="(unused)" },
+ { .T=3117, .default_val=10, .desc="(unused)" },
+ { .T=3119, .default_val=10, .desc="(unused)" },
+ { .T=3122, .default_val=GSM_T3122_DEFAULT, .desc="Wait time after RR Immediate Assignment Reject" },
+ { .T=3141, .default_val=10, .desc="(unused)" },
+ { .T=3212, .default_val=5, .unit=T_CUSTOM,
+ .desc="Periodic Location Update timer, sent to MS (1 = 6 minutes)" },
+ { .T=993210, .default_val=20, .desc="After L3 Complete, wait for MSC to confirm" },
+ { .T=999, .default_val=60, .desc="After Clear Request, wait for MSC to Clear Command (sanity)" },
+ { .T=992427, .default_val=4, .desc="MGCP timeout (2427 is the default MGCP port number)" },
+ {}
+};
/* Initialize the bare minimum of struct gsm_network, minimizing required dependencies.
* This part is shared among the thin programs in osmo-bsc/src/utils/.
@@ -43,9 +70,6 @@ struct gsm_network *gsm_network_init(void *ctx)
/* Permit a compile-time default of A5/3 and A5/1 */
net->a5_encryption_mask = (1 << 3) | (1 << 1);
- /* Use 30 min periodic update interval as sane default */
- net->t3212 = 5;
-
INIT_LLIST_HEAD(&net->subscr_conns);
net->bsc_subscribers = talloc_zero(net, struct llist_head);
@@ -53,22 +77,9 @@ struct gsm_network *gsm_network_init(void *ctx)
INIT_LLIST_HEAD(&net->bts_list);
net->num_bts = 0;
- net->T3101 = GSM_T3101_DEFAULT;
- net->T3103 = GSM_T3103_DEFAULT;
- net->T3105 = GSM_T3105_DEFAULT;
- net->T3107 = GSM_T3107_DEFAULT;
- net->T3109 = GSM_T3109_DEFAULT;
- net->T3111 = GSM_T3111_DEFAULT;
- net->T3113 = GSM_T3113_DEFAULT;
- net->T3115 = GSM_T3115_DEFAULT;
- net->T3117 = GSM_T3117_DEFAULT;
- net->T3119 = GSM_T3119_DEFAULT;
- net->T3122 = GSM_T3122_DEFAULT;
- net->T3141 = GSM_T3141_DEFAULT;
- net->T10 = GSM_T10_DEFAULT;
- net->T7 = GSM_T7_DEFAULT;
- net->T8 = GSM_T8_DEFAULT;
- net->T101 = GSM_T101_DEFAULT;
+
+ net->T_defs = gsm_network_T_defs;
+ T_defs_reset(net->T_defs);
return net;
}
diff --git a/src/osmo-bsc/paging.c b/src/osmo-bsc/paging.c
index 7e833d39c..8012f674a 100644
--- a/src/osmo-bsc/paging.c
+++ b/src/osmo-bsc/paging.c
@@ -51,6 +51,7 @@
#include <osmocom/bsc/chan_alloc.h>
#include <osmocom/bsc/bsc_api.h>
#include <osmocom/bsc/gsm_04_08_rr.h>
+#include <osmocom/bsc/gsm_timers.h>
void *tall_paging_ctx = NULL;
@@ -315,7 +316,7 @@ static int _paging_request(struct gsm_bts *bts, struct bsc_subscr *bsub, int typ
req->chan_type = type;
req->msc = msc;
osmo_timer_setup(&req->T3113, paging_T3113_expired, req);
- osmo_timer_schedule(&req->T3113, bts->network->T3113, 0);
+ osmo_timer_schedule(&req->T3113, T_def_get(bts->network->T_defs, 3113, T_S, -1), 0);
llist_add_tail(&req->entry, &bts_entry->pending_requests);
paging_schedule_if_needed(bts_entry);
diff --git a/src/utils/Makefile.am b/src/utils/Makefile.am
index 543344b53..42118a05c 100644
--- a/src/utils/Makefile.am
+++ b/src/utils/Makefile.am
@@ -54,6 +54,7 @@ bs11_config_LDADD = \
$(top_builddir)/src/osmo-bsc/bts_siemens_bs11.o \
$(top_builddir)/src/osmo-bsc/e1_config.o \
$(top_builddir)/src/osmo-bsc/gsm_data.o \
+ $(top_builddir)/src/osmo-bsc/gsm_timers.o \
$(top_builddir)/src/osmo-bsc/net_init.o \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
@@ -122,6 +123,7 @@ meas_json_SOURCES = \
meas_json_LDADD = \
$(top_builddir)/src/osmo-bsc/gsm_data.o \
+ $(top_builddir)/src/osmo-bsc/gsm_timers.o \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOABIS_LIBS) \
diff --git a/tests/abis/Makefile.am b/tests/abis/Makefile.am
index 60054d94d..4fc3605c0 100644
--- a/tests/abis/Makefile.am
+++ b/tests/abis/Makefile.am
@@ -27,6 +27,7 @@ abis_test_SOURCES = \
abis_test_LDADD = \
$(top_builddir)/src/osmo-bsc/abis_nm.o \
$(top_builddir)/src/osmo-bsc/gsm_data.o \
+ $(top_builddir)/src/osmo-bsc/gsm_timers.o \
$(top_builddir)/src/osmo-bsc/net_init.o \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOABIS_LIBS) \
diff --git a/tests/bsc/Makefile.am b/tests/bsc/Makefile.am
index 6ffeed296..ce973be19 100644
--- a/tests/bsc/Makefile.am
+++ b/tests/bsc/Makefile.am
@@ -43,6 +43,7 @@ bsc_test_LDADD = \
$(top_builddir)/src/osmo-bsc/gsm_04_08_rr.o \
$(top_builddir)/src/osmo-bsc/gsm_04_80_utils.o \
$(top_builddir)/src/osmo-bsc/gsm_data.o \
+ $(top_builddir)/src/osmo-bsc/gsm_timers.o \
$(top_builddir)/src/osmo-bsc/handover_cfg.o \
$(top_builddir)/src/osmo-bsc/handover_logic.o \
$(top_builddir)/src/osmo-bsc/neighbor_ident.o \
diff --git a/tests/gsm0408/Makefile.am b/tests/gsm0408/Makefile.am
index 3eb47f650..d790fc870 100644
--- a/tests/gsm0408/Makefile.am
+++ b/tests/gsm0408/Makefile.am
@@ -25,6 +25,7 @@ gsm0408_test_SOURCES = \
gsm0408_test_LDADD = \
$(top_builddir)/src/osmo-bsc/arfcn_range_encode.o \
$(top_builddir)/src/osmo-bsc/gsm_data.o \
+ $(top_builddir)/src/osmo-bsc/gsm_timers.o \
$(top_builddir)/src/osmo-bsc/net_init.o \
$(top_builddir)/src/osmo-bsc/rest_octets.o \
$(top_builddir)/src/osmo-bsc/system_information.o \
diff --git a/tests/handover/Makefile.am b/tests/handover/Makefile.am
index f8c2664e6..6f0fed66c 100644
--- a/tests/handover/Makefile.am
+++ b/tests/handover/Makefile.am
@@ -54,6 +54,7 @@ handover_test_LDADD = \
$(top_builddir)/src/osmo-bsc/gsm_04_08_rr.o \
$(top_builddir)/src/osmo-bsc/gsm_04_80_utils.o \
$(top_builddir)/src/osmo-bsc/gsm_data.o \
+ $(top_builddir)/src/osmo-bsc/gsm_timers.o \
$(top_builddir)/src/osmo-bsc/handover_cfg.o \
$(top_builddir)/src/osmo-bsc/handover_decision.o \
$(top_builddir)/src/osmo-bsc/handover_decision_2.o \
diff --git a/tests/nanobts_omlattr/Makefile.am b/tests/nanobts_omlattr/Makefile.am
index aa7045e49..312cf7d93 100644
--- a/tests/nanobts_omlattr/Makefile.am
+++ b/tests/nanobts_omlattr/Makefile.am
@@ -26,6 +26,7 @@ nanobts_omlattr_test_LDADD = \
$(top_builddir)/src/osmo-bsc/abis_nm.o \
$(top_builddir)/src/osmo-bsc/bts_ipaccess_nanobts_omlattr.o \
$(top_builddir)/src/osmo-bsc/gsm_data.o \
+ $(top_builddir)/src/osmo-bsc/gsm_timers.o \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOGSM_LIBS) \
$(LIBOSMOABIS_LIBS) \
diff --git a/tests/nanobts_omlattr/nanobts_omlattr_test.c b/tests/nanobts_omlattr/nanobts_omlattr_test.c
index 72dabe542..7a3a80e2d 100644
--- a/tests/nanobts_omlattr/nanobts_omlattr_test.c
+++ b/tests/nanobts_omlattr/nanobts_omlattr_test.c
@@ -21,6 +21,7 @@
#include <osmocom/bsc/debug.h>
#include <osmocom/bsc/gsm_data.h>
+#include <osmocom/bsc/gsm_timers.h>
#include <osmocom/bsc/bts_ipaccess_nanobts_omlattr.h>
#include <osmocom/core/talloc.h>
@@ -191,6 +192,13 @@ static const struct log_info log_info = {
.num_cat = ARRAY_SIZE(log_categories),
};
+static struct T_def gsm_network_T_defs[] = {
+ { .T=3105, .default_val=100, .val=13, .unit=T_MS, .desc="Physical Information" },
+ { .T=3212, .default_val=5, .unit=T_CUSTOM,
+ .desc="Periodic Location Update timer, sent to MS (1 = 6 minutes)" },
+ {}
+};
+
int main(int argc, char **argv)
{
void *ctx;
@@ -207,6 +215,7 @@ int main(int argc, char **argv)
/* Allocate environmental structs (bts, net, trx) */
net = talloc_zero(ctx, struct gsm_network);
INIT_LLIST_HEAD(&net->bts_list);
+ net->T_defs = gsm_network_T_defs;
gsm_bts_model_register(&bts_model_nanobts);
bts = gsm_bts_alloc_register(net, GSM_BTS_TYPE_NANOBTS, 63);
OSMO_ASSERT(bts);