lchan: Introduce T3109 handling for the release procedure

T3109 is started when the SACCH is deactivated. It is stopped when
the phones sends the DISC/UA/UM on LAPDm for the main signalling
link. In case of timeout the abnormal release procedure will be
initiated. Make sure to not issue the SACCH Deactivate twice to
avoid confusing the equipment.

This is still not fully spec compliant. In case of a timeout the
abnormal release handling will be started which involves starting
T3111+2. The error handling should be split out of the rf channel
release method, e.g. lchan_release should be called and check if
the channel release was already initiated.
diff --git a/openbsc/src/libbsc/abis_rsl.c b/openbsc/src/libbsc/abis_rsl.c
index 04d5c61..6e1ce78 100644
--- a/openbsc/src/libbsc/abis_rsl.c
+++ b/openbsc/src/libbsc/abis_rsl.c
@@ -46,6 +46,11 @@
 #define RSL_ALLOC_SIZE		1024
 #define RSL_ALLOC_HEADROOM	128
 
+enum sacch_deact {
+	SACCH_NONE,
+	SACCH_DEACTIVATE,
+};
+
 static int rsl_send_imm_assignment(struct gsm_lchan *lchan);
 
 static void send_lchan_signal(int sig_no, struct gsm_lchan *lchan,
@@ -644,12 +649,16 @@
 static int rsl_rx_rf_chan_rel_ack(struct gsm_lchan *lchan);
 
 /* Chapter 8.4.14 / 4.7: Tell BTS to release the radio channel */
-static int rsl_rf_chan_release(struct gsm_lchan *lchan, int error)
+static int rsl_rf_chan_release(struct gsm_lchan *lchan, int error,
+				enum sacch_deact deact_sacch)
 {
 	struct abis_rsl_dchan_hdr *dh;
 	struct msgb *msg;
 	int rc;
 
+	/* Stop timers that should lead to a channel release */
+	osmo_timer_del(&lchan->T3109);
+
 	if (lchan->state == LCHAN_S_REL_ERR) {
 		LOGP(DRSL, LOGL_NOTICE, "%s is in error state not sending release.\n",
 		     gsm_lchan_name(lchan));
@@ -688,7 +697,8 @@
 		/*
 		 * sacch de-activate and "local end release"
 		 */
-		rsl_deact_sacch(lchan);
+		if (deact_sacch == SACCH_DEACTIVATE)
+			rsl_deact_sacch(lchan);
 		rsl_release_sapis_from(lchan, 0, RSL_REL_LOCAL_END);
 
 		/*
@@ -939,7 +949,7 @@
 		if (*cause != RSL_ERR_RCH_ALR_ACTV_ALLOC)
 			rsl_lchan_set_state(msg->lchan, LCHAN_S_BROKEN);
 		else
-			rsl_rf_chan_release(msg->lchan, 1);
+			rsl_rf_chan_release(msg->lchan, 1, SACCH_DEACTIVATE);
 
 	} else
 		rsl_lchan_set_state(msg->lchan, LCHAN_S_BROKEN);
@@ -969,7 +979,7 @@
 	LOGPC(DRSL, LOGL_NOTICE, "\n");
 	/* FIXME: only free it after channel release ACK */
 	osmo_counter_inc(msg->lchan->ts->trx->bts->network->stats.chan.rf_fail);
-	return rsl_rf_chan_release(msg->lchan, 1);
+	return rsl_rf_chan_release(msg->lchan, 1, SACCH_DEACTIVATE);
 }
 
 static void print_meas_rep_uni(struct gsm_meas_rep_unidir *mru,
@@ -1250,7 +1260,7 @@
 {
 	struct gsm_lchan *lchan = data;
 
-	rsl_rf_chan_release(lchan, 1);
+	rsl_rf_chan_release(lchan, 1, SACCH_DEACTIVATE);
 }
 
 /* If T3111 expires, we will send the RF Channel Request */
@@ -1258,7 +1268,17 @@
 {
 	struct gsm_lchan *lchan = data;
 
-	rsl_rf_chan_release(lchan, 0);
+	rsl_rf_chan_release(lchan, 0, SACCH_NONE);
+}
+
+/* If T3109 expires the MS has not send a UA/UM do the error release */
+static void t3109_expired(void *data)
+{
+	struct gsm_lchan *lchan = data;
+
+	LOGP(DRSL, LOGL_ERROR,
+		"%s SACCH deactivation timeout.\n", gsm_lchan_name(lchan));
+	rsl_rf_chan_release(lchan, 1, SACCH_NONE);
 }
 
 #define GSM48_LEN2PLEN(a)	(((a) << 2) | 1)
@@ -1516,7 +1536,7 @@
 
 	if (rlm_cause[1] == RLL_CAUSE_T200_EXPIRED) {
 		osmo_counter_inc(msg->lchan->ts->trx->bts->network->stats.chan.rll_err);
-		return rsl_rf_chan_release(msg->lchan, 1);
+		return rsl_rf_chan_release(msg->lchan, 1, SACCH_DEACTIVATE);
 	}
 
 	return 0;
@@ -1544,8 +1564,8 @@
 	}
 
 
-
-	/* wait a bit to send the RF Channel Release */
+	/* Stop T3109 and wait for T3111 before re-using the channel */
+	osmo_timer_del(&lchan->T3109);
 	lchan->T3111.cb = t3111_expired;
 	lchan->T3111.data = lchan;
 	bts = lchan->ts->trx->bts;
@@ -2107,3 +2127,17 @@
 
 	return no_sapi;
 }
+
+int rsl_start_t3109(struct gsm_lchan *lchan)
+{
+	struct gsm_bts *bts = lchan->ts->trx->bts;
+
+	/* Disabled, mostly legacy code */
+	if (bts->network->T3109 == 0)
+		return -1;
+
+	lchan->T3109.cb = t3109_expired;
+	lchan->T3109.data = lchan;
+	osmo_timer_schedule(&lchan->T3109, bts->network->T3109, 0);
+	return 0;
+}