bsc_msc: Remove use_count from the subscriber connection

A channel will be released in case of
    * Errors via the clear_request callback...
    * no more transactions and operations are going on.

This means that if we do something without a transaction
the channel might be closed down right away. The bug fix
will be to create a transaction/operation.
diff --git a/openbsc/doc/channel_release.txt b/openbsc/doc/channel_release.txt
index bacf09c..a10e9d5 100644
--- a/openbsc/doc/channel_release.txt
+++ b/openbsc/doc/channel_release.txt
@@ -31,6 +31,8 @@
 
 == Implementation in OpenBSC ==
 
+THIS IS OUTDATED and will be updated...
+
 chan_alloc.c:lchan_auto_release()
 	* checks if use count still > 0 (abort)
 	* calls gsm48_send_rr_release()
diff --git a/openbsc/include/openbsc/bsc_api.h b/openbsc/include/openbsc/bsc_api.h
index e92da21..da942d2 100644
--- a/openbsc/include/openbsc/bsc_api.h
+++ b/openbsc/include/openbsc/bsc_api.h
@@ -28,5 +28,6 @@
 int gsm0808_submit_dtap(struct gsm_subscriber_connection *conn, struct msgb *msg, int link_id);
 int gsm0808_page(struct gsm_bts *bts, unsigned int page_group,
 		 unsigned int mi_len, uint8_t *mi, int chan_type);
+int gsm0808_clear(struct gsm_subscriber_connection *conn);
 
 #endif
diff --git a/openbsc/include/openbsc/chan_alloc.h b/openbsc/include/openbsc/chan_alloc.h
index bbbe536..3afcb3c 100644
--- a/openbsc/include/openbsc/chan_alloc.h
+++ b/openbsc/include/openbsc/chan_alloc.h
@@ -46,8 +46,8 @@
 void lchan_free(struct gsm_lchan *lchan);
 void lchan_reset(struct gsm_lchan *lchan);
 
-/* Consider releasing the channel */
-int lchan_auto_release(struct gsm_lchan *lchan);
+/* Release the given lchan */
+int lchan_release(struct gsm_lchan *lchan, int sach_deact, int reason);
 
 struct load_counter {
 	unsigned int total;
diff --git a/openbsc/include/openbsc/gsm_data.h b/openbsc/include/openbsc/gsm_data.h
index b1091cd..491cca1 100644
--- a/openbsc/include/openbsc/gsm_data.h
+++ b/openbsc/include/openbsc/gsm_data.h
@@ -120,26 +120,6 @@
 		     struct msgb *msg,
 		     void *data, void *param);
 
-/*
- * Use the channel. As side effect the lchannel recycle timer
- * will be started.
- */
-#define LCHAN_RELEASE_TIMEOUT 20, 0
-#define use_subscr_con(con) \
-	do {	(con)->use_count++; \
-		DEBUGP(DREF, "lchan (bts=%d,trx=%d,ts=%d,ch=%d) increases usage to: %d\n", \
-			(con)->lchan->ts->trx->bts->nr, (con)->lchan->ts->trx->nr, (con)->lchan->ts->nr, \
-			(con)->lchan->nr, (con)->use_count); \
-		bsc_schedule_timer(&(con)->release_timer, LCHAN_RELEASE_TIMEOUT); } while(0);
-
-#define put_subscr_con(con) \
-	do { (con)->use_count--; \
-		DEBUGP(DREF, "lchan (bts=%d,trx=%d,ts=%d,ch=%d) decreases usage to: %d\n", \
-			(con)->lchan->ts->trx->bts->nr, (con)->lchan->ts->trx->nr, (con)->lchan->ts->nr, \
-			(con)->lchan->nr, (con)->use_count); \
-	} while(0);
-
-
 /* Real authentication information containing Ki */
 enum gsm_auth_algo {
 	AUTH_ALGO_NONE,
@@ -239,22 +219,17 @@
 	/* To whom we are allocated at the moment */
 	struct gsm_subscriber *subscr;
 
-	/* Timer started to release the channel */
-	struct timer_list release_timer;
-
 	/*
 	 * Operations that have a state and might be pending
 	 */
 	struct gsm_loc_updating_operation *loc_operation;
 	struct gsm_security_operation *sec_operation;
 
-	/* use count. how many users use this channel */
-	unsigned int use_count;
-
 	/* Are we part of a special "silent" call */
 	int silent_call;
 
 	/* back pointers */
+	int in_release;
 	struct gsm_lchan *lchan;
 	struct gsm_bts *bts;
 };
