[asterisk-commits] irroot: branch irroot/distrotech-customers-1.8 r325996 - in /team/irroot/dist...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Jul 1 03:13:14 CDT 2011


Author: irroot
Date: Fri Jul  1 03:13:10 2011
New Revision: 325996

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=325996
Log:
Backport recent T38 Gateway changes from trunk branch

Modified:
    team/irroot/distrotech-customers-1.8/CHANGES
    team/irroot/distrotech-customers-1.8/include/asterisk/res_fax.h
    team/irroot/distrotech-customers-1.8/res/res_fax.c
    team/irroot/distrotech-customers-1.8/res/res_fax_spandsp.c

Modified: team/irroot/distrotech-customers-1.8/CHANGES
URL: http://svnview.digium.com/svn/asterisk/team/irroot/distrotech-customers-1.8/CHANGES?view=diff&rev=325996&r1=325995&r2=325996
==============================================================================
--- team/irroot/distrotech-customers-1.8/CHANGES (original)
+++ team/irroot/distrotech-customers-1.8/CHANGES Fri Jul  1 03:13:10 2011
@@ -7,6 +7,12 @@
 === and the other UPGRADE files for older releases.
 ===
 ======================================================================
+
+res_fax
+--------------------------
+ * The ReceiveFAXStatus and SendFAXStatus manager events have been consolidated
+   into a FAXStatus event with an 'Operation' header that will be either
+   'send', 'receive', and 'gateway'.
 
 ------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 1.6.2 to Asterisk 1.8 ----------------

Modified: team/irroot/distrotech-customers-1.8/include/asterisk/res_fax.h
URL: http://svnview.digium.com/svn/asterisk/team/irroot/distrotech-customers-1.8/include/asterisk/res_fax.h?view=diff&rev=325996&r1=325995&r2=325996
==============================================================================
--- team/irroot/distrotech-customers-1.8/include/asterisk/res_fax.h (original)
+++ team/irroot/distrotech-customers-1.8/include/asterisk/res_fax.h Fri Jul  1 03:13:10 2011
@@ -160,8 +160,6 @@
 			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 */
@@ -172,6 +170,8 @@
 	struct ast_fax_t38_parameters our_t38_parameters;
 	/*! the other endpoint's T.38 session parameters, if any */
 	struct ast_fax_t38_parameters their_t38_parameters;
+	/*! the id of the t.38 gateway framehook for this channel */
+	int gateway_id;
 };
 
 struct ast_fax_tech;
@@ -210,24 +210,6 @@
 
 /* if this overlaps with any AST_FRFLAG_* values, problems will occur */
 #define AST_FAX_FRFLAG_GATEWAY (1 << 13)
-
-/*! \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;
-	/*Original audio formats*/
-	unsigned int chan_read_format;
-	unsigned int chan_write_format;
-	unsigned int peer_read_format;
-	unsigned int peer_write_format;
-};
 
 /*! \brief used to register a FAX technology module with res_fax */
 struct ast_fax_tech {

Modified: team/irroot/distrotech-customers-1.8/res/res_fax.c
URL: http://svnview.digium.com/svn/asterisk/team/irroot/distrotech-customers-1.8/res/res_fax.c?view=diff&rev=325996&r1=325995&r2=325996
==============================================================================
--- team/irroot/distrotech-customers-1.8/res/res_fax.c (original)
+++ team/irroot/distrotech-customers-1.8/res/res_fax.c Fri Jul  1 03:13:10 2011
@@ -5,6 +5,7 @@
  *
  * Dwayne M. Hubbard <dhubbard at digium.com>
  * Kevin P. Fleming <kpfleming at digium.com>
+ * Matthew Nicholson <mnicholson at digium.com>
  *
  * Initial T.38-gateway code
  * 2008, Daniel Ferenci <daniel.ferenci at nethemba.com>
@@ -12,7 +13,7 @@
  * 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>
+ * 2008-2011, Gregory Hinton Nietsky <gregory at distrotech.co.za>
  * dns Telecom http://www.dnstelecom.co.za
  *
  * Modified to make T.38-gateway compatible with Asterisk 1.6.2
@@ -43,6 +44,8 @@
  *
  * \author Dwayne M. Hubbard <dhubbard at digium.com>
  * \author Kevin P. Fleming <kpfleming at digium.com>
+ * \author Matthew Nicholson <mnicholson at digium.com>
+ * \author Gregory H. Nietsky  <gregory at distrotech.co.za>
  * 
  * A generic FAX resource module that provides SendFAX and ReceiveFAX applications.
  * This module requires FAX technology modules, like res_fax_spandsp, to register with it
@@ -176,7 +179,7 @@
 					<enum name="modem">
 						<para>R/W Modem type (v17/v27/v29).</para>
 					</enum>
-					<enum name="t38gateway">
+					<enum name="gateway">
 						<para>R/W T38 Gateway Enabled (yes/no)</para>
 					</enum>
 					<enum name="pages">
@@ -244,12 +247,37 @@
 	struct ast_dsp *dsp;
 };
 
