mirror of
https://github.com/signalwire/freeswitch.git
synced 2025-02-04 18:27:36 +00:00
Merge branch 'moy.iodump'
This commit is contained in:
commit
004cc9d7aa
@ -55,10 +55,6 @@ COMPILE = $(CC) $(FTDM_CFLAGS)
|
|||||||
LTCOMPILE = $(LIBTOOL) --mode=compile --tag=CC $(COMPILE)
|
LTCOMPILE = $(LIBTOOL) --mode=compile --tag=CC $(COMPILE)
|
||||||
LINK = $(LIBTOOL) --mode=link --tag=CC $(CC) $(FTDM_CFLAGS) $(LDFLAGS) -o $@
|
LINK = $(LIBTOOL) --mode=link --tag=CC $(CC) $(FTDM_CFLAGS) $(LDFLAGS) -o $@
|
||||||
|
|
||||||
if WANT_DEBUGDTMF
|
|
||||||
FTDM_CFLAGS += -DFTDM_DEBUG_DTMF
|
|
||||||
endif
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# GNU pkgconfig file
|
# GNU pkgconfig file
|
||||||
|
@ -45,3 +45,29 @@ fxs-channel => 1
|
|||||||
number => 2
|
number => 2
|
||||||
fxo-channel => 3
|
fxo-channel => 3
|
||||||
|
|
||||||
|
; MFC-R2 typical span configuration
|
||||||
|
|
||||||
|
; MFC-R2 with wanpipe (Sangoma)
|
||||||
|
[span wanpipe myWanpipeSpan]
|
||||||
|
trunk_type => E1
|
||||||
|
cas-channel => 1-15:1101
|
||||||
|
cas-channel => 17-31:1101
|
||||||
|
|
||||||
|
; MFC-R2 with Zaptel/DAHDI
|
||||||
|
[span zt myWanpipeSpan]
|
||||||
|
trunk_type => E1
|
||||||
|
cas-channel => 1-15:1101
|
||||||
|
cas-channel => 17-31:1101
|
||||||
|
|
||||||
|
; generic channel parameters
|
||||||
|
; this parameters are accepted by any type of span/channel
|
||||||
|
; remember that for generic channel parameters only channels
|
||||||
|
; below the parameter within the span will be affected
|
||||||
|
|
||||||
|
; Channel audio gain
|
||||||
|
; rxgain => 0.0
|
||||||
|
; txgain => 0.0
|
||||||
|
|
||||||
|
; Whether to perform media dumps for DTMF debugging
|
||||||
|
; debugdtmf => yes
|
||||||
|
|
||||||
|
@ -1,6 +1,14 @@
|
|||||||
<!-- Please refer to http://wiki.freeswitch.org/wiki/FreeTDM for further documentation -->
|
<!-- Please refer to http://wiki.freeswitch.org/wiki/FreeTDM for further documentation -->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
This is a sample FreeSWITCH XML configuration for FreeTDM
|
||||||
|
Remember you still need to configure freetdm.conf (no XML extension) in $prefix/conf/
|
||||||
|
directory of FreeSWITCH. The freetdm.conf (no XML extension) is a simple text file
|
||||||
|
definining the I/O interfaces (Sangoma, DAHDI etc). This file (freetdm.conf.xml) deals
|
||||||
|
with the signaling protocols that you can run on top of your I/O interfaces.
|
||||||
|
-->
|
||||||
<configuration name="freetdm.conf" description="FreeTDM Configuration">
|
<configuration name="freetdm.conf" description="FreeTDM Configuration">
|
||||||
|
|
||||||
<settings>
|
<settings>
|
||||||
<param name="debug" value="0"/>
|
<param name="debug" value="0"/>
|
||||||
<!--<param name="hold-music" value="$${moh_uri}"/>-->
|
<!--<param name="hold-music" value="$${moh_uri}"/>-->
|
||||||
@ -8,30 +16,9 @@
|
|||||||
<!--<param name="enable-analog-option" value="3-way"/>-->
|
<!--<param name="enable-analog-option" value="3-way"/>-->
|
||||||
</settings>
|
</settings>
|
||||||
|
|
||||||
<!-- use the <pri_spans> tag for native ISDN support (most likely broken at this point, check sangoma_pri_spans or libpri_spans for alternatives) -->
|
<!-- Sample analog configuration -->
|
||||||
<pri_spans>
|
|
||||||
<span name="PRI_1">
|
|
||||||
<!-- Log Levels: none, alert, crit, err, warning, notice, info, debug -->
|
|
||||||
<param name="q921loglevel" value="alert"/>
|
|
||||||
<param name="q931loglevel" value="alert"/>
|
|
||||||
<param name="mode" value="user"/>
|
|
||||||
<param name="dialect" value="5ess"/>
|
|
||||||
<param name="dialplan" value="XML"/>
|
|
||||||
<param name="context" value="default"/>
|
|
||||||
</span>
|
|
||||||
<span name="PRI_2">
|
|
||||||
<param name="q921loglevel" value="alert"/>
|
|
||||||
<param name="q931loglevel" value="alert"/>
|
|
||||||
<param name="mode" value="user"/>
|
|
||||||
<param name="dialect" value="5ess"/>
|
|
||||||
<param name="dialplan" value="XML"/>
|
|
||||||
<param name="context" value="default"/>
|
|
||||||
</span>
|
|
||||||
</pri_spans>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- Analog spans go here -->
|
|
||||||
<analog_spans>
|
<analog_spans>
|
||||||
|
<!-- The span name must match the name in your freetdm.conf -->
|
||||||
<span name="myAnalog">
|
<span name="myAnalog">
|
||||||
<!--<param name="hold-music" value="$${moh_uri}"/>-->
|
<!--<param name="hold-music" value="$${moh_uri}"/>-->
|
||||||
<!--<param name="enable-analog-option" value="call-swap"/>-->
|
<!--<param name="enable-analog-option" value="call-swap"/>-->
|
||||||
@ -49,4 +36,185 @@
|
|||||||
</span>
|
</span>
|
||||||
</analog_spans>
|
</analog_spans>
|
||||||
|
|
||||||
|
<!-- openr2 (MFC-R2 signaling) spans
|
||||||
|
MFC-R2 signaling has lots of variants from country to country and even sometimes
|
||||||
|
minor variants inside the same country. The only mandatory parameters here are:
|
||||||
|
variant, but typically you also want to set max_ani and max_dnis.
|
||||||
|
IT IS RECOMMENDED that you leave the default values (leaving them commented) for the
|
||||||
|
other parameters unless you have problems or you have been instructed to change some
|
||||||
|
parameter. OpenR2 library uses the 'variant' parameter to try to determine the
|
||||||
|
best defaults for your country. If you want to contribute your configs for a particular
|
||||||
|
country send them to the e-mail of the primary OpenR2 developer that you can find in the
|
||||||
|
AUTHORS file of the OpenR2 package, they will be added to the samples directory of openr2.
|
||||||
|
-->
|
||||||
|
<r2_spans>
|
||||||
|
<span name="wp1" cfgprofile="testr2">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
MFC/R2 variant. This depends on the OpenR2 supported variants
|
||||||
|
A list of values can be found by executing the openr2 command r2test -l
|
||||||
|
some valid values are:
|
||||||
|
mx (Mexico)
|
||||||
|
ar (Argentina)
|
||||||
|
br (Brazil)
|
||||||
|
ph (Philippines)
|
||||||
|
itu (per ITU spec)
|
||||||
|
-->
|
||||||
|
<param name="variant" value="mx"/>
|
||||||
|
|
||||||
|
<!-- switch parameters (required), where to send calls to -->
|
||||||
|
<param name="dialplan" value="XML"/>
|
||||||
|
<param name="context" value="default"/>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Max amount of ANI (caller id digits) to ask for
|
||||||
|
<param name="max_ani" value="4"/>
|
||||||
|
-->
|
||||||
|
<!--
|
||||||
|
Max amount of DNIS to ask for
|
||||||
|
<param name="max_dnis" value="4"/>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- Do not set parameters below this line unless you desire to tweak it because is not working -->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Whether or not to get the ANI before getting DNIS (only affects incoming calls)
|
||||||
|
Some telcos require ANI first some others do not care, if default go wrong on
|
||||||
|
incoming calls, change this value
|
||||||
|
<param name="get_ani_first" value="yes"/>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Caller Category to send. Accepted values:
|
||||||
|
- national_subscriber
|
||||||
|
- national_priority_subscriber
|
||||||
|
- international_subscriber
|
||||||
|
- international_priority_subscriber
|
||||||
|
- collect_call
|
||||||
|
Usually national_subscriber (the default) works just fine
|
||||||
|
<param name="category" value="national_subscriber"/>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Brazil uses a special calling party category for collect calls (llamadas por cobrar)
|
||||||
|
instead of using the operator (as in Mexico). The R2 spec in Brazil says a special GB tone
|
||||||
|
should be used to reject collect calls. If you want to ALLOW collect calls specify 'yes',
|
||||||
|
if you want to BLOCK collect calls then say 'no'. Default is to block collect calls.
|
||||||
|
(see also 'double_answer')
|
||||||
|
<param name="allow_collect_calls" value="yes"/>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
This feature is related but independent of allow_collect_calls
|
||||||
|
Some PBX's require a double-answer process to block collect calls, if
|
||||||
|
you ever have problems blocking collect calls using Group B signals (allow_collect_calls=no)
|
||||||
|
then you may want to try with double_answer=yes, this will cause that every answer signal
|
||||||
|
is changed to perform 'answer -> clear back -> answer' (sort of a flash)
|
||||||
|
(see also 'allow_collect_calls')
|
||||||
|
<param name="double_answer" value="yes"/>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
This feature allows to skip the use of Group B/II signals and go directly
|
||||||
|
to the accepted state for incoming calls
|
||||||
|
<param name="immediate_accept" value="yes"/>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Skip request of calling party category and ANI
|
||||||
|
<param name="skip_category" value="yes"/>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Brazil use a special signal to force the release of the line (hangup) from the
|
||||||
|
backward perspective. When forced_release=no, the normal clear back signal
|
||||||
|
will be sent on hangup, which is OK for all mfcr2 variants I know of, except for
|
||||||
|
Brazilian variant, where the central will leave the line up for several seconds (30, 60)
|
||||||
|
which sometimes is not what people really want. When forced_release=yes, a different
|
||||||
|
signal will be sent to hangup the call indicating that the line should be released immediately
|
||||||
|
<param name="forced_release" value="yes"/>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Whether or not report to the other end 'accept call with charge'
|
||||||
|
This setting has no effect with most telecos, usually is safe
|
||||||
|
leave the default (yes), but once in a while when interconnecting with
|
||||||
|
old PBXs this may be useful.
|
||||||
|
Concretely this affects the Group B signal used to accept calls
|
||||||
|
<param name="charge_calls" value="yes"/>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
MFC/R2 value in milliseconds for the MF timeout. Any negative value
|
||||||
|
means 'default', smaller values than 500ms are not recommended
|
||||||
|
and can cause malfunctioning. If you experience protocol error
|
||||||
|
due to MF timeout try incrementing this value in 500ms steps
|
||||||
|
<param name="mfback_timeout" value="1500"/>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
MFC/R2 value in milliseconds for the metering pulse timeout.
|
||||||
|
Metering pulses are sent by some telcos for some R2 variants
|
||||||
|
during a call presumably for billing purposes to indicate costs,
|
||||||
|
however this pulses use the same signal that is used to indicate
|
||||||
|
call hangup, therefore a timeout is sometimes required to distinguish
|
||||||
|
between a *real* hangup and a billing pulse that should not
|
||||||
|
last more than 500ms, If you experience call drops after some
|
||||||
|
minutes of being stablished try setting a value of some ms here,
|
||||||
|
values greater than 500ms are not recommended.
|
||||||
|
BE AWARE that choosing the proper protocol variant parameter
|
||||||
|
implicitly sets a good recommended value for this timer, use this
|
||||||
|
parameter only when you *really* want to override the default, otherwise
|
||||||
|
just comment out this value.
|
||||||
|
<param name="metering_pulse_timeout" value="1000"/>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
WARNING: advanced users only! I really mean it
|
||||||
|
this parameter is commented by default because
|
||||||
|
YOU DON'T NEED IT UNLESS YOU REALLY GROK MFC/R2
|
||||||
|
READ COMMENTS on doc/r2proto.conf in openr2 package
|
||||||
|
for more info
|
||||||
|
<param name="advanced_protocol_file" value="/usr/local/freeswitch/conf/r2proto.conf"/>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!-- USE THIS FOR DEBUGGING MFC-R2 PROTOCOL -->
|
||||||
|
<!--
|
||||||
|
Where to dump advanced call file protocol logs
|
||||||
|
<param name="logdir" value="$${base_dir}/log/mfcr2"/>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
MFC/R2 valid logging values are: all,error,warning,debug,notice,cas,mf,nothing
|
||||||
|
error,warning,debug and notice are self-descriptive
|
||||||
|
'cas' is for logging ABCD CAS tx and rx
|
||||||
|
'mf' is for logging of the Multi Frequency tones
|
||||||
|
You can mix up values, like: loglevel=error,debug,mf to log just error, debug and
|
||||||
|
multi frequency messages
|
||||||
|
'all' is a special value to log all the activity
|
||||||
|
'nothing' is a clean-up value, in case you want to not log any activity for
|
||||||
|
a channel or group of channels
|
||||||
|
BE AWARE that the level of output logged will ALSO depend on
|
||||||
|
the value you have in FreeSWITCH logging configurations, if you disable output FreeSWITCH
|
||||||
|
then it does not matter if you specify 'all' here, nothing will be logged
|
||||||
|
so FreeSWITCH has the last word on what is going to be logged
|
||||||
|
<param name="logging" value="debug,notice,warning,error,mf,cas"/>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
whether or not to drop protocol call files into 'logdir'
|
||||||
|
<param name="call_files" value="yes"/>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Use only for very technical debugging
|
||||||
|
This is the size (if 0, dumps are disabled) of MF dump files. MF dump files
|
||||||
|
are audio files that are dumped when a protocol error occurs.
|
||||||
|
The files are dumped in whatever you set in the logdir parameter.
|
||||||
|
Value -1 uses a default recommended size (which stores 5 seconds of audio)
|
||||||
|
<param name="mf_dump_size" value="-1"/>
|
||||||
|
-->
|
||||||
|
</span>
|
||||||
|
</r2_spans>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|
||||||
|
@ -167,17 +167,6 @@ AC_ARG_WITH([pritap],
|
|||||||
HAVE_PRITAP="${enable_pritap}"
|
HAVE_PRITAP="${enable_pritap}"
|
||||||
AM_CONDITIONAL([HAVE_PRITAP],[test "${enable_pritap}" = "yes"])
|
AM_CONDITIONAL([HAVE_PRITAP],[test "${enable_pritap}" = "yes"])
|
||||||
|
|
||||||
# debug dtmf?
|
|
||||||
AC_ARG_WITH([debugdtmf],
|
|
||||||
[AS_HELP_STRING([--with-debugdtmf], [Debug DTMF])],
|
|
||||||
[case "${withval}" in
|
|
||||||
no) enable_debugdtmf="no" ;;
|
|
||||||
*) enable_debugdtmf="yes" ;;
|
|
||||||
esac],
|
|
||||||
[enable_debugdtmf="no"]
|
|
||||||
)
|
|
||||||
AM_CONDITIONAL([WANT_DEBUGDTMF], [test "${enable_debugdtmf}" = "yes"])
|
|
||||||
|
|
||||||
##
|
##
|
||||||
# OpenR2 stack
|
# OpenR2 stack
|
||||||
#
|
#
|
||||||
|
@ -1417,6 +1417,24 @@ fail:
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ftdm_enable_channel_dtmf(ftdm_channel_t *fchan, switch_channel_t *channel)
|
||||||
|
{
|
||||||
|
if (channel) {
|
||||||
|
const char *var;
|
||||||
|
if ((var = switch_channel_get_variable(channel, "freetdm_disable_dtmf"))) {
|
||||||
|
if (switch_true(var)) {
|
||||||
|
ftdm_channel_command(fchan, FTDM_COMMAND_DISABLE_DTMF_DETECT, NULL);
|
||||||
|
ftdm_log(FTDM_LOG_INFO, "DTMF detection disabled in channel %d:%d\n", ftdm_channel_get_span_id(fchan), ftdm_channel_get_id(fchan));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* the variable is not present or has a negative value then proceed to enable DTMF ... */
|
||||||
|
}
|
||||||
|
if (ftdm_channel_command(fchan, FTDM_COMMAND_ENABLE_DTMF_DETECT, NULL) != FTDM_SUCCESS) {
|
||||||
|
ftdm_log(FTDM_LOG_ERROR, "Failed to enable DTMF detection in channel %d:%d\n", ftdm_channel_get_span_id(fchan), ftdm_channel_get_id(fchan));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ftdm_status_t ftdm_channel_from_event(ftdm_sigmsg_t *sigmsg, switch_core_session_t **sp)
|
ftdm_status_t ftdm_channel_from_event(ftdm_sigmsg_t *sigmsg, switch_core_session_t **sp)
|
||||||
{
|
{
|
||||||
switch_core_session_t *session = NULL;
|
switch_core_session_t *session = NULL;
|
||||||
@ -1440,6 +1458,9 @@ ftdm_status_t ftdm_channel_from_event(ftdm_sigmsg_t *sigmsg, switch_core_session
|
|||||||
return FTDM_FAIL;
|
return FTDM_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* I guess we always want DTMF detection */
|
||||||
|
ftdm_enable_channel_dtmf(sigmsg->channel, NULL);
|
||||||
|
|
||||||
switch_core_session_add_stream(session, NULL);
|
switch_core_session_add_stream(session, NULL);
|
||||||
|
|
||||||
tech_pvt = (private_t *) switch_core_session_alloc(session, sizeof(private_t));
|
tech_pvt = (private_t *) switch_core_session_alloc(session, sizeof(private_t));
|
||||||
@ -1633,24 +1654,6 @@ static FIO_SIGNAL_CB_FUNCTION(on_common_signal)
|
|||||||
return FTDM_BREAK;
|
return FTDM_BREAK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ftdm_enable_channel_dtmf(ftdm_channel_t *fchan, switch_channel_t *channel)
|
|
||||||
{
|
|
||||||
if (channel) {
|
|
||||||
const char *var;
|
|
||||||
if ((var = switch_channel_get_variable(channel, "freetdm_disable_dtmf"))) {
|
|
||||||
if (switch_true(var)) {
|
|
||||||
ftdm_channel_command(fchan, FTDM_COMMAND_DISABLE_DTMF_DETECT, NULL);
|
|
||||||
ftdm_log(FTDM_LOG_INFO, "DTMF detection disabled in channel %d:%d\n", ftdm_channel_get_span_id(fchan), ftdm_channel_get_id(fchan));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* the variable is not present or has a negative value then proceed to enable DTMF ... */
|
|
||||||
}
|
|
||||||
if (ftdm_channel_command(fchan, FTDM_COMMAND_ENABLE_DTMF_DETECT, NULL) != FTDM_SUCCESS) {
|
|
||||||
ftdm_log(FTDM_LOG_ERROR, "Failed to enable DTMF detection in channel %d:%d\n", ftdm_channel_get_span_id(fchan), ftdm_channel_get_id(fchan));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static FIO_SIGNAL_CB_FUNCTION(on_fxo_signal)
|
static FIO_SIGNAL_CB_FUNCTION(on_fxo_signal)
|
||||||
{
|
{
|
||||||
switch_core_session_t *session = NULL;
|
switch_core_session_t *session = NULL;
|
||||||
@ -2059,6 +2062,8 @@ static FIO_SIGNAL_CB_FUNCTION(on_r2_signal)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case FTDM_SIGEVENT_PROCEED:{} break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unhandled event %d from R2 for channel %d:%d\n",
|
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Unhandled event %d from R2 for channel %d:%d\n",
|
||||||
@ -2092,8 +2097,6 @@ static FIO_SIGNAL_CB_FUNCTION(on_clear_channel_signal)
|
|||||||
{
|
{
|
||||||
ftdm_channel_add_var(sigmsg->channel, "screening_ind", ftdm_screening2str(caller_data->screen));
|
ftdm_channel_add_var(sigmsg->channel, "screening_ind", ftdm_screening2str(caller_data->screen));
|
||||||
ftdm_channel_add_var(sigmsg->channel, "presentation_ind", ftdm_presentation2str(caller_data->pres));
|
ftdm_channel_add_var(sigmsg->channel, "presentation_ind", ftdm_presentation2str(caller_data->pres));
|
||||||
|
|
||||||
ftdm_enable_channel_dtmf(sigmsg->channel, NULL);
|
|
||||||
return ftdm_channel_from_event(sigmsg, &session);
|
return ftdm_channel_from_event(sigmsg, &session);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -86,6 +86,114 @@ FT_DECLARE(ftdm_time_t) ftdm_current_time_in_ms(void)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void write_chan_io_dump(ftdm_channel_t *fchan, ftdm_io_dump_t *dump, char *dataptr, int dlen)
|
||||||
|
{
|
||||||
|
int windex = dump->windex;
|
||||||
|
int avail = dump->size - windex;
|
||||||
|
|
||||||
|
if (!dump->buffer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dlen > avail) {
|
||||||
|
int diff = dlen - avail;
|
||||||
|
|
||||||
|
ftdm_assert(diff < dump->size, "Very small buffer or very big IO chunk!\n");
|
||||||
|
|
||||||
|
/* write only what we can and the rest at the beginning of the buffer */
|
||||||
|
memcpy(&dump->buffer[windex], dataptr, avail);
|
||||||
|
memcpy(&dump->buffer[0], &dataptr[avail], diff);
|
||||||
|
windex = diff;
|
||||||
|
|
||||||
|
/*ftdm_log_chan(fchan, FTDM_LOG_DEBUG, "wrapping around dump buffer %p up to index %d\n\n", dump, windex);*/
|
||||||
|
dump->wrapped = 1;
|
||||||
|
} else {
|
||||||
|
memcpy(&dump->buffer[windex], dataptr, dlen);
|
||||||
|
windex += dlen;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (windex == dump->size) {
|
||||||
|
/*ftdm_log_chan(fchan, FTDM_LOG_DEBUG, "wrapping around dump buffer %p\n", dump);*/
|
||||||
|
windex = 0;
|
||||||
|
dump->wrapped = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dump->windex = windex;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dump_chan_io_to_file(ftdm_channel_t *fchan, ftdm_io_dump_t *dump, FILE *file)
|
||||||
|
{
|
||||||
|
/* write the saved audio buffer */
|
||||||
|
int rc = 0;
|
||||||
|
int towrite = dump->size - dump->windex;
|
||||||
|
if (dump->wrapped) {
|
||||||
|
rc = fwrite(&dump->buffer[dump->windex], 1, towrite, file);
|
||||||
|
if (rc != towrite) {
|
||||||
|
ftdm_log_chan(fchan, FTDM_LOG_ERROR, "only wrote %d out of %d bytes in DTMF debug buffer\n", rc, towrite);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dump->windex) {
|
||||||
|
towrite = dump->windex;
|
||||||
|
rc = fwrite(&dump->buffer[0], 1, towrite, file);
|
||||||
|
if (rc != towrite) {
|
||||||
|
ftdm_log_chan(fchan, FTDM_LOG_ERROR, "only wrote %d out of %d bytes in DTMF debug buffer\n", rc, towrite);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dump->windex = 0;
|
||||||
|
dump->wrapped = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void stop_chan_io_dump(ftdm_io_dump_t *dump)
|
||||||
|
{
|
||||||
|
if (!dump->buffer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ftdm_safe_free(dump->buffer);
|
||||||
|
memset(dump, 0, sizeof(dump));
|
||||||
|
}
|
||||||
|
|
||||||
|
static ftdm_status_t start_chan_io_dump(ftdm_channel_t *chan, ftdm_io_dump_t *dump, ftdm_size_t size)
|
||||||
|
{
|
||||||
|
if (dump->buffer) {
|
||||||
|
ftdm_log_chan_msg(chan, FTDM_LOG_ERROR, "IO dump is already started\n");
|
||||||
|
return FTDM_FAIL;
|
||||||
|
}
|
||||||
|
memset(dump, 0, sizeof(*dump));
|
||||||
|
dump->buffer = ftdm_malloc(size);
|
||||||
|
if (!dump->buffer) {
|
||||||
|
return FTDM_FAIL;
|
||||||
|
}
|
||||||
|
dump->size = size;
|
||||||
|
return FTDM_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void close_dtmf_debug_file(ftdm_channel_t *ftdmchan)
|
||||||
|
{
|
||||||
|
if (ftdmchan->dtmfdbg.file) {
|
||||||
|
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "closing debug dtmf file\n");
|
||||||
|
fclose(ftdmchan->dtmfdbg.file);
|
||||||
|
ftdmchan->dtmfdbg.file = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ftdm_status_t disable_dtmf_debug(ftdm_channel_t *ftdmchan)
|
||||||
|
{
|
||||||
|
if (!ftdmchan->dtmfdbg.enabled) {
|
||||||
|
return FTDM_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ftdmchan->rxdump.buffer) {
|
||||||
|
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "DTMF debug enabled but no rx dump?\n");
|
||||||
|
return FTDM_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
close_dtmf_debug_file(ftdmchan);
|
||||||
|
stop_chan_io_dump(&ftdmchan->rxdump);
|
||||||
|
ftdmchan->dtmfdbg.enabled = 0;
|
||||||
|
return FTDM_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t enabled;
|
uint8_t enabled;
|
||||||
uint8_t running;
|
uint8_t running;
|
||||||
@ -448,10 +556,6 @@ static ftdm_status_t ftdm_channel_destroy(ftdm_channel_t *ftdmchan)
|
|||||||
ftdm_sleep(500);
|
ftdm_sleep(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef FTDM_DEBUG_DTMF
|
|
||||||
ftdm_mutex_destroy(&ftdmchan->dtmfdbg.mutex);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ftdm_mutex_lock(ftdmchan->pre_buffer_mutex);
|
ftdm_mutex_lock(ftdmchan->pre_buffer_mutex);
|
||||||
ftdm_buffer_destroy(&ftdmchan->pre_buffer);
|
ftdm_buffer_destroy(&ftdmchan->pre_buffer);
|
||||||
ftdm_mutex_unlock(ftdmchan->pre_buffer_mutex);
|
ftdm_mutex_unlock(ftdmchan->pre_buffer_mutex);
|
||||||
@ -870,9 +974,6 @@ FT_DECLARE(ftdm_status_t) ftdm_span_add_channel(ftdm_span_t *span, ftdm_socket_t
|
|||||||
|
|
||||||
ftdm_mutex_create(&new_chan->mutex);
|
ftdm_mutex_create(&new_chan->mutex);
|
||||||
ftdm_mutex_create(&new_chan->pre_buffer_mutex);
|
ftdm_mutex_create(&new_chan->pre_buffer_mutex);
|
||||||
#ifdef FTDM_DEBUG_DTMF
|
|
||||||
ftdm_mutex_create(&new_chan->dtmfdbg.mutex);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ftdm_buffer_create(&new_chan->digit_buffer, 128, 128, 0);
|
ftdm_buffer_create(&new_chan->digit_buffer, 128, 128, 0);
|
||||||
ftdm_buffer_create(&new_chan->gen_dtmf_buffer, 128, 128, 0);
|
ftdm_buffer_create(&new_chan->gen_dtmf_buffer, 128, 128, 0);
|
||||||
@ -2400,23 +2501,6 @@ FT_DECLARE(ftdm_status_t) ftdm_span_get_sig_status(ftdm_span_t *span, ftdm_signa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef FTDM_DEBUG_DTMF
|
|
||||||
static void close_dtmf_debug(ftdm_channel_t *ftdmchan)
|
|
||||||
{
|
|
||||||
ftdm_mutex_lock(ftdmchan->dtmfdbg.mutex);
|
|
||||||
|
|
||||||
if (ftdmchan->dtmfdbg.file) {
|
|
||||||
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "closing debug dtmf file\n");
|
|
||||||
fclose(ftdmchan->dtmfdbg.file);
|
|
||||||
ftdmchan->dtmfdbg.file = NULL;
|
|
||||||
}
|
|
||||||
ftdmchan->dtmfdbg.windex = 0;
|
|
||||||
ftdmchan->dtmfdbg.wrapped = 0;
|
|
||||||
|
|
||||||
ftdm_mutex_unlock(ftdmchan->dtmfdbg.mutex);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static ftdm_status_t ftdm_channel_clear_vars(ftdm_channel_t *ftdmchan);
|
static ftdm_status_t ftdm_channel_clear_vars(ftdm_channel_t *ftdmchan);
|
||||||
FT_DECLARE(ftdm_status_t) ftdm_channel_done(ftdm_channel_t *ftdmchan)
|
FT_DECLARE(ftdm_status_t) ftdm_channel_done(ftdm_channel_t *ftdmchan)
|
||||||
{
|
{
|
||||||
@ -2445,9 +2529,6 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_done(ftdm_channel_t *ftdmchan)
|
|||||||
ftdm_buffer_destroy(&ftdmchan->pre_buffer);
|
ftdm_buffer_destroy(&ftdmchan->pre_buffer);
|
||||||
ftdmchan->pre_buffer_size = 0;
|
ftdmchan->pre_buffer_size = 0;
|
||||||
ftdm_mutex_unlock(ftdmchan->pre_buffer_mutex);
|
ftdm_mutex_unlock(ftdmchan->pre_buffer_mutex);
|
||||||
#ifdef FTDM_DEBUG_DTMF
|
|
||||||
close_dtmf_debug(ftdmchan);
|
|
||||||
#endif
|
|
||||||
ftdm_channel_clear_vars(ftdmchan);
|
ftdm_channel_clear_vars(ftdmchan);
|
||||||
if (ftdmchan->hangup_timer) {
|
if (ftdmchan->hangup_timer) {
|
||||||
ftdm_sched_cancel_timer(globals.timingsched, ftdmchan->hangup_timer);
|
ftdm_sched_cancel_timer(globals.timingsched, ftdmchan->hangup_timer);
|
||||||
@ -2456,7 +2537,9 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_done(ftdm_channel_t *ftdmchan)
|
|||||||
ftdmchan->init_state = FTDM_CHANNEL_STATE_DOWN;
|
ftdmchan->init_state = FTDM_CHANNEL_STATE_DOWN;
|
||||||
ftdmchan->state = FTDM_CHANNEL_STATE_DOWN;
|
ftdmchan->state = FTDM_CHANNEL_STATE_DOWN;
|
||||||
|
|
||||||
ftdm_log(FTDM_LOG_DEBUG, "channel done %u:%u\n", ftdmchan->span_id, ftdmchan->chan_id);
|
ftdm_channel_command(ftdmchan, FTDM_COMMAND_DISABLE_DEBUG_DTMF, NULL);
|
||||||
|
ftdm_channel_command(ftdmchan, FTDM_COMMAND_DISABLE_INPUT_DUMP, NULL);
|
||||||
|
ftdm_channel_command(ftdmchan, FTDM_COMMAND_DISABLE_OUTPUT_DUMP, NULL);
|
||||||
|
|
||||||
if (FTDM_IS_VOICE_CHANNEL(ftdmchan)) {
|
if (FTDM_IS_VOICE_CHANNEL(ftdmchan)) {
|
||||||
ftdm_sigmsg_t sigmsg;
|
ftdm_sigmsg_t sigmsg;
|
||||||
@ -2468,6 +2551,8 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_done(ftdm_channel_t *ftdmchan)
|
|||||||
ftdm_span_send_signal(ftdmchan->span, &sigmsg);
|
ftdm_span_send_signal(ftdmchan->span, &sigmsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "channel done\n");
|
||||||
|
|
||||||
ftdm_mutex_unlock(ftdmchan->mutex);
|
ftdm_mutex_unlock(ftdmchan->mutex);
|
||||||
|
|
||||||
return FTDM_SUCCESS;
|
return FTDM_SUCCESS;
|
||||||
@ -2476,7 +2561,7 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_done(ftdm_channel_t *ftdmchan)
|
|||||||
FT_DECLARE(ftdm_status_t) ftdm_channel_use(ftdm_channel_t *ftdmchan)
|
FT_DECLARE(ftdm_status_t) ftdm_channel_use(ftdm_channel_t *ftdmchan)
|
||||||
{
|
{
|
||||||
|
|
||||||
assert(ftdmchan != NULL);
|
ftdm_assert(ftdmchan != NULL, "Null channel\n");
|
||||||
|
|
||||||
ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_INUSE);
|
ftdm_set_flag_locked(ftdmchan, FTDM_CHANNEL_INUSE);
|
||||||
|
|
||||||
@ -2550,8 +2635,8 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_command(ftdm_channel_t *ftdmchan, ftdm_co
|
|||||||
{
|
{
|
||||||
ftdm_status_t status = FTDM_FAIL;
|
ftdm_status_t status = FTDM_FAIL;
|
||||||
|
|
||||||
assert(ftdmchan != NULL);
|
ftdm_assert_return(ftdmchan != NULL, FTDM_FAIL, "No channel\n");
|
||||||
assert(ftdmchan->fio != NULL);
|
ftdm_assert_return(ftdmchan->fio != NULL, FTDM_FAIL, "No IO attached to channel\n");
|
||||||
|
|
||||||
ftdm_mutex_lock(ftdmchan->mutex);
|
ftdm_mutex_lock(ftdmchan->mutex);
|
||||||
|
|
||||||
@ -2578,7 +2663,7 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_command(ftdm_channel_t *ftdmchan, ftdm_co
|
|||||||
break;
|
break;
|
||||||
case FTDM_COMMAND_TRACE_INPUT:
|
case FTDM_COMMAND_TRACE_INPUT:
|
||||||
{
|
{
|
||||||
char *path = (char *) obj;
|
char *path = FTDM_COMMAND_OBJ_CHAR_P;
|
||||||
if (ftdmchan->fds[FTDM_READ_TRACE_INDEX] > 0) {
|
if (ftdmchan->fds[FTDM_READ_TRACE_INDEX] > 0) {
|
||||||
close(ftdmchan->fds[FTDM_READ_TRACE_INDEX]);
|
close(ftdmchan->fds[FTDM_READ_TRACE_INDEX]);
|
||||||
ftdmchan->fds[FTDM_READ_TRACE_INDEX] = -1;
|
ftdmchan->fds[FTDM_READ_TRACE_INDEX] = -1;
|
||||||
@ -2621,6 +2706,128 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_command(ftdm_channel_t *ftdmchan, ftdm_co
|
|||||||
GOTO_STATUS(done, FTDM_SUCCESS);
|
GOTO_STATUS(done, FTDM_SUCCESS);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
/*!< Enable DTMF debugging */
|
||||||
|
case FTDM_COMMAND_ENABLE_DEBUG_DTMF:
|
||||||
|
{
|
||||||
|
if (ftdmchan->dtmfdbg.enabled) {
|
||||||
|
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Cannot enable debug DTMF again\n");
|
||||||
|
GOTO_STATUS(done, FTDM_FAIL);
|
||||||
|
}
|
||||||
|
if (ftdmchan->rxdump.buffer) {
|
||||||
|
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Cannot debug DTMF if Rx dumping is already enabled\n");
|
||||||
|
GOTO_STATUS(done, FTDM_FAIL);
|
||||||
|
}
|
||||||
|
if (start_chan_io_dump(ftdmchan, &ftdmchan->rxdump, FTDM_IO_DUMP_DEFAULT_BUFF_SIZE) != FTDM_SUCCESS) {
|
||||||
|
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Failed to enable rx dump for DTMF debugging\n");
|
||||||
|
GOTO_STATUS(done, FTDM_FAIL);
|
||||||
|
}
|
||||||
|
ftdmchan->dtmfdbg.enabled = 1;
|
||||||
|
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Enabled DTMF debugging\n");
|
||||||
|
GOTO_STATUS(done, FTDM_SUCCESS);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
/*!< Disable DTMF debugging (if not disabled explicitly, it is disabled automatically when calls hangup) */
|
||||||
|
case FTDM_COMMAND_DISABLE_DEBUG_DTMF:
|
||||||
|
{
|
||||||
|
if (!ftdmchan->dtmfdbg.enabled) {
|
||||||
|
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "DTMF debug is already disabled\n");
|
||||||
|
GOTO_STATUS(done, FTDM_SUCCESS);
|
||||||
|
}
|
||||||
|
if (disable_dtmf_debug(ftdmchan) != FTDM_SUCCESS) {
|
||||||
|
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Failed to disable DTMF debug\n");
|
||||||
|
GOTO_STATUS(done, FTDM_FAIL);
|
||||||
|
}
|
||||||
|
GOTO_STATUS(done, FTDM_SUCCESS);
|
||||||
|
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Disabled DTMF debugging\n");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
/*!< Start dumping all input to a circular buffer. The size of the circular buffer can be specified, default used otherwise */
|
||||||
|
case FTDM_COMMAND_ENABLE_INPUT_DUMP:
|
||||||
|
{
|
||||||
|
ftdm_size_t size = obj ? FTDM_COMMAND_OBJ_SIZE : FTDM_IO_DUMP_DEFAULT_BUFF_SIZE;
|
||||||
|
if (ftdmchan->rxdump.buffer) {
|
||||||
|
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Input dump is already enabled\n");
|
||||||
|
GOTO_STATUS(done, FTDM_FAIL);
|
||||||
|
}
|
||||||
|
if (start_chan_io_dump(ftdmchan, &ftdmchan->rxdump, size) != FTDM_SUCCESS) {
|
||||||
|
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Failed to enable input dump\n");
|
||||||
|
GOTO_STATUS(done, FTDM_FAIL);
|
||||||
|
}
|
||||||
|
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Enabled input dump with size %zd\n", size);
|
||||||
|
GOTO_STATUS(done, FTDM_SUCCESS);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
/*!< Stop dumping all input to a circular buffer. */
|
||||||
|
case FTDM_COMMAND_DISABLE_INPUT_DUMP:
|
||||||
|
{
|
||||||
|
if (!ftdmchan->rxdump.buffer) {
|
||||||
|
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "No need to disable input dump\n");
|
||||||
|
GOTO_STATUS(done, FTDM_SUCCESS);
|
||||||
|
}
|
||||||
|
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Disabled input dump of size %zd\n", ftdmchan->rxdump.size);
|
||||||
|
stop_chan_io_dump(&ftdmchan->rxdump);
|
||||||
|
GOTO_STATUS(done, FTDM_SUCCESS);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
/*!< Start dumping all output to a circular buffer. The size of the circular buffer can be specified, default used otherwise */
|
||||||
|
case FTDM_COMMAND_ENABLE_OUTPUT_DUMP:
|
||||||
|
{
|
||||||
|
ftdm_size_t size = obj ? FTDM_COMMAND_OBJ_SIZE : FTDM_IO_DUMP_DEFAULT_BUFF_SIZE;
|
||||||
|
if (ftdmchan->txdump.buffer) {
|
||||||
|
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Output dump is already enabled\n");
|
||||||
|
GOTO_STATUS(done, FTDM_FAIL);
|
||||||
|
}
|
||||||
|
if (start_chan_io_dump(ftdmchan, &ftdmchan->txdump, size) != FTDM_SUCCESS) {
|
||||||
|
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Failed to enable output dump\n");
|
||||||
|
GOTO_STATUS(done, FTDM_FAIL);
|
||||||
|
}
|
||||||
|
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Enabled output dump with size %zd\n", size);
|
||||||
|
GOTO_STATUS(done, FTDM_SUCCESS);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
/*!< Stop dumping all output to a circular buffer. */
|
||||||
|
case FTDM_COMMAND_DISABLE_OUTPUT_DUMP:
|
||||||
|
{
|
||||||
|
if (!ftdmchan->txdump.buffer) {
|
||||||
|
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "No need to disable output dump\n");
|
||||||
|
GOTO_STATUS(done, FTDM_SUCCESS);
|
||||||
|
}
|
||||||
|
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Disabled output dump of size %zd\n", ftdmchan->rxdump.size);
|
||||||
|
stop_chan_io_dump(&ftdmchan->txdump);
|
||||||
|
GOTO_STATUS(done, FTDM_SUCCESS);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
/*!< Dump the current input circular buffer to the specified FILE* structure */
|
||||||
|
case FTDM_COMMAND_DUMP_INPUT:
|
||||||
|
{
|
||||||
|
if (!obj) {
|
||||||
|
GOTO_STATUS(done, FTDM_FAIL);
|
||||||
|
}
|
||||||
|
dump_chan_io_to_file(ftdmchan, &ftdmchan->rxdump, obj);
|
||||||
|
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Dumped input of size %zd to file %p\n", ftdmchan->rxdump.size, obj);
|
||||||
|
GOTO_STATUS(done, FTDM_SUCCESS);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
/*!< Dump the current output circular buffer to the specified FILE* structure */
|
||||||
|
case FTDM_COMMAND_DUMP_OUTPUT:
|
||||||
|
{
|
||||||
|
if (!obj) {
|
||||||
|
GOTO_STATUS(done, FTDM_FAIL);
|
||||||
|
}
|
||||||
|
dump_chan_io_to_file(ftdmchan, &ftdmchan->txdump, obj);
|
||||||
|
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Dumped input of size %zd to file %p\n", ftdmchan->txdump.size, obj);
|
||||||
|
GOTO_STATUS(done, FTDM_SUCCESS);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case FTDM_COMMAND_SET_INTERVAL:
|
case FTDM_COMMAND_SET_INTERVAL:
|
||||||
{
|
{
|
||||||
if (!ftdm_channel_test_feature(ftdmchan, FTDM_CHANNEL_FEATURE_INTERVAL)) {
|
if (!ftdm_channel_test_feature(ftdmchan, FTDM_CHANNEL_FEATURE_INTERVAL)) {
|
||||||
@ -3089,12 +3296,14 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_queue_dtmf(ftdm_channel_t *ftdmchan, cons
|
|||||||
ftdm_size_t wr = 0;
|
ftdm_size_t wr = 0;
|
||||||
const char *p;
|
const char *p;
|
||||||
|
|
||||||
assert(ftdmchan != NULL);
|
ftdm_assert_return(ftdmchan != NULL, FTDM_FAIL, "No channel\n");
|
||||||
|
|
||||||
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Queuing DTMF %s\n", dtmf);
|
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Queuing DTMF %s (debug = %d)\n", dtmf, ftdmchan->dtmfdbg.enabled);
|
||||||
|
|
||||||
|
if (!ftdmchan->dtmfdbg.enabled) {
|
||||||
|
goto skipdebug;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef FTDM_DEBUG_DTMF
|
|
||||||
ftdm_mutex_lock(ftdmchan->dtmfdbg.mutex);
|
|
||||||
if (!ftdmchan->dtmfdbg.file) {
|
if (!ftdmchan->dtmfdbg.file) {
|
||||||
struct tm currtime;
|
struct tm currtime;
|
||||||
time_t currsec;
|
time_t currsec;
|
||||||
@ -3111,33 +3320,15 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_queue_dtmf(ftdm_channel_t *ftdmchan, cons
|
|||||||
if (!ftdmchan->dtmfdbg.file) {
|
if (!ftdmchan->dtmfdbg.file) {
|
||||||
ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "failed to open debug dtmf file %s\n", dfile);
|
ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "failed to open debug dtmf file %s\n", dfile);
|
||||||
} else {
|
} else {
|
||||||
/* write the saved audio buffer */
|
|
||||||
int rc = 0;
|
|
||||||
int towrite = sizeof(ftdmchan->dtmfdbg.buffer) - ftdmchan->dtmfdbg.windex;
|
|
||||||
|
|
||||||
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "created debug DTMF file %s\n", dfile);
|
|
||||||
ftdmchan->dtmfdbg.closetimeout = DTMF_DEBUG_TIMEOUT;
|
ftdmchan->dtmfdbg.closetimeout = DTMF_DEBUG_TIMEOUT;
|
||||||
if (ftdmchan->dtmfdbg.wrapped) {
|
ftdm_channel_command(ftdmchan, FTDM_COMMAND_DUMP_INPUT, ftdmchan->dtmfdbg.file);
|
||||||
rc = fwrite(&ftdmchan->dtmfdbg.buffer[ftdmchan->dtmfdbg.windex], 1, towrite, ftdmchan->dtmfdbg.file);
|
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Dumped initial DTMF output to %s\n", dfile);
|
||||||
if (rc != towrite) {
|
|
||||||
ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "only wrote %d out of %d bytes in DTMF debug buffer\n", rc, towrite);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (ftdmchan->dtmfdbg.windex) {
|
|
||||||
towrite = ftdmchan->dtmfdbg.windex;
|
|
||||||
rc = fwrite(&ftdmchan->dtmfdbg.buffer[0], 1, towrite, ftdmchan->dtmfdbg.file);
|
|
||||||
if (rc != towrite) {
|
|
||||||
ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "only wrote %d out of %d bytes in DTMF debug buffer\n", rc, towrite);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ftdmchan->dtmfdbg.windex = 0;
|
|
||||||
ftdmchan->dtmfdbg.wrapped = 0;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ftdmchan->dtmfdbg.closetimeout = DTMF_DEBUG_TIMEOUT;
|
ftdmchan->dtmfdbg.closetimeout = DTMF_DEBUG_TIMEOUT;
|
||||||
}
|
}
|
||||||
ftdm_mutex_unlock(ftdmchan->dtmfdbg.mutex);
|
|
||||||
#endif
|
skipdebug:
|
||||||
|
|
||||||
if (ftdmchan->pre_buffer) {
|
if (ftdmchan->pre_buffer) {
|
||||||
ftdm_buffer_zero(ftdmchan->pre_buffer);
|
ftdm_buffer_zero(ftdmchan->pre_buffer);
|
||||||
@ -3182,16 +3373,16 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_queue_dtmf(ftdm_channel_t *ftdmchan, cons
|
|||||||
|
|
||||||
static FIO_WRITE_FUNCTION(ftdm_raw_write)
|
static FIO_WRITE_FUNCTION(ftdm_raw_write)
|
||||||
{
|
{
|
||||||
if (ftdmchan->fds[FTDM_WRITE_TRACE_INDEX] > -1) {
|
|
||||||
int dlen = (int) *datalen;
|
int dlen = (int) *datalen;
|
||||||
|
if (ftdmchan->fds[FTDM_WRITE_TRACE_INDEX] > -1) {
|
||||||
if ((write(ftdmchan->fds[FTDM_WRITE_TRACE_INDEX], data, dlen)) != dlen) {
|
if ((write(ftdmchan->fds[FTDM_WRITE_TRACE_INDEX], data, dlen)) != dlen) {
|
||||||
ftdm_log(FTDM_LOG_WARNING, "Raw output trace failed to write all of the %zd bytes\n", dlen);
|
ftdm_log(FTDM_LOG_WARNING, "Raw output trace failed to write all of the %zd bytes\n", dlen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
write_chan_io_dump(ftdmchan, &ftdmchan->txdump, data, dlen);
|
||||||
return ftdmchan->fio->write(ftdmchan, data, datalen);
|
return ftdmchan->fio->write(ftdmchan, data, datalen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static FIO_READ_FUNCTION(ftdm_raw_read)
|
static FIO_READ_FUNCTION(ftdm_raw_read)
|
||||||
{
|
{
|
||||||
ftdm_status_t status = ftdmchan->fio->read(ftdmchan, data, datalen);
|
ftdm_status_t status = ftdmchan->fio->read(ftdmchan, data, datalen);
|
||||||
@ -3206,48 +3397,24 @@ static FIO_READ_FUNCTION(ftdm_raw_read)
|
|||||||
ftdmchan->span->sig_read(ftdmchan, data, *datalen);
|
ftdmchan->span->sig_read(ftdmchan, data, *datalen);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef FTDM_DEBUG_DTMF
|
|
||||||
if (status == FTDM_SUCCESS) {
|
if (status == FTDM_SUCCESS) {
|
||||||
int dlen = (int) *datalen;
|
int dlen = (int) *datalen;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
ftdm_mutex_lock(ftdmchan->dtmfdbg.mutex);
|
|
||||||
if (!ftdmchan->dtmfdbg.file) {
|
|
||||||
/* no file yet, write to our circular buffer */
|
|
||||||
int windex = ftdmchan->dtmfdbg.windex;
|
|
||||||
int avail = sizeof(ftdmchan->dtmfdbg.buffer) - windex;
|
|
||||||
char *dataptr = data;
|
|
||||||
|
|
||||||
if (dlen > avail) {
|
write_chan_io_dump(ftdmchan, &ftdmchan->rxdump, data, dlen);
|
||||||
int diff = dlen - avail;
|
|
||||||
/* write only what we can and the rest at the beginning of the buffer */
|
/* if dtmf debug is enabled and initialized, write there too */
|
||||||
memcpy(&ftdmchan->dtmfdbg.buffer[windex], dataptr, avail);
|
if (ftdmchan->dtmfdbg.file) {
|
||||||
memcpy(&ftdmchan->dtmfdbg.buffer[0], &dataptr[avail], diff);
|
|
||||||
windex = diff;
|
|
||||||
/*ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "wrapping around dtmf read buffer up to index %d\n\n", windex);*/
|
|
||||||
ftdmchan->dtmfdbg.wrapped = 1;
|
|
||||||
} else {
|
|
||||||
memcpy(&ftdmchan->dtmfdbg.buffer[windex], dataptr, dlen);
|
|
||||||
windex += dlen;
|
|
||||||
}
|
|
||||||
if (windex == sizeof(ftdmchan->dtmfdbg.buffer)) {
|
|
||||||
/*ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "wrapping around dtmf read buffer\n");*/
|
|
||||||
windex = 0;
|
|
||||||
ftdmchan->dtmfdbg.wrapped = 1;
|
|
||||||
}
|
|
||||||
ftdmchan->dtmfdbg.windex = windex;
|
|
||||||
} else {
|
|
||||||
rc = fwrite(data, 1, dlen, ftdmchan->dtmfdbg.file);
|
rc = fwrite(data, 1, dlen, ftdmchan->dtmfdbg.file);
|
||||||
if (rc != dlen) {
|
if (rc != dlen) {
|
||||||
ftdm_log(FTDM_LOG_WARNING, "DTMF debugger wrote only %d out of %d bytes: %s\n", rc, datalen, strerror(errno));
|
ftdm_log(FTDM_LOG_WARNING, "DTMF debugger wrote only %d out of %d bytes: %s\n", rc, datalen, strerror(errno));
|
||||||
}
|
}
|
||||||
ftdmchan->dtmfdbg.closetimeout--;
|
ftdmchan->dtmfdbg.closetimeout--;
|
||||||
if (!ftdmchan->dtmfdbg.closetimeout) {
|
if (!ftdmchan->dtmfdbg.closetimeout) {
|
||||||
close_dtmf_debug(ftdmchan);
|
close_dtmf_debug_file(ftdmchan);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ftdm_mutex_unlock(ftdmchan->dtmfdbg.mutex);
|
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4043,6 +4210,7 @@ static ftdm_status_t ftdm_set_channels_alarms(ftdm_span_t *span, int currindex)
|
|||||||
FT_DECLARE(ftdm_status_t) ftdm_configure_span_channels(ftdm_span_t *span, const char* str, ftdm_channel_config_t *chan_config, unsigned *configured)
|
FT_DECLARE(ftdm_status_t) ftdm_configure_span_channels(ftdm_span_t *span, const char* str, ftdm_channel_config_t *chan_config, unsigned *configured)
|
||||||
{
|
{
|
||||||
int currindex;
|
int currindex;
|
||||||
|
unsigned chan_index = 0;
|
||||||
|
|
||||||
ftdm_assert_return(span != NULL, FTDM_EINVAL, "span is null\n");
|
ftdm_assert_return(span != NULL, FTDM_EINVAL, "span is null\n");
|
||||||
ftdm_assert_return(chan_config != NULL, FTDM_EINVAL, "config is null\n");
|
ftdm_assert_return(chan_config != NULL, FTDM_EINVAL, "config is null\n");
|
||||||
@ -4076,6 +4244,14 @@ FT_DECLARE(ftdm_status_t) ftdm_configure_span_channels(ftdm_span_t *span, const
|
|||||||
return FTDM_FAIL;
|
return FTDM_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (chan_config->debugdtmf) {
|
||||||
|
for (chan_index = currindex+1; chan_index <= span->chan_count; chan_index++) {
|
||||||
|
if (!FTDM_IS_VOICE_CHANNEL(span->channels[chan_index])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
span->channels[chan_index]->dtmfdbg.requested = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
return FTDM_SUCCESS;
|
return FTDM_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4094,7 +4270,7 @@ static ftdm_status_t load_config(void)
|
|||||||
ftdm_channel_config_t chan_config;
|
ftdm_channel_config_t chan_config;
|
||||||
|
|
||||||
memset(&chan_config, 0, sizeof(chan_config));
|
memset(&chan_config, 0, sizeof(chan_config));
|
||||||
sprintf(chan_config.group_name,"default");
|
sprintf(chan_config.group_name, "__default");
|
||||||
|
|
||||||
if (!ftdm_config_open_file(&cfg, cfg_name)) {
|
if (!ftdm_config_open_file(&cfg, cfg_name)) {
|
||||||
return FTDM_FAIL;
|
return FTDM_FAIL;
|
||||||
@ -4131,6 +4307,9 @@ static ftdm_status_t load_config(void)
|
|||||||
if (ftdm_span_create(type, name, &span) == FTDM_SUCCESS) {
|
if (ftdm_span_create(type, name, &span) == FTDM_SUCCESS) {
|
||||||
ftdm_log(FTDM_LOG_DEBUG, "created span %d (%s) of type %s\n", span->span_id, span->name, type);
|
ftdm_log(FTDM_LOG_DEBUG, "created span %d (%s) of type %s\n", span->span_id, span->name, type);
|
||||||
d = 0;
|
d = 0;
|
||||||
|
/* it is confusing that parameters from one span affect others, so let's clear them */
|
||||||
|
memset(&chan_config, 0, sizeof(chan_config));
|
||||||
|
sprintf(chan_config.group_name, "__default");
|
||||||
} else {
|
} else {
|
||||||
ftdm_log(FTDM_LOG_CRIT, "failure creating span of type %s\n", type);
|
ftdm_log(FTDM_LOG_CRIT, "failure creating span of type %s\n", type);
|
||||||
span = NULL;
|
span = NULL;
|
||||||
@ -4254,6 +4433,9 @@ static ftdm_status_t load_config(void)
|
|||||||
if (sscanf(val, "%f", &(chan_config.rxgain)) != 1) {
|
if (sscanf(val, "%f", &(chan_config.rxgain)) != 1) {
|
||||||
ftdm_log(FTDM_LOG_ERROR, "invalid rxgain: '%s'\n", val);
|
ftdm_log(FTDM_LOG_ERROR, "invalid rxgain: '%s'\n", val);
|
||||||
}
|
}
|
||||||
|
} else if (!strcasecmp(var, "debugdtmf")) {
|
||||||
|
chan_config.debugdtmf = ftdm_true(val);
|
||||||
|
ftdm_log(FTDM_LOG_DEBUG, "Setting debugdtmf to '%s'\n", chan_config.debugdtmf ? "yes" : "no");
|
||||||
} else if (!strcasecmp(var, "group")) {
|
} else if (!strcasecmp(var, "group")) {
|
||||||
len = strlen(val);
|
len = strlen(val);
|
||||||
if (len >= FTDM_MAX_NAME_STR_SZ) {
|
if (len >= FTDM_MAX_NAME_STR_SZ) {
|
||||||
@ -4905,6 +5087,9 @@ FT_DECLARE(ftdm_status_t) ftdm_span_send_signal(ftdm_span_t *span, ftdm_sigmsg_t
|
|||||||
case FTDM_SIGEVENT_START:
|
case FTDM_SIGEVENT_START:
|
||||||
{
|
{
|
||||||
ftdm_set_echocancel_call_begin(sigmsg->channel);
|
ftdm_set_echocancel_call_begin(sigmsg->channel);
|
||||||
|
if (sigmsg->channel->dtmfdbg.requested) {
|
||||||
|
ftdm_channel_command(sigmsg->channel, FTDM_COMMAND_ENABLE_DEBUG_DTMF, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/* when cleaning up the public API I added this because mod_freetdm.c on_fxs_signal was
|
/* when cleaning up the public API I added this because mod_freetdm.c on_fxs_signal was
|
||||||
* doing it during SIGEVENT_START, but now that flags are private they can't, wonder if
|
* doing it during SIGEVENT_START, but now that flags are private they can't, wonder if
|
||||||
|
@ -68,6 +68,7 @@ typedef struct ftdm_r2_call_t {
|
|||||||
ftdm_channel_state_t chanstate;
|
ftdm_channel_state_t chanstate;
|
||||||
ftdm_size_t dnis_index;
|
ftdm_size_t dnis_index;
|
||||||
ftdm_size_t ani_index;
|
ftdm_size_t ani_index;
|
||||||
|
char logname[255];
|
||||||
char name[10];
|
char name[10];
|
||||||
unsigned long txdrops;
|
unsigned long txdrops;
|
||||||
} ftdm_r2_call_t;
|
} ftdm_r2_call_t;
|
||||||
@ -88,13 +89,13 @@ typedef struct ft_r2_conf_s {
|
|||||||
int32_t max_dnis;
|
int32_t max_dnis;
|
||||||
int32_t mfback_timeout;
|
int32_t mfback_timeout;
|
||||||
int32_t metering_pulse_timeout;
|
int32_t metering_pulse_timeout;
|
||||||
|
int32_t mf_dump_size;
|
||||||
|
|
||||||
/* booleans */
|
/* booleans */
|
||||||
int immediate_accept;
|
int immediate_accept;
|
||||||
int skip_category;
|
int skip_category;
|
||||||
int get_ani_first;
|
int get_ani_first;
|
||||||
int call_files;
|
int call_files;
|
||||||
int mf_files;
|
|
||||||
int double_answer;
|
int double_answer;
|
||||||
int charge_calls;
|
int charge_calls;
|
||||||
int forced_release;
|
int forced_release;
|
||||||
@ -117,6 +118,8 @@ typedef struct ftdm_r2_data_s {
|
|||||||
int forced_release:1;
|
int forced_release:1;
|
||||||
/* whether accept the call when offered, or wait until the user decides to accept */
|
/* whether accept the call when offered, or wait until the user decides to accept */
|
||||||
int accept_on_offer:1;
|
int accept_on_offer:1;
|
||||||
|
/* Size of multi-frequency (or any media) dumps used during protocol errors */
|
||||||
|
int32_t mf_dump_size;
|
||||||
/* max time spent in ms doing real work in a single loop */
|
/* max time spent in ms doing real work in a single loop */
|
||||||
int32_t jobmax;
|
int32_t jobmax;
|
||||||
/* Total number of loops performed so far */
|
/* Total number of loops performed so far */
|
||||||
@ -125,6 +128,8 @@ typedef struct ftdm_r2_data_s {
|
|||||||
uint64_t loops[11];
|
uint64_t loops[11];
|
||||||
/* LWP */
|
/* LWP */
|
||||||
uint32_t monitor_thread_id;
|
uint32_t monitor_thread_id;
|
||||||
|
/* Logging directory */
|
||||||
|
char logdir[512];
|
||||||
} ftdm_r2_data_t;
|
} ftdm_r2_data_t;
|
||||||
|
|
||||||
/* one element per span will be stored in g_mod_data_hash global var to keep track of them
|
/* one element per span will be stored in g_mod_data_hash global var to keep track of them
|
||||||
@ -409,10 +414,11 @@ static FIO_CHANNEL_GET_SIG_STATUS_FUNCTION(ftdm_r2_get_channel_sig_status)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* always called from the monitor thread */
|
/* always called from the monitor thread */
|
||||||
static void ftdm_r2_on_call_init(openr2_chan_t *r2chan)
|
static void ftdm_r2_on_call_init(openr2_chan_t *r2chan, const char *logname)
|
||||||
{
|
{
|
||||||
ftdm_r2_call_t *r2call;
|
ftdm_r2_call_t *r2call;
|
||||||
ftdm_channel_t *ftdmchan = openr2_chan_get_client_data(r2chan);
|
ftdm_channel_t *ftdmchan = openr2_chan_get_client_data(r2chan);
|
||||||
|
ftdm_r2_data_t *r2data = ftdmchan->span->signal_data;
|
||||||
|
|
||||||
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_NOTICE, "Received request to start call\n");
|
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_NOTICE, "Received request to start call\n");
|
||||||
|
|
||||||
@ -441,12 +447,15 @@ static void ftdm_r2_on_call_init(openr2_chan_t *r2chan)
|
|||||||
ft_r2_clean_call(ftdmchan->call_data);
|
ft_r2_clean_call(ftdmchan->call_data);
|
||||||
r2call = R2CALL(ftdmchan);
|
r2call = R2CALL(ftdmchan);
|
||||||
|
|
||||||
if (ftdmchan->state == FTDM_CHANNEL_STATE_DOWN) {
|
snprintf(r2call->logname, sizeof(r2call->logname), "%s", logname);
|
||||||
R2CALL(ftdmchan)->chanstate = FTDM_CHANNEL_STATE_DOWN;
|
|
||||||
} else {
|
/* start io dump */
|
||||||
R2CALL(ftdmchan)->chanstate = FTDM_CHANNEL_STATE_DIALING;
|
if (r2data->mf_dump_size) {
|
||||||
|
ftdm_channel_command(ftdmchan, FTDM_COMMAND_ENABLE_INPUT_DUMP, &r2data->mf_dump_size);
|
||||||
|
ftdm_channel_command(ftdmchan, FTDM_COMMAND_ENABLE_OUTPUT_DUMP, &r2data->mf_dump_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
R2CALL(ftdmchan)->chanstate = FTDM_CHANNEL_STATE_DOWN;
|
||||||
ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_COLLECT);
|
ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_COLLECT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -454,9 +463,16 @@ static void ftdm_r2_on_call_init(openr2_chan_t *r2chan)
|
|||||||
static void ftdm_r2_on_call_offered(openr2_chan_t *r2chan, const char *ani, const char *dnis, openr2_calling_party_category_t category)
|
static void ftdm_r2_on_call_offered(openr2_chan_t *r2chan, const char *ani, const char *dnis, openr2_calling_party_category_t category)
|
||||||
{
|
{
|
||||||
ftdm_channel_t *ftdmchan = openr2_chan_get_client_data(r2chan);
|
ftdm_channel_t *ftdmchan = openr2_chan_get_client_data(r2chan);
|
||||||
|
ftdm_r2_data_t *r2data = ftdmchan->span->signal_data;
|
||||||
|
|
||||||
ftdm_log_chan(ftdmchan, FTDM_LOG_NOTICE, "Call offered with ANI = %s, DNIS = %s, Category = (%d)\n", ani, dnis, category);
|
ftdm_log_chan(ftdmchan, FTDM_LOG_NOTICE, "Call offered with ANI = %s, DNIS = %s, Category = (%d)\n", ani, dnis, category);
|
||||||
ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_RING);
|
ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_RING);
|
||||||
|
|
||||||
|
/* nothing went wrong during call setup, MF has ended, we can and must disable the MF dump */
|
||||||
|
if (r2data->mf_dump_size) {
|
||||||
|
ftdm_channel_command(ftdmchan, FTDM_COMMAND_DISABLE_INPUT_DUMP, NULL);
|
||||||
|
ftdm_channel_command(ftdmchan, FTDM_COMMAND_DISABLE_OUTPUT_DUMP, NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -484,6 +500,32 @@ static void clear_accept_pending(ftdm_channel_t *fchan)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void dump_mf(openr2_chan_t *r2chan)
|
||||||
|
{
|
||||||
|
char dfile[512];
|
||||||
|
FILE *f = NULL;
|
||||||
|
ftdm_channel_t *ftdmchan = openr2_chan_get_client_data(r2chan);
|
||||||
|
ftdm_r2_data_t *r2data = ftdmchan->span->signal_data;
|
||||||
|
if (r2data->mf_dump_size) {
|
||||||
|
char *logname = R2CALL(ftdmchan)->logname;
|
||||||
|
|
||||||
|
ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Dumping IO output in prefix %s\n", logname);
|
||||||
|
snprintf(dfile, sizeof(dfile), logname ? "%s.s%dc%d.input.alaw" : "%s/s%dc%d.input.alaw",
|
||||||
|
logname ? logname : r2data->logdir, ftdmchan->span_id, ftdmchan->chan_id);
|
||||||
|
f = fopen(dfile, "w");
|
||||||
|
ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Dumping IO input in file %s\n", dfile);
|
||||||
|
ftdm_channel_command(ftdmchan, FTDM_COMMAND_DUMP_INPUT, f);
|
||||||
|
fclose(f);
|
||||||
|
|
||||||
|
snprintf(dfile, sizeof(dfile), logname ? "%s.s%dc%d.output.alaw" : "%s/s%dc%d.output.alaw",
|
||||||
|
logname ? logname : r2data->logdir, ftdmchan->span_id, ftdmchan->chan_id);
|
||||||
|
f = fopen(dfile, "w");
|
||||||
|
ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Dumping IO output in file %s\n", dfile);
|
||||||
|
ftdm_channel_command(ftdmchan, FTDM_COMMAND_DUMP_OUTPUT, f);
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void ftdm_r2_on_call_accepted(openr2_chan_t *r2chan, openr2_call_mode_t mode)
|
static void ftdm_r2_on_call_accepted(openr2_chan_t *r2chan, openr2_call_mode_t mode)
|
||||||
{
|
{
|
||||||
ftdm_channel_t *ftdmchan = openr2_chan_get_client_data(r2chan);
|
ftdm_channel_t *ftdmchan = openr2_chan_get_client_data(r2chan);
|
||||||
@ -493,6 +535,7 @@ static void ftdm_r2_on_call_accepted(openr2_chan_t *r2chan, openr2_call_mode_t m
|
|||||||
|
|
||||||
/* at this point the MF signaling has ended and there is no point on keep reading */
|
/* at this point the MF signaling has ended and there is no point on keep reading */
|
||||||
openr2_chan_disable_read(r2chan);
|
openr2_chan_disable_read(r2chan);
|
||||||
|
|
||||||
R2CALL(ftdmchan)->accepted = 1;
|
R2CALL(ftdmchan)->accepted = 1;
|
||||||
|
|
||||||
if (OR2_DIR_BACKWARD == openr2_chan_get_direction(r2chan)) {
|
if (OR2_DIR_BACKWARD == openr2_chan_get_direction(r2chan)) {
|
||||||
@ -579,7 +622,7 @@ static void ftdm_r2_on_protocol_error(openr2_chan_t *r2chan, openr2_protocol_err
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Protocol error\n");
|
dump_mf(r2chan);
|
||||||
|
|
||||||
clear_accept_pending(ftdmchan);
|
clear_accept_pending(ftdmchan);
|
||||||
|
|
||||||
@ -1042,24 +1085,28 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_r2_configure_span_signaling)
|
|||||||
/* .variant */ OR2_VAR_ITU,
|
/* .variant */ OR2_VAR_ITU,
|
||||||
/* .category */ OR2_CALLING_PARTY_CATEGORY_NATIONAL_SUBSCRIBER,
|
/* .category */ OR2_CALLING_PARTY_CATEGORY_NATIONAL_SUBSCRIBER,
|
||||||
/* .loglevel */ OR2_LOG_ERROR | OR2_LOG_WARNING,
|
/* .loglevel */ OR2_LOG_ERROR | OR2_LOG_WARNING,
|
||||||
/* .logdir */ (char *)"/usr/local/freeswitch/log/", /* FIXME: get PREFIX variable */
|
#ifdef WIN32
|
||||||
|
/* .logdir */ (char *)"c:\\",
|
||||||
|
#else
|
||||||
|
/* .logdir */ (char *)"/tmp",
|
||||||
|
#endif
|
||||||
/* .advanced_protocol_file */ NULL,
|
/* .advanced_protocol_file */ NULL,
|
||||||
/* .max_ani */ 10,
|
/* .max_ani */ 10,
|
||||||
/* .max_dnis */ 4,
|
/* .max_dnis */ 4,
|
||||||
/* .mfback_timeout */ -1,
|
/* .mfback_timeout */ -1,
|
||||||
/* .metering_pulse_timeout */ -1,
|
/* .metering_pulse_timeout */ -1,
|
||||||
|
/* .mf_dump_size */ 0,
|
||||||
/* .immediate_accept */ -1,
|
/* .immediate_accept */ -1,
|
||||||
/* .skip_category */ -1,
|
/* .skip_category */ -1,
|
||||||
/* .get_ani_first */ -1,
|
/* .get_ani_first */ -1,
|
||||||
/* .call_files */ 0,
|
/* .call_files */ 0,
|
||||||
/* .mf_files */ 0,
|
|
||||||
/* .double_answer */ -1,
|
/* .double_answer */ -1,
|
||||||
/* .charge_calls */ -1,
|
/* .charge_calls */ -1,
|
||||||
/* .forced_release */ -1,
|
/* .forced_release */ -1,
|
||||||
/* .allow_collect_calls */ -1
|
/* .allow_collect_calls */ -1
|
||||||
};
|
};
|
||||||
|
|
||||||
assert(sig_cb != NULL);
|
ftdm_assert_return(sig_cb != NULL, FTDM_FAIL, "No signaling cb provided\n");
|
||||||
|
|
||||||
if (span->signal_type) {
|
if (span->signal_type) {
|
||||||
snprintf(span->last_error, sizeof(span->last_error), "Span is already configured for signalling.");
|
snprintf(span->last_error, sizeof(span->last_error), "Span is already configured for signalling.");
|
||||||
@ -1119,6 +1166,7 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_r2_configure_span_signaling)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
log_level = val;
|
log_level = val;
|
||||||
|
ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %s with loglevel %s\n", span->name, val);
|
||||||
} else if (!strcasecmp(var, "advanced_protocol_file")) {
|
} else if (!strcasecmp(var, "advanced_protocol_file")) {
|
||||||
if (!val) {
|
if (!val) {
|
||||||
break;
|
break;
|
||||||
@ -1128,46 +1176,51 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_r2_configure_span_signaling)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
r2conf.advanced_protocol_file = (char *)val;
|
r2conf.advanced_protocol_file = (char *)val;
|
||||||
ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with advanced protocol file %s\n", span->span_id, val);
|
ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %s with advanced protocol file %s\n", span->name, val);
|
||||||
|
} else if (!strcasecmp(var, "mf_dump_size")) {
|
||||||
|
r2conf.mf_dump_size = atoi(val);
|
||||||
|
if (r2conf.mf_dump_size < 0) {
|
||||||
|
r2conf.mf_dump_size = FTDM_IO_DUMP_DEFAULT_BUFF_SIZE;
|
||||||
|
ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %s with default mf_dump_size = %d bytes\n", span->name, r2conf.mf_dump_size);
|
||||||
|
} else {
|
||||||
|
ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %s with mf_dump_size = %d bytes\n", span->name, r2conf.mf_dump_size);
|
||||||
|
}
|
||||||
} else if (!strcasecmp(var, "allow_collect_calls")) {
|
} else if (!strcasecmp(var, "allow_collect_calls")) {
|
||||||
r2conf.allow_collect_calls = ftdm_true(val);
|
r2conf.allow_collect_calls = ftdm_true(val);
|
||||||
ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with allow collect calls max ani = %d\n", span->span_id, r2conf.allow_collect_calls);
|
ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %s with allow collect calls max ani = %d\n", span->name, r2conf.allow_collect_calls);
|
||||||
} else if (!strcasecmp(var, "double_answer")) {
|
} else if (!strcasecmp(var, "double_answer")) {
|
||||||
r2conf.double_answer = ftdm_true(val);
|
r2conf.double_answer = ftdm_true(val);
|
||||||
ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with double answer = %d\n", span->span_id, r2conf.double_answer);
|
ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %s with double answer = %d\n", span->name, r2conf.double_answer);
|
||||||
} else if (!strcasecmp(var, "immediate_accept")) {
|
} else if (!strcasecmp(var, "immediate_accept")) {
|
||||||
r2conf.immediate_accept = ftdm_true(val);
|
r2conf.immediate_accept = ftdm_true(val);
|
||||||
ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with immediate accept = %d\n", span->span_id, r2conf.immediate_accept);
|
ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %s with immediate accept = %d\n", span->name, r2conf.immediate_accept);
|
||||||
} else if (!strcasecmp(var, "skip_category")) {
|
} else if (!strcasecmp(var, "skip_category")) {
|
||||||
r2conf.skip_category = ftdm_true(val);
|
r2conf.skip_category = ftdm_true(val);
|
||||||
ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with skip category = %d\n", span->span_id, r2conf.skip_category);
|
ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %s with skip category = %d\n", span->name, r2conf.skip_category);
|
||||||
} else if (!strcasecmp(var, "forced_release")) {
|
} else if (!strcasecmp(var, "forced_release")) {
|
||||||
r2conf.forced_release = ftdm_true(val);
|
r2conf.forced_release = ftdm_true(val);
|
||||||
ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with forced release = %d\n", span->span_id, r2conf.forced_release);
|
ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %s with forced release = %d\n", span->name, r2conf.forced_release);
|
||||||
} else if (!strcasecmp(var, "charge_calls")) {
|
} else if (!strcasecmp(var, "charge_calls")) {
|
||||||
r2conf.charge_calls = ftdm_true(val);
|
r2conf.charge_calls = ftdm_true(val);
|
||||||
ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with charge calls = %d\n", span->span_id, r2conf.charge_calls);
|
ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %s with charge calls = %d\n", span->name, r2conf.charge_calls);
|
||||||
} else if (!strcasecmp(var, "get_ani_first")) {
|
} else if (!strcasecmp(var, "get_ani_first")) {
|
||||||
r2conf.get_ani_first = ftdm_true(val);
|
r2conf.get_ani_first = ftdm_true(val);
|
||||||
ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with get ani first = %d\n", span->span_id, r2conf.get_ani_first);
|
ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %s with get ani first = %d\n", span->name, r2conf.get_ani_first);
|
||||||
} else if (!strcasecmp(var, "call_files")) {
|
} else if (!strcasecmp(var, "call_files")) {
|
||||||
r2conf.call_files = ftdm_true(val);
|
r2conf.call_files = ftdm_true(val);
|
||||||
ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with call files = %d\n", span->span_id, r2conf.call_files);
|
ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %s with call files = %d\n", span->name, r2conf.call_files);
|
||||||
} else if (!strcasecmp(var, "mf_files")) {
|
|
||||||
r2conf.mf_files = ftdm_true(val);
|
|
||||||
ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with mf files = %d\n", span->span_id, r2conf.mf_files);
|
|
||||||
} else if (!strcasecmp(var, "mfback_timeout")) {
|
} else if (!strcasecmp(var, "mfback_timeout")) {
|
||||||
r2conf.mfback_timeout = atoi(val);
|
r2conf.mfback_timeout = atoi(val);
|
||||||
ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with MF backward timeout = %dms\n", span->span_id, r2conf.mfback_timeout);
|
ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %s with MF backward timeout = %dms\n", span->name, r2conf.mfback_timeout);
|
||||||
} else if (!strcasecmp(var, "metering_pulse_timeout")) {
|
} else if (!strcasecmp(var, "metering_pulse_timeout")) {
|
||||||
r2conf.metering_pulse_timeout = atoi(val);
|
r2conf.metering_pulse_timeout = atoi(val);
|
||||||
ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with metering pulse timeout = %dms\n", span->span_id, r2conf.metering_pulse_timeout);
|
ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %s with metering pulse timeout = %dms\n", span->name, r2conf.metering_pulse_timeout);
|
||||||
} else if (!strcasecmp(var, "max_ani")) {
|
} else if (!strcasecmp(var, "max_ani")) {
|
||||||
r2conf.max_ani = atoi(val);
|
r2conf.max_ani = atoi(val);
|
||||||
ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with max ani = %d\n", span->span_id, r2conf.max_ani);
|
ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %s with max ani = %d\n", span->name, r2conf.max_ani);
|
||||||
} else if (!strcasecmp(var, "max_dnis")) {
|
} else if (!strcasecmp(var, "max_dnis")) {
|
||||||
r2conf.max_dnis = atoi(val);
|
r2conf.max_dnis = atoi(val);
|
||||||
ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %d with max dnis = %d\n", span->span_id, r2conf.max_dnis);
|
ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %s with max dnis = %d\n", span->name, r2conf.max_dnis);
|
||||||
} else {
|
} else {
|
||||||
snprintf(span->last_error, sizeof(span->last_error), "Unknown R2 parameter [%s]", var);
|
snprintf(span->last_error, sizeof(span->last_error), "Unknown R2 parameter [%s]", var);
|
||||||
return FTDM_FAIL;
|
return FTDM_FAIL;
|
||||||
@ -1212,10 +1265,10 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_r2_configure_span_signaling)
|
|||||||
openr2_context_set_immediate_accept(r2data->r2context, r2conf.immediate_accept);
|
openr2_context_set_immediate_accept(r2data->r2context, r2conf.immediate_accept);
|
||||||
openr2_context_set_span_id(r2data->r2context, span->span_id);
|
openr2_context_set_span_id(r2data->r2context, span->span_id);
|
||||||
|
|
||||||
if (r2conf.logdir && r2conf.logdir[0]) {
|
ftdm_log(FTDM_LOG_DEBUG, "Setting span %s logdir to %s\n", span->name, r2conf.logdir);
|
||||||
ftdm_log(FTDM_LOG_DEBUG, "Setting openr2 for span %s logdir to %s\n", span->name, r2conf.logdir);
|
|
||||||
openr2_context_set_log_directory(r2data->r2context, r2conf.logdir);
|
openr2_context_set_log_directory(r2data->r2context, r2conf.logdir);
|
||||||
}
|
snprintf(r2data->logdir, sizeof(r2data->logdir), "%s", r2conf.logdir);
|
||||||
|
|
||||||
if (r2conf.advanced_protocol_file) {
|
if (r2conf.advanced_protocol_file) {
|
||||||
openr2_context_configure_from_advanced_file(r2data->r2context, r2conf.advanced_protocol_file);
|
openr2_context_configure_from_advanced_file(r2data->r2context, r2conf.advanced_protocol_file);
|
||||||
}
|
}
|
||||||
@ -1253,6 +1306,7 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_r2_configure_span_signaling)
|
|||||||
hashtable_insert(spanpvt->r2calls, (void *)r2call->name, r2call, HASHTABLE_FLAG_FREE_VALUE);
|
hashtable_insert(spanpvt->r2calls, (void *)r2call->name, r2call, HASHTABLE_FLAG_FREE_VALUE);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
r2data->mf_dump_size = r2conf.mf_dump_size;
|
||||||
r2data->flags = 0;
|
r2data->flags = 0;
|
||||||
spanpvt->r2context = r2data->r2context;
|
spanpvt->r2context = r2data->r2context;
|
||||||
|
|
||||||
|
@ -351,6 +351,7 @@ typedef struct ftdm_channel_config {
|
|||||||
ftdm_chan_type_t type;
|
ftdm_chan_type_t type;
|
||||||
float rxgain;
|
float rxgain;
|
||||||
float txgain;
|
float txgain;
|
||||||
|
uint8_t debugdtmf;
|
||||||
} ftdm_channel_config_t;
|
} ftdm_channel_config_t;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -431,9 +432,38 @@ typedef enum {
|
|||||||
FTDM_COMMAND_WINK,
|
FTDM_COMMAND_WINK,
|
||||||
FTDM_COMMAND_ENABLE_PROGRESS_DETECT,
|
FTDM_COMMAND_ENABLE_PROGRESS_DETECT,
|
||||||
FTDM_COMMAND_DISABLE_PROGRESS_DETECT,
|
FTDM_COMMAND_DISABLE_PROGRESS_DETECT,
|
||||||
|
|
||||||
|
/*!< Start tracing input and output from channel to the given file */
|
||||||
FTDM_COMMAND_TRACE_INPUT,
|
FTDM_COMMAND_TRACE_INPUT,
|
||||||
FTDM_COMMAND_TRACE_OUTPUT,
|
FTDM_COMMAND_TRACE_OUTPUT,
|
||||||
|
|
||||||
|
/*!< Stop both Input and Output trace, closing the files */
|
||||||
FTDM_COMMAND_TRACE_END_ALL,
|
FTDM_COMMAND_TRACE_END_ALL,
|
||||||
|
|
||||||
|
/*!< Enable DTMF debugging */
|
||||||
|
FTDM_COMMAND_ENABLE_DEBUG_DTMF,
|
||||||
|
|
||||||
|
/*!< Disable DTMF debugging (if not disabled explicitly, it is disabled automatically when calls hangup) */
|
||||||
|
FTDM_COMMAND_DISABLE_DEBUG_DTMF,
|
||||||
|
|
||||||
|
/*!< Start dumping all input to a circular buffer. The size of the circular buffer can be specified, default used otherwise */
|
||||||
|
FTDM_COMMAND_ENABLE_INPUT_DUMP,
|
||||||
|
|
||||||
|
/*!< Stop dumping all input to a circular buffer. */
|
||||||
|
FTDM_COMMAND_DISABLE_INPUT_DUMP,
|
||||||
|
|
||||||
|
/*!< Start dumping all output to a circular buffer. The size of the circular buffer can be specified, default used otherwise */
|
||||||
|
FTDM_COMMAND_ENABLE_OUTPUT_DUMP,
|
||||||
|
|
||||||
|
/*!< Stop dumping all output to a circular buffer. */
|
||||||
|
FTDM_COMMAND_DISABLE_OUTPUT_DUMP,
|
||||||
|
|
||||||
|
/*!< Dump the current input circular buffer to the specified FILE* structure */
|
||||||
|
FTDM_COMMAND_DUMP_INPUT,
|
||||||
|
|
||||||
|
/*!< Dump the current output circular buffer to the specified FILE* structure */
|
||||||
|
FTDM_COMMAND_DUMP_OUTPUT,
|
||||||
|
|
||||||
FTDM_COMMAND_ENABLE_CALLERID_DETECT,
|
FTDM_COMMAND_ENABLE_CALLERID_DETECT,
|
||||||
FTDM_COMMAND_DISABLE_CALLERID_DETECT,
|
FTDM_COMMAND_DISABLE_CALLERID_DETECT,
|
||||||
FTDM_COMMAND_ENABLE_ECHOCANCEL,
|
FTDM_COMMAND_ENABLE_ECHOCANCEL,
|
||||||
|
@ -342,21 +342,24 @@ typedef enum {
|
|||||||
FTDM_TYPE_CHANNEL
|
FTDM_TYPE_CHANNEL
|
||||||
} ftdm_data_type_t;
|
} ftdm_data_type_t;
|
||||||
|
|
||||||
#ifdef FTDM_DEBUG_DTMF
|
/* number of bytes for the IO dump circular buffer (5 seconds worth of audio by default) */
|
||||||
/* number of bytes for the circular buffer (5 seconds worth of audio) */
|
#define FTDM_IO_DUMP_DEFAULT_BUFF_SIZE 8 * 5000
|
||||||
#define DTMF_DEBUG_SIZE 8 * 5000
|
|
||||||
/* number of 20ms cycles before timeout and close the debug dtmf file (5 seconds) */
|
|
||||||
#define DTMF_DEBUG_TIMEOUT 250
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
FILE *file;
|
char *buffer;
|
||||||
char buffer[DTMF_DEBUG_SIZE];
|
ftdm_size_t size;
|
||||||
int windex;
|
int windex;
|
||||||
int wrapped;
|
int wrapped;
|
||||||
int closetimeout;
|
} ftdm_io_dump_t;
|
||||||
|
|
||||||
|
/* number of interval cycles before timeout and close the debug dtmf file (5 seconds if interval is 20) */
|
||||||
|
#define DTMF_DEBUG_TIMEOUT 250
|
||||||
|
typedef struct {
|
||||||
|
uint8_t enabled;
|
||||||
|
uint8_t requested;
|
||||||
|
FILE *file;
|
||||||
|
int32_t closetimeout;
|
||||||
ftdm_mutex_t *mutex;
|
ftdm_mutex_t *mutex;
|
||||||
} ftdm_dtmf_debug_t;
|
} ftdm_dtmf_debug_t;
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char *file;
|
const char *file;
|
||||||
@ -471,9 +474,9 @@ struct ftdm_channel {
|
|||||||
void *user_private;
|
void *user_private;
|
||||||
ftdm_timer_id_t hangup_timer;
|
ftdm_timer_id_t hangup_timer;
|
||||||
ftdm_channel_iostats_t iostats;
|
ftdm_channel_iostats_t iostats;
|
||||||
#ifdef FTDM_DEBUG_DTMF
|
|
||||||
ftdm_dtmf_debug_t dtmfdbg;
|
ftdm_dtmf_debug_t dtmfdbg;
|
||||||
#endif
|
ftdm_io_dump_t rxdump;
|
||||||
|
ftdm_io_dump_t txdump;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ftdm_span {
|
struct ftdm_span {
|
||||||
|
@ -58,6 +58,7 @@ typedef int ftdm_filehandle_t;
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define FTDM_COMMAND_OBJ_SIZE *((ftdm_size_t *)obj)
|
||||||
#define FTDM_COMMAND_OBJ_INT *((int *)obj)
|
#define FTDM_COMMAND_OBJ_INT *((int *)obj)
|
||||||
#define FTDM_COMMAND_OBJ_CHAR_P (char *)obj
|
#define FTDM_COMMAND_OBJ_CHAR_P (char *)obj
|
||||||
#define FTDM_COMMAND_OBJ_FLOAT *(float *)obj
|
#define FTDM_COMMAND_OBJ_FLOAT *(float *)obj
|
||||||
|
Loading…
x
Reference in New Issue
Block a user