From f1dc620467d9b218ad5041b6416bb030aec9c68c Mon Sep 17 00:00:00 2001
From: "Kevin P. Fleming" <kpfleming@digium.com>
Date: Tue, 16 Jun 2009 21:10:15 +0000
Subject: [PATCH] Enable applications to enable/disable digit and tone
 detection.

Some applications (notably app_fax) do not need digit detection nor FAX tone
detection while they are running, and if Asterisk is using software DSPs to provide
the detection, this consumes extra CPU cycles that could be better spent on the
actual application. This patch allows applications to query and control the state
of digit and tone detection on a channel, and modifies app_fax to disable them
while the FAX operations are occurring (and re-enable digit detection afterwards).



git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@201139 65c4cc65-6c06-0410-ace0-fbb531ad65f3
---
 apps/app_fax.c           |  50 ++++++++++++++++
 channels/chan_dahdi.c    | 122 ++++++++++++++++++++++++++++-----------
 channels/chan_sip.c      |  97 +++++++++++++++++++++----------
 include/asterisk/frame.h |   6 ++
 4 files changed, 212 insertions(+), 63 deletions(-)

diff --git a/apps/app_fax.c b/apps/app_fax.c
index bd6bdf9f63..e2b7a90ee2 100644
--- a/apps/app_fax.c
+++ b/apps/app_fax.c
@@ -710,6 +710,7 @@ static int sndfax_exec(struct ast_channel *chan, const char *data)
 	int res = 0;
 	char *parse;
 	fax_session session;
+	char restore_digit_detect = 0;
 
 	AST_DECLARE_APP_ARGS(args,
 		AST_APP_ARG(file_name);
@@ -744,8 +745,32 @@ static int sndfax_exec(struct ast_channel *chan, const char *data)
 	session.chan = chan;
 	session.finished = 0;
 
+	/* get current digit detection mode, then disable digit detection if enabled */
+	{
+		int dummy = sizeof(restore_digit_detect);
+
+		ast_channel_queryoption(chan, AST_OPTION_DIGIT_DETECT, &restore_digit_detect, &dummy, 0);
+	}
+
+	if (restore_digit_detect) {
+		char new_digit_detect = 0;
+
+		ast_channel_setoption(chan, AST_OPTION_DIGIT_DETECT, &new_digit_detect, sizeof(new_digit_detect), 0);
+	}
+
+	/* disable FAX tone detection if enabled */
+	{
+		char new_fax_detect = 0;
+
+		ast_channel_setoption(chan, AST_OPTION_FAX_DETECT, &new_fax_detect, sizeof(new_fax_detect), 0);
+	}
+
 	res = transmit(&session);
 
+	if (restore_digit_detect) {
+		ast_channel_setoption(chan, AST_OPTION_DIGIT_DETECT, &restore_digit_detect, sizeof(restore_digit_detect), 0);
+	}
+
 	return res;
 }
 
@@ -754,6 +779,7 @@ static int rcvfax_exec(struct ast_channel *chan, const char *data)
 	int res = 0;
 	char *parse;
 	fax_session session;
+	char restore_digit_detect = 0;
 
 	AST_DECLARE_APP_ARGS(args,
 		AST_APP_ARG(file_name);
@@ -788,8 +814,32 @@ static int rcvfax_exec(struct ast_channel *chan, const char *data)
 	session.chan = chan;
 	session.finished = 0;
 
+	/* get current digit detection mode, then disable digit detection if enabled */
+	{
+		int dummy = sizeof(restore_digit_detect);
+
+		ast_channel_queryoption(chan, AST_OPTION_DIGIT_DETECT, &restore_digit_detect, &dummy, 0);
+	}
+
+	if (restore_digit_detect) {
+		char new_digit_detect = 0;
+
+		ast_channel_setoption(chan, AST_OPTION_DIGIT_DETECT, &new_digit_detect, sizeof(new_digit_detect), 0);
+	}
+
+	/* disable FAX tone detection if enabled */
+	{
+		char new_fax_detect = 0;
+
+		ast_channel_setoption(chan, AST_OPTION_FAX_DETECT, &new_fax_detect, sizeof(new_fax_detect), 0);
+	}
+
 	res = transmit(&session);
 
+	if (restore_digit_detect) {
+		ast_channel_setoption(chan, AST_OPTION_DIGIT_DETECT, &restore_digit_detect, sizeof(restore_digit_detect), 0);
+	}
+
 	return res;
 }
 
diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c
index 3567aa9e9e..bd11e51ea5 100644
--- a/channels/chan_dahdi.c
+++ b/channels/chan_dahdi.c
@@ -1507,6 +1507,7 @@ static struct ast_frame *dahdi_exception(struct ast_channel *ast);
 static int dahdi_indicate(struct ast_channel *chan, int condition, const void *data, size_t datalen);
 static int dahdi_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
 static int dahdi_setoption(struct ast_channel *chan, int option, void *data, int datalen);
+static int dahdi_queryoption(struct ast_channel *chan, int option, void *data, int *datalen);
 static int dahdi_func_read(struct ast_channel *chan, const char *function, char *data, char *buf, size_t len);
 static int handle_init_event(struct dahdi_pvt *i, int event);
 static int dahdi_func_write(struct ast_channel *chan, const char *function, char *data, const char *value);
@@ -1529,6 +1530,7 @@ static const struct ast_channel_tech dahdi_tech = {
 	.indicate = dahdi_indicate,
 	.fixup = dahdi_fixup,
 	.setoption = dahdi_setoption,
+	.queryoption = dahdi_queryoption,
 	.func_channel_read = dahdi_func_read,
 	.func_channel_write = dahdi_func_write,
 };
@@ -5890,6 +5892,66 @@ static int dahdi_answer(struct ast_channel *ast)
 	return res;
 }
 
+static void disable_dtmf_detect(struct dahdi_pvt *p)
+{
+	int val = 0;
+
+	p->ignoredtmf = 1;
+
+	ioctl(p->subs[SUB_REAL].dfd, DAHDI_TONEDETECT, &val);
+
+	if (!p->hardwaredtmf && p->dsp) {
+		p->dsp_features &= ~DSP_FEATURE_DIGIT_DETECT;
+		ast_dsp_set_features(p->dsp, p->dsp_features);
+	}
+}
+
+static void enable_dtmf_detect(struct dahdi_pvt *p)
+{
+	int val = DAHDI_TONEDETECT_ON | DAHDI_TONEDETECT_MUTE;
+
+	if (p->channel == CHAN_PSEUDO)
+		return;
+
+	p->ignoredtmf = 0;
+
+	ioctl(p->subs[SUB_REAL].dfd, DAHDI_TONEDETECT, &val);
+
+	if (!p->hardwaredtmf && p->dsp) {
+		p->dsp_features |= DSP_FEATURE_DIGIT_DETECT;
+		ast_dsp_set_features(p->dsp, p->dsp_features);
+	}
+}
+
+static int dahdi_queryoption(struct ast_channel *chan, int option, void *data, int *datalen)
+{
+	char *cp;
+	struct dahdi_pvt *p = chan->tech_pvt;
+
+	/* all supported options require data */
+	if (!data || (*datalen < 1)) {
+		errno = EINVAL;
+		return -1;
+	}
+
+	switch (option) {
+	case AST_OPTION_DIGIT_DETECT:
+		cp = (char *) data;
+		*cp = p->ignoredtmf ? 0 : 1;
+		ast_debug(1, "Reporting digit detection %sabled on %s\n", *cp ? "en" : "dis", chan->name);
+		break;
+	case AST_OPTION_FAX_DETECT:
+		cp = (char *) data;
+		*cp = (p->callprogress & CALLPROGRESS_FAX) ? 0 : 1;
+		ast_debug(1, "Reporting fax tone detection %sabled on %s\n", *cp ? "en" : "dis", chan->name);
+		break;
+	}
+
+	errno = 0;
+
+	return 0;
+}
+
 static int dahdi_setoption(struct ast_channel *chan, int option, void *data, int datalen)
 {
 	char *cp;
@@ -6072,6 +6134,29 @@ static int dahdi_setoption(struct ast_channel *chan, int option, void *data, int
 			dahdi_disable_ec(p);
 		}
 		break;
+	case AST_OPTION_DIGIT_DETECT:
+		cp = (char *) data;
+		ast_debug(1, "%sabling digit detection on %s\n", *cp ? "En" : "Dis", chan->name);
+		if (*cp) {
+			enable_dtmf_detect(p);
+		} else {
+			disable_dtmf_detect(p);
+		}
+		break;
+	case AST_OPTION_FAX_DETECT:
+		cp = (char *) data;
+		if (p->dsp) {
+			ast_debug(1, "%sabling fax tone detection on %s\n", *cp ? "En" : "Dis", chan->name);
+			if (*cp) {
+				p->callprogress |= CALLPROGRESS_FAX;
+				p->dsp_features |= DSP_FEATURE_FAX_DETECT;
+			} else {
+				p->callprogress &= ~CALLPROGRESS_FAX;
+				p->dsp_features &= ~DSP_FEATURE_FAX_DETECT;
+			}
+			ast_dsp_set_features(p->dsp, p->dsp_features);
+		}
+		break;
 	default:
 		return -1;
 	}