+/*! \brief used for gateway framehook */
+struct fax_gateway {
+	/*! \brief FAX Session */
+	struct ast_fax_session *s;
+	/*! \brief reserved fax session token */
+	struct ast_fax_tech_token *token;
+	/*! \brief the start of our timeout counter */
+	struct timeval timeout_start;
+	/*! \brief DSP Processor */
+	struct ast_dsp *chan_dsp;
+	struct ast_dsp *peer_dsp;
+	/*! \brief framehook used in gateway mode */
+	int framehook;
+	/*! \brief bridged */
+	int bridged:1;
+	/*! \brief a flag to track the state of our negotiation */
+	enum ast_t38_state t38_state;
+	/*! \brief Original audio formats */
+	unsigned int chan_read_format;
+	unsigned int chan_write_format;
+	unsigned int peer_read_format;
+	unsigned int peer_write_format;
+};
+
 static int fax_logger_level = -1;
 
 /*! \brief maximum buckets for res_fax ao2 containers */
 #define FAX_MAXBUCKETS 10
 
 #define RES_FAX_TIMEOUT 10000
+#define FAX_GATEWAY_TIMEOUT RES_FAX_TIMEOUT
 
 /*! \brief The faxregistry is used to manage information and statistics for all FAX sessions. */
 static struct {
@@ -424,7 +452,7 @@
 	d->modems = general_options.modems;
 	d->minrate = general_options.minrate;
 	d->maxrate = general_options.maxrate;
-	d->option.t38gateway = AST_FAX_OPTFLAG_FALSE;
+	d->gateway_id = -1;
 
 	return d;
 }
@@ -481,7 +509,7 @@
 	/* add the datastore to the channel and increment the refcount */
 	datastore->data = details;
 
-	/*init default T38 paramaters*/
+	/* initialize default T.38 parameters */
 	t38_parameters_ast_to_fax(&details->our_t38_parameters, &our_t38_parameters);
 	t38_parameters_ast_to_fax(&details->their_t38_parameters, &our_t38_parameters);
 
@@ -759,6 +787,19 @@
 	}
 }
 