diff --git a/openbsc/include/openbsc/osmo_msc.h b/openbsc/include/openbsc/osmo_msc.h
index d5d8917..beb3f5e 100644
--- a/openbsc/include/openbsc/osmo_msc.h
+++ b/openbsc/include/openbsc/osmo_msc.h
@@ -6,5 +6,6 @@
 #include "bsc_api.h"
 
 struct bsc_api *msc_bsc_api();
+void msc_release_connection(struct gsm_subscriber_connection *conn);
 
 #endif
diff --git a/openbsc/src/bsc_api.c b/openbsc/src/bsc_api.c
index 92fe661..46a3343 100644
--- a/openbsc/src/bsc_api.c
+++ b/openbsc/src/bsc_api.c
@@ -104,13 +104,20 @@
 
 		if (rc != BSC_API_CONN_POL_ACCEPT) {
 			subscr_con_free(lchan->conn);
-			lchan_auto_release(lchan);
+			lchan_release(lchan, 0, 0);
 		}
 	}
 
 	return 0;
 }
 
+int gsm0808_clear(struct gsm_subscriber_connection* conn)
+{
+	subscr_con_free(conn);
+	lchan_release(conn->lchan, 1, 0);
+	return 0;
+}
+
 static void send_sapi_reject(struct gsm_subscriber_connection *conn, int link_id)
 {
 	struct bsc_api *api;
@@ -155,12 +162,12 @@
 	if (!lchan || !lchan->conn)
 		return 0;
 
-
 	bsc = lchan->ts->trx->bts->network->bsc_api;
 	if (!bsc || !bsc->clear_request)
 		return 0;
 
 	bsc->clear_request(lchan->conn, 0);
+	subscr_con_free(lchan->conn);
 	return 0;
 }
 
diff --git a/openbsc/src/bsc_vty.c b/openbsc/src/bsc_vty.c
index 3215745..a308ec4 100644
--- a/openbsc/src/bsc_vty.c
+++ b/openbsc/src/bsc_vty.c
@@ -700,8 +700,8 @@
 		lchan->nr, lchan->ts->nr, lchan->ts->trx->nr,
 		lchan->ts->trx->bts->nr, gsm_lchant_name(lchan->type),
 		VTY_NEWLINE);