@@ -6279,39 +6364,6 @@ static void dahdi_link(struct dahdi_pvt *slave, struct dahdi_pvt *master) {
 	ast_debug(1, "Making %d slave to master %d at %d\n", slave->channel, master->channel, x);
 }
 
-static void disable_dtmf_detect(struct dahdi_pvt *p)
-{
-	int val;
-
-	p->ignoredtmf = 1;
-
-	val = 0;
-	ioctl(p->subs[SUB_REAL].dfd, DAHDI_TONEDETECT, &val);
-
-	if (!p->hardwaredtmf && p->dsp) {
-		p->dsp_features &= ~DSP_FEATURE_DIGIT_DETECT;
-		ast_dsp_set_features(p->dsp, p->dsp_features);
-	}
-}
-
-static void enable_dtmf_detect(struct dahdi_pvt *p)
-{
-	int val;
-
-	if (p->channel == CHAN_PSEUDO)
-		return;
-
-	p->ignoredtmf = 0;
-
-	val = DAHDI_TONEDETECT_ON | DAHDI_TONEDETECT_MUTE;
-	ioctl(p->subs[SUB_REAL].dfd, DAHDI_TONEDETECT, &val);
-
-	if (!p->hardwaredtmf && p->dsp) {
-		p->dsp_features |= DSP_FEATURE_DIGIT_DETECT;
-		ast_dsp_set_features(p->dsp, p->dsp_features);
-	}
-}
-
 static enum ast_bridge_result dahdi_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms)
 {
 	struct ast_channel *who;
@@ -6814,6 +6866,10 @@ static void dahdi_handle_dtmfup(struct ast_channel *ast, int idx, struct ast_fra
 				}
 			}
 			p->faxhandled = 1;