+/*! \brief Release a session token.
+ * \param s a session returned from fax_session_reserve()
+ * \param token a token generated from fax_session_reserve()
+ *
+ * This function releases the given token and marks the given session as no
+ * longer reserved. It is safe to call on a session that is not actually
+ * reserved and with a NULL token. This is so that sessions returned by
+ * technologies that do not support reserved sessions don't require extra logic
+ * to handle.
+ *
+ * \note This function DOES NOT release the given fax session, only the given
+ * token.
+ */
 static void fax_session_release(struct ast_fax_session *s, struct ast_fax_tech_token *token)
 {
 	if (token) {
@@ -805,6 +846,19 @@
 	ast_free(s->chan_uniqueid);
 }
 
+/*! \brief Reserve a fax session.
+ * \param details the fax session details
+ * \param token a pointer to a place to store a token to be passed to fax_session_new() later
+ *
+ * This function reserves a fax session for use later. If the selected fax
+ * technology does not support reserving sessions a session will still be
+ * returned but token will not be set.
+ *
+ * \note The reference returned by this function does not get consumed by
+ * fax_session_new() and must always be dereferenced separately.
+ *
+ * \return NULL or an uninitialized and possibly reserved session
+ */
 static struct ast_fax_session *fax_session_reserve(struct ast_fax_session_details *details, struct ast_fax_tech_token **token)
 {
 	struct ast_fax_session *s;
@@ -816,6 +870,8 @@
 	}
 
 	s->state = AST_FAX_STATE_INACTIVE;
+	s->details = details;
+	ao2_ref(s->details, 1);
 
 	/* locate a FAX technology module that can handle said requirements
 	 * Note: the requirements have not yet been finalized as T.38
@@ -854,7 +910,22 @@
 	return s;
 }
 
-/*! \brief create a FAX session */
+/*! \brief create a FAX session
+ *
+ * \param details details for the session
+ * \param chan the channel the session will run on
+ * \param reserved a reserved session to base this session on (can be NULL)
+ * \param token the token for a reserved session (can be NULL)
+ *
+ * Create a new fax session based on the given details structure.
+ *
+ * \note The given token is always consumed (by tech->new_session() or by
+ * fax_session_release() in the event of a failure). The given reference to a
+ * reserved session is never consumed and must be dereferenced separately from
+ * the reference returned by this function.
+ *
+ * \return NULL or a reference to a new fax session
+ */
 static struct ast_fax_session *fax_session_new(struct ast_fax_session_details *details, struct ast_channel *chan, struct ast_fax_session *reserved, struct ast_fax_tech_token *token)
 {
 	struct ast_fax_session *s = NULL;
@@ -864,6 +935,12 @@
 	if (reserved) {
 		s = reserved;
 		ao2_ref(reserved, +1);
+
+		/* NOTE: we don't consume the reference to the reserved
+		 * session. The session returned from fax_session_new() is a
+		 * new reference and must be derefed in addition to the
+		 * reserved session.
+		 */
 
 		if (s->state == AST_FAX_STATE_RESERVED) {
 			ast_atomic_fetchadd_int(&faxregistry.reserved_sessions, -1);
@@ -907,8 +984,10 @@
 	}
 
 	s->chan = chan;
-	s->details = details;
-	ao2_ref(s->details, 1);
+	if (!s->details) {
+		s->details = details;
+		ao2_ref(s->details, 1);
+	}
 
 	details->id = s->id = ast_atomic_fetchadd_int(&faxregistry.nextsessionname, 1);
 
@@ -1008,9 +1087,6 @@
 static int report_fax_status(struct ast_channel *chan, struct ast_fax_session_details *details, const char *status)
 {
 	char *filenames = generate_filenames_string(details, "FileName: ", "\r\n");
-	if (!filenames) {
-		return 1;
-	}
 
 	ast_channel_lock(chan);
 	if (details->option.statusevents) {
@@ -1018,24 +1094,30 @@
 
 		get_manager_event_info(chan, &info);
 		manager_event(EVENT_FLAG_CALL,
-			      (details->caps & AST_FAX_TECH_RECEIVE) ? "ReceiveFAXStatus" : "SendFAXStatus",
+			      "FAXStatus",
+			      "Operation: %s\r\n"
 			      "Status: %s\r\n"
 			      "Channel: %s\r\n"
 			      "Context: %s\r\n"
 			      "Exten: %s\r\n"
 			      "CallerID: %s\r\n"
 			      "LocalStationID: %s\r\n"
-			      "%s\r\n",
+			      "%s%s",
+			      (details->caps & AST_FAX_TECH_GATEWAY) ? "gateway" : (details->caps & AST_FAX_TECH_RECEIVE) ? "receive" : "send",
 			      status,
 			      chan->name,
 			      info.context,
 			      info.exten,
 			      info.cid,
 			      details->localstationid,
-			      filenames);
+			      S_OR(filenames, ""),
+			      filenames ? "\r\n" : "");
 	}
 	ast_channel_unlock(chan);
-	ast_free(filenames);
+
+	if (filenames) {
+		ast_free(filenames);
+	}
 
 	return 0;
 }
@@ -1085,16 +1167,11 @@
 	case T38_STATE_UNKNOWN:
 		details->caps |= AST_FAX_TECH_T38;
 		break;
-	case T38_STATE_REJECTED:
 	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 ?? */
+		/* already in T.38 mode? This should not happen. */
 	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
@@ -1629,6 +1706,14 @@
 	ast_string_field_set(details, error, "INIT_ERROR");
 	set_channel_variables(chan, details);
 
+	if (details->caps & AST_FAX_TECH_GATEWAY) {
+		ast_string_field_set(details, resultstr, "can't receive a fax on a channel with a T.38 gateway");
+		set_channel_variables(chan, details);
+		ast_log(LOG_ERROR, "executing ReceiveFAX on a channel with a T.38 Gateway is not supported\n");
+		ao2_ref(details, -1);
+		return -1;
+	}
+
 	if (details->maxrate < details->minrate) {
 		ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
 		ast_string_field_set(details, error, "INVALID_ARGUMENTS");
@@ -2101,6 +2186,14 @@
 	ast_string_field_set(details, error, "INIT_ERROR");
 	set_channel_variables(chan, details);
 
+	if (details->caps & AST_FAX_TECH_GATEWAY) {
+		ast_string_field_set(details, resultstr, "can't send a fax on a channel with a T.38 gateway");
+		set_channel_variables(chan, details);
+		ast_log(LOG_ERROR, "executing SendFAX on a channel with a T.38 Gateway is not supported\n");
+		ao2_ref(details, -1);
+		return -1;
+	}
+
 	if (details->maxrate < details->minrate) {
 		ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1);
 		ast_string_field_set(details, error, "INVALID_ARGUMENTS");
@@ -2338,175 +2431,401 @@
 	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;
+/*! \brief destroy a FAX gateway session structure */
+static void destroy_gateway(void *data)
+{
+	struct fax_gateway *gateway = data;
+
+	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) {
+		fax_session_release(gateway->s, gateway->token);
+		gateway->token = NULL;
+		gateway->s->details->caps |= ~AST_FAX_TECH_GATEWAY;
+
+		ao2_lock(faxregistry.container);
+		ao2_unlink(faxregistry.container, gateway->s);
+		ao2_unlock(faxregistry.container);
+
+		ao2_ref(gateway->s, -1);
 		gateway->s = NULL;
-		gateway->bridged = 0;
-
-		ast_channel_lock(chan);
-		ast_channel_datastore_add(chan, datastore);
-		ast_channel_unlock(chan);
-	} else if (datastore && datastore->data) {
-		gateway = datastore->data;
-	} else {
+	}
+}
+
+/*! \brief Create a new fax gateway object.
+ * \param details the fax session details
+ * \return NULL or a fax gateway object
+ */
+static struct fax_gateway *fax_gateway_new(struct ast_fax_session_details *details)
+{
+	struct fax_gateway *gateway = ao2_alloc(sizeof(*gateway), destroy_gateway);
+	if (!gateway) {
 		return NULL;
 	}
 
-	/* i need a ref here for create and find as the datastore must hold one */
-	ao2_ref(gateway, 1);
+	gateway->chan_dsp = ast_dsp_new();
+	if (!gateway->chan_dsp) {
+		ao2_ref(gateway, -1);
+		return NULL;
+	}
+
+	gateway->peer_dsp = ast_dsp_new();
+	if (!gateway->peer_dsp) {
+		ao2_ref(gateway, -1);
+		return NULL;
+	}
+
+	gateway->framehook = -1;
+
+	ast_dsp_set_features(gateway->chan_dsp, DSP_FEATURE_FAX_DETECT);
+	ast_dsp_set_faxmode(gateway->chan_dsp, DSP_FAXMODE_DETECT_CED);
+
+	ast_dsp_set_features(gateway->peer_dsp, DSP_FEATURE_FAX_DETECT);
+	ast_dsp_set_faxmode(gateway->peer_dsp, DSP_FAXMODE_DETECT_CED);
+
+	details->caps = AST_FAX_TECH_GATEWAY;
+	if (!(gateway->s = fax_session_reserve(details, &gateway->token))) {
+		details->caps |= ~AST_FAX_TECH_GATEWAY;
+		ast_log(LOG_ERROR, "Can't reserve a FAX session, gateway attempt failed.\n");
+		ao2_ref(gateway, -1);
+		return NULL;
+	}
+
 	return gateway;
 }
 
-/*! \brief create a fax session and start T.30<->T.38 gateway mode
+/*! \brief Create a fax session and start T.30<->T.38 gateway mode
+ * \param gateway a fax gateway object
  * \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;
-	}
+static int fax_gateway_start(struct fax_gateway *gateway, struct ast_fax_session_details *details, struct ast_channel *chan)
+{
+	struct ast_fax_session *s;
 
 	/* 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");
+	if (!(s = fax_session_new(details, chan, gateway->s, gateway->token))) {
+		gateway->token = NULL;
+		ast_string_field_set(details, result, "FAILED");
+		ast_string_field_set(details, resultstr, "error starting gateway session");
+		ast_string_field_set(details, error, "INIT_ERROR");
+		set_channel_variables(chan, details);
 		report_fax_status(chan, details, "No Available Resource");
-		ao2_ref(gateway, -1);
-		return 0;
-	}
+		ast_log(LOG_ERROR, "Can't create a FAX session, gateway attempt failed.\n");
+		return -1;
+	}
+	/* release the reference for the reserved session and replace it with
+	 * the real session */
+	ao2_ref(gateway->s, -1);
+	gateway->s = s;
+	gateway->token = NULL;
 
 	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;
+		ast_string_field_set(details, result, "FAILED");
+		ast_string_field_set(details, resultstr, "error starting gateway session");
+		ast_string_field_set(details, error, "INIT_ERROR");
+		set_channel_variables(chan, details);
+		return -1;
+	}
+
+	gateway->timeout_start.tv_sec = 0;
+	gateway->timeout_start.tv_usec = 0;
+
+	report_fax_status(chan, details, "FAX Transmission In Progress");
+
+	return 0;
+}
+
+static struct ast_frame *fax_gateway_detect_ced(struct fax_gateway *gateway, struct ast_channel *chan, struct ast_channel *peer, struct ast_channel *active, struct ast_frame *f)
+{
+	struct ast_frame *dfr = ast_frdup(f);
+	struct ast_dsp *active_dsp = (active == chan) ? gateway->chan_dsp : gateway->peer_dsp;
+	struct ast_channel *other = (active == chan) ? peer : chan;
+
+	if (!dfr) {
+		return f;
+	}
+
+	if (!(dfr = ast_dsp_process(active, active_dsp, dfr))) {
+		return f;
+	}
+
+	if (dfr->frametype == AST_FRAME_DTMF && dfr->subclass.integer == 'e') {
+		if (ast_channel_get_t38_state(other) == T38_STATE_UNKNOWN) {
+			struct ast_control_t38_parameters t38_parameters = {
+				.request_response = AST_T38_REQUEST_NEGOTIATE,
+			};
+			struct ast_frame control_frame = {
+				.src = "res_fax",
+				.frametype = AST_FRAME_CONTROL,
+				.datalen = sizeof(t38_parameters),
+				.subclass.integer = AST_CONTROL_T38_PARAMETERS,
+				.data.ptr = &t38_parameters,
+			};
+
+			struct ast_fax_session_details *details = find_details(chan);
+			ast_frfree(dfr);
+
+			if (!details) {
+				ast_log(LOG_ERROR, "no FAX session details found on chan %s for T.38 gateway session, odd\n", chan->name);
+				ast_framehook_detach(chan, gateway->framehook);
+				return f;
+			}
+
+			t38_parameters_fax_to_ast(&t38_parameters, &details->our_t38_parameters);
+			ao2_ref(details, -1);
+
+			if (!(dfr = ast_frisolate(&control_frame))) {
+				ast_log(LOG_ERROR, "error generating T.38 request control frame on chan %s for T.38 gateway session\n", chan->name);
+				return f;
+			}
+
+			gateway->t38_state = T38_STATE_NEGOTIATING;
+			gateway->timeout_start = ast_tvnow();
+
+			ast_debug(1, "detected CED tone on %s, requesting T.38 on %s for T.38 gateway session\n", active->name, other->name);
+			return dfr;
+		} else {
+			ast_debug(1, "detected CED tone on %s, but %s does not support T.38 for T.38 gateway session\n", active->name, other->name);
+		}
+	}
+
+	ast_frfree(dfr);
+	return f;
+}
+
+static int fax_gateway_indicate_t38(struct ast_channel *chan, struct ast_channel *active, struct ast_control_t38_parameters *control_params)
+{
+	if (active == chan) {
+		return ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, control_params, sizeof(*control_params));
+	} else {
+		return ast_queue_control_data(chan, AST_CONTROL_T38_PARAMETERS, control_params, sizeof(*control_params));
+	}
 }
 
 /*! \brief T38 Gateway Negotiate t38 parameters
+ * \param gateway gateway object
  * \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;
+ * \return processed control frame or null frame
+ */
+static struct ast_frame *fax_gateway_detect_t38(struct fax_gateway *gateway, struct ast_channel *chan, struct ast_channel *peer, struct ast_channel *active, struct ast_frame *f)
+{
+	struct ast_control_t38_parameters *control_params = f->data.ptr;
+	struct ast_channel *other = (active == chan) ? peer : chan;
 	struct ast_fax_session_details *details;
 
-	/* Get a FAX session details structure from the channel's FAX datastore*/
+	if (f->datalen != sizeof(struct ast_control_t38_parameters)) {
+		/* invalaid AST_CONTROL_T38_PARAMETERS frame, we can't
+		 * do anything with it, pass it on */
+		return f;
+	}
+
+	/* ignore frames from ourselves */
+	if ((gateway->t38_state == T38_STATE_NEGOTIATED && control_params->request_response == AST_T38_NEGOTIATED)
+		|| (gateway->t38_state == T38_STATE_REJECTED && control_params->request_response == AST_T38_REFUSED)
+		|| (gateway->t38_state == T38_STATE_NEGOTIATING && control_params->request_response == AST_T38_REQUEST_TERMINATE)) {
+
+		return f;
+	}
+
 	if (!(details = find_details(chan))) {
+		ast_log(LOG_ERROR, "no FAX session details found on chan %s for T.38 gateway session, odd\n", chan->name);
+		ast_framehook_detach(chan, gateway->framehook);
 		return f;
 	}
 
-	/* i am not a faxgateway */
-	if (!details->option.t38gateway) {
-		ast_debug(1, "T.38 Parameter without gateway enabled\n");
+	if (control_params->request_response == AST_T38_REQUEST_NEGOTIATE) {
+		enum ast_t38_state state = ast_channel_get_t38_state(other);
+		if (state == T38_STATE_UNKNOWN) {
+			/* we detected a request to negotiate T.38 and the
+			 * other channel appears to support T.38, we'll pass
+			 * the request through and only step in if the other
+			 * channel rejects the request */
+			ast_debug(1, "%s is attempting to negotiate T.38 with %s, we'll see what happens\n", active->name, other->name);
+			t38_parameters_ast_to_fax(&details->their_t38_parameters, control_params);
+			gateway->t38_state = T38_STATE_UNKNOWN;
+			gateway->timeout_start = ast_tvnow();
+			ao2_ref(details, -1);
+			return f;
+		} else if (state == T38_STATE_UNAVAILABLE || state == T38_STATE_REJECTED) {
+			/* the other channel does not support T.38, we need to
+			 * step in here */
+			ast_debug(1, "%s is attempting to negotiate T.38 but %s does not support it\n", active->name, other->name);
+			ast_debug(1, "starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", active->name, other->name);
+
+			t38_parameters_ast_to_fax(&details->their_t38_parameters, control_params);
+			t38_parameters_fax_to_ast(control_params, &details->our_t38_parameters);
+
+			if (fax_gateway_start(gateway, details, chan)) {
+				ast_log(LOG_ERROR, "error starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", active->name, other->name);
+				gateway->t38_state = T38_STATE_REJECTED;
+				control_params->request_response = AST_T38_REFUSED;
+
+				ast_framehook_detach(chan, details->gateway_id);
+				details->gateway_id = -1;
+			} else {
+				gateway->t38_state = T38_STATE_NEGOTIATED;
+				control_params->request_response = AST_T38_NEGOTIATED;
+				report_fax_status(chan, details, "T.38 Negotiated");
+			}
+
+			fax_gateway_indicate_t38(chan, active, control_params);
+
+			ao2_ref(details, -1);
+			return &ast_null_frame;
+		} else if (gateway->t38_state == T38_STATE_NEGOTIATING) {
+			/* we got a request to negotiate T.38 after we already
+			 * sent one to the other party based on CED tone
+			 * detection. We'll just pretend we passed this request
+			 * through in the first place. */
+
+			t38_parameters_ast_to_fax(&details->their_t38_parameters, control_params);
+			gateway->t38_state = T38_STATE_UNKNOWN;
+			gateway->timeout_start = ast_tvnow();
+
+			ast_debug(1, "%s is attempting to negotiate T.38 after we already sent a negotiation request based on CED detection\n", active->name);
+			ao2_ref(details, -1);
+			return &ast_null_frame;
+		} else if (gateway->t38_state == T38_STATE_NEGOTIATED) {
+			/* we got a request to negotiate T.38 after we already
+			 * sent one to the other party based on CED tone
+			 * detection and received a response. We need to
+			 * respond to this and shut down the gateway. */
+
+			t38_parameters_fax_to_ast(control_params, &details->their_t38_parameters);
+			ast_framehook_detach(chan, details->gateway_id);
+			details->gateway_id = -1;
+
+			control_params->request_response = AST_T38_NEGOTIATED;
+
+			fax_gateway_indicate_t38(chan, active, control_params);
+
+			ast_string_field_set(details, result, "SUCCESS");
+			ast_string_field_set(details, resultstr, "no gateway necessary");
+			ast_string_field_set(details, error, "NATIVE_T38");
+			set_channel_variables(chan, details);
+
+			ast_debug(1, "%s is attempting to negotiate T.38 after we already negotiated T.38 with %s, disabling the gateway\n", active->name, other->name);
+			ao2_ref(details, -1);
+			return &ast_null_frame;
+		} else {
+			ast_log(LOG_WARNING, "%s is attempting to negotiate T.38 while %s is in an unsupported state\n", active->name, other->name);
+			ao2_ref(details, -1);
+			return f;
+		}
+	} else if (gateway->t38_state == T38_STATE_NEGOTIATING
+		&& control_params->request_response == AST_T38_REFUSED) {
+
+		ast_debug(1, "unable to negotiate T.38 on %s for fax gateway\n", active->name);
+
+		/* our request to negotiate T.38 was refused, if the other
+		 * channel supports T.38, they might still reinvite and save
+		 * the day.  Otherwise disable the gateway. */
+		if (ast_channel_get_t38_state(other) == T38_STATE_UNKNOWN) {
+			gateway->t38_state = T38_STATE_UNAVAILABLE;
+		} else {
+			ast_framehook_detach(chan, details->gateway_id);
+			details->gateway_id = -1;
+
+			ast_string_field_set(details, result, "FAILED");
+			ast_string_field_set(details, resultstr, "unable to negotiate T.38");
+			ast_string_field_set(details, error, "T38_NEG_ERROR");
+			set_channel_variables(chan, details);
+		}
+
+		ao2_ref(details, -1);
+		return &ast_null_frame;
+	} else if (gateway->t38_state == T38_STATE_NEGOTIATING
+		&& control_params->request_response == AST_T38_NEGOTIATED) {
+
+		ast_debug(1, "starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", active->name, other->name);
+
+		t38_parameters_ast_to_fax(&details->their_t38_parameters, control_params);
+
+		if (fax_gateway_start(gateway, details, chan)) {
+			ast_log(LOG_ERROR, "error starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", active->name, other->name);
+			gateway->t38_state = T38_STATE_NEGOTIATING;
+			control_params->request_response = AST_T38_REQUEST_TERMINATE;
+
+			fax_gateway_indicate_t38(chan, active, control_params);
+		} else {
+			gateway->t38_state = T38_STATE_NEGOTIATED;
+			report_fax_status(chan, details, "T.38 Negotiated");
+		}
+
+		ao2_ref(details, -1);
+		return &ast_null_frame;
+	} else if (control_params->request_response == AST_T38_REFUSED) {
+		/* the other channel refused the request to negotiate T.38,
+		 * we'll step in here and pretend the request was accepted */
+
+		ast_debug(1, "%s attempted to negotiate T.38 but %s refused the request\n", other->name, active->name);
+		ast_debug(1, "starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", other->name, active->name);
+
+		t38_parameters_fax_to_ast(control_params, &details->our_t38_parameters);
+
+		if (fax_gateway_start(gateway, details, chan)) {
+			ast_log(LOG_ERROR, "error starting T.38 gateway for T.38 channel %s and G.711 channel %s\n", active->name, other->name);
+			gateway->t38_state = T38_STATE_REJECTED;
+			control_params->request_response = AST_T38_REFUSED;
+
+			ast_framehook_detach(chan, details->gateway_id);
+			details->gateway_id = -1;
+		} else {
+			gateway->t38_state = T38_STATE_NEGOTIATED;
+			control_params->request_response = AST_T38_NEGOTIATED;
+		}
+
 		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);
-		}
+	} else if (control_params->request_response == AST_T38_REQUEST_TERMINATE) {
+		/* the channel wishes to end our short relationship, we shall
+		 * oblige */
+
+		ast_debug(1, "T.38 channel %s is requesting a shutdown of T.38, disabling the gateway\n", active->name);
+
+		ast_framehook_detach(chan, details->gateway_id);
+		details->gateway_id = -1;
+
+		gateway->t38_state = T38_STATE_REJECTED;
+		control_params->request_response = AST_T38_TERMINATED;
+
+		fax_gateway_indicate_t38(chan, active, control_params);
+
+		ao2_ref(details, -1);
+		return &ast_null_frame;
+	} else if (control_params->request_response == AST_T38_NEGOTIATED) {
+		ast_debug(1, "T.38 successfully negotiated between %s and %s, no gateway necessary\n", active->name, other->name);
+
+		ast_framehook_detach(chan, details->gateway_id);
+		details->gateway_id = -1;
+
+		ast_string_field_set(details, result, "SUCCESS");
+		ast_string_field_set(details, resultstr, "no gateway necessary");
+		ast_string_field_set(details, error, "NATIVE_T38");
+		set_channel_variables(chan, details);
+
+		ao2_ref(details, -1);
+		return f;
+	} else if (control_params->request_response == AST_T38_TERMINATED) {
+		ast_debug(1, "T.38 disabled on channel %s\n", active->name);
+
+		ast_framehook_detach(chan, details->gateway_id);
+		details->gateway_id = -1;
+
+		ao2_ref(details, -1);
+		return &ast_null_frame;
 	}
 
 	ao2_ref(details, -1);
