From 9d45690006947148913279f83d4d92e1fb4783bc Mon Sep 17 00:00:00 2001
From: Moises Silva <moy@sangoma.com>
Date: Thu, 20 May 2010 11:43:40 -0400
Subject: [PATCH] freetdm: add pvt data to freetdm channels          fix fxs
 features

---
 libs/freetdm/mod_freetdm/mod_freetdm.c        | 70 +++++++++++--------
 libs/freetdm/src/ftdm_io.c                    | 28 ++++----
 .../src/ftmod/ftmod_analog/ftmod_analog.c     |  4 +-
 libs/freetdm/src/include/freetdm.h            | 18 +++++
 libs/freetdm/src/include/private/ftdm_core.h  |  1 +
 5 files changed, 76 insertions(+), 45 deletions(-)

diff --git a/libs/freetdm/mod_freetdm/mod_freetdm.c b/libs/freetdm/mod_freetdm/mod_freetdm.c
index 57dd2393c6..fd36deb611 100644
--- a/libs/freetdm/mod_freetdm/mod_freetdm.c
+++ b/libs/freetdm/mod_freetdm/mod_freetdm.c
@@ -55,19 +55,6 @@ typedef enum {
 	ANALOG_OPTION_CALL_SWAP = (1 << 1)
 } analog_option_t;
 
