Merged revisions 328247 via svnmerge from
[asterisk/asterisk.git] / res / res_fax.c
index 06b39e0..8073f2d 100644 (file)
  * \ingroup applications
  */
 
+/*** MODULEINFO
+       <support_level>core</support_level>
+ ***/
+
 #include "asterisk.h"
 
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
@@ -253,6 +257,8 @@ struct fax_gateway {
        int framehook;
        /*! \brief bridged */
        int bridged:1;
+       /*! \brief 1 if a v21 preamble has been detected */
+       int detected_v21:1;
        /*! \brief a flag to track the state of our negotiation */
        enum ast_t38_state t38_state;
        /*! \brief original audio formats */
@@ -2407,10 +2413,10 @@ static struct fax_gateway *fax_gateway_new(struct ast_fax_session_details *detai
        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_faxmode(gateway->chan_dsp, DSP_FAXMODE_DETECT_V21);
 
        ast_dsp_set_features(gateway->peer_dsp, DSP_FEATURE_FAX_DETECT);
-       ast_dsp_set_faxmode(gateway->peer_dsp, DSP_FAXMODE_DETECT_CED);
+       ast_dsp_set_faxmode(gateway->peer_dsp, DSP_FAXMODE_DETECT_V21);
 
        details->caps = AST_FAX_TECH_GATEWAY;
        if (!(gateway->s = fax_session_reserve(details, &gateway->token))) {
@@ -2465,57 +2471,68 @@ static int fax_gateway_start(struct fax_gateway *gateway, struct ast_fax_session
        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)
+static struct ast_frame *fax_gateway_request_t38(struct fax_gateway *gateway, struct ast_channel *chan, 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;
+       struct ast_frame *fp;
+       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,
+       };
 
-       if (!dfr) {
+       struct ast_fax_session_details *details = find_details(chan);
+
+       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;
        }
 
-       if (!(dfr = ast_dsp_process(active, active_dsp, dfr))) {
+       t38_parameters_fax_to_ast(&t38_parameters, &details->our_t38_parameters);
+       ao2_ref(details, -1);
+
+       if (!(fp = 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;
        }
 
-       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,
-                       };
+       gateway->t38_state = T38_STATE_NEGOTIATING;
+       gateway->timeout_start = ast_tvnow();
 
-                       struct ast_fax_session_details *details = find_details(chan);
-                       ast_frfree(dfr);
+       ast_debug(1, "requesting T.38 for gateway session for %s\n", chan->name);
+       return fp;
+}
 
-                       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;
-                       }
+static struct ast_frame *fax_gateway_detect_v21(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;
 
-                       t38_parameters_fax_to_ast(&t38_parameters, &details->our_t38_parameters);
-                       ao2_ref(details, -1);
+       if (gateway->detected_v21) {
+               return f;
+       }
 
-                       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;
-                       }
+       if (!dfr) {
+               return f;
+       }
 
-                       gateway->t38_state = T38_STATE_NEGOTIATING;
-                       gateway->timeout_start = ast_tvnow();
+       if (!(dfr = ast_dsp_process(active, active_dsp, dfr))) {
+               return f;
+       }
 
-                       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;
+       if (dfr->frametype == AST_FRAME_DTMF && dfr->subclass.integer == 'g') {
+               gateway->detected_v21 = 1;
+               if (ast_channel_get_t38_state(other) == T38_STATE_UNKNOWN) {
+                       ast_debug(1, "detected v21 preamble from %s\n", active->name);
+                       return fax_gateway_request_t38(gateway, chan, f);
                } 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_debug(1, "detected v21 preamble on %s, but %s does not support T.38 for T.38 gateway session\n", active->name, other->name);
                }
        }
 
@@ -2568,6 +2585,7 @@ static struct ast_frame *fax_gateway_detect_t38(struct fax_gateway *gateway, str
 
        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
@@ -2607,7 +2625,7 @@ static struct ast_frame *fax_gateway_detect_t38(struct fax_gateway *gateway, str
                        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
+                        * sent one to the other party based on v21 preamble
                         * detection. We'll just pretend we passed this request
                         * through in the first place. */
 
@@ -2615,12 +2633,12 @@ static struct ast_frame *fax_gateway_detect_t38(struct fax_gateway *gateway, str
                        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);
+                       ast_debug(1, "%s is attempting to negotiate T.38 after we already sent a negotiation request based on v21 preamble 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
+                        * sent one to the other party based on v21 preamble
                         * detection and received a response. We need to
                         * respond to this and shut down the gateway. */
 
@@ -2841,7 +2859,8 @@ static struct ast_frame *fax_gateway_framehook(struct ast_channel *chan, struct
 
                gateway->timeout_start = ast_tvnow();
 
-               /* we are bridged, change r/w formats to SLIN for CED detection and T.30 */
+               /* we are bridged, change r/w formats to SLIN for v21 preamble
+                * detection and T.30 */
                ast_format_copy(&gateway->chan_read_format, &chan->readformat);
                ast_format_copy(&gateway->chan_write_format, &chan->readformat);
 
@@ -2916,10 +2935,10 @@ static struct ast_frame *fax_gateway_framehook(struct ast_channel *chan, struct
                return fax_gateway_detect_t38(gateway, chan, peer, active, f);
        }
 
-       /* not in gateway mode yet, listen for CED */
-       /* XXX this should detect a v21 preamble instead of CED */
-       if (gateway->t38_state == T38_STATE_UNAVAILABLE && f->frametype == AST_FRAME_VOICE) {
-               return fax_gateway_detect_ced(gateway, chan, peer, active, f);
+       if (!gateway->detected_v21 && gateway->t38_state == T38_STATE_UNAVAILABLE && f->frametype == AST_FRAME_VOICE) {
+               /* not in gateway mode and have not detected v21 yet, listen
+                * for v21 */
+               return fax_gateway_detect_v21(gateway, chan, peer, active, f);
        }
 
        /* in gateway mode, gateway some packets */
@@ -2942,6 +2961,25 @@ static struct ast_frame *fax_gateway_framehook(struct ast_channel *chan, struct
                return f;
        }
 
+       /* force silence on the line if T.38 negotiation might be taking place */
+       if (gateway->t38_state != T38_STATE_UNAVAILABLE && gateway->t38_state != T38_STATE_REJECTED) {
+               if (f->frametype == AST_FRAME_VOICE && f->subclass.format.id == AST_FORMAT_SLINEAR) {
+                       short silence_buf[f->samples];
+                       struct ast_frame silence_frame = {
+                               .frametype = AST_FRAME_VOICE,
+                               .data.ptr = silence_buf,
+                               .samples = f->samples,
+                               .datalen = sizeof(silence_buf),
+                       };
+                       ast_format_set(&silence_frame.subclass.format, AST_FORMAT_SLINEAR, 0);
+                       memset(silence_buf, 0, sizeof(silence_buf));
+
+                       return ast_frisolate(&silence_frame);
+               } else {
+                       return &ast_null_frame;
+               }
+       }
+
        return f;
 }