Change in osmo-bsc[master]: add gsm_timers, for Tnnn definitions usable by FSMs

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

Neels Hofmeyr gerrit-no-reply at lists.osmocom.org
Sat Jul 28 10:43:57 UTC 2018


Neels Hofmeyr has submitted this change and it was merged. ( https://gerrit.osmocom.org/9670 )

Change subject: add gsm_timers, for Tnnn definitions usable by FSMs
......................................................................

add gsm_timers, for Tnnn definitions usable by FSMs

Change-Id: If212fcd042051b6fa53484254223614c5b93a9c6
---
M include/osmocom/bsc/Makefile.am
M include/osmocom/bsc/gsm_data.h
A include/osmocom/bsc/gsm_timers.h
M src/ipaccess/Makefile.am
M src/osmo-bsc/Makefile.am
M src/osmo-bsc/abis_om2000.c
M src/osmo-bsc/abis_rsl.c
M src/osmo-bsc/bsc_subscr_conn_fsm.c
M src/osmo-bsc/bsc_vty.c
M src/osmo-bsc/bts_ipaccess_nanobts_omlattr.c
M src/osmo-bsc/bts_siemens_bs11.c
M src/osmo-bsc/gsm_data.c
A src/osmo-bsc/gsm_timers.c
A src/osmo-bsc/gsm_timers_vty.c
M src/osmo-bsc/net_init.c
M src/osmo-bsc/paging.c
M src/utils/Makefile.am
M tests/abis/Makefile.am
M tests/bsc/Makefile.am
M tests/gsm0408/Makefile.am
M tests/handover/Makefile.am
M tests/nanobts_omlattr/Makefile.am
M tests/nanobts_omlattr/nanobts_omlattr_test.c
23 files changed, 513 insertions(+), 180 deletions(-)

Approvals:
  Jenkins Builder: Verified
  Neels Hofmeyr: Looks good to me, approved



diff --git a/include/osmocom/bsc/Makefile.am b/include/osmocom/bsc/Makefile.am
index 1e26c34..0f134c8 100644
--- a/include/osmocom/bsc/Makefile.am
+++ b/include/osmocom/bsc/Makefile.am
@@ -19,6 +19,7 @@
 	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 2872493..272b192 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 @@
 	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 @@
 	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 @@
 	 * 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 0000000..fde8c93
--- /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 2c6282d..ec3e027 100644
--- a/src/ipaccess/Makefile.am
+++ b/src/ipaccess/Makefile.am
@@ -55,6 +55,7 @@
 	$(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 @@
 	$(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 0bc78d5..9183791 100644
--- a/src/osmo-bsc/Makefile.am
+++ b/src/osmo-bsc/Makefile.am
@@ -58,6 +58,8 @@
 	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 d533ea1..2934590 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 @@
 		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 @@
 				  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 @@
 				  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 bf8dfa5..97d2aa2 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 @@
 	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 @@
 		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 @@
 
 	/* 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 @@
 	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 @@
 	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 3249270..f97b778 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 @@
 	}
 }
 
+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 @@
 		} 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 @@
 /* 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 @@
 	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 @@
 			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 @@
 				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 @@
 		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 @@
 		 * 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 @@
 			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 @@
 			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 @@
 			/* 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 @@
 		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 @@
 		/* 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 @@
 		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 @@
 		 * 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 @@
 		/* 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 @@
 		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 @@
 		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 b187704..a551fb1 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 @@
 	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 @@
 
 	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 @@
 			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 @@
 	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 @@
       "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 @@
       "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 @@
 	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 1a8d9b0..d674c18 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 @@
 	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 2d23517..1da189f 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 @@
 	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 fc12d0f..99438bc 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 @@
 	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 0000000..cc9bdd9
--- /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 at sysmocom.de>
+ *
+ * Author: Neels Hofmeyr <neels at 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 0000000..8a13259
--- /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 at sysmocom.de>
+ *
+ * Author: Neels Hofmeyr <neels at 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 208b4ef..1199bdc 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 @@
 	/* 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 @@
 
 	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 7e833d3..8012f67 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 @@
 	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 543344b..42118a0 100644
--- a/src/utils/Makefile.am
+++ b/src/utils/Makefile.am
@@ -54,6 +54,7 @@
 	$(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_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 60054d9..4fc3605 100644
--- a/tests/abis/Makefile.am
+++ b/tests/abis/Makefile.am
@@ -27,6 +27,7 @@
 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 6ffeed2..ce973be 100644
--- a/tests/bsc/Makefile.am
+++ b/tests/bsc/Makefile.am
@@ -43,6 +43,7 @@
 	$(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 3eb47f6..d790fc8 100644
--- a/tests/gsm0408/Makefile.am
+++ b/tests/gsm0408/Makefile.am
@@ -25,6 +25,7 @@
 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 f8c2664..6f0fed6 100644
--- a/tests/handover/Makefile.am
+++ b/tests/handover/Makefile.am
@@ -54,6 +54,7 @@
 	$(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 aa7045e..312cf7d 100644
--- a/tests/nanobts_omlattr/Makefile.am
+++ b/tests/nanobts_omlattr/Makefile.am
@@ -26,6 +26,7 @@
 	$(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 72dabe5..7a3a80e 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 @@
 	.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 @@
 	/* 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);

-- 
To view, visit https://gerrit.osmocom.org/9670
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-bsc
Gerrit-Branch: master
Gerrit-MessageType: merged
Gerrit-Change-Id: If212fcd042051b6fa53484254223614c5b93a9c6
Gerrit-Change-Number: 9670
Gerrit-PatchSet: 17
Gerrit-Owner: Neels Hofmeyr <nhofmeyr at sysmocom.de>
Gerrit-Reviewer: Harald Welte <laforge at gnumonks.org>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: Neels Hofmeyr <nhofmeyr at sysmocom.de>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20180728/b44c7117/attachment.htm>


More information about the gerrit-log mailing list