-struct span_config {
-	ftdm_span_t *span;
-	char dialplan[80];
-	char context[80];
-	char dial_regex[256];
-	char fail_dial_regex[256];
-	char hold_music[256];
-	char type[256];	
-	analog_option_t analog_options;
-};
-
-static struct span_config SPAN_CONFIG[FTDM_MAX_SPANS_INTERFACE] = {{0}};
-
 typedef enum {
 	TFLAG_IO = (1 << 0),
 	TFLAG_DTMF = (1 << 1),
@@ -95,6 +82,7 @@ static struct {
 	switch_hash_t *ss7_configs;
 } globals;
 
+/* private data attached to each fs session */
 struct private_object {
 	unsigned int flags;
 	switch_codec_t read_codec;
@@ -114,8 +102,26 @@ struct private_object {
 	uint32_t wr_error;
 };
 
+/* private data attached to FTDM channels (only FXS for now) */
+typedef struct chan_pvt {
+	unsigned int flags;
+} chan_pvt_t;
+
 typedef struct private_object private_t;
 
+struct span_config {
+	ftdm_span_t *span;
+	char dialplan[80];
+	char context[80];
+	char dial_regex[256];
+	char fail_dial_regex[256];
+	char hold_music[256];
+	char type[256];	
+	analog_option_t analog_options;
+	chan_pvt_t pvts[FTDM_MAX_CHANNELS_SPAN];
+};
+
+static struct span_config SPAN_CONFIG[FTDM_MAX_SPANS_INTERFACE] = {{0}};
 
 static switch_status_t channel_on_init(switch_core_session_t *session);
 static switch_status_t channel_on_hangup(switch_core_session_t *session);
@@ -873,17 +879,7 @@ static switch_status_t channel_receive_message_fxo(switch_core_session_t *sessio
 	switch (msg->message_id) {
 	case SWITCH_MESSAGE_INDICATE_PROGRESS:
 	case SWITCH_MESSAGE_INDICATE_ANSWER:
-#if 0
-		if (switch_channel_test_flag(channel, CF_OUTBOUND)) {
-			ftdm_set_flag_locked(tech_pvt->ftdmchan, FTDM_CHANNEL_ANSWERED);
-			ftdm_set_flag_locked(tech_pvt->ftdmchan, FTDM_CHANNEL_PROGRESS);
-			ftdm_set_flag_locked(tech_pvt->ftdmchan, FTDM_CHANNEL_MEDIA);
-		} else {
-			ftdm_set_state_locked(tech_pvt->ftdmchan, FTDM_CHANNEL_STATE_UP);
-		}
-#else
 		ftdm_channel_call_answer(tech_pvt->ftdmchan);
-#endif
 		break;
 	default:
 		break;
@@ -1514,7 +1510,7 @@ static FIO_SIGNAL_CB_FUNCTION(on_fxs_signal)
 	spanid = ftdm_channel_get_span_id(sigmsg->channel);
 	tokencount = ftdm_channel_get_token_count(sigmsg->channel);
 
-    ftdm_log(FTDM_LOG_DEBUG, "got FXS sig [%s]\n", ftdm_signal_event2str(sigmsg->event_id));
+	ftdm_log(FTDM_LOG_DEBUG, "got FXS sig [%s]\n", ftdm_signal_event2str(sigmsg->event_id));
 
     switch(sigmsg->event_id) {
     case FTDM_SIGEVENT_UP:
@@ -1621,6 +1617,12 @@ static FIO_SIGNAL_CB_FUNCTION(on_fxs_signal)
 		break;
     case FTDM_SIGEVENT_FLASH:
 		{
+			chan_pvt_t *chanpvt = ftdm_channel_get_private(sigmsg->channel);
+			if (!chanpvt) {
+				ftdm_log(FTDM_LOG_ERROR, "%d:%d has no private data, can't handle FXS features! (this is a bug)\n",
+						chanid, spanid);
+				break;
+			}
 			if (ftdm_channel_call_check_hold(sigmsg->channel) && tokencount == 1) {
 				switch_core_session_t *session;
 				if ((session = ftdm_channel_get_session(sigmsg->channel, 0))) {
@@ -1636,10 +1638,9 @@ static FIO_SIGNAL_CB_FUNCTION(on_fxs_signal)
 					switch_clear_flag_locked(tech_pvt, TFLAG_HOLD);
 					switch_core_session_rwunlock(session);
 				}
-#if 0
 			} else if (tokencount == 2 && (SPAN_CONFIG[sigmsg->span_id].analog_options & ANALOG_OPTION_3WAY)) {
-				if (ftdm_test_flag(sigmsg->channel, FTDM_CHANNEL_3WAY)) {
-					ftdm_clear_flag(sigmsg->channel, FTDM_CHANNEL_3WAY);
+				if (switch_test_flag(chanpvt, ANALOG_OPTION_3WAY)) {
+					switch_clear_flag(chanpvt, ANALOG_OPTION_3WAY);
 					if ((session = ftdm_channel_get_session(sigmsg->channel, 1))) {
 						channel = switch_core_session_get_channel(session);
 						switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING);
@@ -1649,8 +1650,8 @@ static FIO_SIGNAL_CB_FUNCTION(on_fxs_signal)
 					cycle_foreground(sigmsg->channel, 1, NULL);
 				} else {
 					char *cmd;
-					cmd = switch_mprintf("three_way::%s", sigmsg->channel->tokens[0]);
-					ftdm_set_flag(sigmsg->channel, FTDM_CHANNEL_3WAY);
+					cmd = switch_mprintf("three_way::%s", ftdm_channel_get_token(sigmsg->channel, 0));
+					switch_set_flag(chanpvt, ANALOG_OPTION_3WAY);
 					cycle_foreground(sigmsg->channel, 1, cmd);
 					free(cmd);
 				}
@@ -1661,7 +1662,6 @@ static FIO_SIGNAL_CB_FUNCTION(on_fxs_signal)
 				if (tokencount == 1) {
 					ftdm_channel_call_hold(sigmsg->channel);
 				}
-#endif
 			}
 			
 		}
@@ -2160,6 +2160,8 @@ static switch_status_t load_config(void)
 	ftdm_span_t *boost_span = NULL;
 	unsigned boosti = 0;
 	unsigned int i = 0;
+	ftdm_channel_t *fchan = NULL;
+	unsigned int chancount = 0;
 
 	memset(boost_spans, 0, sizeof(boost_spans));
 	memset(&globals, 0, sizeof(globals));
@@ -2366,7 +2368,7 @@ static switch_status_t load_config(void)
 								   "hotline", hotline,
 								   "enable_callerid", enable_callerid,
 								   FTDM_TAG_END) != FTDM_SUCCESS) {
-				ftdm_log(FTDM_LOG_ERROR, "Error starting FreeTDM span %d\n", span_id);
+				ftdm_log(FTDM_LOG_ERROR, "Error configuring FreeTDM analog span %s\n", ftdm_span_get_name(span));
 				continue;
 			}
 
@@ -2375,6 +2377,12 @@ static switch_status_t load_config(void)
 			switch_set_string(SPAN_CONFIG[span_id].dialplan, dialplan);
 			SPAN_CONFIG[span_id].analog_options = analog_options | globals.analog_options;
 			
+			chancount = ftdm_span_get_chan_count(span);
+			for (i = 1; i <= chancount; i++) {
+				fchan = ftdm_span_get_channel(span, i);
+				ftdm_channel_set_private(fchan, &SPAN_CONFIG[span_id].pvts[i]);
+			}
+			
 			if (dial_regex) {
 				switch_set_string(SPAN_CONFIG[span_id].dial_regex, dial_regex);
 			}
diff --git a/libs/freetdm/src/ftdm_io.c b/libs/freetdm/src/ftdm_io.c
index cddd4849f2..6afa24da62 100644
--- a/libs/freetdm/src/ftdm_io.c
+++ b/libs/freetdm/src/ftdm_io.c
@@ -1002,6 +1002,16 @@ FT_DECLARE(void) ftdm_channel_replace_token(ftdm_channel_t *ftdmchan, const char
 	}
 }
 
+FT_DECLARE(void) ftdm_channel_set_private(ftdm_channel_t *ftdmchan, void *pvt)
+{
+	ftdmchan->user_private = pvt;
+}
+
+FT_DECLARE(void *) ftdm_channel_get_private(const ftdm_channel_t *ftdmchan)
+{
+	return ftdmchan->user_private;
+}
+
 FT_DECLARE(uint32_t) ftdm_channel_get_token_count(const ftdm_channel_t *ftdmchan)
 {
 	uint32_t count;
@@ -1747,7 +1757,7 @@ FT_DECLARE(ftdm_bool_t) ftdm_channel_call_check_hold(const ftdm_channel_t *ftdmc
 {
 	ftdm_bool_t condition;
 	ftdm_channel_lock(ftdmchan);
-	condition = ftdm_test_flag(ftdmchan, FTDM_CHANNEL_HOLD);
+	condition = ftdm_test_flag(ftdmchan, FTDM_CHANNEL_HOLD) ? FTDM_TRUE : FTDM_FALSE;
 	ftdm_channel_unlock(ftdmchan);
 	return condition;
 }
@@ -1801,7 +1811,7 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_hold(const char *file, const char *
 {
 	ftdm_channel_lock(ftdmchan);
 	ftdm_set_flag(ftdmchan, FTDM_CHANNEL_HOLD);
-	ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_DIALTONE, 1);
+	ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_DIALTONE, 0);
 	ftdm_channel_unlock(ftdmchan);
 	return FTDM_SUCCESS;
 }
@@ -1809,9 +1819,7 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_hold(const char *file, const char *
 FT_DECLARE(ftdm_status_t) _ftdm_channel_call_unhold(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan)
 {
 	ftdm_channel_lock(ftdmchan);
-	if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_HOLD)) {
-		ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_UP, 1);
-	}
+	ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_UP, 0);
 	ftdm_channel_unlock(ftdmchan);
 	return FTDM_SUCCESS;
 }
@@ -1820,15 +1828,11 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_answer(const char *file, const char
 {
 	ftdm_channel_lock(ftdmchan);
 
-	if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_ANSWERED)) {
-		ftdm_channel_unlock(ftdmchan);
-		return FTDM_SUCCESS;
-	}
+	ftdm_set_flag(ftdmchan, FTDM_CHANNEL_ANSWERED);
+	ftdm_set_flag(ftdmchan, FTDM_CHANNEL_PROGRESS);
+	ftdm_set_flag(ftdmchan, FTDM_CHANNEL_MEDIA);
 
 	if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
-		ftdm_set_flag(ftdmchan, FTDM_CHANNEL_ANSWERED);
-		ftdm_set_flag(ftdmchan, FTDM_CHANNEL_PROGRESS);
-		ftdm_set_flag(ftdmchan, FTDM_CHANNEL_MEDIA);
 		ftdm_channel_unlock(ftdmchan);
 		return FTDM_SUCCESS;
 	}