-	vty_out(vty, "  Use Count: %u, State: %s%s",
-		lchan->conn ? lchan->conn->use_count : -23,
+	vty_out(vty, "  Connection: %u, State: %s%s",
+		lchan->conn ? 1: 0,
 		gsm_lchans_name(lchan->state), VTY_NEWLINE);
 	vty_out(vty, "  BS Power: %u dBm, MS Power: %u dBm%s",
 		lchan->ts->trx->nominal_power - lchan->ts->trx->max_power_red
diff --git a/openbsc/src/chan_alloc.c b/openbsc/src/chan_alloc.c
index f5c4ec6..62d476a 100644
--- a/openbsc/src/chan_alloc.c
+++ b/openbsc/src/chan_alloc.c
@@ -35,8 +35,6 @@
 
 #include <osmocore/talloc.h>
 
-static void auto_release_channel(void *_lchan);
-
 static int ts_is_usable(struct gsm_bts_trx_ts *ts)
 {
 	/* FIXME: How does this behave for BS-11 ? */
@@ -296,18 +294,8 @@
 
 
 	if (lchan->conn) {
-		if (lchan->conn->subscr) {
-			subscr_put(lchan->conn->subscr);
-			lchan->conn->subscr = NULL;
-		}
-
 		/* We might kill an active channel... */
-		if (lchan->conn->use_count != 0) {
-			dispatch_signal(SS_LCHAN, S_LCHAN_UNEXPECTED_RELEASE, lchan);
-			lchan->conn->use_count = 0;
-		}
-		/* stop the timer */
-		bsc_del_timer(&lchan->conn->release_timer);
+		dispatch_signal(SS_LCHAN, S_LCHAN_UNEXPECTED_RELEASE, lchan);
 	}
 
 
@@ -334,6 +322,7 @@
 	dispatch_signal(SS_CHALLOC, S_CHALLOC_FREED, &sig);
 
 	if (lchan->conn) {
+		LOGP(DRLL, LOGL_ERROR, "the subscriber connection should be gone.\n");
 		subscr_con_free(lchan->conn);
 		lchan->conn = NULL;
 	}
@@ -364,40 +353,19 @@
 
 
 /* Consider releasing the channel now */
-int lchan_auto_release(struct gsm_lchan *lchan)
+int lchan_release(struct gsm_lchan *lchan, int sach_deact, int reason)
 {
-	if (!lchan->conn)
-		return 0;
-
-	if (lchan->conn->use_count > 0) {
-		return 0;
-	}
-
 	/* Assume we have GSM04.08 running and send a release */
-	if (lchan->conn->subscr) {
+	if (sach_deact) {
 		gsm48_send_rr_release(lchan);
 	}
 
-	/* spoofed? message */
-	if (lchan->conn->use_count < 0)
-		LOGP(DRLL, LOGL_ERROR, "Channel count is negative: %d\n",
-			lchan->conn->use_count);
-
 	DEBUGP(DRLL, "%s Recycling Channel\n", gsm_lchan_name(lchan));
 	rsl_lchan_set_state(lchan, LCHAN_S_REL_REQ);
 	rsl_release_request(lchan, 0, 0);
 	return 1;
 }
 
-/* Auto release the channel when the use count is zero */
-static void auto_release_channel(void *_lchan)
-{
-	struct gsm_lchan *lchan = _lchan;
-
-	if (!lchan_auto_release(lchan))
-		bsc_schedule_timer(&lchan->conn->release_timer, LCHAN_RELEASE_TIMEOUT);
-}
-
 static struct gsm_lchan* lchan_find(struct gsm_bts *bts, struct gsm_subscriber *subscr) {
 	struct gsm_bts_trx *trx;
 	int ts_no, lchan_no;
@@ -490,10 +458,6 @@
 	/* Configure the time and start it so it will be closed */
 	conn->lchan = lchan;
 	conn->bts = lchan->ts->trx->bts;
-	conn->release_timer.cb = auto_release_channel;
-	conn->release_timer.data = lchan;
-	bsc_schedule_timer(&conn->release_timer, LCHAN_RELEASE_TIMEOUT);
-
 	lchan->conn = conn;
 	return conn;
 }
@@ -507,6 +471,13 @@
 	if (!conn)
 		return;
 
+
+	if (conn->subscr) {
+		subscr_put(conn->subscr);
+		conn->subscr = NULL;
+	}
+
+
 	lchan = conn->lchan;
 	talloc_free(conn);
 
diff --git a/openbsc/src/gsm_04_08.c b/openbsc/src/gsm_04_08.c
index e807052..c1b438e 100644
--- a/openbsc/src/gsm_04_08.c
+++ b/openbsc/src/gsm_04_08.c
@@ -109,13 +109,11 @@
 
 	talloc_free(conn->sec_operation);
 	conn->sec_operation = NULL;
-	put_subscr_con(conn);
+	msc_release_connection(conn);
 }
 
 static void allocate_security_operation(struct gsm_subscriber_connection *conn)
 {
-	use_subscr_con(conn)
-
 	conn->sec_operation = talloc_zero(tall_authciphop_ctx,
 	                                  struct gsm_security_operation);
 }
@@ -222,12 +220,13 @@
 	bsc_del_timer(&conn->loc_operation->updating_timer);
 	talloc_free(conn->loc_operation);
 	conn->loc_operation = 0;
-	put_subscr_con(conn);
+	msc_release_connection(conn);
 }
 
 static void allocate_loc_updating_req(struct gsm_subscriber_connection *conn)
 {
-	use_subscr_con(conn)
+	if (conn->loc_operation)
+		LOGP(DMM, LOGL_ERROR, "Connection already had operation.\n");
 	release_loc_updating_req(conn);
 
 	conn->loc_operation = talloc_zero(tall_locop_ctx,
@@ -263,7 +262,6 @@
 
 			/* try to close channel ASAP */
 			release_loc_updating_req(conn);
-			lchan_auto_release(conn->lchan);
 			break;
 
 		default:
@@ -285,6 +283,10 @@
 void gsm0408_clear_request(struct gsm_subscriber_connection* conn, uint32_t cause)
 {
 	struct gsm_trans *trans, *temp;
+
+	/* avoid someone issuing a clear */
+	conn->in_release = 1;
+
 	/*
 	 * Cancel any outstanding location updating request
 	 * operation taking place on the subscriber connection.
@@ -424,7 +426,6 @@
 
 	gsm0408_loc_upd_rej(conn, bts->network->reject_cause);
 	release_loc_updating_req(conn);
-	lchan_auto_release(lchan);
 }
 
 static void schedule_reject(struct gsm_subscriber_connection *conn)
@@ -710,7 +711,6 @@
 
 	DEBUGP(DMM, "-> CM SERVICE Reject cause: %d\n", value);
 	msg->lchan = conn->lchan;
-	use_subscr_con(conn);
 	return gsm48_conn_sendmsg(msg, conn, NULL);
 }
 
@@ -871,8 +871,6 @@
 	 * imagine an IMSI DETACH happening during an active call! */
 
 	/* subscriber is detached: should we release lchan? */
-	lchan_auto_release(msg->lchan);
-
 	return 0;
 }
 
@@ -1385,7 +1383,6 @@
 			/* Assign lchan */
 			if (!transt->conn) {
 				transt->conn = conn;
-				use_subscr_con(transt->conn);
 			}
 			/* send SETUP request to called party */
 			gsm48_cc_tx_setup(transt, &transt->cc.msg);
@@ -2289,9 +2286,6 @@
 		case GSM_CSTATE_RELEASE_REQ:
 			rc = mncc_recvmsg(trans->subscr->net, trans,
 					  MNCC_REL_CNF, &rel);
-			/* FIXME: in case of multiple calls, we can't simply
-			 * hang up here ! */
-			lchan_auto_release(msg->lchan);
 			break;
 		default:
 			rc = mncc_recvmsg(trans->subscr->net, trans,
@@ -2924,7 +2918,6 @@
 		}
 		/* Assign lchan */
 		trans->conn = conn;
-		use_subscr_con(trans->conn);
 		subscr_put(subscr);
 	}
 
@@ -3065,7 +3058,6 @@
 		}
 		/* Assign transaction */
 		trans->conn = conn;
-		use_subscr_con(trans->conn);
 	}
 
 	/* find function for current state and message */
diff --git a/openbsc/src/gsm_04_11.c b/openbsc/src/gsm_04_11.c
index d4df989..6cce57d 100644
--- a/openbsc/src/gsm_04_11.c
+++ b/openbsc/src/gsm_04_11.c
@@ -943,7 +943,6 @@
 		trans->sms.link_id = UM_SAPI_SMS;
 
 		trans->conn = conn;
-		use_subscr_con(trans->conn);
 	}
 
 	switch(msg_type) {
@@ -1066,7 +1065,6 @@
 	trans->sms.link_id = UM_SAPI_SMS;	/* FIXME: main or SACCH ? */
 
 	trans->conn = conn;
-	use_subscr_con(trans->conn);
 
 	/* Hardcode SMSC Originating Address for now */
 	data = (u_int8_t *)msgb_put(msg, 8);
@@ -1122,7 +1120,6 @@
 
 	switch (event) {
 	case GSM_PAGING_SUCCEEDED:
-		use_subscr_con(conn);
 		gsm411_send_sms(conn, sms);
 		break;
 	case GSM_PAGING_EXPIRED:
@@ -1147,7 +1144,6 @@
 	 * if yes, send the SMS this way */
 	conn = connection_for_subscr(subscr);
 	if (conn) {
-		use_subscr_con(conn);
 		return gsm411_send_sms(conn, sms);
 	}
 
@@ -1174,7 +1170,6 @@
 		sms = db_sms_get_unsent_for_subscr(subscr);
 		if (!sms)
 			break;
-		use_subscr_con(conn);
 		gsm411_send_sms(conn, sms);
 		break;
 	default:
diff --git a/openbsc/src/gsm_subscriber_base.c b/openbsc/src/gsm_subscriber_base.c
index 264f128..50e6865 100644
--- a/openbsc/src/gsm_subscriber_base.c
+++ b/openbsc/src/gsm_subscriber_base.c
@@ -205,8 +205,6 @@
 	 * will listen to the paging requests before we timeout
 	 */
 
-	put_subscr_con(conn);
-
 	if (conn->subscr && !llist_empty(&conn->subscr->requests))
 		subscr_send_paging_request(conn->subscr);
 }
diff --git a/openbsc/src/handover_logic.c b/openbsc/src/handover_logic.c
index b75dc98..baf03b3 100644
--- a/openbsc/src/handover_logic.c
+++ b/openbsc/src/handover_logic.c
@@ -231,7 +231,7 @@
 	trans_lchan_change(ho->old_lchan->conn, new_lchan->conn);
 
 	rsl_lchan_set_state(ho->old_lchan, LCHAN_S_INACTIVE);
-	lchan_auto_release(ho->old_lchan);
+	lchan_release(ho->old_lchan, 0, 1);
 
 	/* do something to re-route the actual speech frames ! */
 
@@ -259,7 +259,6 @@
 	bsc_del_timer(&ho->T3103);
 	llist_del(&ho->list);
 	conn = ho->new_lchan->conn;
-	put_subscr_con(conn);
 	talloc_free(ho);
 
 	return 0;
diff --git a/openbsc/src/osmo_msc.c b/openbsc/src/osmo_msc.c
index 569634d..6a94e7a 100644
--- a/openbsc/src/osmo_msc.c
+++ b/openbsc/src/osmo_msc.c
@@ -24,6 +24,7 @@
 
 #include <openbsc/bsc_api.h>
 #include <openbsc/debug.h>
+#include <openbsc/transaction.h>
 
 #include <openbsc/gsm_04_11.h>
 
@@ -64,3 +65,30 @@
 struct bsc_api *msc_bsc_api() {
 	return &msc_handler;
 }
+
+/* lchan release handling */
+void msc_release_connection(struct gsm_subscriber_connection *conn)
+{
+	struct gsm_trans *trans;
+
+	/* skip when we are in release, e.g. due an error */
+	if (conn->in_release)
+		return;
+
+	/* skip releasing of silent calls as they have no transaction */
+	if (conn->silent_call)
+		return;
+
+	/* check if there is a pending operation */
+	if (conn->loc_operation || conn->sec_operation)
+		return;
+
+	llist_for_each_entry(trans, &conn->bts->network->trans_list, entry) {
+		if (trans->conn == conn)
+			return;
+	}
+
+	/* no more connections, asking to release the channel */
+	conn->in_release = 1;
+	gsm0808_clear(conn);
+}
diff --git a/openbsc/src/silent_call.c b/openbsc/src/silent_call.c
index 397a3e4..7ba451b 100644
--- a/openbsc/src/silent_call.c
+++ b/openbsc/src/silent_call.c
@@ -57,7 +57,6 @@
 		conn->silent_call = 1;
 		/* increment lchan reference count */
 		dispatch_signal(SS_SCALL, S_SCALL_SUCCESS, &sigdata);
-		use_subscr_con(conn);
 		break;
 	case GSM_PAGING_EXPIRED:
 		DEBUGP(DSMS, "expired\n");
@@ -135,7 +134,8 @@
 	if (!conn->silent_call)
 		return -EINVAL;
 
-	put_subscr_con(conn);
+	conn->silent_call = 0;
+	msc_release_connection(conn);
 
 	return 0;
 }
