Asterisk - The Open Source Telephony Project  GIT-master-a24979a
res_tonedetect.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2021, Naveen Albert
5  *
6  * Naveen Albert <asterisk@phreaknet.org>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 
19 /*! \file
20  *
21  * \brief Tone detection module
22  *
23  * \author Naveen Albert <asterisk@phreaknet.org>
24  *
25  * \ingroup resources
26  */
27 
28 /*** MODULEINFO
29  <support_level>extended</support_level>
30  ***/
31 
32 #include "asterisk.h"
33 
34 #include <math.h>
35 
36 #include "asterisk/module.h"
37 #include "asterisk/frame.h"
38 #include "asterisk/format_cache.h"
39 #include "asterisk/channel.h"
40 #include "asterisk/dsp.h"
41 #include "asterisk/pbx.h"
42 #include "asterisk/audiohook.h"
43 #include "asterisk/app.h"
44 #include "asterisk/indications.h"
45 #include "asterisk/conversions.h"
46 
47 /*** DOCUMENTATION
48  <application name="WaitForTone" language="en_US">
49  <since>
50  <version>16.21.0</version>
51  <version>18.7.0</version>
52  <version>19.0.0</version>
53  </since>
54  <synopsis>
55  Wait for tone
56  </synopsis>
57  <syntax>
58  <parameter name="freq" required="true">
59  <para>Frequency of the tone to wait for.</para>
60  </parameter>
61  <parameter name="duration_ms" required="false">
62  <para>Minimum duration of tone, in ms. Default is 500ms.
63  Using a minimum duration under 50ms is unlikely to produce
64  accurate results.</para>
65  </parameter>
66  <parameter name="timeout" required="false">
67  <para>Maximum amount of time, in seconds, to wait for specified tone.
68  Default is forever.</para>
69  </parameter>
70  <parameter name="times" required="false">
71  <para>Number of times the tone should be detected (subject to the
72  provided timeout) before returning. Default is 1.</para>
73  </parameter>
74  <parameter name="options" required="false">
75  <optionlist>
76  <option name="d">
77  <para>Custom decibel threshold to use. Default is 16.</para>
78  </option>
79  <option name="s">
80  <para>Squelch tone.</para>
81  </option>
82  </optionlist>
83  </parameter>
84  </syntax>
85  <description>
86  <para>Waits for a single-frequency tone to be detected before dialplan execution continues.</para>
87  <variablelist>
88  <variable name="WAITFORTONESTATUS">
89  <para>This indicates the result of the wait.</para>
90  <value name="SUCCESS"/>
91  <value name="ERROR"/>
92  <value name="TIMEOUT"/>
93  <value name="HANGUP"/>
94  </variable>
95  </variablelist>
96  </description>
97  <see-also>
98  <ref type="application">PlayTones</ref>
99  </see-also>
100  </application>
101  <application name="ToneScan" language="en_US">
102  <since>
103  <version>16.23.0</version>
104  <version>18.9.0</version>
105  <version>19.1.0</version>
106  </since>
107  <synopsis>
108  Wait for period of time while scanning for call progress tones
109  </synopsis>
110  <syntax>
111  <parameter name="zone" required="false">
112  <para>Call progress zone. Default is the system default.</para>
113  </parameter>
114  <parameter name="timeout" required="false">
115  <para>Maximum amount of time, in seconds, to wait for call progress
116  or signal tones. Default is forever.</para>
117  </parameter>
118  <parameter name="threshold" required="false">
119  <para>DSP threshold required for a match. A higher number will
120  require a longer match and may reduce false positives, at the
121  expense of false negatives. Default is 1.</para>
122  </parameter>
123  <parameter name="options" required="false">
124  <optionlist>
125  <option name="f">
126  <para>Enable fax machine detection. By default, this is disabled.</para>
127  </option>
128  <option name="v">
129  <para>Enable voice detection. By default, this is disabled.</para>
130  </option>
131  </optionlist>
132  </parameter>
133  </syntax>
134  <description>
135  <para>Waits for a a distinguishable call progress tone and then exits.
136  Unlike a conventional scanner, this is not currently capable of
137  scanning for modem carriers.</para>
138  <variablelist>
139  <variable name="TONESCANSTATUS">
140  This indicates the result of the scan.
141  <value name="RINGING">
142  Audible ringback tone
143  </value>
144  <value name="BUSY">
145  Busy tone
146  </value>
147  <value name="SIT">
148  Special Information Tones
149  </value>
150  <value name="VOICE">
151  Human voice detected
152  </value>
153  <value name="DTMF">
154  DTMF digit
155  </value>
156  <value name="FAX">
157  Fax (answering)
158  </value>
159  <value name="MODEM">
160  Modem (answering)
161  </value>
162  <value name="DIALTONE">
163  Dial tone
164  </value>
165  <value name="NUT">
166  UK Number Unobtainable tone
167  </value>
168  <value name="TIMEOUT">
169  Timeout reached before any positive detection
170  </value>
171  <value name="HANGUP">
172  Caller hung up before any positive detection
173  </value>
174  </variable>
175  </variablelist>
176  </description>
177  <see-also>
178  <ref type="application">WaitForTone</ref>
179  </see-also>
180  </application>
181  <function name="TONE_DETECT" language="en_US">
182  <since>
183  <version>16.21.0</version>
184  <version>18.7.0</version>
185  <version>19.0.0</version>
186  </since>
187  <synopsis>
188  Asynchronously detects a tone
189  </synopsis>
190  <syntax>
191  <parameter name="freq" required="true">
192  <para>Frequency of the tone to detect. To disable frequency
193  detection completely (e.g. for signal detection only),
194  specify 0 for the frequency.</para>
195  </parameter>
196  <parameter name="duration_ms" required="false">
197  <para>Minimum duration of tone, in ms. Default is 500ms.
198  Using a minimum duration under 50ms is unlikely to produce
199  accurate results.</para>
200  </parameter>
201  <parameter name="options">
202  <optionlist>
203  <option name="a">
204  <para>Match immediately on Special Information Tones, instead of or in addition
205  to a particular frequency.</para>
206  </option>
207  <option name="b">
208  <para>Match immediately on a busy signal, instead of or in addition to
209  a particular frequency.</para>
210  </option>
211  <option name="c">
212  <para>Match immediately on a dial tone, instead of or in addition to
213  a particular frequency.</para>
214  </option>
215  <option name="d">
216  <para>Custom decibel threshold to use. Default is 16.</para>
217  </option>
218  <option name="g">
219  <para>Go to the specified context,exten,priority if tone is received on this channel.
220  Detection will not end automatically.</para>
221  </option>
222  <option name="h">
223  <para>Go to the specified context,exten,priority if tone is transmitted on this channel.
224  Detection will not end automatically.</para>
225  </option>
226  <option name="n">
227  <para>Number of times the tone should be detected (subject to the
228  provided timeout) before going to the destination provided in the <literal>g</literal>
229  or <literal>h</literal> option. Default is 1.</para>
230  </option>
231  <option name="r">
232  <para>Apply to received frames only. Default is both directions.</para>
233  </option>
234  <option name="s">
235  <para>Squelch tone.</para>
236  </option>
237  <option name="t">
238  <para>Apply to transmitted frames only. Default is both directions.</para>
239  </option>
240  <option name="x">
241  <para>Destroy the detector (stop detection).</para>
242  </option>
243  </optionlist>
244  </parameter>
245  </syntax>
246  <description>
247  <para>The TONE_DETECT function detects a single-frequency tone and keeps
248  track of how many times the tone has been detected.</para>
249  <para>When reading this function (instead of writing), supply <literal>tx</literal>
250  to get the number of times a tone has been detected in the TX direction and
251  <literal>rx</literal> to get the number of times a tone has been detected in the
252  RX direction.</para>
253  <example title="intercept2600">
254  same => n,Set(TONE_DETECT(2600,1000,g(got-2600,s,1))=) ; detect 2600 Hz
255  same => n,Wait(15)
256  same => n,NoOp(${TONE_DETECT(rx)})
257  </example>
258  <example title="dropondialtone">
259  same => n,Set(TONE_DETECT(0,,bg(my-hangup,s,1))=) ; disconnect a call if we hear a busy signal
260  same => n,Goto(somewhere-else)
261  same => n(myhangup),Hangup()
262  </example>
263  <example title="removedetector">
264  same => n,Set(TONE_DETECT(0,,x)=) ; remove the detector from the channel
265  </example>
266  </description>
267  </function>
268  ***/
269 
271  struct ast_dsp *dsp;
272  struct ast_audiohook audiohook;
273  int freq1;
274  int freq2;
275  int duration;
276  int db;
277  char *gototx;
278  char *gotorx;
279  unsigned short int squelch;
280  unsigned short int tx;
281  unsigned short int rx;
282  int txcount;
283  int rxcount;
286 };
287 
288 enum td_opts {
289  OPT_TX = (1 << 1),
290  OPT_RX = (1 << 2),
291  OPT_END_FILTER = (1 << 3),
292  OPT_GOTO_RX = (1 << 4),
293  OPT_GOTO_TX = (1 << 5),
294  OPT_DECIBEL = (1 << 6),
295  OPT_SQUELCH = (1 << 7),
296  OPT_HITS_REQ = (1 << 8),
297  OPT_SIT = (1 << 9),
298  OPT_BUSY = (1 << 10),
299  OPT_DIALTONE = (1 << 11),
300 };
301 
302 enum {
307  /* note: this entry _MUST_ be the last one in the enum */
309 };
310 
312  AST_APP_OPTION('a', OPT_SIT),
313  AST_APP_OPTION('b', OPT_BUSY),
320  AST_APP_OPTION('t', OPT_TX),
321  AST_APP_OPTION('r', OPT_RX),
323 });
324 
325 static void destroy_callback(void *data)
326 {
327  struct detect_information *di = data;
328  ast_dsp_free(di->dsp);
329  if (di->gotorx) {
330  ast_free(di->gotorx);
331  }
332  if (di->gototx) {
333  ast_free(di->gototx);
334  }
335  ast_audiohook_lock(&di->audiohook);
336  ast_audiohook_detach(&di->audiohook);
337  ast_audiohook_unlock(&di->audiohook);
338  ast_audiohook_destroy(&di->audiohook);
339  ast_free(di);
340  return;
341 }
342 
343 static const struct ast_datastore_info detect_datastore = {
344  .type = "detect",
345  .destroy = destroy_callback
346 };
347 
348 static int detect_callback(struct ast_audiohook *audiohook, struct ast_channel *chan, struct ast_frame *frame, enum ast_audiohook_direction direction)
349 {
350  struct ast_datastore *datastore = NULL;
351  struct detect_information *di = NULL;
352  int match = 0;
353 
354  /* If the audiohook is stopping it means the channel is shutting down.... but we let the datastore destroy take care of it */
356  return 0;
357  }
358 
359  /* Grab datastore which contains our gain information */
360  if (!(datastore = ast_channel_datastore_find(chan, &detect_datastore, NULL))) {
361  return 0;
362  }
363 
364  di = datastore->data;
365 
366  if (!frame || frame->frametype != AST_FRAME_VOICE) {
367  return 0;
368  }
369 
370  if (!(direction == AST_AUDIOHOOK_DIRECTION_READ ? di->rx : di->tx)) {
371  return 0;
372  }
373 
374  /* ast_dsp_process may free the frame and return a new one */
375  frame = ast_frdup(frame);
376  frame = ast_dsp_process(chan, di->dsp, frame);
377  if (frame->frametype == AST_FRAME_DTMF) {
378  char result = frame->subclass.integer;
379  if (result == 'q') {
380  int now;
381  match = 1;
383  di->rxcount = di->rxcount + 1;
384  now = di->rxcount;
385  } else {
386  di->txcount = di->txcount + 1;
387  now = di->txcount;
388  }
389  ast_debug(1, "TONE_DETECT just got a hit (#%d in this direction, waiting for %d total)\n", now, di->hitsrequired);
390  if (now >= di->hitsrequired) {
391  if (direction == AST_AUDIOHOOK_DIRECTION_READ && di->gotorx) {
392  ast_async_parseable_goto(chan, di->gotorx);
393  } else if (di->gototx) {
394  ast_async_parseable_goto(chan, di->gototx);
395  }
396  }
397  }
398  }
399  if (di->signalfeatures && !match) { /* skip unless there are call progress/signal options */
400  int tstate, tcount;
401  tcount = ast_dsp_get_tcount(di->dsp);
402  tstate = ast_dsp_get_tstate(di->dsp);
403  if (tstate > 0) {
404  ast_debug(3, "tcount: %d, tstate: %d\n", tcount, tstate);
405  switch (tstate) {
407  if (di->signalfeatures & DSP_FEATURE_WAITDIALTONE) {
408  match = 1;
409  }
410  break;
411  case DSP_TONE_STATE_BUSY:
412  if (di->signalfeatures & DSP_PROGRESS_BUSY) {
413  match = 1;
414  }
415  break;
417  if (di->signalfeatures & DSP_PROGRESS_CONGESTION) {
418  match = 1;
419  }
420  break;
421  default: /* ignore */
422  break;
423  }
424  if (match) {
425  if (direction == AST_AUDIOHOOK_DIRECTION_READ && di->gotorx) {
426  ast_async_parseable_goto(chan, di->gotorx);
427  } else if (di->gototx) {
428  ast_async_parseable_goto(chan, di->gototx);
429  } else {
430  ast_debug(3, "Detected call progress signal, but don't know where to go\n");
431  }
432  }
433  }
434  }
435  /* this could be the duplicated frame or a new one, doesn't matter */
436  ast_frfree(frame);
437  return 0;
438 }
439 
440 static int remove_detect(struct ast_channel *chan)
441 {
442  struct ast_datastore *datastore = NULL;
443  struct detect_information *data;
444  SCOPED_CHANNELLOCK(chan_lock, chan);
445 
446  datastore = ast_channel_datastore_find(chan, &detect_datastore, NULL);
447  if (!datastore) {
448  ast_log(AST_LOG_WARNING, "Cannot remove TONE_DETECT from %s: TONE_DETECT not currently enabled\n",
449  ast_channel_name(chan));
450  return -1;
451  }
452  data = datastore->data;
453 
454  if (ast_audiohook_remove(chan, &data->audiohook)) {
455  ast_log(AST_LOG_WARNING, "Failed to remove TONE_DETECT audiohook from channel %s\n", ast_channel_name(chan));
456  return -1;
457  }
458 
459  if (ast_channel_datastore_remove(chan, datastore)) {
460  ast_log(AST_LOG_WARNING, "Failed to remove TONE_DETECT datastore from channel %s\n",
461  ast_channel_name(chan));
462  return -1;
463  }
464  ast_datastore_free(datastore);
465 
466  return 0;
467 }
468 
469 static int freq_parser(char *freqs, int *freq1, int *freq2) {
470  char *f1, *f2, *f3;
471  if (ast_strlen_zero(freqs)) {
472  ast_log(LOG_ERROR, "No frequency specified\n");
473  return -1;
474  }
475  f3 = ast_strdupa(freqs);
476  f1 = strsep(&f3, "+");
477  f2 = strsep(&f3, "+");
478  if (!ast_strlen_zero(f3)) {
479  ast_log(LOG_WARNING, "Only up to 2 frequencies may be specified: %s\n", freqs);
480  return -1;
481  }
482  if (ast_str_to_int(f1, freq1)) {
483  ast_log(LOG_WARNING, "Frequency must be an integer: %s\n", f1);
484  return -1;
485  }
486  if (*freq1 < 0) {
487  ast_log(LOG_WARNING, "Sorry, no negative frequencies: %d\n", *freq1);
488  return -1;
489  }
490  if (!ast_strlen_zero(f2)) {
491  ast_log(LOG_WARNING, "Sorry, currently only 1 frequency is supported\n");
492  return -1;
493  /* not supported just yet, but possibly will be in the future */
494  if (ast_str_to_int(f2, freq2)) {
495  ast_log(LOG_WARNING, "Frequency must be an integer: %s\n", f2);
496  return -1;
497  }
498  if (*freq2 < 1) {
499  ast_log(LOG_WARNING, "Sorry, positive frequencies only: %d\n", *freq2);
500  return -1;
501  }
502  }
503  return 0;
504 }
505 
506 static char* goto_parser(struct ast_channel *chan, char *loc) {
507  char *exten, *pri, *context, *parse;
508  char *dest;
509  int size;
510  parse = ast_strdupa(loc);
511  context = strsep(&parse, ",");
512  exten = strsep(&parse, ",");
513  pri = strsep(&parse, ",");
514  if (!exten) {
515  pri = context;
516  exten = NULL;
517  context = NULL;
518  } else if (!pri) {
519  pri = exten;
520  exten = context;
521  context = NULL;
522  }
523  ast_channel_lock(chan);
524  if (ast_strlen_zero(exten)) {
526  }
527  if (ast_strlen_zero(context)) {
529  }
530  ast_channel_unlock(chan);
531 
532  /* size + 3: for 1 null terminator + 2 commas */
533  size = strlen(context) + strlen(exten) + strlen(pri) + 3;
534  dest = ast_malloc(size + 1);
535  if (!dest) {
536  ast_log(LOG_ERROR, "Failed to parse goto: %s,%s,%s\n", context, exten, pri);
537  return NULL;
538  }
539  snprintf(dest, size, "%s,%s,%s", context, exten, pri);
540  return dest;
541 }
542 
543 static int detect_read(struct ast_channel *chan, const char *cmd, char *data, char *buffer, size_t buflen)
544 {
545  struct ast_datastore *datastore = NULL;
546  struct detect_information *di = NULL;
547 
548  if (!chan) {
549  ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
550  return -1;
551  }
552 
553  ast_channel_lock(chan);
554  if (!(datastore = ast_channel_datastore_find(chan, &detect_datastore, NULL))) {
555  ast_channel_unlock(chan);
556  return -1; /* function not initiated yet, so nothing to read */
557  } else {
558  ast_channel_unlock(chan);
559  di = datastore->data;
560  }
561 
562  if (strchr(data, 't')) {
563  snprintf(buffer, buflen, "%d", di->txcount);
564  } else if (strchr(data, 'r')) {
565  snprintf(buffer, buflen, "%d", di->rxcount);
566  } else {
567  ast_log(LOG_WARNING, "Invalid direction: %s\n", data);
568  }
569 
570  return 0;
571 }
572 
573 static int parse_signal_features(struct ast_flags *flags)
574 {
575  int features = 0;
576 
577  if (ast_test_flag(flags, OPT_SIT)) {
578  features |= DSP_PROGRESS_CONGESTION;
579  }
580  if (ast_test_flag(flags, OPT_BUSY)) {
581  features |= DSP_PROGRESS_BUSY;
582  }
583  if (ast_test_flag(flags, OPT_DIALTONE)) {
584  features |= DSP_FEATURE_WAITDIALTONE;
585  }
586 
587  return features;
588 }
589 
590 static int detect_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
591 {
592  char *parse;
593  struct ast_datastore *datastore = NULL;
594  struct detect_information *di = NULL;
595  struct ast_flags flags = { 0 };
596  char *opt_args[OPT_ARG_ARRAY_SIZE];
597  struct ast_dsp *dsp;
598  int freq1 = 0, freq2 = 0, duration = 500, db = 16, squelch = 0, hitsrequired = 1;
599  int signalfeatures = 0;
600 
603  AST_APP_ARG(duration);
605  );
606 
607  if (!chan) {
608  ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
609  return -1;
610  }
611  parse = ast_strdupa(data);
613 
614  if (!ast_strlen_zero(args.options)) {
615  ast_app_parse_options(td_opts, &flags, opt_args, args.options);
616  }
617  if (ast_test_flag(&flags, OPT_END_FILTER)) {
618  return remove_detect(chan);
619  }
620  if (freq_parser(args.freqs, &freq1, &freq2)) {
621  return -1;
622  }
623  if (!ast_strlen_zero(args.duration) && (ast_str_to_int(args.duration, &duration) || duration < 1)) {
624  ast_log(LOG_WARNING, "Invalid duration: %s\n", args.duration);
625  return -1;
626  }
627  if (ast_test_flag(&flags, OPT_HITS_REQ) && !ast_strlen_zero(opt_args[OPT_ARG_HITS_REQ])) {
628  if ((ast_str_to_int(opt_args[OPT_ARG_HITS_REQ], &hitsrequired) || hitsrequired < 1)) {
629  ast_log(LOG_WARNING, "Invalid number hits required: %s\n", opt_args[OPT_ARG_HITS_REQ]);
630  return -1;
631  }
632  }
633  if (ast_test_flag(&flags, OPT_DECIBEL) && !ast_strlen_zero(opt_args[OPT_ARG_DECIBEL])) {
634  if ((ast_str_to_int(opt_args[OPT_ARG_DECIBEL], &db) || db < 1)) {
635  ast_log(LOG_WARNING, "Invalid decibel level: %s\n", opt_args[OPT_ARG_DECIBEL]);
636  return -1;
637  }
638  }
639  signalfeatures = parse_signal_features(&flags);
640 
641  ast_channel_lock(chan);
642  if (!(datastore = ast_channel_datastore_find(chan, &detect_datastore, NULL))) {
643  if (!(datastore = ast_datastore_alloc(&detect_datastore, NULL))) {
644  ast_channel_unlock(chan);
645  return 0;
646  }
647  if (!(di = ast_calloc(1, sizeof(*di)))) {
648  ast_datastore_free(datastore);
649  ast_channel_unlock(chan);
650  return 0;
651  }
653  di->audiohook.manipulate_callback = detect_callback;
654  if (!(dsp = ast_dsp_new())) {
655  ast_datastore_free(datastore);
656  ast_channel_unlock(chan);
657  ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
658  return -1;
659  }
660  di->signalfeatures = signalfeatures; /* we're not including freq detect */
661  if (freq1 > 0) {
662  signalfeatures |= DSP_FEATURE_FREQ_DETECT;
663  ast_dsp_set_freqmode(dsp, freq1, duration, db, squelch);
664  }
665  ast_dsp_set_features(dsp, signalfeatures);
666  di->dsp = dsp;
667  di->txcount = 0;
668  di->rxcount = 0;
669  ast_debug(1, "Keeping our ears open for %s Hz, %d db\n", args.freqs, db);
670  datastore->data = di;
671  ast_channel_datastore_add(chan, datastore);
672  ast_audiohook_attach(chan, &di->audiohook);
673  } else {
674  di = datastore->data;
675  dsp = di->dsp;
676  di->signalfeatures = signalfeatures; /* we're not including freq detect */
677  if (freq1 > 0) {
678  signalfeatures |= DSP_FEATURE_FREQ_DETECT;
679  ast_dsp_set_freqmode(dsp, freq1, duration, db, squelch);
680  }
681  ast_dsp_set_features(dsp, signalfeatures);
682  }
683  di->duration = duration;
684  di->gotorx = NULL;
685  di->gototx = NULL;
686  /* resolve gotos now, in case a full context,exten,pri wasn't specified */
687  if (ast_test_flag(&flags, OPT_GOTO_RX) && !ast_strlen_zero(opt_args[OPT_ARG_GOTO_RX])) {
688  di->gotorx = goto_parser(chan, opt_args[OPT_ARG_GOTO_RX]);
689  }
690  if (ast_test_flag(&flags, OPT_GOTO_TX) && !ast_strlen_zero(opt_args[OPT_ARG_GOTO_TX])) {
691  di->gototx = goto_parser(chan, opt_args[OPT_ARG_GOTO_TX]);
692  }
693  di->db = db;
694  di->hitsrequired = hitsrequired;
695  di->squelch = ast_test_flag(&flags, OPT_SQUELCH);
696  di->tx = 1;
697  di->rx = 1;
698  if (ast_strlen_zero(args.options) || ast_test_flag(&flags, OPT_TX)) {
699  di->tx = 1;
700  di->rx = 0;
701  }
702  if (ast_strlen_zero(args.options) || ast_test_flag(&flags, OPT_RX)) {
703  di->rx = 1;
704  di->tx = 0;
705  }
706  ast_channel_unlock(chan);
707 
708  return 0;
709 }
710 
711 enum {
712  OPT_APP_DECIBEL = (1 << 0),
713  OPT_APP_SQUELCH = (1 << 1),
714 };
715 
716 enum {
718  /* note: this entry _MUST_ be the last one in the enum */
720 };
721 
725 END_OPTIONS);
726 
727 static int wait_exec(struct ast_channel *chan, const char *data)
728 {
729  char *appdata;
730  struct ast_flags flags = {0};
731  char *opt_args[OPT_APP_ARG_ARRAY_SIZE];
732  double timeoutf = 0;
733  int freq1 = 0, freq2 = 0, timeout = 0, duration = 500, times = 1, db = 16, squelch = 0;
734  struct ast_frame *frame = NULL;
735  struct ast_dsp *dsp;
736  struct timeval start;
737  int remaining_time = 0;
738  int hits = 0;
740  AST_APP_ARG(freqs);
741  AST_APP_ARG(duration);
742  AST_APP_ARG(timeout);
743  AST_APP_ARG(times);
745  );
746 
747  appdata = ast_strdupa(data);
748  AST_STANDARD_APP_ARGS(args, appdata);
749 
750  if (!ast_strlen_zero(args.options)) {
751  ast_app_parse_options(wait_exec_options, &flags, opt_args, args.options);
752  }
753  if (freq_parser(args.freqs, &freq1, &freq2)) {
754  pbx_builtin_setvar_helper(chan, "WAITFORTONESTATUS", "ERROR");
755  return -1;
756  }
757  if (!ast_strlen_zero(args.timeout) && (sscanf(args.timeout, "%30lf", &timeoutf) != 1 || timeoutf < 0)) {
758  ast_log(LOG_WARNING, "Invalid timeout: %s\n", args.timeout);
759  pbx_builtin_setvar_helper(chan, "WAITFORTONESTATUS", "ERROR");
760  return -1;
761  }
762  timeout = 1000 * timeoutf;
763  if (!ast_strlen_zero(args.duration) && (ast_str_to_int(args.duration, &duration) || duration < 1)) {
764  ast_log(LOG_WARNING, "Invalid duration: %s\n", args.duration);
765  pbx_builtin_setvar_helper(chan, "WAITFORTONESTATUS", "ERROR");
766  return -1;
767  }
768  if (!ast_strlen_zero(args.times) && (ast_str_to_int(args.times, &times) || times < 1)) {
769  ast_log(LOG_WARNING, "Invalid number of times: %s\n", args.times);
770  pbx_builtin_setvar_helper(chan, "WAITFORTONESTATUS", "ERROR");
771  return -1;
772  }
774  if ((ast_str_to_int(opt_args[OPT_APP_ARG_DECIBEL], &db) || db < 1)) {
775  ast_log(LOG_WARNING, "Invalid decibel level: %s\n", opt_args[OPT_APP_ARG_DECIBEL]);
776  pbx_builtin_setvar_helper(chan, "WAITFORTONESTATUS", "ERROR");
777  return -1;
778  }
779  }
780  squelch = ast_test_flag(&flags, OPT_APP_SQUELCH);
781  if (!(dsp = ast_dsp_new())) {
782  ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
783  pbx_builtin_setvar_helper(chan, "WAITFORTONESTATUS", "ERROR");
784  return -1;
785  }
787  ast_dsp_set_freqmode(dsp, freq1, duration, db, squelch);
788  ast_debug(1, "Waiting for %s Hz, %d time(s), timeout %d ms, %d db\n", args.freqs, times, timeout, db);
789  start = ast_tvnow();
790  do {
791  if (timeout > 0) {
792  remaining_time = ast_remaining_ms(start, timeout);
793  if (remaining_time <= 0) {
794  pbx_builtin_setvar_helper(chan, "WAITFORTONESTATUS", "TIMEOUT");
795  break;
796  }
797  }
798  if (ast_waitfor(chan, 1000) > 0) {
799  if (!(frame = ast_read(chan))) {
800  ast_debug(1, "Channel '%s' did not return a frame; probably hung up.\n", ast_channel_name(chan));
801  pbx_builtin_setvar_helper(chan, "WAITFORTONESTATUS", "HANGUP");
802  break;
803  } else if (frame->frametype == AST_FRAME_VOICE) {
804  frame = ast_dsp_process(chan, dsp, frame);
805  if (frame->frametype == AST_FRAME_DTMF) {
806  char result = frame->subclass.integer;
807  if (result == 'q') {
808  hits++;
809  ast_debug(1, "We just detected %s Hz (hit #%d)\n", args.freqs, hits);
810  if (hits >= times) {
811  ast_frfree(frame);
812  pbx_builtin_setvar_helper(chan, "WAITFORTONESTATUS", "SUCCESS");
813  break;
814  }
815  }
816  }
817  }
818  ast_frfree(frame);
819  } else {
820  pbx_builtin_setvar_helper(chan, "WAITFORTONESTATUS", "HANGUP");
821  }
822  } while (timeout == 0 || remaining_time > 0);
823  ast_dsp_free(dsp);
824 
825  return 0;
826 }
827 
828 static char *waitapp = "WaitForTone";
829 static char *scanapp = "ToneScan";
830 
831 static int scan_exec(struct ast_channel *chan, const char *data)
832 {
833  char *appdata;
834  double timeoutf = 0;
835  int timeout = 0;
836  struct ast_frame *frame = NULL, *frame2 = NULL;
837  struct ast_dsp *dsp = NULL, *dsp2 = NULL;
838  struct timeval start;
839  int remaining_time = 0;
840  int features, match = 0, fax = 0, voice = 0, threshold = 1;
842  AST_APP_ARG(zone);
843  AST_APP_ARG(timeout);
846  );
847 
848  appdata = ast_strdupa(data);
849  AST_STANDARD_APP_ARGS(args, appdata);
850 
851  if (!ast_strlen_zero(args.timeout) && (sscanf(args.timeout, "%30lf", &timeoutf) != 1 || timeout < 0)) {
852  ast_log(LOG_WARNING, "Invalid timeout: %s\n", args.timeout);
853  pbx_builtin_setvar_helper(chan, "TONESCANSTATUS", "ERROR");
854  return -1;
855  }
856  if (!ast_strlen_zero(args.threshold) && (ast_str_to_int(args.threshold, &threshold) || threshold < 1)) {
857  ast_log(LOG_WARNING, "Invalid threshold: %s\n", args.threshold);
858  pbx_builtin_setvar_helper(chan, "TONESCANSTATUS", "ERROR");
859  return -1;
860  }
861  timeout = 1000 * timeoutf;
862 
863  if (!ast_strlen_zero(args.options) && strchr(args.options, 'f')) {
864  fax = 1;
865  }
866  if (!ast_strlen_zero(args.options) && strchr(args.options, 'v')) {
867  voice = 1;
868  }
869 
870  if (!(dsp = ast_dsp_new())) {
871  ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
872  pbx_builtin_setvar_helper(chan, "TONESCANSTATUS", "ERROR");
873  return -1;
874  }
875 
876  if (!ast_strlen_zero(args.zone)) {
877  if (ast_dsp_set_call_progress_zone(dsp, args.zone)) {
878  ast_log(LOG_WARNING, "Invalid call progress zone: %s\n", args.zone);
879  pbx_builtin_setvar_helper(chan, "TONESCANSTATUS", "ERROR");
880  ast_dsp_free(dsp);
881  return -1;
882  }
883  }
884 
885  if (fax) {
886  if (!(dsp2 = ast_dsp_new())) {
887  ast_dsp_free(dsp);
888  ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
889  pbx_builtin_setvar_helper(chan, "TONESCANSTATUS", "ERROR");
890  return -1;
891  }
892  }
893 
894  features = DSP_PROGRESS_RINGING; /* audible ringback tone */
895  features |= DSP_PROGRESS_BUSY; /* busy signal */
896  features |= DSP_PROGRESS_CONGESTION; /* SIT tones (not reorder!) */
897  features |= DSP_PROGRESS_TALK; /* voice. */
898  features |= DSP_FEATURE_WAITDIALTONE; /* dial tone */
899  features |= DSP_FEATURE_FREQ_DETECT; /* modem answer */
900  if (voice) {
901  features |= DSP_TONE_STATE_TALKING; /* voice */
902  }
903  ast_dsp_set_features(dsp, features);
904  /* all modems begin negotiating with Bell 103. An answering modem just sends mark tone, or 2225 Hz */
905  ast_dsp_set_freqmode(dsp, 2225, 400, 16, 0); /* this needs to be pretty short, or the progress tones code will thing this is voice */
906 
907  if (fax) { /* fax detect uses same tone detect internals as modem and causes things to not work as intended, so use a separate DSP if needed. */
908  ast_dsp_set_features(dsp2, DSP_FEATURE_FAX_DETECT); /* fax tone */
909  ast_dsp_set_faxmode(dsp2, DSP_FAXMODE_DETECT_CED); /* we only care about the answering side (CED), not originating (CNG) */
910  }
911 
912  ast_debug(1, "Starting tone scan, timeout: %d ms, threshold: %d\n", timeout, threshold);
913  start = ast_tvnow();
914  do {
915  if (timeout > 0) {
916  remaining_time = ast_remaining_ms(start, timeout);
917  if (remaining_time <= 0) {
918  pbx_builtin_setvar_helper(chan, "TONESCANSTATUS", "TIMEOUT");
919  break;
920  }
921  }
922  if (ast_waitfor(chan, 1000) > 0) {
923  if (!(frame = ast_read(chan))) {
924  ast_debug(1, "Channel '%s' did not return a frame; probably hung up.\n", ast_channel_name(chan));
925  pbx_builtin_setvar_helper(chan, "TONESCANSTATUS", "HANGUP");
926  break;
927  } else if (frame->frametype == AST_FRAME_VOICE) {
928  if (fax) {
929  frame2 = ast_frdup(frame);
930  }
931  frame = ast_dsp_process(chan, dsp, frame);
932  if (frame->frametype == AST_FRAME_DTMF) {
933  char result = frame->subclass.integer;
934  match = 1;
935  if (result == 'q') {
936  pbx_builtin_setvar_helper(chan, "TONESCANSTATUS", "MODEM");
937  } else {
938  pbx_builtin_setvar_helper(chan, "TONESCANSTATUS", "DTMF");
939  }
940  } else if (fax) {
941  char result;
942  frame2 = ast_dsp_process(chan, dsp2, frame2);
943  result = frame->subclass.integer;
944  if (result == AST_FRAME_DTMF) {
945  if (result == 'e') {
946  pbx_builtin_setvar_helper(chan, "TONESCANSTATUS", "FAX");
947  match = 1;
948  } else {
949  ast_debug(1, "Ignoring inactionable event\n"); /* shouldn't happen */
950  }
951  }
952  ast_frfree(frame2);
953  }
954  if (!match) {
955  int tstate, tcount;
956  tcount = ast_dsp_get_tcount(dsp);
957  tstate = ast_dsp_get_tstate(dsp);
958  if (tstate > 0) {
959  ast_debug(3, "tcount: %d, tstate: %d\n", tcount, tstate);
960  if (tcount >= threshold) {
961  match = 1;
962  switch (tstate) {
964  pbx_builtin_setvar_helper(chan, "TONESCANSTATUS", "RINGING");
965  break;
967  pbx_builtin_setvar_helper(chan, "TONESCANSTATUS", "DIALTONE");
968  break;
970  /* even if we don't specify this feature, it's still checked, so we always need to handle it.
971  Even if we are looking for it, we need to wait a while or tones will be interpreted
972  as voice, because this will match first (and this should match last). */
973  if (voice && tcount > 15 && tcount >= threshold) {
974  pbx_builtin_setvar_helper(chan, "TONESCANSTATUS", "VOICE");
975  } else {
976  match = 0;
977  }
978  break;
979  case DSP_TONE_STATE_BUSY:
980  pbx_builtin_setvar_helper(chan, "TONESCANSTATUS", "BUSY");
981  break;
983  pbx_builtin_setvar_helper(chan, "TONESCANSTATUS", "SIT");
984  break;
985  case DSP_TONE_STATE_HUNGUP: /* UK only */
986  pbx_builtin_setvar_helper(chan, "TONESCANSTATUS", "NUT");
987  break;
988  default:
989  match = 0;
990  ast_debug(1, "Something else we weren't expecting? tstate: %d, #%d\n", tstate, tcount);
991  }
992  }
993  }
994  }
995  }
996  ast_frfree(frame);
997  } else {
998  pbx_builtin_setvar_helper(chan, "TONESCANSTATUS", "HANGUP");
999  }
1000  } while (!match && (timeout == 0 || remaining_time > 0));
1001  ast_dsp_free(dsp);
1002  if (dsp2) {
1003  ast_dsp_free(dsp2);
1004  }
1005 
1006  return 0;
1007 }
1008 
1009 static struct ast_custom_function detect_function = {
1010  .name = "TONE_DETECT",
1011  .read = detect_read,
1012  .write = detect_write,
1013 };
1014 
1015 static int unload_module(void)
1016 {
1017  int res;
1018 
1022 
1023  return res;
1024 }
1025 
1026 static int load_module(void)
1027 {
1028  int res;
1029 
1033 
1034  return res;
1035 }
1036 
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition: astmm.h:180
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
#define ast_log
Definition: astobj2.c:42
Audiohooks Architecture.
@ AST_AUDIOHOOK_MANIPULATE_ALL_RATES
Definition: audiohook.h:75
int ast_audiohook_init(struct ast_audiohook *audiohook, enum ast_audiohook_type type, const char *source, enum ast_audiohook_init_flags flags)
Initialize an audiohook structure.
Definition: audiohook.c:100
int ast_audiohook_remove(struct ast_channel *chan, struct ast_audiohook *audiohook)
Remove an audiohook from a specified channel.
Definition: audiohook.c:699
ast_audiohook_direction
Definition: audiohook.h:48
@ AST_AUDIOHOOK_DIRECTION_READ
Definition: audiohook.h:49
#define ast_audiohook_lock(ah)
Lock an audiohook.
Definition: audiohook.h:304
int ast_audiohook_detach(struct ast_audiohook *audiohook)
Detach audiohook from channel.
Definition: audiohook.c:528
int ast_audiohook_attach(struct ast_channel *chan, struct ast_audiohook *audiohook)
Attach audiohook to channel.
Definition: audiohook.c:462
int ast_audiohook_destroy(struct ast_audiohook *audiohook)
Destroys an audiohook structure.
Definition: audiohook.c:121
#define ast_audiohook_unlock(ah)
Unlock an audiohook.
Definition: audiohook.h:309
@ AST_AUDIOHOOK_TYPE_MANIPULATE
Definition: audiohook.h:38
@ AST_AUDIOHOOK_STATUS_DONE
Definition: audiohook.h:45
static sqlite3 * db
static PGresult * result
Definition: cel_pgsql.c:84
static char exten[AST_MAX_EXTENSION]
Definition: chan_alsa.c:122
static char context[AST_MAX_CONTEXT]
Definition: chan_alsa.c:120
static int match(struct ast_sockaddr *addr, unsigned short callno, unsigned short dcallno, const struct chan_iax2_pvt *cur, int check_dcallno)
Definition: chan_iax2.c:2312
static void parse(struct mgcp_request *req)
Definition: chan_mgcp.c:1844
General Asterisk PBX channel definitions.
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2384
int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
Remove a datastore from a channel.
Definition: channel.c:2393
#define ast_channel_lock(chan)
Definition: channel.h:2922
const char * ast_channel_context(const struct ast_channel *chan)
int ast_waitfor(struct ast_channel *chan, int ms)
Wait for input on a channel.
Definition: channel.c:3163
const char * ast_channel_name(const struct ast_channel *chan)
struct ast_frame * ast_read(struct ast_channel *chan)
Reads a frame.
Definition: channel.c:4292
const char * ast_channel_exten(const struct ast_channel *chan)
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
Definition: channel.c:2398
#define ast_channel_unlock(chan)
Definition: channel.h:2923
Conversion utility functions.
int ast_str_to_int(const char *str, int *res)
Convert the given string to a signed integer.
Definition: conversions.c:44
#define ast_datastore_alloc(info, uid)
Definition: datastore.h:85
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition: datastore.c:68
Convenient Signal Processing routines.
void ast_dsp_free(struct ast_dsp *dsp)
Definition: dsp.c:1773
struct ast_dsp * ast_dsp_new(void)
Allocates a new dsp, assumes 8khz for internal sample rate.
Definition: dsp.c:1748
int ast_dsp_get_tcount(struct ast_dsp *dsp)
Get tcount (Threshold counter)
Definition: dsp.c:1906
threshold
Definition: dsp.h:71
#define DSP_PROGRESS_RINGING
Definition: dsp.h:40
#define DSP_TONE_STATE_SPECIAL3
Definition: dsp.h:59
#define DSP_FEATURE_WAITDIALTONE
Definition: dsp.h:44
#define DSP_TONE_STATE_DIALTONE
Definition: dsp.h:54
#define DSP_PROGRESS_TALK
Definition: dsp.h:39
#define DSP_TONE_STATE_BUSY
Definition: dsp.h:56
#define DSP_PROGRESS_BUSY
Definition: dsp.h:41
#define DSP_FEATURE_FAX_DETECT
Definition: dsp.h:29
#define DSP_FAXMODE_DETECT_CED
Definition: dsp.h:48
#define DSP_PROGRESS_CONGESTION
Definition: dsp.h:42
struct ast_frame * ast_dsp_process(struct ast_channel *chan, struct ast_dsp *dsp, struct ast_frame *inf)
Return AST_FRAME_NULL frames when there is silence, AST_FRAME_BUSY on busies, and call progress,...
Definition: dsp.c:1497
#define DSP_TONE_STATE_HUNGUP
Definition: dsp.h:60
#define DSP_FEATURE_FREQ_DETECT
Definition: dsp.h:45
#define DSP_TONE_STATE_TALKING
Definition: dsp.h:55
int ast_dsp_get_tstate(struct ast_dsp *dsp)
Get tstate (Tone State)
Definition: dsp.c:1901
#define DSP_TONE_STATE_RINGING
Definition: dsp.h:53
int ast_dsp_set_faxmode(struct ast_dsp *dsp, int faxmode)
Set fax mode.
Definition: dsp.c:1873
int ast_dsp_set_freqmode(struct ast_dsp *dsp, int freq, int dur, int db, int squelch)
Set arbitrary frequency detection mode.
Definition: dsp.c:1862
void ast_dsp_set_features(struct ast_dsp *dsp, int features)
Select feature set.
Definition: dsp.c:1758
int ast_dsp_set_call_progress_zone(struct ast_dsp *dsp, char *zone)
Set zone for doing progress detection.
Definition: dsp.c:1882
Media Format Cache API.
direction
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define AST_APP_ARG(name)
Define an application argument.
#define END_OPTIONS
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
#define AST_APP_OPTION_ARG(option, flagno, argno)
Declares an application option that accepts an argument.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define BEGIN_OPTIONS
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: main/app.c:3126
char * strsep(char **str, const char *delims)
Asterisk internal frame definitions.
#define AST_FRAME_DTMF
#define ast_frdup(fr)
Copies a frame.
#define ast_frfree(fr)
@ AST_FRAME_VOICE
#define AST_LOG_WARNING
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_ERROR
#define LOG_WARNING
Tone Indication Support.
#define SCOPED_CHANNELLOCK(varname, chan)
scoped lock specialization for channels.
Definition: lock.h:617
Asterisk module definitions.
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:626
Core PBX routines and definitions.
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name.
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1543
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
int ast_async_parseable_goto(struct ast_channel *chan, const char *goto_string)
static int detect_callback(struct ast_audiohook *audiohook, struct ast_channel *chan, struct ast_frame *frame, enum ast_audiohook_direction direction)
static int wait_exec(struct ast_channel *chan, const char *data)
static int detect_read(struct ast_channel *chan, const char *cmd, char *data, char *buffer, size_t buflen)
static int remove_detect(struct ast_channel *chan)
@ OPT_APP_DECIBEL
@ OPT_APP_SQUELCH
static int scan_exec(struct ast_channel *chan, const char *data)
static const struct ast_datastore_info detect_datastore
@ OPT_ARG_GOTO_TX
@ OPT_ARG_DECIBEL
@ OPT_ARG_HITS_REQ
@ OPT_ARG_GOTO_RX
@ OPT_ARG_ARRAY_SIZE
static struct ast_custom_function detect_function
@ OPT_APP_ARG_DECIBEL
@ OPT_APP_ARG_ARRAY_SIZE
AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Tone detection module")
static char * waitapp
static int parse_signal_features(struct ast_flags *flags)
static char * goto_parser(struct ast_channel *chan, char *loc)
static void destroy_callback(void *data)
static int load_module(void)
static int freq_parser(char *freqs, int *freq1, int *freq2)
static int unload_module(void)
td_opts
@ OPT_GOTO_RX
@ OPT_SIT
@ OPT_END_FILTER
@ OPT_DIALTONE
@ OPT_SQUELCH
@ OPT_DECIBEL
@ OPT_RX
@ OPT_GOTO_TX
@ OPT_TX
@ OPT_BUSY
@ OPT_HITS_REQ
static const struct ast_app_option wait_exec_options[128]
static char * scanapp
static int detect_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
#define NULL
Definition: resample.c:96
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
enum ast_audiohook_status status
Definition: audiohook.h:108
Main Channel structure associated with a channel.
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
const char * name
Definition: pbx.h:119
Structure for a data store type.
Definition: datastore.h:31
const char * type
Definition: datastore.h:32
Structure for a data store object.
Definition: datastore.h:64
void * data
Definition: datastore.h:66
Definition: dsp.c:407
goertzel_state_t freqs[FREQ_ARRAY_SIZE]
Definition: dsp.c:421
Structure used to handle boolean flags.
Definition: utils.h:199
unsigned int flags
Definition: utils.h:200
Data structure associated with a single frame of data.
struct ast_frame_subclass subclass
enum ast_frame_type frametype
unsigned short int tx
struct ast_audiohook audiohook
unsigned short int squelch
struct ast_dsp * dsp
unsigned short int rx
int value
Definition: syslog.c:37
static float di[4]
Definition: tdd.c:58
const char * args
static struct test_options options
int ast_remaining_ms(struct timeval start, int max_ms)
Calculate remaining milliseconds given a starting timestamp and upper bound.
Definition: main/utils.c:2179
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:157
#define ast_test_flag(p, flag)
Definition: utils.h:63