aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkpfleming <kpfleming@f38db490-d61c-443f-a65b-d21fe96a405b>2010-03-25 15:27:31 +0000
committerkpfleming <kpfleming@f38db490-d61c-443f-a65b-d21fe96a405b>2010-03-25 15:27:31 +0000
commita37e15e1be76f5fad4416a51fa2e612218eeb1f2 (patch)
treec00be692d3305292c8ad05f0b612de765e577e4c
parent63374e809f387000b500b373afecb6f6a337092d (diff)
Improve handling of T.38 re-INVITEs that arrive before a T.38-capable
application is executing on a channel. This patch addresses an issue found during working with end-users using res_fax. If an incoming call is answered in the dialplan, or jumps to the 'fax' extension due to reception of a CNG tone (with faxdetect enabled), and then the remote endpoint sends a T.38 re-INVITE, it is possible for the channel's T.38 state to be 'T38_STATE_NEGOTIATING' when the application starts up. Unfortunately, even if the application wants to use T.38, it can't respond to the peer's negotiation request, because the AST_CONTROL_T38_PARAMETERS control frame that chan_sip sent originally has been lost, and the application needs the content of that frame to be able to formulate a reply. This patch adds a new 'request' type to AST_CONTROL_T38_PARAMETERS, AST_T38_REQUEST_PARMS. If the application sends this request, chan_sip will re-send the original control frame (with AST_T38_REQUEST_NEGOTIATE as the request type), and the application can respond as normal. If this occurs within the five second timeout in chan_sip, the automatic cancellation of the peer reinvite will be stopped, and the application will 'own' the negotiation process from that point onwards. This also improves the code path in chan_sip to allow sip_indicate(), when called for AST_CONTROL_T38_PARAMETERS, to be able to return a non-zero response, which should have been in place before since the control frame *can* fail to be processed properly. It also modifies ast_indicate() to return whatever result the channel driver returned for this control frame, rather than converting all non-zero results into '-1'. Finally, the new request type intentionally returns a positive value, so that an application that sends AST_T38_REQUEST_PARMS can know for certain whether the channel driver accepted it and will be replying with a control frame of its own, or whether it was ignored (if the sip_indicate()/ast_indicate() path had properly supported failure responses before, this would not be necessary). This patch also modifies res_fax to take advantage of the new request. In addition, this patch makes sip_t38_abort() actually lock the private structure before doing its work... bad programmer, no donut. This patch also enhances chan_sip's 'faxdetect' support to allow triggering on T.38 re-INVITEs received as well as CNG tone detection. Review: https://reviewboard.asterisk.org/r/556/ git-svn-id: http://svn.digium.com/svn/asterisk/trunk@254450 f38db490-d61c-443f-a65b-d21fe96a405b
-rw-r--r--channels/chan_sip.c110
-rw-r--r--channels/sip/include/sip.h78
-rw-r--r--configs/sip.conf.sample8
-rw-r--r--include/asterisk/frame.h3
-rw-r--r--main/channel.c7
-rw-r--r--res/res_fax.c101
6 files changed, 207 insertions, 100 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index be9db0776..9cd0608d9 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -3052,7 +3052,7 @@ static void enable_dsp_detect(struct sip_pvt *p)
}
}
- if (ast_test_flag(&p->flags[1], SIP_PAGE2_FAX_DETECT)) {
+ if (ast_test_flag(&p->flags[1], SIP_PAGE2_FAX_DETECT_CNG)) {
features |= DSP_FEATURE_FAX_DETECT;
}
@@ -4163,6 +4163,11 @@ static int sip_call(struct ast_channel *ast, char *dest, int timeout)
res = 0;
ast_set_flag(&p->flags[0], SIP_OUTGOING);
+ /* T.38 re-INVITE FAX detection should never be done for outgoing calls,
+ * so ensure it is disabled.
+ */
+ ast_clear_flag(&p->flags[1], SIP_PAGE2_FAX_DETECT_T38);
+
if (p->options->transfer) {
char buf[SIPBUFSIZE/2];
@@ -5199,22 +5204,24 @@ static int sip_transfer(struct ast_channel *ast, const char *dest)
}
/*! \brief Helper function which updates T.38 capability information and triggers a reinvite */
-static void interpret_t38_parameters(struct sip_pvt *p, const struct ast_control_t38_parameters *parameters)
+static int interpret_t38_parameters(struct sip_pvt *p, const struct ast_control_t38_parameters *parameters)
{
+ int res = 0;
+
if (!ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT)) {
- return;
+ return -1;
}
switch (parameters->request_response) {
case AST_T38_NEGOTIATED:
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);
- }
- break;
+ 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);
+ }
+ 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"));
p->t38.our_parms = *parameters;
@@ -5256,9 +5263,28 @@ static void interpret_t38_parameters(struct sip_pvt *p, const struct ast_control
} else if (p->t38.state == T38_ENABLED)
transmit_reinvite_with_sdp(p, FALSE, FALSE);
break;
+ case AST_T38_REQUEST_PARMS: { /* Application wants remote's parameters re-sent */
+ struct ast_control_t38_parameters parameters = p->t38.their_parms;
+
+ if (p->t38.state == T38_PEER_REINVITE) {
+ AST_SCHED_DEL(sched, p->t38id);
+ parameters.max_ifp = ast_udptl_get_far_max_ifp(p->udptl);
+ parameters.request_response = AST_T38_REQUEST_NEGOTIATE;
+ ast_queue_control_data(p->owner, AST_CONTROL_T38_PARAMETERS, &parameters, sizeof(parameters));
+ /* we need to return a positive value here, so that applications that
+ * send this request can determine conclusively whether it was accepted or not...
+ * older versions of chan_sip would just silently accept it and return zero.
+ */
+ res = AST_T38_REQUEST_PARMS;
+ }
+ break;
+ }
default:
+ res = -1;
break;
}
+
+ return res;
}
/*! \brief Play indication to user
@@ -5348,9 +5374,10 @@ static int sip_indicate(struct ast_channel *ast, int condition, const void *data
case AST_CONTROL_T38_PARAMETERS:
if (datalen != sizeof(struct ast_control_t38_parameters)) {
ast_log(LOG_ERROR, "Invalid datalen for AST_CONTROL_T38_PARAMETERS. Expected %d, got %d\n", (int) sizeof(struct ast_control_t38_parameters), (int) datalen);
+ res = -1;
} else {
const struct ast_control_t38_parameters *parameters = data;
- interpret_t38_parameters(p, parameters);
+ res = interpret_t38_parameters(p, parameters);
}
break;
case AST_CONTROL_SRCUPDATE:
@@ -5838,20 +5865,20 @@ static struct ast_frame *sip_read(struct ast_channel *ast)
p->lastrtprx = time(NULL);
/* If we detect a CNG tone and fax detection is enabled then send us off to the fax extension */
- if (faxdetected && ast_test_flag(&p->flags[1], SIP_PAGE2_FAX_DETECT)) {
+ if (faxdetected && ast_test_flag(&p->flags[1], SIP_PAGE2_FAX_DETECT_CNG)) {
ast_channel_lock(ast);
if (strcmp(ast->exten, "fax")) {
const char *target_context = S_OR(ast->macrocontext, ast->context);
ast_channel_unlock(ast);
if (ast_exists_extension(ast, target_context, "fax", 1, ast->cid.cid_num)) {
- ast_verbose(VERBOSE_PREFIX_2 "Redirecting '%s' to fax extension\n", ast->name);
+ ast_verbose(VERBOSE_PREFIX_2 "Redirecting '%s' to fax extension due to CNG detection\n", ast->name);
pbx_builtin_setvar_helper(ast, "FAXEXTEN", ast->exten);
if (ast_async_goto(ast, target_context, "fax", 1)) {
ast_log(LOG_NOTICE, "Failed to async goto '%s' into fax of '%s'\n", ast->name, target_context);
}
fr = &ast_null_frame;
} else {
- ast_log(LOG_NOTICE, "Fax detected but no fax extension\n");
+ ast_log(LOG_NOTICE, "FAX CNG detected but no fax extension\n");
}
} else {
ast_channel_unlock(ast);
@@ -7154,6 +7181,25 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req, int t38action
} else if ((t38action == SDP_T38_INITIATE) &&
p->owner && p->lastinvite) {
change_t38_state(p, T38_PEER_REINVITE); /* T38 Offered in re-invite from remote party */
+ /* If fax detection is enabled then send us off to the fax extension */
+ if (ast_test_flag(&p->flags[1], SIP_PAGE2_FAX_DETECT_T38)) {
+ ast_channel_lock(p->owner);
+ if (strcmp(p->owner->exten, "fax")) {
+ const char *target_context = S_OR(p->owner->macrocontext, p->owner->context);
+ ast_channel_unlock(p->owner);
+ if (ast_exists_extension(p->owner, target_context, "fax", 1, p->owner->cid.cid_num)) {
+ ast_verbose(VERBOSE_PREFIX_2 "Redirecting '%s' to fax extension due to peer T.38 re-INVITE\n", p->owner->name);
+ pbx_builtin_setvar_helper(p->owner, "FAXEXTEN", p->owner->exten);
+ if (ast_async_goto(p->owner, target_context, "fax", 1)) {
+ ast_log(LOG_NOTICE, "Failed to async goto '%s' into fax of '%s'\n", p->owner->name, target_context);
+ }
+ } else {
+ ast_log(LOG_NOTICE, "T.38 re-INVITE detected but no fax extension\n");
+ }
+ } else {
+ ast_channel_unlock(p->owner);
+ }
+ }
}
} else {
ast_udptl_stop(p->udptl);
@@ -18825,15 +18871,24 @@ static int do_magic_pickup(struct ast_channel *channel, const char *extension, c
return 0;
}
+/*! \brief Called to deny a T38 reinvite if the core does not respond to our request */
static int sip_t38_abort(const void *data)
{
struct sip_pvt *p = (struct sip_pvt *) data;
- change_t38_state(p, T38_DISABLED);
- 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");
-
+ sip_pvt_lock(p);
+ /* an application may have taken ownership of the T.38 negotiation on this
+ * channel while we were waiting to grab the lock... if it did, the scheduler
+ * id will have been reset to -1, which is our indication that we do *not*
+ * want to abort the negotiation process
+ */
+ if (p->t38id != -1) {
+ change_t38_state(p, T38_DISABLED);
+ 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");
+ }
+ sip_pvt_unlock(p);
return 0;
}
@@ -22603,7 +22658,24 @@ static int handle_common_options(struct ast_flags *flags, struct ast_flags *mask
ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_IGNORESDPVERSION);
} else if (!strcasecmp(v->name, "faxdetect")) {
ast_set_flag(&mask[1], SIP_PAGE2_FAX_DETECT);
- ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_FAX_DETECT);
+ if (ast_true(v->value)) {
+ ast_set_flag(&flags[1], SIP_PAGE2_FAX_DETECT_BOTH);
+ } else if (ast_false(v->value)) {
+ ast_clear_flag(&flags[1], SIP_PAGE2_FAX_DETECT_BOTH);
+ } else {
+ char *buf = ast_strdupa(v->value);
+ char *word, *next = buf;
+
+ while ((word = strsep(&next, ","))) {
+ if (!strcasecmp(word, "cng")) {
+ ast_set_flag(&flags[1], SIP_PAGE2_FAX_DETECT_CNG);
+ } else if (!strcasecmp(word, "t38")) {
+ ast_set_flag(&flags[1], SIP_PAGE2_FAX_DETECT_T38);
+ } else {
+ ast_log(LOG_WARNING, "Unknown faxdetect mode '%s' on line %d.\n", word, v->lineno);
+ }
+ }
+ }
} else if (!strcasecmp(v->name, "rfc2833compensate")) {
ast_set_flag(&mask[1], SIP_PAGE2_RFC2833_COMPENSATE);
ast_set2_flag(&flags[1], ast_true(v->value), SIP_PAGE2_RFC2833_COMPENSATE);
diff --git a/channels/sip/include/sip.h b/channels/sip/include/sip.h
index 57fac84ca..ce87f0f23 100644
--- a/channels/sip/include/sip.h
+++ b/channels/sip/include/sip.h
@@ -302,43 +302,47 @@
a second page of flags (for flags[1] */
/*@{*/
/* realtime flags */
-#define SIP_PAGE2_RTCACHEFRIENDS (1 << 0) /*!< GP: Should we keep RT objects in memory for extended time? */
-#define SIP_PAGE2_RTAUTOCLEAR (1 << 2) /*!< GP: Should we clean memory from peers after expiry? */
-#define SIP_PAGE2_RPID_UPDATE (1 << 3)
-#define SIP_PAGE2_Q850_REASON (1 << 4) /*!< DP: Get/send cause code via Reason header */
-
-/* Space for addition of other realtime flags in the future */
-#define SIP_PAGE2_SYMMETRICRTP (1 << 8) /*!< GDP: Whether symmetric RTP is enabled or not */
-#define SIP_PAGE2_STATECHANGEQUEUE (1 << 9) /*!< D: Unsent state pending change exists */
-
-#define SIP_PAGE2_CONNECTLINEUPDATE_PEND (1 << 10)
-#define SIP_PAGE2_RPID_IMMEDIATE (1 << 11)
-#define SIP_PAGE2_RPORT_PRESENT (1 << 12) /*!< Was rport received in the Via header? */
-#define SIP_PAGE2_PREFERRED_CODEC (1 << 13) /*!< GDP: Only respond with single most preferred joint codec */
-#define SIP_PAGE2_VIDEOSUPPORT (1 << 14) /*!< DP: Video supported if offered? */
-#define SIP_PAGE2_TEXTSUPPORT (1 << 15) /*!< GDP: Global text enable */
-#define SIP_PAGE2_ALLOWSUBSCRIBE (1 << 16) /*!< GP: Allow subscriptions from this peer? */
-#define SIP_PAGE2_ALLOWOVERLAP (1 << 17) /*!< DP: Allow overlap dialing ? */
-#define SIP_PAGE2_SUBSCRIBEMWIONLY (1 << 18) /*!< GP: Only issue MWI notification if subscribed to */
-#define SIP_PAGE2_IGNORESDPVERSION (1 << 19) /*!< GDP: Ignore the SDP session version number we receive and treat all sessions as new */
-
-#define SIP_PAGE2_T38SUPPORT (3 << 20) /*!< GDP: T.38 Fax Support */
-#define SIP_PAGE2_T38SUPPORT_UDPTL (1 << 20) /*!< GDP: T.38 Fax Support (no error correction) */
-#define SIP_PAGE2_T38SUPPORT_UDPTL_FEC (2 << 20) /*!< GDP: T.38 Fax Support (FEC error correction) */
-#define SIP_PAGE2_T38SUPPORT_UDPTL_REDUNDANCY (3 << 20) /*!< GDP: T.38 Fax Support (redundancy error correction) */
-
-#define SIP_PAGE2_CALL_ONHOLD (3 << 23) /*!< D: Call hold states: */
-#define SIP_PAGE2_CALL_ONHOLD_ACTIVE (1 << 23) /*!< D: Active hold */
-#define SIP_PAGE2_CALL_ONHOLD_ONEDIR (2 << 23) /*!< D: One directional hold */
-#define SIP_PAGE2_CALL_ONHOLD_INACTIVE (3 << 23) /*!< D: Inactive hold */
-
-#define SIP_PAGE2_RFC2833_COMPENSATE (1 << 25) /*!< DP: Compensate for buggy RFC2833 implementations */
-#define SIP_PAGE2_BUGGY_MWI (1 << 26) /*!< DP: Buggy CISCO MWI fix */
-#define SIP_PAGE2_DIALOG_ESTABLISHED (1 << 27) /*!< 29: Has a dialog been established? */
-#define SIP_PAGE2_FAX_DETECT (1 << 28) /*!< DP: Fax Detection support */
-#define SIP_PAGE2_REGISTERTRYING (1 << 29) /*!< DP: Send 100 Trying on REGISTER attempts */
-#define SIP_PAGE2_UDPTL_DESTINATION (1 << 30) /*!< DP: Use source IP of RTP as destination if NAT is enabled */
-#define SIP_PAGE2_VIDEOSUPPORT_ALWAYS (1 << 31) /*!< DP: Always set up video, even if endpoints don't support it */
+#define SIP_PAGE2_RTCACHEFRIENDS (1 << 0) /*!< GP: Should we keep RT objects in memory for extended time? */
+#define SIP_PAGE2_RTAUTOCLEAR (1 << 1) /*!< GP: Should we clean memory from peers after expiry? */
+#define SIP_PAGE2_RPID_UPDATE (1 << 2)
+#define SIP_PAGE2_Q850_REASON (1 << 3) /*!< DP: Get/send cause code via Reason header */
+
+#define SIP_PAGE2_SYMMETRICRTP (1 << 4) /*!< GDP: Whether symmetric RTP is enabled or not */
+#define SIP_PAGE2_STATECHANGEQUEUE (1 << 5) /*!< D: Unsent state pending change exists */
+
+#define SIP_PAGE2_CONNECTLINEUPDATE_PEND (1 << 6)
+#define SIP_PAGE2_RPID_IMMEDIATE (1 << 7)
+#define SIP_PAGE2_RPORT_PRESENT (1 << 8) /*!< Was rport received in the Via header? */
+#define SIP_PAGE2_PREFERRED_CODEC (1 << 9) /*!< GDP: Only respond with single most preferred joint codec */
+#define SIP_PAGE2_VIDEOSUPPORT (1 << 10) /*!< DP: Video supported if offered? */
+#define SIP_PAGE2_TEXTSUPPORT (1 << 11) /*!< GDP: Global text enable */
+#define SIP_PAGE2_ALLOWSUBSCRIBE (1 << 12) /*!< GP: Allow subscriptions from this peer? */
+#define SIP_PAGE2_ALLOWOVERLAP (1 << 13) /*!< DP: Allow overlap dialing ? */
+#define SIP_PAGE2_SUBSCRIBEMWIONLY (1 << 14) /*!< GP: Only issue MWI notification if subscribed to */
+#define SIP_PAGE2_IGNORESDPVERSION (1 << 15) /*!< GDP: Ignore the SDP session version number we receive and treat all sessions as new */
+
+#define SIP_PAGE2_T38SUPPORT (3 << 16) /*!< GDP: T.38 Fax Support */
+#define SIP_PAGE2_T38SUPPORT_UDPTL (1 << 16) /*!< GDP: T.38 Fax Support (no error correction) */
+#define SIP_PAGE2_T38SUPPORT_UDPTL_FEC (2 << 16) /*!< GDP: T.38 Fax Support (FEC error correction) */
+#define SIP_PAGE2_T38SUPPORT_UDPTL_REDUNDANCY (3 << 16) /*!< GDP: T.38 Fax Support (redundancy error correction) */
+
+#define SIP_PAGE2_CALL_ONHOLD (3 << 18) /*!< D: Call hold states: */
+#define SIP_PAGE2_CALL_ONHOLD_ACTIVE (1 << 18) /*!< D: Active hold */
+#define SIP_PAGE2_CALL_ONHOLD_ONEDIR (2 << 18) /*!< D: One directional hold */
+#define SIP_PAGE2_CALL_ONHOLD_INACTIVE (3 << 18) /*!< D: Inactive hold */
+
+#define SIP_PAGE2_RFC2833_COMPENSATE (1 << 20) /*!< DP: Compensate for buggy RFC2833 implementations */
+#define SIP_PAGE2_BUGGY_MWI (1 << 21) /*!< DP: Buggy CISCO MWI fix */
+#define SIP_PAGE2_DIALOG_ESTABLISHED (1 << 22) /*!< 29: Has a dialog been established? */
+
+#define SIP_PAGE2_FAX_DETECT (3 << 23) /*!< DP: Fax Detection support */
+#define SIP_PAGE2_FAX_DETECT_CNG (1 << 23) /*!< DP: Fax Detection support - detect CNG in audio */
+#define SIP_PAGE2_FAX_DETECT_T38 (2 << 23) /*!< DP: Fax Detection support - detect T.38 reinvite from peer */
+#define SIP_PAGE2_FAX_DETECT_BOTH (3 << 23) /*!< DP: Fax Detection support - detect both */
+
+#define SIP_PAGE2_REGISTERTRYING (1 << 24) /*!< DP: Send 100 Trying on REGISTER attempts */
+#define SIP_PAGE2_UDPTL_DESTINATION (1 << 25) /*!< DP: Use source IP of RTP as destination if NAT is enabled */
+#define SIP_PAGE2_VIDEOSUPPORT_ALWAYS (1 << 26) /*!< DP: Always set up video, even if endpoints don't support it */
#define SIP_PAGE2_FLAGS_TO_COPY \
(SIP_PAGE2_ALLOWSUBSCRIBE | SIP_PAGE2_ALLOWOVERLAP | SIP_PAGE2_IGNORESDPVERSION | \
diff --git a/configs/sip.conf.sample b/configs/sip.conf.sample
index a2d25f261..a1ed83cd2 100644
--- a/configs/sip.conf.sample
+++ b/configs/sip.conf.sample
@@ -533,9 +533,13 @@ srvlookup=yes ; Enable DNS SRV lookups on outbound calls
; ; send 400 byte T.38 FAX packets to it.
;
; FAX detection will cause the SIP channel to jump to the 'fax' extension (if it exists)
-; when a CNG tone is detected on an incoming call.
+; based one or more events being detected. The events that can be detected are an incoming
+; CNG tone or an incoming T.38 re-INVITE request.
;
-; faxdetect = yes ; Default false
+; faxdetect = yes ; Default 'no', 'yes' enables both CNG and T.38 detection
+; faxdetect = cng ; Enables only CNG detection
+; faxdetect = t38 ; Enables only T.38 detection
+; faxdetect = both ; Enables both CNG and T.38 detection (same as 'yes')
;
;----------------------------------------- OUTBOUND SIP REGISTRATIONS ------------------------
; Asterisk can register as a SIP user agent to a SIP proxy (provider)
diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h
index 6f45386cd..30119d49b 100644
--- a/include/asterisk/frame.h
+++ b/include/asterisk/frame.h
@@ -332,7 +332,8 @@ enum ast_control_t38 {
AST_T38_REQUEST_TERMINATE, /*!< Terminate T38 on a channel (fax to voice) */
AST_T38_NEGOTIATED, /*!< T38 negotiated (fax mode) */
AST_T38_TERMINATED, /*!< T38 terminated (back to voice) */
- AST_T38_REFUSED /*!< T38 refused for some reason (usually rejected by remote end) */
+ AST_T38_REFUSED, /*!< T38 refused for some reason (usually rejected by remote end) */
+ AST_T38_REQUEST_PARMS, /*!< request far end T.38 parameters for a channel in 'negotiating' state */
};
enum ast_control_t38_rate {
diff --git a/main/channel.c b/main/channel.c
index 191a4298f..892d0306a 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -3715,9 +3715,10 @@ int ast_indicate_data(struct ast_channel *chan, int _condition,
* control frames, so we need to return failure, but there
* is also no value in the log message below being emitted
* since failure to handle these frames is not an 'error'
- * so just return right now.
- */
- return -1;
+ * so just return right now. in addition, we want to return
+ * whatever value the channel driver returned, in case it
+ * has some meaning.*/
+ return res;
case AST_CONTROL_RINGING:
ts = ast_get_indication_tone(chan->zone, "ring");
/* It is common practice for channel drivers to return -1 if trying
diff --git a/res/res_fax.c b/res/res_fax.c
index 55e30b0c2..0b04b1b31 100644
--- a/res/res_fax.c
+++ b/res/res_fax.c
@@ -654,6 +654,7 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
struct ast_channel *c = chan;
unsigned int orig_write_format = 0, orig_read_format = 0;
unsigned int request_t38 = 0;
+ unsigned int send_audio = 1;
details->our_t38_parameters.version = 0;
details->our_t38_parameters.max_ifp = 400;
@@ -662,8 +663,57 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
chancount = 1;
- /* generate 3 seconds of CED if we are in receive mode */
- if (details->caps & AST_FAX_TECH_RECEIVE) {
+ switch ((t38_state = ast_channel_get_t38_state(chan))) {
+ case T38_STATE_UNKNOWN:
+ if (details->caps & AST_FAX_TECH_SEND) {
+ if (details->option.allow_audio) {
+ details->caps |= AST_FAX_TECH_AUDIO;
+ } else {
+ /* we are going to send CNG to attempt to stimulate the receiver
+ * into switching to T.38, since audio mode is not allowed
+ */
+ send_cng = 0;
+ }
+ } else {
+ /* we *always* request a switch to T.38 if allowed; if audio is also
+ * allowed, then we will allow the switch to happen later if needed
+ */
+ if (details->option.allow_audio) {
+ details->caps |= AST_FAX_TECH_AUDIO;
+ }
+ request_t38 = 1;
+ }
+ details->caps |= AST_FAX_TECH_T38;
+ break;
+ case T38_STATE_UNAVAILABLE:
+ details->caps |= AST_FAX_TECH_AUDIO;
+ break;
+ 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
+ * not, we can't proceed, because we can't create a proper reply without them.
+ * if it does work, the channel driver will send an AST_CONTROL_T38_PARAMETERS
+ * with a request of AST_T38_REQUEST_NEGOTIATE, which will be read by the function
+ * that gets called after this one completes
+ */
+ struct ast_control_t38_parameters parameters = { .request_response = AST_T38_REQUEST_PARMS, };
+ ast_log(LOG_NOTICE, "Channel is already in T.38 negotiation state; retrieving remote parameters.\n");
+ if (ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &parameters, sizeof(parameters)) != AST_T38_REQUEST_PARMS) {
+ ast_log(LOG_ERROR, "channel '%s' is in an unsupported T.38 negotiation state, cannot continue.\n", chan->name);
+ return -1;
+ }
+ details->caps |= AST_FAX_TECH_T38;
+ details->option.allow_audio = 0;
+ send_audio = 0;
+ break;
+ }
+ default:
+ ast_log(LOG_ERROR, "channel '%s' is in an unsupported T.38 negotiation state, cannot continue.\n", chan->name);
+ return -1;
+ }
+
+ /* generate 3 seconds of CED if we are in receive mode and not already negotiating T.38 */
+ if (send_audio && (details->caps & AST_FAX_TECH_RECEIVE)) {
ms = 3000;
if (ast_tonepair_start(chan, 2100, 0, ms, 0)) {
ast_log(LOG_ERROR, "error generating CED tone on %s\n", chan->name);
@@ -719,36 +769,6 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
ast_tonepair_stop(chan);
}
- switch ((t38_state = ast_channel_get_t38_state(chan))) {
- case T38_STATE_UNKNOWN:
- if (details->caps & AST_FAX_TECH_SEND) {
- if (details->option.allow_audio) {
- details->caps |= AST_FAX_TECH_AUDIO;
- } else {
- /* we are going to send CNG to attempt to stimulate the receiver
- * into switching to T.38, since audio mode is not allowed
- */
- send_cng = 0;
- }
- } else {
- /* we *always* request a switch to T.38 if allowed; if audio is also
- * allowed, then we will allow the switch to happen later if needed
- */
- if (details->option.allow_audio) {
- details->caps |= AST_FAX_TECH_AUDIO;
- }
- request_t38 = 1;
- }
- details->caps |= AST_FAX_TECH_T38;
- break;
- case T38_STATE_UNAVAILABLE:
- details->caps |= AST_FAX_TECH_AUDIO;
- break;
- default:
- ast_log(LOG_ERROR, "channel '%s' is in an unsupported T.38 negotiation state, cannot continue.\n", chan->name);
- return -1;
- }
-
if (request_t38) {
/* wait up to five seconds for negotiation to complete */
timeout = 5000;
@@ -772,19 +792,23 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
if (request_t38 || !details->option.allow_audio) {
struct ast_silence_generator *silence_gen = NULL;
- if (send_cng != -1) {
+ if (send_audio && (send_cng != -1)) {
silence_gen = ast_channel_start_silence_generator(chan);
}
while (timeout > 0) {
if (send_cng > 3000) {
- ast_channel_stop_silence_generator(chan, silence_gen);
- silence_gen = NULL;
- ast_tonepair_start(chan, 1100, 0, 500, 0);
+ if (send_audio) {
+ ast_channel_stop_silence_generator(chan, silence_gen);
+ silence_gen = NULL;
+ ast_tonepair_start(chan, 1100, 0, 500, 0);
+ }
send_cng = 0;
} else if (!chan->generator && (send_cng != -1)) {
- /* The CNG tone is done so restart silence generation. */
- silence_gen = ast_channel_start_silence_generator(chan);
+ if (send_audio) {
+ /* The CNG tone is done so restart silence generation. */
+ silence_gen = ast_channel_start_silence_generator(chan);
+ }
}
/* this timeout *MUST* be 500ms, in order to keep the spacing
* of CNG tones correct when this loop is sending them
@@ -828,6 +852,7 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det
t38_parameters.request_response = AST_T38_NEGOTIATED;
ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters));
stop = 0;
+ send_audio = 0;
break;
case AST_T38_NEGOTIATED:
ast_log(LOG_NOTICE, "Negotiated T.38 for %s on %s\n", (details->caps & AST_FAX_TECH_SEND) ? "send" : "receive", chan->name);