diff --git a/openbsc/src/transaction.c b/openbsc/src/transaction.c
index c7478d6..ffabdd3 100644
--- a/openbsc/src/transaction.c
+++ b/openbsc/src/transaction.c
@@ -95,9 +95,6 @@
 		break;
 	}
 
-	if (trans->conn)
-		put_subscr_con(trans->conn);
-
 	if (!trans->conn && trans->subscr && trans->subscr->net) {
 		/* Stop paging on all bts' */
 		paging_request_stop(NULL, trans->subscr, NULL);
@@ -108,6 +105,10 @@
 
 	llist_del(&trans->entry);
 
+	if (trans->conn)
+		msc_release_connection(trans->conn);
+
+
 	talloc_free(trans);
 }
 
@@ -155,15 +156,17 @@
 	struct gsm_trans *trans;
 	int num = 0;
 
+	if (conn_old == conn_new) {
+		LOGP(DCC, LOGL_ERROR, "Exchanging transaction with itself.\n");
+		return;
+	}
+
 	llist_for_each_entry(trans, &net->trans_list, entry) {
 		if (trans->conn == conn_old) {
+			msc_release_connection(conn_old);
 
-			/* drop old channel use count */
-			put_subscr_con(conn_old);
 			/* assign new channel */
 			trans->conn = conn_new;
-			/* bump new channel use count */
-			use_subscr_con(conn_new);
 			num++;
 		}
 	}