From 200b25e256bd8f2ab901846b4145101833878759 Mon Sep 17 00:00:00 2001
From: Moises Silva <moy@sangoma.com>
Date: Thu, 14 Jan 2010 05:23:40 +0000
Subject: [PATCH] added new boost msg EVENT_CALL_RELEASED to fix race condition
 on call hangup

git-svn-id: http://svn.openzap.org/svn/openzap/branches/sangoma_boost@960 a93c3328-9c30-0410-af19-c9cd2b2d52af
---
 .../ozmod_sangoma_boost/ozmod_sangoma_boost.c | 64 +++++++++++++++----
 .../sangoma_boost_client.c                    | 15 +++--
 .../src/ozmod/ozmod_sangoma_boost/sigboost.h  | 13 ++++
 libs/freetdm/src/zap_io.c                     | 33 ++++++----
 4 files changed, 92 insertions(+), 33 deletions(-)

diff --git a/libs/freetdm/src/ozmod/ozmod_sangoma_boost/ozmod_sangoma_boost.c b/libs/freetdm/src/ozmod/ozmod_sangoma_boost/ozmod_sangoma_boost.c
index 4d8581f806..cc85b0bfee 100644
--- a/libs/freetdm/src/ozmod/ozmod_sangoma_boost/ozmod_sangoma_boost.c
+++ b/libs/freetdm/src/ozmod/ozmod_sangoma_boost/ozmod_sangoma_boost.c
@@ -451,8 +451,8 @@ static ZIO_CHANNEL_REQUEST_FUNCTION(sangoma_boost_channel_request)
 		nack_map[r] = 1;
 		if (sangoma_boost_data->sigmod) {
 			sangomabc_exec_command(&sangoma_boost_data->mcon,
-								(*zchan)->physical_span_id,
-								(*zchan)->physical_chan_id,
+								BOOST_SPAN((*zchan)),
+								BOOST_CHAN((*zchan)),
 								r,
 								SIGBOOST_EVENT_CALL_START_NACK,
 								0);
@@ -616,10 +616,22 @@ static void handle_call_done(zap_span_t *span, sangomabc_connection_t *mcon, san
 {
 	zap_channel_t *zchan;
 	int r = 0;
-
+	
 	if ((zchan = find_zchan(span, event, 1))) {
+		zap_sangoma_boost_data_t *sangoma_boost_data = zchan->span->signal_data;
 		zap_mutex_lock(zchan->mutex);
 
+		if (sangoma_boost_data->sigmod) {
+			/* not really completely done, but if we ever get an incoming call before moving to HANGUP_COMPLETE
+			 * handle_incoming_call() will take care of moving the state machine to release the channel */
+			sangomabc_exec_command(&sangoma_boost_data->mcon,
+								BOOST_SPAN(zchan),
+								BOOST_CHAN(zchan),
+								0,
+								SIGBOOST_EVENT_CALL_RELEASED,
+								0);
+		}
+
 		if (zchan->state == ZAP_CHANNEL_STATE_DOWN || zchan->state == ZAP_CHANNEL_STATE_HANGUP_COMPLETE) {
 			goto done;
 		}
@@ -729,6 +741,18 @@ static void handle_call_start_nack(zap_span_t *span, sangomabc_connection_t *mco
 #endif
 }
 
+static void handle_call_released(zap_span_t *span, sangomabc_connection_t *mcon, sangomabc_short_event_t *event)
+{
+	zap_channel_t *zchan;
+	
+	if ((zchan = find_zchan(span, event, 1))) {
+		zap_log(ZAP_LOG_DEBUG, "Releasing completely chan s%dc%d\n", event->span, event->chan);
+		zap_channel_done(zchan);
+	} else {
+		zap_log(ZAP_LOG_ERROR, "Odd, We could not find chan: s%dc%d to release the call completely!!\n", event->span, event->chan);
+	}
+}
+
 /**
  * \brief Handler for call stop event
  * \param span Span where event was fired
@@ -773,10 +797,10 @@ static void handle_call_stop(zap_span_t *span, sangomabc_connection_t *mcon, san
 		if (r) {
 			return;
 		}
-	} /* else we have to do it ourselves.... */
-
-	zap_log(ZAP_LOG_WARNING, "We could not find chan: s%dc%d\n", event->span, event->chan);
-	release_request_id_span_chan(event->span, event->chan);
+	} else { /* we have to do it ourselves.... */
+		zap_log(ZAP_LOG_ERROR, "Odd, We could not find chan: s%dc%d\n", event->span, event->chan);
+		release_request_id_span_chan(event->span, event->chan);
+	}
 }
 
 /**
@@ -829,6 +853,9 @@ static void handle_call_start(zap_span_t *span, sangomabc_connection_t *mcon, sa
 			zap_log(ZAP_LOG_CRIT, "START CANT FIND CHAN %d:%d AT ALL\n", event->span+1,event->chan+1);
 			goto error;
 		}
+		/* this handles race conditions where state handlers are still pending to be executed for finished calls
+		   but an incoming call arrives first, we must complete the channel states and then try again to get the 
+		   zap channel */
 		advance_chan_states(zchan);
 		if (!(zchan = find_zchan(span, (sangomabc_short_event_t*)event, 0))) {
 			zap_log(ZAP_LOG_CRIT, "START CANT FIND CHAN %d:%d EVEN AFTER STATE ADVANCE\n", event->span+1,event->chan+1);
@@ -996,6 +1023,9 @@ static int parse_sangoma_event(zap_span_t *span, sangomabc_connection_t *mcon, s
     case SIGBOOST_EVENT_CALL_STOPPED:
 		handle_call_stop(span, mcon, event);
 		break;
+    case SIGBOOST_EVENT_CALL_RELEASED:
+		handle_call_released(span, mcon, event);
+		break;
     case SIGBOOST_EVENT_CALL_START_ACK:
 		handle_call_start_ack(mcon, event);
 		break;
@@ -1070,6 +1100,8 @@ static __inline__ void state_advance(zap_channel_t *zchan)
 	switch (zchan->state) {
 	case ZAP_CHANNEL_STATE_DOWN:
 		{
+			int call_stopped_ack_sent = 0;
+			zap_sangoma_boost_data_t *sangoma_boost_data = zchan->span->signal_data;
 			if (zchan->extra_id) {
 				zchan->extra_id = 0;
 			}
@@ -1083,24 +1115,32 @@ static __inline__ void state_advance(zap_channel_t *zchan)
 
 				if (zchan->call_data && ((uint32_t)(intptr_t)zchan->call_data == SIGBOOST_EVENT_CALL_START_NACK)) {
 					sangomabc_exec_command(mcon,
-									zchan->physical_span_id-1,
-									zchan->physical_chan_id-1,
+									BOOST_SPAN(zchan),
+									BOOST_CHAN(zchan),
 									0,
 									SIGBOOST_EVENT_CALL_START_NACK_ACK,
 									0);
 					
 				} else {
+					/* we got a call stop msg, time to reply with call stopped ack  */
 					sangomabc_exec_command(mcon,
-									zchan->physical_span_id-1,
-									zchan->physical_chan_id-1,
+									BOOST_SPAN(zchan),
+									BOOST_CHAN(zchan),
 									0,
 									SIGBOOST_EVENT_CALL_STOPPED_ACK,
 									0);
+					call_stopped_ack_sent = 1;
 				}
 			}
 			zchan->sflags = 0;
 			zchan->call_data = NULL;
-			zap_channel_done(zchan);
+			if (sangoma_boost_data->sigmod && call_stopped_ack_sent) {
+				/* we dont want to call zap_channel_done just yet until call released is received */
+				zap_log(ZAP_LOG_DEBUG, "Waiting for call release confirmation before declaring chan %d:%d as available \n", 
+						zchan->span_id, zchan->chan_id);
+			} else {
+				zap_channel_done(zchan);
+			}
 		}
 		break;
 	case ZAP_CHANNEL_STATE_PROGRESS_MEDIA:
diff --git a/libs/freetdm/src/ozmod/ozmod_sangoma_boost/sangoma_boost_client.c b/libs/freetdm/src/ozmod/ozmod_sangoma_boost/sangoma_boost_client.c
index 9e332d1504..ebd27cccac 100644
--- a/libs/freetdm/src/ozmod/ozmod_sangoma_boost/sangoma_boost_client.c
+++ b/libs/freetdm/src/ozmod/ozmod_sangoma_boost/sangoma_boost_client.c
@@ -60,6 +60,7 @@ static struct sangomabc_map sangomabc_table[] = {
 	{SIGBOOST_EVENT_CALL_ANSWERED, "CALL_ANSWERED"}, 
 	{SIGBOOST_EVENT_CALL_STOPPED, "CALL_STOPPED"}, 
 	{SIGBOOST_EVENT_CALL_STOPPED_ACK, "CALL_STOPPED_ACK"}, 
+	{SIGBOOST_EVENT_CALL_RELEASED, "CALL_RELEASED"}, 
 	{SIGBOOST_EVENT_SYSTEM_RESTART, "SYSTEM_RESTART"}, 
 	{SIGBOOST_EVENT_SYSTEM_RESTART_ACK, "SYSTEM_RESTART_ACK"}, 
 	{SIGBOOST_EVENT_HEARTBEAT, "HEARTBEAT"}, 
@@ -457,7 +458,13 @@ int __sangomabc_connection_write(sangomabc_connection_t *mcon, sangomabc_event_t
 		event->fseqno = mcon->txseq++;
 	}
 	event->bseqno = mcon->rxseq;
-    event->version = SIGBOOST_VERSION; 
+    	event->version = SIGBOOST_VERSION; 
+
+	if (boost_full_event(event->event_id)) {
+		sangomabc_print_event_call(mcon, event, 0, 1, file, func, line);
+	} else {
+		sangomabc_print_event_short(mcon, (sangomabc_short_event_t*)event, 0, 1, file, func, line);
+	}
 
 	if (mcon->sigmod) {
 		mcon->sigmod->write_msg(mcon->span, event, event_size);
@@ -473,12 +480,6 @@ int __sangomabc_connection_write(sangomabc_connection_t *mcon, sangomabc_event_t
 
 	zap_assert_return(err == event_size, -1, "Failed to send the boost message completely!");
 
-	if (boost_full_event(event->event_id)) {
-		sangomabc_print_event_call(mcon, event, 0, 1, file, func, line);
-	} else {
-		sangomabc_print_event_short(mcon, (sangomabc_short_event_t*)event, 0, 1, file, func, line);
-	}
-
 	return err;
 }
 
diff --git a/libs/freetdm/src/ozmod/ozmod_sangoma_boost/sigboost.h b/libs/freetdm/src/ozmod/ozmod_sangoma_boost/sigboost.h
index 9e97e9014d..b9cbf110f6 100644
--- a/libs/freetdm/src/ozmod/ozmod_sangoma_boost/sigboost.h
+++ b/libs/freetdm/src/ozmod/ozmod_sangoma_boost/sigboost.h
@@ -28,6 +28,19 @@ enum	e_sigboost_event_id_values
 	SIGBOOST_EVENT_CALL_ANSWERED			= 0x84, /*132*/
 	SIGBOOST_EVENT_CALL_STOPPED			= 0x85, /*133*/
 	SIGBOOST_EVENT_CALL_STOPPED_ACK			= 0x86, /*134*/
+	/* CALL_RELEASED is aimed to fix a race condition that became obvious
+	 * when the boost socket was replaced by direct function calls
+	 * and the channel hunting was moved to openzap, the problem is
+	 * we can get CALL_STOPPED msg and reply with CALL_STOPPED_ACK
+	 * but the signaling module will still (in PRI) send RELEASE and
+	 * wait for RELEASE_COMPLETE from the isdn network before
+	 * marking the channel as available, therefore openzap should
+	 * also not mark the channel as available until CALL_RELEASED
+	 * is received, for socket mode we can continue working as usual
+	 * with CALL_STOPPED being the last step because the hunting is
+	 * done in the signaling module.
+	 * */
+	SIGBOOST_EVENT_CALL_RELEASED			= 0x51, /* 81 */
 	SIGBOOST_EVENT_SYSTEM_RESTART			= 0x87, /*135*/
 	SIGBOOST_EVENT_SYSTEM_RESTART_ACK		= 0x88, /*136*/
 	SIGBOOST_EVENT_CALL_PROGRESS            = 0x50, /*decimal  80*/
diff --git a/libs/freetdm/src/zap_io.c b/libs/freetdm/src/zap_io.c
index 445fb7094f..6a39c9fc70 100644
--- a/libs/freetdm/src/zap_io.c
+++ b/libs/freetdm/src/zap_io.c
@@ -1507,22 +1507,24 @@ OZ_DECLARE(zap_status_t) zap_channel_done(zap_channel_t *zchan)
 {
 	assert(zchan != NULL);
 
+	zap_mutex_lock(zchan->mutex);
+
 	memset(&zchan->caller_data, 0, sizeof(zchan->caller_data));
 
-	zap_clear_flag_locked(zchan, ZAP_CHANNEL_INUSE);
-	zap_clear_flag_locked(zchan, ZAP_CHANNEL_OUTBOUND);
-	zap_clear_flag_locked(zchan, ZAP_CHANNEL_WINK);
-	zap_clear_flag_locked(zchan, ZAP_CHANNEL_FLASH);
-	zap_clear_flag_locked(zchan, ZAP_CHANNEL_STATE_CHANGE);
-	zap_clear_flag_locked(zchan, ZAP_CHANNEL_HOLD);
-	zap_clear_flag_locked(zchan, ZAP_CHANNEL_OFFHOOK);
-	zap_clear_flag_locked(zchan, ZAP_CHANNEL_RINGING);
-	zap_clear_flag_locked(zchan, ZAP_CHANNEL_PROGRESS_DETECT);
-	zap_clear_flag_locked(zchan, ZAP_CHANNEL_CALLERID_DETECT);
-	zap_clear_flag_locked(zchan, ZAP_CHANNEL_3WAY);
-	zap_clear_flag_locked(zchan, ZAP_CHANNEL_PROGRESS);
-	zap_clear_flag_locked(zchan, ZAP_CHANNEL_MEDIA);
-	zap_clear_flag_locked(zchan, ZAP_CHANNEL_ANSWERED);
+	zap_clear_flag(zchan, ZAP_CHANNEL_INUSE);
+	zap_clear_flag(zchan, ZAP_CHANNEL_OUTBOUND);
+	zap_clear_flag(zchan, ZAP_CHANNEL_WINK);
+	zap_clear_flag(zchan, ZAP_CHANNEL_FLASH);
+	zap_clear_flag(zchan, ZAP_CHANNEL_STATE_CHANGE);
+	zap_clear_flag(zchan, ZAP_CHANNEL_HOLD);
+	zap_clear_flag(zchan, ZAP_CHANNEL_OFFHOOK);
+	zap_clear_flag(zchan, ZAP_CHANNEL_RINGING);
+	zap_clear_flag(zchan, ZAP_CHANNEL_PROGRESS_DETECT);
+	zap_clear_flag(zchan, ZAP_CHANNEL_CALLERID_DETECT);
+	zap_clear_flag(zchan, ZAP_CHANNEL_3WAY);
+	zap_clear_flag(zchan, ZAP_CHANNEL_PROGRESS);
+	zap_clear_flag(zchan, ZAP_CHANNEL_MEDIA);
+	zap_clear_flag(zchan, ZAP_CHANNEL_ANSWERED);
 	zap_mutex_lock(zchan->pre_buffer_mutex);
 	zap_buffer_destroy(&zchan->pre_buffer);
 	zchan->pre_buffer_size = 0;
@@ -1530,8 +1532,11 @@ OZ_DECLARE(zap_status_t) zap_channel_done(zap_channel_t *zchan)
 
 	zchan->init_state = ZAP_CHANNEL_STATE_DOWN;
 	zchan->state = ZAP_CHANNEL_STATE_DOWN;
+
 	zap_log(ZAP_LOG_DEBUG, "channel done %u:%u\n", zchan->span_id, zchan->chan_id);
 
+	zap_mutex_unlock(zchan->mutex);
+
 	return ZAP_SUCCESS;
 }