diff --git a/libs/freetdm/src/ftmod/ftmod_analog/ftmod_analog.c b/libs/freetdm/src/ftmod/ftmod_analog/ftmod_analog.c
index daddb2115c..dd54a88d72 100644
--- a/libs/freetdm/src/ftmod/ftmod_analog/ftmod_analog.c
+++ b/libs/freetdm/src/ftmod/ftmod_analog/ftmod_analog.c
@@ -691,8 +691,8 @@ static void *ftdm_analog_channel_run(ftdm_thread_t *me, void *obj)
 		}
 
 		if (ftdm_channel_read(ftdmchan, frame, &len) != FTDM_SUCCESS) {
-			ftdm_log(FTDM_LOG_ERROR, "READ ERROR [%s]\n", ftdmchan->last_error);
-			goto done;
+			ftdm_log(FTDM_LOG_WARNING, "read error [%s]\n", ftdmchan->last_error);
+			continue;
 		}
 
 		if (ftdmchan->type == FTDM_CHAN_TYPE_FXO && ftdmchan->detected_tones[0]) {
diff --git a/libs/freetdm/src/include/freetdm.h b/libs/freetdm/src/include/freetdm.h
index cf73969862..480182a3b2 100644
--- a/libs/freetdm/src/include/freetdm.h
+++ b/libs/freetdm/src/include/freetdm.h
@@ -658,6 +658,24 @@ FT_DECLARE(ftdm_status_t) ftdm_span_get_sig_status(ftdm_span_t *span, ftdm_signa
 /*! \brief Get span signaling status (ie: whether protocol layer is up or down) */
 FT_DECLARE(void) ftdm_channel_clear_detected_tones(ftdm_channel_t *ftdmchan);
 
+/*! 
+ * \brief Set user private data in the channel
+ *
+ * \param ftdmchan The channel where the private data will be stored
+ * \param pvt The private pointer to store
+ *
+ */
+FT_DECLARE(void) ftdm_channel_set_private(ftdm_channel_t *ftdmchan, void *pvt);
+
+/*! 
+ * \brief Get user private data in the channel
+ *
+ * \param ftdmchan The channel to retrieve the private data
+ * \retval The private data (if any or NULL if no data has been stored)
+ *
+ */
+FT_DECLARE(void *) ftdm_channel_get_private(const ftdm_channel_t *ftdmchan);
+
 /*! 
  * \brief Remove the given token from the channel
  *
diff --git a/libs/freetdm/src/include/private/ftdm_core.h b/libs/freetdm/src/include/private/ftdm_core.h
index a900b0d808..93f197f0ed 100644
--- a/libs/freetdm/src/include/private/ftdm_core.h
+++ b/libs/freetdm/src/include/private/ftdm_core.h
@@ -407,6 +407,7 @@ struct ftdm_channel {
 	uint8_t txgain_table[FTDM_GAINS_TABLE_SIZE];
 	float rxgain;
 	float txgain;
+	void *user_private;
 };
 
 struct ftdm_span {