[asterisk-commits] irroot: branch irroot/t38gateway-1.8 r325995 - in /team/irroot/t38gateway-1.8...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Fri Jul 1 02:59:31 CDT 2011
Author: irroot
Date: Fri Jul 1 02:59:27 2011
New Revision: 325995
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=325995
Log:
Backport recent changes from trunk branch
Modified:
team/irroot/t38gateway-1.8/CHANGES
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
Modified: team/irroot/t38gateway-1.8/CHANGES
URL: http://svnview.digium.com/svn/asterisk/team/irroot/t38gateway-1.8/CHANGES?view=diff&rev=325995&r1=325994&r2=325995
==============================================================================
--- team/irroot/t38gateway-1.8/CHANGES (original)
+++ team/irroot/t38gateway-1.8/CHANGES Fri Jul 1 02:59:27 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 ----------------
@@ -78,6 +84,7 @@
* Addition of the 'auth_options_requests' option for turning on and off
authentication for OPTIONS requests in chan_sip.
* Add T38 support for REJECTED state where T.38 Negotiation is explicitly rejected.
+
IAX2 Changes
-----------
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=325995&r1=325994&r2=325995
==============================================================================
--- team/irroot/t38gateway-1.8/include/asterisk/res_fax.h (original)
+++ team/irroot/t38gateway-1.8/include/asterisk/res_fax.h Fri Jul 1 02:59:27 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/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=325995&r1=325994&r2=325995
==============================================================================
--- team/irroot/t38gateway-1.8/res/res_fax.c (original)
+++ team/irroot/t38gateway-1.8/res/res_fax.c Fri Jul 1 02:59:27 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;
+
[... 785 lines stripped ...]
More information about the asterisk-commits
mailing list