+			p->callprogress &= ~CALLPROGRESS_FAX;
+			p->dsp_features &= ~DSP_FEATURE_FAX_DETECT;
+			ast_dsp_set_features(p->dsp, p->dsp_features);
+			ast_debug(1, "Disabling FAX tone detection on %s after tone received\n", ast->name);
 			if (strcmp(ast->exten, "fax")) {
 				const char *target_context = S_OR(ast->macrocontext, ast->context);
 
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 4158ba5d48..4fd774b944 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -1833,7 +1833,7 @@ struct sip_pvt {
 	int laststate;				/*!< SUBSCRIBE: Last known extension state */
 	int dialogver;				/*!< SUBSCRIBE: Version for subscription dialog-info */
 
-	struct ast_dsp *vad;			/*!< Inband DTMF Detection dsp */
+	struct ast_dsp *dsp;			/*!< Inband DTMF Detection dsp */
 
 	struct sip_peer *relatedpeer;		/*!< If this dialog is related to a peer, which one 
 							Used in peerpoke, mwi subscriptions */
@@ -4076,21 +4076,62 @@ static int send_request(struct sip_pvt *p, struct sip_request *req, enum xmittyp
 	return res;
 }
 
+static void enable_digit_detect(struct sip_pvt *p)
+{
+	if (p->dsp) {
+		return;
+	}
+
+	if (!(p->dsp = ast_dsp_new())) {
+		return;
+	}
+
+	ast_dsp_set_features(p->dsp, DSP_FEATURE_DIGIT_DETECT);
+	if (global_relaxdtmf) {
+		ast_dsp_set_digitmode(p->dsp, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF);
+	}
+}
+
+static void disable_digit_detect(struct sip_pvt *p)
+{
+	if (p->dsp) {
+		ast_dsp_free(p->dsp);
+		p->dsp = NULL;
+	}
+}
+
 /*! \brief Set an option on a SIP dialog */
 static int sip_setoption(struct ast_channel *chan, int option, void *data, int datalen)
 {
 	int res = -1;
 	struct sip_pvt *p = chan->tech_pvt;
 
-	if (option == AST_OPTION_FORMAT_READ) {
-		int format = *(int *)data;
-		res = ast_rtp_instance_set_read_format(p->rtp, format);
-	} else if (option == AST_OPTION_FORMAT_WRITE) {
-		int format = *(int *)data;
-		res = ast_rtp_instance_set_write_format(p->rtp, format);
-	} else if (option == AST_OPTION_MAKE_COMPATIBLE) {
-		struct ast_channel *peer = data;
-		res = ast_rtp_instance_make_compatible(chan, p->rtp, peer);
+	switch (option) {
+	case AST_OPTION_FORMAT_READ:
+		res = ast_rtp_instance_set_read_format(p->rtp, *(int *) data);
+		break;
+	case AST_OPTION_FORMAT_WRITE:
+		res = ast_rtp_instance_set_write_format(p->rtp, *(int *) data);
+		break;
+	case AST_OPTION_MAKE_COMPATIBLE:
+		res = ast_rtp_instance_make_compatible(chan, p->rtp, (struct ast_channel *) data);
+		break;
+	case AST_OPTION_DIGIT_DETECT:
+		if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) ||
+		    (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) {
+			char *cp = (char *) data;
+
+			ast_debug(1, "%sabling digit detection on %s\n", *cp ? "En" : "Dis", chan->name);
+			if (*cp) {
+				enable_digit_detect(p);
+			} else {
+				disable_digit_detect(p);
+			}
+			res = 0;
+		}
+		break;
+	default:
+		break;
 	}
 
 	return res;
@@ -4102,6 +4143,7 @@ static int sip_queryoption(struct ast_channel *chan, int option, void *data, int
 	int res = -1;
 	enum ast_t38_state state = T38_STATE_UNAVAILABLE;
 	struct sip_pvt *p = (struct sip_pvt *) chan->tech_pvt;
+	char *cp;
 
 	switch (option) {
 	case AST_OPTION_T38_STATE:
@@ -4134,6 +4176,11 @@ static int sip_queryoption(struct ast_channel *chan, int option, void *data, int
 		*((enum ast_t38_state *) data) = state;
 		res = 0;
 
+		break;
+	case AST_OPTION_DIGIT_DETECT:
+		cp = (char *) data;
+		*cp = p->dsp ? 1 : 0;
+		ast_debug(1, "Reporting digit detection %sabled on %s\n", *cp ? "en" : "dis", chan->name);
 		break;
 	default:
 		break;
@@ -5823,7 +5870,6 @@ static const char *hangup_cause2sip(int cause)
 	return 0;
 }
 
-
 /*! \brief  sip_hangup: Hangup SIP call
  * Part of PBX interface, called from ast_hangup */
 static int sip_hangup(struct ast_channel *ast)
@@ -5902,8 +5948,7 @@ static int sip_hangup(struct ast_channel *ast)
 	append_history(p, needcancel ? "Cancel" : "Hangup", "Cause %s", p->owner ? ast_cause2str(p->hangupcause) : "Unknown");
 
 	/* Disconnect */
-	if (p->vad)
-		ast_dsp_free(p->vad);
+	disable_digit_detect(p);
 
 	p->owner = NULL;
 	ast->tech_pvt = dialog_unref(ast->tech_pvt, "unref ast->tech_pvt");
@@ -6453,7 +6498,6 @@ static int sip_indicate(struct ast_channel *ast, int condition, const void *data
 	return res;
 }
 
-
 /*! \brief Initiate a call in the SIP channel
 	called from sip_request_call (calls from the pbx ) for outbound channels
 	and from handle_request_invite for inbound channels
@@ -6552,12 +6596,10 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *tit
 	else
 		ast_debug(3, "This channel will not be able to handle video.\n");
 
-	if ((ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) || (ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) {
+	if ((ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) ||
+	    (ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) {
 		if (!i->rtp || ast_rtp_instance_dtmf_mode_set(i->rtp, AST_RTP_DTMF_MODE_INBAND)) {
-			i->vad = ast_dsp_new();
-			ast_dsp_set_features(i->vad, DSP_FEATURE_DIGIT_DETECT);
-			if (global_relaxdtmf)
-				ast_dsp_set_digitmode(i->vad, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF);
+			enable_digit_detect(i);
 		}
 	} else if (ast_test_flag(&i->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833) {
 		if (i->rtp) {
@@ -6853,8 +6895,8 @@ static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p
 		ast_set_write_format(p->owner, p->owner->writeformat);
 	}
 
-	if (f && (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) && p->vad) {
-		f = ast_dsp_process(p->owner, p->vad, f);
+	if (f && p->dsp) {
+		f = ast_dsp_process(p->owner, p->dsp, f);
 		if (f && f->frametype == AST_FRAME_DTMF) {
 			if (ast_test_flag(&p->t38.t38support, SIP_PAGE2_T38SUPPORT_UDPTL) && f->subclass == 'f') {
 				ast_debug(1, "Fax CNG detected on %s\n", ast->name);
@@ -25354,16 +25396,11 @@ static int sip_dtmfmode(struct ast_channel *chan, const char *data)
 		ast_log(LOG_WARNING, "I don't know about this dtmf mode: %s\n", mode);
 	if (p->rtp)
 		ast_rtp_instance_set_prop(p->rtp, AST_RTP_PROPERTY_DTMF, ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_RFC2833);
-	if (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) {
-		if (!p->vad) {
-			p->vad = ast_dsp_new();
-			ast_dsp_set_features(p->vad, DSP_FEATURE_DIGIT_DETECT);
-		}
+	if ((ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_INBAND) ||
+	    (ast_test_flag(&p->flags[0], SIP_DTMF) == SIP_DTMF_AUTO)) {
+		enable_digit_detect(p);
 	} else {
-		if (p->vad) {
-			ast_dsp_free(p->vad);
-			p->vad = NULL;
-		}
+		disable_digit_detect(p);
 	}
 	sip_pvt_unlock(p);
 	ast_channel_unlock(chan);
diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h
index 46500d2e98..8b79a5e998 100644
--- a/include/asterisk/frame.h
+++ b/include/asterisk/frame.h
@@ -400,6 +400,12 @@ enum ast_control_transfer {
 /*! Request that the channel driver make two channels of the same tech type compatible if possible */
 #define AST_OPTION_MAKE_COMPATIBLE      13
 
+/*! Get or set the digit detection state of the channel */
+#define AST_OPTION_DIGIT_DETECT		14
+
+/*! Get or set the fax tone detection state of the channel */
+#define AST_OPTION_FAX_DETECT		15
+
 struct oprmode {
 	struct ast_channel *peer;
 	int mode;
-- 
GitLab