[svn-commits] kpfleming: trunk r254450 - in /trunk: channels/ channels/sip/include/ configs...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Thu Mar 25 10:27:35 CDT 2010


Author: kpfleming
Date: Thu Mar 25 10:27:31 2010
New Revision: 254450

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=254450
Log:
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/


Modified:
    trunk/channels/chan_sip.c
    trunk/channels/sip/include/sip.h
    trunk/configs/sip.conf.sample
    trunk/include/asterisk/frame.h
    trunk/main/channel.c
    trunk/res/res_fax.c

Modified: trunk/channels/chan_sip.c
URL: http://svnview.digium.com/svn/asterisk/trunk/channels/chan_sip.c?view=diff&rev=254450&r1=254449&r2=254450
==============================================================================
--- trunk/channels/chan_sip.c (original)
+++ trunk/channels/chan_sip.c Thu Mar 25 10:27:31 2010
@@ -3052,7 +3052,7 @@
                 }
 	}
 
-	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;
 	}
 
@@ -4162,6 +4162,11 @@
 
 	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 @@
 }
 
 /*! \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 @@
 		} 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 @@
 	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 @@
 	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 @@
 			} 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 @@
 	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 @@
 		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);

Modified: trunk/channels/sip/include/sip.h
URL: http://svnview.digium.com/svn/asterisk/trunk/channels/sip/include/sip.h?view=diff&rev=254450&r1=254449&r2=254450
==============================================================================
--- trunk/channels/sip/include/sip.h (original)
+++ trunk/channels/sip/include/sip.h Thu Mar 25 10:27:31 2010
@@ -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 | \

Modified: trunk/configs/sip.conf.sample
URL: http://svnview.digium.com/svn/asterisk/trunk/configs/sip.conf.sample?view=diff&rev=254450&r1=254449&r2=254450
==============================================================================
--- trunk/configs/sip.conf.sample (original)
+++ trunk/configs/sip.conf.sample Thu Mar 25 10:27:31 2010
@@ -533,9 +533,13 @@
 ;                                       ; 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.
-;
-; faxdetect = yes              ; Default false
+; 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 '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)

Modified: trunk/include/asterisk/frame.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/frame.h?view=diff&rev=254450&r1=254449&r2=254450
==============================================================================
--- trunk/include/asterisk/frame.h (original)
+++ trunk/include/asterisk/frame.h Thu Mar 25 10:27:31 2010
@@ -332,7 +332,8 @@
 	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 {

Modified: trunk/main/channel.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/channel.c?view=diff&rev=254450&r1=254449&r2=254450
==============================================================================
--- trunk/main/channel.c (original)
+++ trunk/main/channel.c Thu Mar 25 10:27:31 2010
@@ -3715,9 +3715,10 @@
 		 * 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

Modified: trunk/res/res_fax.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_fax.c?view=diff&rev=254450&r1=254449&r2=254450
==============================================================================
--- trunk/res/res_fax.c (original)
+++ trunk/res/res_fax.c Thu Mar 25 10:27:31 2010
@@ -654,6 +654,7 @@
 	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 @@
 
 	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 @@
 		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 @@
 	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 @@
 					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);




More information about the svn-commits mailing list