@@ -2515,287 +2834,242 @@
 
 /*! \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;
-	}
+static void fax_gateway_framehook_destroy(void *data) {
+	struct fax_gateway *gateway = data;
 
 	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);
-	}
+		switch (gateway->s->state) {
+		case AST_FAX_STATE_INITIALIZED:
+		case AST_FAX_STATE_OPEN:
+		case AST_FAX_STATE_ACTIVE:
+		case AST_FAX_STATE_COMPLETE:
+			if (gateway->s->tech->cancel_session) {
+				gateway->s->tech->cancel_session(gateway->s);
+			}
+			/* fall through */
+		default:
+			break;
+		}
+	}
+
 	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
+/*! \brief T.30<->T.38 gateway framehook.
+ *
+ * Intercept packets on bridged channels and determine if a T.38 gateway is
+ * required. If a gateway is required, start a gateway and handle T.38
+ * negotiation if necessary.
+ *
+ * \param chan channel running the gateway
  * \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)) {
+ * \param data framehook data (struct fax_gateway *)
+ *
+ * \return processed frame or NULL when f is NULL or a null frame
+ */
+static struct ast_frame *fax_gateway_framehook(struct ast_channel *chan, struct ast_frame *f, enum ast_framehook_event event, void *data) {
+	struct fax_gateway *gateway = data;
+	struct ast_channel *peer, *active;
+
+	/* restore audio formats when we are detached */
+	if (event == AST_FRAMEHOOK_EVENT_DETACHED) {
+		set_channel_variables(chan, gateway->s->details);
+
+		if (gateway->bridged) {
+			ast_set_read_format(chan, gateway->chan_read_format);
+			ast_set_read_format(chan, gateway->chan_write_format);
+
+			if ((peer = ast_bridged_channel(chan))) {
+				ast_set_read_format(peer, gateway->peer_read_format);
+				ast_set_read_format(peer, gateway->peer_write_format);
+				ast_channel_make_compatible(chan, peer);
+			}
+		}
+
+		return NULL;
+	}
+
+	if (!f || (event == AST_FRAMEHOOK_EVENT_ATTACHED)) {
 		return NULL;
 	};
-
-	if (!gateway) {
-		return f;
-	}
 
 	/* this frame was generated by the fax gateway, pass it on */
 	if (ast_test_flag(f, AST_FAX_FRFLAG_GATEWAY)) {
 		return f;
 	}
 
-	peer = ast_bridged_channel(chan);
-	s = gateway->s;
-
-	switch (event) {
-		case AST_FRAMEHOOK_EVENT_WRITE:
-			/* when we become active change the formats to SLIN for CED detect and T.30*/
-			if (peer && !gateway->bridged) {
-				gateway->chan_read_format = chan->readformat;
-				gateway->chan_write_format = chan->writeformat;
-				gateway->peer_read_format = peer->readformat;
-				gateway->peer_write_format = peer->writeformat;
-				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);
+	if (!(peer = ast_bridged_channel(chan))) {
+		/* not bridged, don't do anything */
+		return f;
+	}
+
+	if (!gateway->bridged && peer) {
+		/* don't start a gateway if neither channel can handle T.38 */
+		if (ast_channel_get_t38_state(chan) == T38_STATE_UNAVAILABLE && ast_channel_get_t38_state(peer) == T38_STATE_UNAVAILABLE) {
+			ast_debug(1, "not starting gateway for %s and %s; neither channel supports T.38\n", chan->name, peer->name);
+			ast_framehook_detach(chan, gateway->framehook);
+			gateway->s->details->gateway_id = -1;
+
+			ast_string_field_set(gateway->s->details, result, "FAILED");
+			ast_string_field_set(gateway->s->details, resultstr, "neither channel supports T.38");
+			ast_string_field_set(gateway->s->details, error, "T38_NEG_ERROR");
+			set_channel_variables(chan, gateway->s->details);
 			return f;
-	}
-
-	/* filter frames*/
+		}
+
+		gateway->timeout_start = ast_tvnow();
+
+		/* we are bridged, change r/w formats to SLIN for CED detection and T.30 */
+		gateway->chan_read_format = chan->readformat;
+		gateway->chan_write_format = chan->readformat;
+
+		gateway->peer_read_format = peer->readformat;

[... 784 lines stripped ...]



More information about the asterisk-commits mailing list