[asterisk-commits] irroot: branch irroot/t38gateway-1.8 r318980 - in /team/irroot/t38gateway-1.8...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Sat May 14 09:14:08 CDT 2011


Author: irroot
Date: Sat May 14 09:14:01 2011
New Revision: 318980

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=318980
Log:
Commit changes to support T.38 gateway (1.8)

r1115 (#18889)
r1116 (#13405)
r1137
r1174 (#19183)


Modified:
    team/irroot/t38gateway-1.8/   (props changed)
    team/irroot/t38gateway-1.8/CHANGES
    team/irroot/t38gateway-1.8/channels/chan_sip.c
    team/irroot/t38gateway-1.8/channels/sip/include/sip.h
    team/irroot/t38gateway-1.8/include/asterisk/res_fax.h
    team/irroot/t38gateway-1.8/res/res_fax.c
    team/irroot/t38gateway-1.8/res/res_fax_spandsp.c

Propchange: team/irroot/t38gateway-1.8/
------------------------------------------------------------------------------
    automerge = *

Propchange: team/irroot/t38gateway-1.8/
------------------------------------------------------------------------------
    automerge-email = gregory at distrotech.co.za

Propchange: team/irroot/t38gateway-1.8/
------------------------------------------------------------------------------
    svnmerge-integrated = /branches/1.8:1-318978

Modified: team/irroot/t38gateway-1.8/CHANGES
URL: http://svnview.digium.com/svn/asterisk/team/irroot/t38gateway-1.8/CHANGES?view=diff&rev=318980&r1=318979&r2=318980
==============================================================================
--- team/irroot/t38gateway-1.8/CHANGES (original)
+++ team/irroot/t38gateway-1.8/CHANGES Sat May 14 09:14:01 2011
@@ -511,6 +511,9 @@
    applications will be lost, and that if the 'fax' logger level is directed to
    the console, the 'core set verbose' and 'core set debug' CLI commands will
    have no effect on whether the messages appear on the console or not.
+ * FAXOPT(faxgateway) will enable a framehook that will take care of T.38
+   negotiation on reciving a CED tone on a channel. this gateway is to allow
+   translation of Audio T.30 [alaw/ulaw] to IFP T.38 terminals.
 
 Miscellaneous
 -------------

Modified: team/irroot/t38gateway-1.8/channels/chan_sip.c
URL: http://svnview.digium.com/svn/asterisk/team/irroot/t38gateway-1.8/channels/chan_sip.c?view=diff&rev=318980&r1=318979&r2=318980
==============================================================================
--- team/irroot/t38gateway-1.8/channels/chan_sip.c (original)
+++ team/irroot/t38gateway-1.8/channels/chan_sip.c Sat May 14 09:14:01 2011
@@ -4268,6 +4268,9 @@
 			case T38_ENABLED:
 				state = T38_STATE_NEGOTIATED;
 				break;
+			case T38_REJECTED:
+				state = T38_STATE_REJECTED;
+				break;
 			default:
 				state = T38_STATE_UNKNOWN;
 			}
@@ -4927,6 +4930,7 @@
 		parameters.request_response = AST_T38_NEGOTIATED;
 		ast_udptl_set_tag(p->udptl, "SIP/%s", p->username);
 		break;
+	case T38_REJECTED:
 	case T38_DISABLED:
 		if (old == T38_ENABLED) {
 			parameters.request_response = AST_T38_TERMINATED;
@@ -6042,7 +6046,10 @@
 	} else
 		ast_debug(1, "Hangup call %s, SIP callid %s\n", ast->name, p->callid);
 
-	sip_pvt_lock(p);
+	while (sip_pvt_trylock(p)) {
+		CHANNEL_DEADLOCK_AVOIDANCE(ast);
+	}
+
 	if (ast_test_flag(&p->flags[0], SIP_INC_COUNT) || ast_test_flag(&p->flags[1], SIP_PAGE2_CALL_ONHOLD)) {
 		if (sipdebug)
 			ast_debug(1, "update_call_counter(%s) - decrement call limit counter on hangup\n", p->username);
@@ -6268,7 +6275,9 @@
 			return 0;
 		}
 		if (p) {
-			sip_pvt_lock(p);
+			while (sip_pvt_trylock(p)) {
+				CHANNEL_DEADLOCK_AVOIDANCE(ast);
+			}
 			if (p->rtp) {
 				/* If channel is not up, activate early media session */
 				if ((ast->_state != AST_STATE_UP) &&
@@ -6377,7 +6386,10 @@
 	}
 	p = newchan->tech_pvt;
 
-	sip_pvt_lock(p);
+	while (sip_pvt_trylock(p)) {
+		CHANNEL_DEADLOCK_AVOIDANCE(oldchan);
+	}
+
 	append_history(p, "Masq", "Old channel: %s\n", oldchan->name);
 	append_history(p, "Masq (cont)", "...new owner: %s\n", newchan->name);
 	if (p->owner != oldchan)
@@ -6431,6 +6443,10 @@
 {
 	struct sip_pvt *p = ast->tech_pvt;
 	int res = 0;
+
+	if (!p) {
+		return res;
+	}
 
 	sip_pvt_lock(p);
 	switch (ast_test_flag(&p->flags[0], SIP_DTMF)) {
@@ -6485,11 +6501,11 @@
 	case AST_T38_REQUEST_NEGOTIATE:         /* Request T38 */
 		/* Negotiation can not take place without a valid max_ifp value. */
 		if (!parameters->max_ifp) {
-			change_t38_state(p, T38_DISABLED);
 			if (p->t38.state == T38_PEER_REINVITE) {
 				AST_SCHED_DEL_UNREF(sched, p->t38id, dialog_unref(p, "when you delete the t38id sched, you should dec the refcount for the stored dialog ptr"));
 				transmit_response_reliable(p, "488 Not acceptable here", &p->initreq);
 			}
+			change_t38_state(p, T38_REJECTED);
 			break;
 		} else if (p->t38.state == T38_PEER_REINVITE) {
 			AST_SCHED_DEL_UNREF(sched, p->t38id, dialog_unref(p, "when you delete the t38id sched, you should dec the refcount for the stored dialog ptr"));
@@ -6527,7 +6543,7 @@
 	case AST_T38_REQUEST_TERMINATE:         /* Shutdown T38 */
 		if (p->t38.state == T38_PEER_REINVITE) {
 			AST_SCHED_DEL_UNREF(sched, p->t38id, dialog_unref(p, "when you delete the t38id sched, you should dec the refcount for the stored dialog ptr"));
-			change_t38_state(p, T38_DISABLED);
+			change_t38_state(p, T38_REJECTED);
 			transmit_response_reliable(p, "488 Not acceptable here", &p->initreq);
 		} else if (p->t38.state == T38_ENABLED)
 			transmit_reinvite_with_sdp(p, FALSE, FALSE);
@@ -7176,7 +7192,10 @@
 	struct sip_pvt *p = ast->tech_pvt;
 	int faxdetected = FALSE;
 
-	sip_pvt_lock(p);
+	while (sip_pvt_trylock(p)) {
+		CHANNEL_DEADLOCK_AVOIDANCE(ast);
+	}
+
 	fr = sip_rtp_read(ast, p, &faxdetected);
 	p->lastrtprx = time(NULL);
 
@@ -8954,7 +8973,7 @@
 		}
 	}
 
-	if ((portno == -1) && (p->t38.state != T38_DISABLED)) {
+	if ((portno == -1) && (p->t38.state != T38_DISABLED) && (p->t38.state != T38_REJECTED)) {
 		ast_debug(3, "Have T.38 but no audio, accepting offer anyway\n");
 		return 0;
         }
@@ -18770,7 +18789,7 @@
 	} else  if (!strcasecmp(data, "peername")) {
 		ast_copy_string(buf, p->peername, len);
 	} else if (!strcasecmp(data, "t38passthrough")) {
-		if (p->t38.state == T38_DISABLED) {
+		if ((p->t38.state == T38_DISABLED) || (p->t38.state == T38_REJECTED)) {
 			ast_copy_string(buf, "0", len);
 		} else { /* T38 is offered or enabled in this call */
 			ast_copy_string(buf, "1", len);
@@ -19536,7 +19555,7 @@
 	case 606: /* Not Acceptable */
 		xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, FALSE);
 		if (p->udptl && p->t38.state == T38_LOCAL_REINVITE) {
-			change_t38_state(p, T38_DISABLED);
+			change_t38_state(p, T38_REJECTED);
 			/* Try to reset RTP timers */
 			//ast_rtp_set_rtptimers_onhold(p->rtp);
 
@@ -21251,7 +21270,7 @@
 	 * want to abort the negotiation process
 	 */
 	if (p->t38id != -1) {
-		change_t38_state(p, T38_DISABLED);
+		change_t38_state(p, T38_REJECTED);
 		transmit_response_reliable(p, "488 Not acceptable here", &p->initreq);
 		p->t38id = -1;
 		dialog_unref(p, "unref the dialog ptr from sip_t38_abort, because it held a dialog ptr");
@@ -22127,7 +22146,7 @@
 			} else if (p->t38.state == T38_ENABLED) {
 				ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
 				transmit_response_with_t38_sdp(p, "200 OK", req, (reinvite ? XMIT_RELIABLE : (req->ignore ?  XMIT_UNRELIABLE : XMIT_CRITICAL)));
-			} else if (p->t38.state == T38_DISABLED) {
+			} else if ((p->t38.state == T38_DISABLED) || (p->t38.state == T38_REJECTED)) {
 				/* If this is not a re-invite or something to ignore - it's critical */
 				if (p->srtp && !ast_test_flag(p->srtp, SRTP_CRYPTO_OFFER_OK)) {
 					ast_log(LOG_WARNING, "Target does not support required crypto\n");
@@ -26068,6 +26087,11 @@
 	while ((mbox = context = strsep(&next, ","))) {
 		struct sip_mailbox *mailbox;
 		int duplicate = 0;
+
+		if (strstr("@",context) == NULL) {
+			strncat(context,"@",sizeof(context)-2);
+			strncat(context,peer->context,sizeof(context)-strlen(peer->context)-1);
+		}
 
 		strsep(&context, "@");
 
@@ -27929,7 +27953,10 @@
 		return AST_RTP_GLUE_RESULT_FORBID;
 	}
 
-	sip_pvt_lock(p);
+	while (sip_pvt_trylock(p)) {
+		CHANNEL_DEADLOCK_AVOIDANCE(chan);
+	}
+
 	if (!(p->rtp)) {
 		sip_pvt_unlock(p);
 		return AST_RTP_GLUE_RESULT_FORBID;

Modified: team/irroot/t38gateway-1.8/channels/sip/include/sip.h
URL: http://svnview.digium.com/svn/asterisk/team/irroot/t38gateway-1.8/channels/sip/include/sip.h?view=diff&rev=318980&r1=318979&r2=318980
==============================================================================
--- team/irroot/t38gateway-1.8/channels/sip/include/sip.h (original)
+++ team/irroot/t38gateway-1.8/channels/sip/include/sip.h Sat May 14 09:14:01 2011
@@ -595,7 +595,8 @@
 	T38_DISABLED = 0,     /*!< Not enabled */
 	T38_LOCAL_REINVITE,   /*!< Offered from local - REINVITE */
 	T38_PEER_REINVITE,    /*!< Offered from peer - REINVITE */
-	T38_ENABLED           /*!< Negotiated (enabled) */
+	T38_ENABLED,          /*!< Negotiated (enabled) */
+	T38_REJECTED          /*!< Refused */
 };
 
 /*! \brief Parameters to know status of transfer */

Modified: team/irroot/t38gateway-1.8/include/asterisk/res_fax.h
URL: http://svnview.digium.com/svn/asterisk/team/irroot/t38gateway-1.8/include/asterisk/res_fax.h?view=diff&rev=318980&r1=318979&r2=318980
==============================================================================
--- team/irroot/t38gateway-1.8/include/asterisk/res_fax.h (original)
+++ team/irroot/t38gateway-1.8/include/asterisk/res_fax.h Sat May 14 09:14:01 2011
@@ -42,6 +42,8 @@
 	AST_FAX_TECH_T38       = (1 << 3),
 	/*! sending mulitple documents supported */
 	AST_FAX_TECH_MULTI_DOC = (1 << 4),
+	/*! T.38 - T.30 Gateway */
+	AST_FAX_TECH_GATEWAY = (1 << 5),
 };
 
 /*! \brief fax modem capabilities */
@@ -158,6 +160,8 @@
 			uint32_t send_cng:1;
 			/*! send a T.38 reinvite */
 			uint32_t request_t38:1;
+			/*! T.38 Gateway enable */
+			uint32_t t38gateway:1;
 		};
 	} option;
 	/*! override the minimum transmission rate with a channel variable */
@@ -202,6 +206,21 @@
 	struct ast_fax_debug_info *debug_info;
 	/*! used to take variable-sized frames in and output frames of an expected size to the fax stack */
 	struct ast_smoother *smoother;
+};
+
+#define AST_GW_GEN_SRC		"T38_FAX_GATEWAY"
+
+/*! \brief used for gateway framehook */
+struct ast_fax_session_gateway {
+	/*! FAX Session*/
+	struct ast_fax_session *s;
+	/*! DSP Processor*/
+	struct ast_dsp *chan_dsp;
+	struct ast_dsp *peer_dsp;
+	/*! framehook used in gateway mode */
+	int framehook;
+	/*! bridged*/
+	int bridged;
 };
 
 /*! \brief used to register a FAX technology module with res_fax */

Modified: team/irroot/t38gateway-1.8/res/res_fax.c
URL: http://svnview.digium.com/svn/asterisk/team/irroot/t38gateway-1.8/res/res_fax.c?view=diff&rev=318980&r1=318979&r2=318980
==============================================================================
--- team/irroot/t38gateway-1.8/res/res_fax.c (original)
+++ team/irroot/t38gateway-1.8/res/res_fax.c Sat May 14 09:14:01 2011
@@ -5,6 +5,22 @@
  *
  * Dwayne M. Hubbard <dhubbard at digium.com>
  * Kevin P. Fleming <kpfleming at digium.com>
+ *
+ * Initial T.38-gateway code
+ * 2008, Daniel Ferenci <daniel.ferenci at nethemba.com>
+ * Created by Nethemba s.r.o. http://www.nethemba.com
+ * Sponsored by IPEX a.s. http://www.ipex.cz
+ *
+ * T.38-gateway integration into asterisk app_fax and rework
+ * 2008, Gregory Hinton Nietsky <gregory at dnstelecom.co.za>
+ * dns Telecom http://www.dnstelecom.co.za
+ *
+ * Modified to make T.38-gateway compatible with Asterisk 1.6.2
+ * 2010, Anton Verevkin <mymail at verevkin.it>
+ * ViaNetTV http://www.vianettv.com
+ *
+ * Modified to make T.38-gateway work
+ * 2010, Klaus Darilion, IPCom GmbH, www.ipcom.at
  *
  * See http://www.asterisk.org for more information about
  * the Asterisk project. Please do not directly contact
@@ -58,6 +74,7 @@
 #include "asterisk/dsp.h"
 #include "asterisk/indications.h"
 #include "asterisk/ast_version.h"
+#include "asterisk/translate.h"
 
 /*** DOCUMENTATION
 	<application name="ReceiveFax" language="en_US">
@@ -159,6 +176,9 @@
 					<enum name="modem">
 						<para>R/W Modem type (v17/v27/v29).</para>
 					</enum>
+					<enum name="t38gateway">
+						<para>R/W T38 Gateway Enabled (yes/no)</para>
+					</enum>
 					<enum name="pages">
 						<para>R/O Number of pages transferred.</para>
 					</enum>
@@ -192,10 +212,25 @@
 			<ref type="application">SendFax</ref>
 		</see-also>
 	</function>
+	<application name="WaitFAX" language="en_US">
+		<synopsis>
+			Generic Fax Detect CNG/T.38 (Wait For Fax)
+		</synopsis>
+		<syntax>
+			<parameter name="timeout" required="true">
+				<para>Specifies the number of seconds we attempt to detect a fax tone on the channel</para>
+			</parameter>
+		</syntax>
+		<description>
+			<para>This application sets the following channel variable upon completion</para>
+			<para>  FAXDETECTED : 0 | 1</para>
+		</description>
+	</application>
 ***/
 
 static const char app_receivefax[] = "ReceiveFAX";
 static const char app_sendfax[] = "SendFAX";
+static const char app_waitfax[] = "WaitFAX";
 
 struct debug_info_history {
 	unsigned int consec_frames;
@@ -389,8 +424,38 @@
 	d->modems = general_options.modems;
 	d->minrate = general_options.minrate;
 	d->maxrate = general_options.maxrate;
+	d->option.t38gateway = AST_FAX_OPTFLAG_FALSE;
 
 	return d;
+}
+
+static struct ast_control_t38_parameters our_t38_parameters = {
+	.version = 0,
+	.max_ifp = 400,
+	.rate = AST_T38_RATE_14400,
+	.rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF,
+};
+
+static void t38_parameters_ast_to_fax(struct ast_fax_t38_parameters *dst, const struct ast_control_t38_parameters *src)
+{
+	dst->version = src->version;
+	dst->max_ifp = src->max_ifp;
+	dst->rate = src->rate;
+	dst->rate_management = src->rate_management;
+	dst->fill_bit_removal = src->fill_bit_removal;
+	dst->transcoding_mmr = src->transcoding_mmr;
+	dst->transcoding_jbig = src->transcoding_jbig;
+}
+
+static void t38_parameters_fax_to_ast(struct ast_control_t38_parameters *dst, const struct ast_fax_t38_parameters *src)
+{
+	dst->version = src->version;
+	dst->max_ifp = src->max_ifp;
+	dst->rate = src->rate;
+	dst->rate_management = src->rate_management;
+	dst->fill_bit_removal = src->fill_bit_removal;
+	dst->transcoding_mmr = src->transcoding_mmr;
+	dst->transcoding_jbig = src->transcoding_jbig;
 }
 
 /*! \brief returns a reference counted details structure from the channel's fax datastore.  If the datastore
@@ -415,6 +480,10 @@
 	}
 	/* add the datastore to the channel and increment the refcount */
 	datastore->data = details;
+
+	/*provide default T38 paramaters*/
+	t38_parameters_ast_to_fax(&details->our_t38_parameters, &our_t38_parameters);
+
 	ao2_ref(details, 1);
 	ast_channel_lock(chan);
 	ast_channel_datastore_add(chan, datastore);
@@ -505,6 +574,13 @@
 			ast_build_string(&buf, &size, ",");
 		}
 		ast_build_string(&buf, &size, "MULTI_DOC");
+		first = 0;
+	}
+	if (caps & AST_FAX_TECH_GATEWAY) {
+		if (!first) {
+			ast_build_string(&buf, &size, ",");
+		}
+		ast_build_string(&buf, &size, "T38_GATEWAY");
 		first = 0;
 	}
 
@@ -1002,28 +1078,6 @@
 		GENERIC_FAX_EXEC_ERROR_QUIET(fax, chan, errorstr, reason); \
 	} while (0)
 
-static void t38_parameters_ast_to_fax(struct ast_fax_t38_parameters *dst, const struct ast_control_t38_parameters *src)
-{
-	dst->version = src->version;
-	dst->max_ifp = src->max_ifp;
-	dst->rate = src->rate;
-	dst->rate_management = src->rate_management;
-	dst->fill_bit_removal = src->fill_bit_removal;
-	dst->transcoding_mmr = src->transcoding_mmr;
-	dst->transcoding_jbig = src->transcoding_jbig;
-}
-
-static void t38_parameters_fax_to_ast(struct ast_control_t38_parameters *dst, const struct ast_fax_t38_parameters *src)
-{
-	dst->version = src->version;
-	dst->max_ifp = src->max_ifp;
-	dst->rate = src->rate;
-	dst->rate_management = src->rate_management;
-	dst->fill_bit_removal = src->fill_bit_removal;
-	dst->transcoding_mmr = src->transcoding_mmr;
-	dst->transcoding_jbig = src->transcoding_jbig;
-}
-
 static int set_fax_t38_caps(struct ast_channel *chan, struct ast_fax_session_details *details)
 {
 	switch (ast_channel_get_t38_state(chan)) {
@@ -1032,7 +1086,13 @@
 		break;
 	case T38_STATE_UNAVAILABLE:
 		details->caps |= AST_FAX_TECH_AUDIO;
+		/* The T.38 Gateway provides T.38 so if its active T.38 is a valid option*/
+		if (details->option.t38gateway) {
+			details->caps |= AST_FAX_TECH_T38;
+		}
 		break;
+	case T38_STATE_NEGOTIATED:
+		/* i am already T.38 have i been answered already and switched to T.38 ?? */
 	case T38_STATE_NEGOTIATING: {
 		/* the other end already sent us a T.38 reinvite, so we need to prod the channel
 		 * driver into resending their parameters to us if it supports doing so... if
@@ -1113,13 +1173,6 @@
 
 	return 0;
 }
-
-static struct ast_control_t38_parameters our_t38_parameters = {
-	.version = 0,
-	.max_ifp = 400,
-	.rate = AST_T38_RATE_14400,
-	.rate_management = AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF,
-};
 
 /*! \brief this is the generic FAX session handling function */
 static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_details *details, struct ast_fax_session *reserved, struct ast_fax_tech_token *token)
@@ -1386,8 +1439,6 @@
 	struct ast_frame *frame = NULL;
 	struct ast_control_t38_parameters t38_parameters;
 
-	t38_parameters_ast_to_fax(&details->our_t38_parameters, &our_t38_parameters);
-
 	/* don't send any audio if we've already received a T.38 reinvite */
 	if (ast_channel_get_t38_state(chan) != T38_STATE_NEGOTIATING) {
 		/* generate 3 seconds of CED */
@@ -1784,8 +1835,6 @@
 	int ms;
 	struct ast_frame *frame = NULL;
 	struct ast_control_t38_parameters t38_parameters;
-
-	t38_parameters_ast_to_fax(&details->our_t38_parameters, &our_t38_parameters);
 
 	/* send CNG tone while listening for the receiver to initiate a switch
 	 * to T.38 mode; if they do, stop sending the CNG tone and proceed with
@@ -2283,6 +2332,494 @@
 	return (!channel_alive) ? -1 : 0;
 }
 
+/*! \brief channel datastore holding a T38 fax gateway object*/
+static const struct ast_datastore_info fax_datastore_gateway = {
+	.type = "res_fax_gateway",
+	.destroy = destroy_callback,
+};
+
+/*! \brief returns a reference counted gateway structure from the channel's fax gateway datastore.
+ * \param chan channel to look for the datastore
+ * \param create flag if set the datastore will be created if not found
+ * \return fax gateway object or null if it does not exist or cant be created depending on create.*/
+static struct ast_fax_session_gateway *find_session_gateway(struct ast_channel *chan, int create)
+{
+	struct ast_fax_session_gateway *gateway;
+	struct ast_datastore *datastore;
+
+	ast_channel_lock(chan);
+	datastore = ast_channel_datastore_find(chan, &fax_datastore_gateway, NULL);
+	ast_channel_unlock(chan);
+	if (create && ((!datastore || !datastore->data))) {
+		gateway = ao2_alloc(sizeof(*gateway), NULL);
+		if (!gateway) {
+			return NULL;
+		}
+
+		if (!(datastore = ast_datastore_alloc(&fax_datastore_gateway, NULL))) {
+			ast_log(LOG_WARNING, "channel '%s' can't get a datastore!\n", chan->name);
+			ao2_ref(gateway, -1);
+			return NULL;
+		}
+
+		/* add the datastore to the channel*/
+		datastore->data = gateway;
+
+		gateway->framehook = -1;
+		gateway->chan_dsp = NULL;
+		gateway->peer_dsp = NULL;
+		gateway->s = NULL;
+		gateway->bridged = 0;
+
+		ast_channel_lock(chan);
+		ast_channel_datastore_add(chan, datastore);
+		ast_channel_unlock(chan);
+	} else if (datastore->data) {
+		gateway = datastore->data;
+	} else {
+		return NULL;
+	}
+
+	/* i need a ref here for create and find as the datastore must hold one */
+	ao2_ref(gateway, 1);
+	return gateway;
+}
+
+/*! \brief create a fax session and start T.30<->T.38 gateway mode
+ * \param details fax session details
+ * \param chan active channel
+ * \return 0 on error 1 on success*/
+static int ast_t38_gateway(struct ast_fax_session_details *details, struct ast_channel *chan)
+{
+	struct ast_fax_session_gateway *gateway;
+
+	gateway = find_session_gateway(chan, 0);
+	if (!gateway) {
+		return 0;
+	}
+
+	if (gateway->s) {
+		ao2_ref(gateway, -1);
+		return 0;
+	}
+
+	/* create the FAX session */
+	details->caps = AST_FAX_TECH_GATEWAY;
+	if (!(gateway->s = fax_session_new(details, chan, NULL, NULL))) {
+		ast_log(LOG_ERROR, "Can't create a FAX session, FAX attempt failed.\n");
+		report_fax_status(chan, details, "No Available Resource");
+		ao2_ref(gateway, -1);
+		return 0;
+	}
+
+	if (gateway->s->tech->start_session(gateway->s) < 0) {
+		ao2_ref(gateway->s, -1);
+		gateway->s = NULL;
+		ao2_ref(gateway, -1);
+		return 0;
+	}
+
+	/*  looks like we good to go */
+	gateway->s->state = AST_FAX_STATE_ACTIVE;
+	ao2_ref(gateway, -1);
+	return 1;
+}
+
+/*! \brief T38 Gateway Negotiate t38 parameters
+ * \param chan channel running the gateway
+ * \param peer channel im bridged too
+ * \param active channel the frame originated on
+ * \param f the control frame to process
+ * \return processed control frame or null frame*/
+static struct ast_frame *ast_t38_gateway_parameters(struct ast_channel *chan,struct ast_channel *peer, struct ast_channel *active, struct ast_frame *f)
+{
+	struct ast_control_t38_parameters *active_param;
+	struct ast_fax_t38_parameters *t38_param;
+	struct ast_channel *inactive;
+	enum ast_t38_state activestate, inactivestate;
+	struct ast_fax_session_details *details;
+
+	/* Get a FAX session details structure from the channel's FAX datastore*/
+	if (!(details = find_details(chan))) {
+		return f;
+	}
+
+	/* i am not a faxgateway */
+	if (!details->option.t38gateway) {
+		ast_debug(1, "T.38 Parameter without gateway enabled\n");
+		ao2_ref(details, -1);
+		return f;
+	}
+
+	/* T.38 States*/
+	inactive = (active == chan) ? peer : chan;
+	activestate = ast_channel_get_t38_state(active);
+	inactivestate = ast_channel_get_t38_state(inactive);
+	t38_param = (active == chan) ? &details->our_t38_parameters : &details->their_t38_parameters;
+
+	active_param=f->data.ptr;
+	switch (active_param->request_response) {
+		/* ill need to do something inteligent with AST_T38_REQUEST_TERMINATE/AST_T38_TERMINATED like turn off frame hooks and reply */
+		case AST_T38_REFUSED:
+			/*this is not good i should never get a refused connection ill fake negotiated other side need not know	*/
+			ast_verb(3, "Refused on %s [%i] Sending Negotiated I: %s [%i]\n", active->name, activestate, inactive->name, inactivestate);
+			t38_parameters_fax_to_ast(active_param, t38_param);
+			active_param->request_response = AST_T38_NEGOTIATED;
+			break;
+		case AST_T38_REQUEST_NEGOTIATE:
+		case AST_T38_NEGOTIATED:
+			if ((inactivestate == T38_STATE_UNAVAILABLE) || (inactivestate == T38_STATE_REJECTED)) {
+				/* im not capable of T.38 so use the options from FAXOPT and queue on other side for me (faking it)*/
+				ast_verb(3, "%s on %s [%i] %s I: %s [%i]\n",
+					(active_param->request_response == AST_T38_NEGOTIATED) ? "Negotiated" : "Request",
+					active->name, activestate, (activestate == T38_STATE_NEGOTIATED) ? "Ignoring" : "Queueing",
+					inactive->name, inactivestate);
+				t38_parameters_fax_to_ast(active_param, t38_param);
+				/*prevent a negotiation storm only talk to unstable ends*/
+				if ((activestate == T38_STATE_NEGOTIATING) || (activestate == T38_STATE_UNKNOWN)) {
+					ast_queue_control_data(inactive, AST_CONTROL_T38_PARAMETERS, active_param, sizeof(struct ast_control_t38_parameters));
+				}
+				ast_frfree(f);
+				f = &ast_null_frame;
+			} else {
+				/*im capable of T.38 make the paramaters mine*/
+				ast_verb(3, "%s on %s [%i] Storing I: %s [%i]\n",
+					(active_param->request_response == AST_T38_NEGOTIATED) ? "Negotiated" : "Request",
+					active->name, activestate, inactive->name, inactivestate);
+				t38_parameters_ast_to_fax(t38_param, active_param);
+			}
+			break;
+		default:
+			ast_verb(3, "Unhandled Request Response [%i] on %s [%i] I: %s [%i]\n", active_param->request_response, 
+				active->name, activestate, inactive->name, inactivestate);
+			break;
+	}
+
+	/* Start gateway if both channels are in a stable T.38 (not negotiating) state and only one of them is not negotiated! */
+	if (((activestate == T38_STATE_NEGOTIATED) && ((inactivestate == T38_STATE_UNAVAILABLE) || (inactivestate == T38_STATE_REJECTED))) ||
+	    ((inactivestate == T38_STATE_NEGOTIATED) && ((activestate == T38_STATE_UNAVAILABLE) || (activestate == T38_STATE_REJECTED))))  {
+		if (ast_t38_gateway(details, chan)) {
+			ast_verb(3, "T.38 Gateway starting for chan %s and peer %s\n", chan->name, peer->name);
+		}
+	}
+
+	ao2_ref(details, -1);
+	return f;
+}
+
+/*! \brief Destroy the gateway data structure when the framehook is detached
+ * \param data framehook data (gateway data)*/
+static void t38_gw_fh_destroy(void *data) {
+	struct ast_fax_session_gateway	*gateway = data;
+	struct ast_fax_session		*s;
+
+	if (!gateway) {
+		return;
+	}
+
+	if (gateway->chan_dsp) {
+		ast_dsp_free(gateway->chan_dsp);
+		gateway->chan_dsp = NULL;
+	}
+
+	if (gateway->peer_dsp) {
+		ast_dsp_free(gateway->peer_dsp);
+		gateway->peer_dsp = NULL;
+	}
+
+	if (gateway->s) {
+		s = gateway->s;
+		s->tech->destroy_session(s);
+		ao2_lock(faxregistry.container);
+		ao2_unlink(faxregistry.container, s);
+		ao2_unlock(faxregistry.container);
+		ao2_ref(s, -1);
+	}
+	ao2_ref(gateway, -1);
+}
+
+/*! \brief T.30<->T.38 gateway frame hook
+ * \details intercept packets on bridged channels and determine if a T.38 gateway is required.
+ * when a CED faxtone is detected send a T.38 negotiate from the non T.38 channel.
+ * all T.38 negotiation needs to be handled where one channel is not T.38 capable.
+ * the gateway object will transmit frames i need to read native frames from both channels.
+ * \param chan channel im attached too
+ * \param f frame to handle may be NULL
+ * \param event framehook event
+ * \param data framehook data (gateway)
+ * \return processed frame or NULL when f is NULL or a null frame*/
+static struct ast_frame *t38_gw_framehook(struct ast_channel *chan,struct ast_frame *f,
+					  enum ast_framehook_event event,void *data) {
+	struct ast_control_t38_parameters *t38_parameters;
+	enum ast_t38_state		t38state;
+	enum ast_t38_state		t38pstate;
+	struct ast_fax_session_gateway	*gateway = data;
+	struct ast_fax_session		*s;
+	struct ast_frame		*dfr = NULL;
+	struct ast_channel		*peer, *active, *inactive;
+	struct ast_dsp			*dsp;
+
+	if (!f || (event == AST_FRAMEHOOK_EVENT_ATTACHED) || (event == AST_FRAMEHOOK_EVENT_DETACHED)) {
+		return NULL;
+	};
+
+	if (!gateway) {
+		return f;
+	}
+
+	peer = ast_bridged_channel(chan);
+	s = gateway->s;
+
+	switch (event) {
+		case AST_FRAMEHOOK_EVENT_WRITE:
+			/* If i have been written by the GW gen i must pass it on*/
+			if (f->src && !strcmp(f->src, AST_GW_GEN_SRC)) {
+				return f;
+			}
+			/* when we become active change the formats to SLIN for CED detect and T.30*/
+			if (peer && !gateway->bridged) {
+				ast_set_read_format(chan, AST_FORMAT_SLINEAR);
+				ast_set_write_format(chan, AST_FORMAT_SLINEAR);
+				ast_set_read_format(peer, AST_FORMAT_SLINEAR);
+				ast_set_write_format(peer, AST_FORMAT_SLINEAR);
+				ast_channel_make_compatible(chan, peer);
+				gateway->bridged = 1;
+			}
+			active = peer;
+			break;
+		case AST_FRAMEHOOK_EVENT_READ:
+			active = chan;
+			break;
+		default:
+			ast_log(LOG_WARNING, "Unhandled Framehook Event %i\n",event);
+			return f;
+	}
+
+	/* filter frames*/
+	switch (f->frametype) {
+		case AST_FRAME_VOICE:
+			switch (f->subclass.codec) {
+				case AST_FORMAT_SLINEAR:
+				case AST_FORMAT_ALAW:
+				case AST_FORMAT_ULAW:
+					break;
+				default:
+					return f;
+			}
+			break;
+		case AST_FRAME_MODEM:
+			if (s && (f->subclass.codec == AST_MODEM_T38)) {
+				break;
+			}
+			return f;
+		case AST_FRAME_CONTROL:
+			break;
+		default:
+			return f;
+	}
+
+	/*handle control frames*/
+	if (f->frametype == AST_FRAME_CONTROL) {
+		switch (f->subclass.integer) {
+			case AST_CONTROL_T38_PARAMETERS:
+				/* invalaid frame im either commited to gateway or incorect size*/
+				if (s || (f->datalen != sizeof(struct ast_control_t38_parameters))) {
+					ast_frfree(f);
+					return &ast_null_frame;
+				}
+				/* i have got a T.38 request before im bridged ?? perhaps im in a app or func*/
+				if (!active) {
+					return f;
+				}
+				f = ast_t38_gateway_parameters(chan,peer,active,f);
+			default:
+				return f;
+		}
+	}
+
+	/* im not a gateway let me listen for CED*/
+	dsp = (active == chan) ? gateway->chan_dsp : gateway->peer_dsp;
+	if (!s && chan && peer && dsp && (f->frametype == AST_FRAME_VOICE)) {
+		dfr = ast_frdup(f);
+		dfr = ast_dsp_process(active, dsp, dfr);
+		if (dfr && (dfr->frametype == AST_FRAME_DTMF) && (dfr->subclass.integer == 'e')) {
+			inactive = (chan == active) ? peer : chan;
+			t38state = ast_channel_get_t38_state(active);
+			t38pstate = ast_channel_get_t38_state(inactive);
+			t38_parameters = &our_t38_parameters;
+			t38_parameters->request_response = AST_T38_REQUEST_NEGOTIATE;
+			/* queue the negotiation on a channel with no  T.38 to get the other side up and going*/
+			if ((t38state == T38_STATE_UNAVAILABLE) || (t38state == T38_STATE_REJECTED)) {
+				ast_queue_control_data(active, AST_CONTROL_T38_PARAMETERS, t38_parameters, sizeof(our_t38_parameters));
+			} else if ((t38pstate == T38_STATE_UNAVAILABLE) || (t38pstate == T38_STATE_REJECTED)) {
+				ast_queue_control_data(inactive, AST_CONTROL_T38_PARAMETERS, t38_parameters, sizeof(our_t38_parameters));
+			}
+			ast_verb(3 ,"Got Fax Tone CED Chan %s [%i] Sending T.38 Params Peer Is %s [%i]\n",
+				active->name, t38state, (inactive) ? inactive->name : "NONE", t38pstate);
+			if (gateway->chan_dsp) {
+				ast_dsp_free(gateway->chan_dsp);
+				gateway->chan_dsp = NULL;
+			}
+			if (gateway->peer_dsp) {
+				ast_dsp_free(gateway->peer_dsp);
+				gateway->peer_dsp = NULL;
+			}
+		}
+		if (dfr) {
+			ast_frfree(dfr);
+		}
+		return f;
+	} else if (!s || !peer) {
+		return f;
+	}
+
+	/*get t38states i may not be bridged yet [these should be stored on gw start to minimise this hook]*/
+	t38state = ast_channel_get_t38_state(active);
+	t38pstate = ast_channel_get_t38_state((active == chan) ? peer : chan);
+
+	/*Im not a gateway and this is not audio channel*/
+	if ((t38state == T38_STATE_NEGOTIATED) && (t38pstate != T38_STATE_UNAVAILABLE) &&
+	    (t38pstate != T38_STATE_REJECTED)) {
+		return f;
+	}
+
+	switch(t38state) {
+		case T38_STATE_UNAVAILABLE:
+		case T38_STATE_REJECTED:
+			/*Im not a gateway and this is a audio channel*/
+			if (t38pstate != T38_STATE_NEGOTIATED) {
+				break;
+			}
+			/* i need to translate here as this is done latter in __ast_read*/
+			if ((f->frametype == AST_FRAME_VOICE) && (f->subclass.codec != AST_FORMAT_SLINEAR)) {
+				if (active->readtrans && (f = ast_translate(active->readtrans, f, 1)) == NULL) {
+					if (!f) {
+						f = &ast_null_frame;
+					}
+					break;
+				}
+			}
+		case T38_STATE_NEGOTIATED:
+			/*process the gateway packet*/
+			if (s->tech->write(s, f)) {
+				if (event == AST_FRAMEHOOK_EVENT_READ) {
+					ast_frfree(f);
+				}
+				f = &ast_null_frame;
+			}
+			break;
+		default:
+			ast_debug(1, "Incompatible T.38 State for gateway mode\n");
+	}
+
+	return f;
+}
+
+/*! \brief Alternate wait app that listens for CNG
+ * \details This app answers the channel and waits for CNG tone
+ * if the channel driver supports faxdetect it will proceed to the fax
+ * extension.
+ * it will return FAXDETECTED = 0 | 1 to allow dialplan processing where
+ * the channel does not have fax detect or the Dialplan needs to handle faxdetection*/
+static int waitfax_exec(struct ast_channel *chan, const char *data)
+{
+	char *parse;
+	int timeout, tone = 0;
+	struct ast_dsp *dsp = NULL;
+	struct ast_frame *f;
+	enum ast_t38_state t38state = T38_STATE_UNKNOWN;
+	struct ast_silence_generator *silgen = NULL;
+	unsigned int orig_read_format = 0;
+	AST_LIST_HEAD_NOLOCK(, ast_frame) deferred_frames;
+
+	parse = ast_strdupa(data);
+
+	AST_DECLARE_APP_ARGS(args,
+		AST_APP_ARG(timeout);
+	);
+	AST_STANDARD_APP_ARGS(args, parse);
+
+	if (args.timeout) {
+		timeout = atoi(args.timeout) * 1000;
+	} else {
+		pbx_builtin_setvar_helper(chan, "FAXDETECTED", "0");
+		return 0;
+	}
+
+	if (chan->_state != AST_STATE_UP)
+		ast_answer(chan);
+
+	/* Setup DSP CNG processing */
+	orig_read_format = chan->readformat;
+	if (!ast_set_read_format(chan, AST_FORMAT_SLINEAR)) {
+		if ((dsp=ast_dsp_new())) {
+			ast_dsp_set_features(dsp, DSP_FEATURE_FAX_DETECT);
+			ast_dsp_set_faxmode(dsp, DSP_FAXMODE_DETECT_CNG);
+		}
+	}
+
+	AST_LIST_HEAD_INIT_NOLOCK(&deferred_frames);
+
+	/* If no other generator is present, start silencegen while waiting */
+	if (ast_opt_transmit_silence && !chan->generatordata) {
+		silgen = ast_channel_start_silence_generator(chan);
+	}
+
+	while ((timeout = ast_waitfor(chan, timeout))) {
+		if (!(f=ast_read(chan))) {
+			pbx_builtin_setvar_helper(chan, "FAXDETECTED", "0");
+			break;
+		}
+
+		if (dsp && (f->frametype == AST_FRAME_VOICE) && ((f->subclass.codec == AST_FORMAT_SLINEAR) ||
+		    (f->subclass.codec == AST_FORMAT_ULAW) || (f->subclass.codec == AST_FORMAT_ALAW))) {
+			f = ast_dsp_process(chan, dsp, f);
+			if ((f->frametype ==  AST_FRAME_DTMF) && (f->subclass.integer == 'f')) {
+				tone=1;
+				ast_dsp_free(dsp);
+				dsp = NULL;
+			}
+		}
+
+		if (ast_is_deferrable_frame(f)) {
+			AST_LIST_INSERT_HEAD(&deferred_frames, f, frame_list);
+		} else {
+			ast_frfree(f);
+		}
+
+		t38state = ast_channel_get_t38_state(chan);
+		if ((t38state == T38_STATE_NEGOTIATING) || (t38state == T38_STATE_NEGOTIATED) || tone) {
+			pbx_builtin_setvar_helper(chan, "FAXDETECTED", "1");
+			break;
+		}
+	}
+
+	if (timeout <= 0) {
+		pbx_builtin_setvar_helper(chan, "FAXDETECTED", "0");
+	}
+
+	/* stop silgen if present */
+	if (silgen) {
+		ast_channel_stop_silence_generator(chan, silgen);
+	}
+
+	if (orig_read_format) {
+		ast_set_read_format(chan, orig_read_format);
+	}
+
+	ast_channel_lock(chan);
+	while ((f = AST_LIST_REMOVE_HEAD(&deferred_frames, frame_list))) {
+		ast_queue_frame_head(chan, ast_frisolate(f));
+	}
+	ast_channel_unlock(chan);
+
+	if (dsp) {
+		ast_dsp_free(dsp);
+	}
+
+	return 0;
+}
+
 /*! \brief hash callback for ao2 */
 static int session_hash_cb(const void *obj, const int flags)
 {
@@ -2679,6 +3216,9 @@
 	}
 	if (!strcasecmp(data, "ecm")) {
 		ast_copy_string(buf, details->option.ecm ? "yes" : "no", len);
+	} else if (!strcasecmp(data, "t38gateway") || !strcasecmp(data, "gateway") ||
+		   !strcasecmp(data, "t38_gateway")) {
+		ast_copy_string(buf, details->option.t38gateway ? "yes" : "no", len);
 	} else if (!strcasecmp(data, "error")) {
 		ast_copy_string(buf, details->error, len);
 	} else if (!strcasecmp(data, "filename")) {
@@ -2753,6 +3293,53 @@
 		} else {
 			ast_log(LOG_WARNING, "Unsupported value '%s' passed to FAXOPT(ecm).\n", value);
 		}
+	} else if (!strcasecmp(data, "t38gateway") || !strcasecmp(data, "gateway") ||
+		   !strcasecmp(data, "t38_gateway")) {
+		const char *val = ast_skip_blanks(value);
+		struct ast_fax_session_gateway *gateway;
+		if (ast_true(val)) {
+			struct ast_framehook_interface fr_hook = {
+					.version = AST_FRAMEHOOK_INTERFACE_VERSION,
+					.event_cb = t38_gw_framehook,
+					.destroy_cb = t38_gw_fh_destroy,
+			};
+
+			details->option.t38gateway = AST_FAX_OPTFLAG_TRUE;
+			t38_parameters_ast_to_fax(&details->their_t38_parameters, &our_t38_parameters);
+			/* set up the frame hook*/
+			gateway = find_session_gateway(chan, 1);
+			if (gateway && (gateway->framehook < 0)) {
+				fr_hook.data = gateway;
+				ast_channel_lock(chan);
+				gateway->framehook = ast_framehook_attach(chan, &fr_hook);
+				ast_channel_unlock(chan);
+				if (gateway->framehook >= 0) {
+					gateway->chan_dsp = ast_dsp_new();
+					if (gateway->chan_dsp) {
+						ast_dsp_set_features(gateway->chan_dsp, DSP_FEATURE_FAX_DETECT);
+						ast_dsp_set_faxmode(gateway->chan_dsp, DSP_FAXMODE_DETECT_CNG | DSP_FAXMODE_DETECT_CED);
+					}
+					gateway->peer_dsp = ast_dsp_new();
+					if (gateway->peer_dsp) {
+						ast_dsp_set_features(gateway->peer_dsp, DSP_FEATURE_FAX_DETECT);
+						ast_dsp_set_faxmode(gateway->peer_dsp, DSP_FAXMODE_DETECT_CNG | DSP_FAXMODE_DETECT_CED);
+					}
+				} else {
+					/* im not in a frame hook*/
+					ao2_ref(gateway, -1);
+				}
+			} else if (gateway) {
+				ao2_ref(gateway, -1);
+			}
+		} else if (ast_false(val)) {
+			gateway = find_session_gateway(chan, 0);
+			if (gateway && (gateway->framehook > 0) && !ast_framehook_detach(chan, gateway->framehook)) {
+				gateway->framehook = -1;
+			}
+			details->option.t38gateway = AST_FAX_OPTFLAG_FALSE;
+		} else {
+			ast_log(LOG_WARNING, "Unsupported value '%s' passed to FAXOPT(t38gateway).\n", value);
+		}
 	} else if (!strcasecmp(data, "headerinfo")) {
 		ast_string_field_set(details, headerinfo, value);
 	} else if (!strcasecmp(data, "localstationid")) {
@@ -2803,6 +3390,10 @@
 		ast_log(LOG_WARNING, "failed to unregister '%s'\n", app_receivefax);
 	}
 
+	if (ast_unregister_application(app_waitfax) < 0) {
+		ast_log(LOG_WARNING, "failed to unregister '%s'\n", app_waitfax);
+	}
+
 	if (fax_logger_level != -1) {
 		ast_logger_unregister_level("FAX");
 	}
@@ -2842,6 +3433,11 @@
 		ao2_ref(faxregistry.container, -1);
 		return AST_MODULE_LOAD_DECLINE;
 	}
+
+	if (ast_register_application_xml(app_waitfax, waitfax_exec) < 0) {
+		ast_log(LOG_WARNING, "failed to register '%s'.\n", app_waitfax);
+	}
+
 	ast_cli_register_multiple(fax_cli, ARRAY_LEN(fax_cli));
 	res = ast_custom_function_register(&acf_faxopt);	
 	fax_logger_level = ast_logger_register_level("FAX");

Modified: team/irroot/t38gateway-1.8/res/res_fax_spandsp.c
URL: http://svnview.digium.com/svn/asterisk/team/irroot/t38gateway-1.8/res/res_fax_spandsp.c?view=diff&rev=318980&r1=318979&r2=318980
==============================================================================
--- team/irroot/t38gateway-1.8/res/res_fax_spandsp.c (original)
+++ team/irroot/t38gateway-1.8/res/res_fax_spandsp.c Sat May 14 09:14:01 2011
@@ -4,6 +4,22 @@
  * Copyright (C) 2009-2010, Digium, Inc.
  *
  * Matthew Nicholson <mnicholson at digium.com>
+ *
+ * Initial T.38-gateway code
+ * 2008, Daniel Ferenci <daniel.ferenci at nethemba.com>
+ * Created by Nethemba s.r.o. http://www.nethemba.com
+ * Sponsored by IPEX a.s. http://www.ipex.cz
+ *
+ * T.38-gateway integration into asterisk app_fax and rework
+ * 2008, Gregory Hinton Nietsky <gregory at dnstelecom.co.za>
+ * dns Telecom http://www.dnstelecom.co.za
+ *
+ * Modified to make T.38-gateway compatible with Asterisk 1.6.2
+ * 2010, Anton Verevkin <mymail at verevkin.it>
+ * ViaNetTV http://www.vianettv.com
+ *
+ * Modified to make T.38-gateway work
+ * 2010, Klaus Darilion, IPCom GmbH, www.ipcom.at
  *
  * See http://www.asterisk.org for more information about
  * the Asterisk project. Please do not directly contact
@@ -46,9 +62,11 @@
 #include "asterisk/timing.h"
 #include "asterisk/astobj2.h"
 #include "asterisk/res_fax.h"
+#include "asterisk/channel.h"
 
 #define SPANDSP_FAX_SAMPLES 160
 #define SPANDSP_FAX_TIMER_RATE 8000 / SPANDSP_FAX_SAMPLES	/* 50 ticks per second, 20ms, 160 samples per second */
+#define SPANDSP_ENGAGE_UDPTL_NAT_RETRY 3
 
 static void *spandsp_fax_new(struct ast_fax_session *s, struct ast_fax_tech_token *token);
 static void spandsp_fax_destroy(struct ast_fax_session *s);
@@ -57,6 +75,9 @@
 static int spandsp_fax_start(struct ast_fax_session *s);
 static int spandsp_fax_cancel(struct ast_fax_session *s);
 static int spandsp_fax_switch_to_t38(struct ast_fax_session *s);
+static int spandsp_fax_gateway_start(struct ast_fax_session *s);
+static int spandsp_fax_gateway_process(struct ast_fax_session *s, const struct ast_frame *f);
+static void spandsp_fax_gateway_clean(struct ast_fax_session *s);
 
 static char *spandsp_fax_cli_show_capabilities(int fd);
 static char *spandsp_fax_cli_show_session(struct ast_fax_session *s, int fd);
@@ -75,7 +96,7 @@
 	 */
 	.version = "pre-20090220",
 #endif
-	.caps = AST_FAX_TECH_AUDIO | AST_FAX_TECH_T38 | AST_FAX_TECH_SEND | AST_FAX_TECH_RECEIVE,

[... 256 lines stripped ...]



More information about the asterisk-commits mailing list