Merge branch 'moy.iodump'

This commit is contained in:
Moises Silva 2010-12-06 07:34:25 -05:00
commit 004cc9d7aa
10 changed files with 668 additions and 213 deletions

View File

@ -55,10 +55,6 @@ COMPILE = $(CC) $(FTDM_CFLAGS)
LTCOMPILE = $(LIBTOOL) --mode=compile --tag=CC $(COMPILE)
LINK = $(LIBTOOL) --mode=link --tag=CC $(CC) $(FTDM_CFLAGS) $(LDFLAGS) -o $@
if WANT_DEBUGDTMF
FTDM_CFLAGS += -DFTDM_DEBUG_DTMF
endif
#
# GNU pkgconfig file

View File

@ -45,3 +45,29 @@ fxs-channel => 1
number => 2
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

View File

@ -1,52 +1,220 @@
<!-- 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">
<settings>
<param name="debug" value="0"/>
<!--<param name="hold-music" value="$${moh_uri}"/>-->
<!--<param name="enable-analog-option" value="call-swap"/>-->
<!--<param name="enable-analog-option" value="3-way"/>-->
</settings>
<settings>
<param name="debug" value="0"/>
<!--<param name="hold-music" value="$${moh_uri}"/>-->
<!--<param name="enable-analog-option" value="call-swap"/>-->
<!--<param name="enable-analog-option" value="3-way"/>-->
</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) -->
<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>
<!-- Sample analog configuration -->
<analog_spans>
<!-- The span name must match the name in your freetdm.conf -->
<span name="myAnalog">
<!--<param name="hold-music" value="$${moh_uri}"/>-->
<!--<param name="enable-analog-option" value="call-swap"/>-->
<!--<param name="enable-analog-option" value="3-way"/>-->
<param name="tonegroup" value="us"/>
<param name="digit-timeout" value="2000"/>
<param name="max-digits" value="11"/>
<param name="dialplan" value="XML"/>
<param name="context" value="default"/>
<param name="enable-callerid" value="true"/>
<!-- regex to stop dialing when it matches -->
<!--<param name="dial-regex" value="5555"/>-->
<!-- regex to stop dialing when it does not match -->
<!--<param name="fail-dial-regex" value="^5"/>-->
</span>
</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"/>
<!-- Analog spans go here -->
<analog_spans>
<span name="myAnalog">
<!--<param name="hold-music" value="$${moh_uri}"/>-->
<!--<param name="enable-analog-option" value="call-swap"/>-->
<!--<param name="enable-analog-option" value="3-way"/>-->
<param name="tonegroup" value="us"/>
<param name="digit-timeout" value="2000"/>
<param name="max-digits" value="11"/>
<param name="dialplan" value="XML"/>
<param name="context" value="default"/>
<param name="enable-callerid" value="true"/>
<!-- regex to stop dialing when it matches -->
<!--<param name="dial-regex" value="5555"/>-->
<!-- regex to stop dialing when it does not match -->
<!--<param name="fail-dial-regex" value="^5"/>-->
</span>
</analog_spans>
<!-- 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>

View File

@ -167,17 +167,6 @@ AC_ARG_WITH([pritap],
HAVE_PRITAP="${enable_pritap}"
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
#

View File

@ -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)
{
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;
}
/* I guess we always want DTMF detection */
ftdm_enable_channel_dtmf(sigmsg->channel, NULL);
switch_core_session_add_stream(session, NULL);
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;
}
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)
{
switch_core_session_t *session = NULL;
@ -2059,6 +2062,8 @@ static FIO_SIGNAL_CB_FUNCTION(on_r2_signal)
}
break;
case FTDM_SIGEVENT_PROCEED:{} break;
default:
{
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, "presentation_ind", ftdm_presentation2str(caller_data->pres));
ftdm_enable_channel_dtmf(sigmsg->channel, NULL);
return ftdm_channel_from_event(sigmsg, &session);
}
break;

View File

@ -86,6 +86,114 @@ FT_DECLARE(ftdm_time_t) ftdm_current_time_in_ms(void)
#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 {
uint8_t enabled;
uint8_t running;
@ -448,10 +556,6 @@ static ftdm_status_t ftdm_channel_destroy(ftdm_channel_t *ftdmchan)
ftdm_sleep(500);
}
#ifdef FTDM_DEBUG_DTMF
ftdm_mutex_destroy(&ftdmchan->dtmfdbg.mutex);
#endif
ftdm_mutex_lock(ftdmchan->pre_buffer_mutex);
ftdm_buffer_destroy(&ftdmchan->pre_buffer);
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->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->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);
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);
ftdmchan->pre_buffer_size = 0;
ftdm_mutex_unlock(ftdmchan->pre_buffer_mutex);
#ifdef FTDM_DEBUG_DTMF
close_dtmf_debug(ftdmchan);
#endif
ftdm_channel_clear_vars(ftdmchan);
if (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->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)) {
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_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "channel done\n");
ftdm_mutex_unlock(ftdmchan->mutex);
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)
{
assert(ftdmchan != NULL);
ftdm_assert(ftdmchan != NULL, "Null channel\n");
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;
assert(ftdmchan != NULL);
assert(ftdmchan->fio != NULL);
ftdm_assert_return(ftdmchan != NULL, FTDM_FAIL, "No channel\n");
ftdm_assert_return(ftdmchan->fio != NULL, FTDM_FAIL, "No IO attached to channel\n");
ftdm_mutex_lock(ftdmchan->mutex);
@ -2578,7 +2663,7 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_command(ftdm_channel_t *ftdmchan, ftdm_co
break;
case FTDM_COMMAND_TRACE_INPUT:
{
char *path = (char *) obj;
char *path = FTDM_COMMAND_OBJ_CHAR_P;
if (ftdmchan->fds[FTDM_READ_TRACE_INDEX] > 0) {
close(ftdmchan->fds[FTDM_READ_TRACE_INDEX]);
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);
}
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:
{
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;
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) {
struct tm currtime;
time_t currsec;
@ -3107,37 +3316,19 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_queue_dtmf(ftdm_channel_t *ftdmchan, cons
ftdmchan->span_id, ftdmchan->chan_id,
currtime.tm_year-100, currtime.tm_mon+1, currtime.tm_mday,
currtime.tm_hour, currtime.tm_min, currtime.tm_sec, ftdmchan->native_codec == FTDM_CODEC_ULAW ? "ulaw" : ftdmchan->native_codec == FTDM_CODEC_ALAW ? "alaw" : "sln");
ftdmchan->dtmfdbg.file = fopen(dfile, "w");
ftdmchan->dtmfdbg.file = fopen(dfile, "w");
if (!ftdmchan->dtmfdbg.file) {
ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "failed to open debug dtmf file %s\n", dfile);
} 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;
if (ftdmchan->dtmfdbg.wrapped) {
rc = fwrite(&ftdmchan->dtmfdbg.buffer[ftdmchan->dtmfdbg.windex], 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);
}
}
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;
ftdm_channel_command(ftdmchan, FTDM_COMMAND_DUMP_INPUT, ftdmchan->dtmfdbg.file);
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Dumped initial DTMF output to %s\n", dfile);
}
} 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) {
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)
{
int dlen = (int) *datalen;
if (ftdmchan->fds[FTDM_WRITE_TRACE_INDEX] > -1) {
int dlen = (int) *datalen;
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);
}
}
write_chan_io_dump(ftdmchan, &ftdmchan->txdump, data, dlen);
return ftdmchan->fio->write(ftdmchan, data, datalen);
}
static FIO_READ_FUNCTION(ftdm_raw_read)
{
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);
}
#ifdef FTDM_DEBUG_DTMF
if (status == FTDM_SUCCESS) {
int dlen = (int) *datalen;
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) {
int diff = dlen - avail;
/* write only what we can and the rest at the beginning of the buffer */
memcpy(&ftdmchan->dtmfdbg.buffer[windex], dataptr, avail);
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 {
write_chan_io_dump(ftdmchan, &ftdmchan->rxdump, data, dlen);
/* if dtmf debug is enabled and initialized, write there too */
if (ftdmchan->dtmfdbg.file) {
rc = fwrite(data, 1, dlen, ftdmchan->dtmfdbg.file);
if (rc != dlen) {
ftdm_log(FTDM_LOG_WARNING, "DTMF debugger wrote only %d out of %d bytes: %s\n", rc, datalen, strerror(errno));
}
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;
}
@ -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)
{
int currindex;
unsigned chan_index = 0;
ftdm_assert_return(span != NULL, FTDM_EINVAL, "span 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;
}
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;
}
@ -4094,7 +4270,7 @@ static ftdm_status_t load_config(void)
ftdm_channel_config_t 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)) {
return FTDM_FAIL;
@ -4131,6 +4307,9 @@ static ftdm_status_t load_config(void)
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);
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 {
ftdm_log(FTDM_LOG_CRIT, "failure creating span of type %s\n", type);
span = NULL;
@ -4254,6 +4433,9 @@ static ftdm_status_t load_config(void)
if (sscanf(val, "%f", &(chan_config.rxgain)) != 1) {
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")) {
len = strlen(val);
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:
{
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
* doing it during SIGEVENT_START, but now that flags are private they can't, wonder if

View File

@ -68,6 +68,7 @@ typedef struct ftdm_r2_call_t {
ftdm_channel_state_t chanstate;
ftdm_size_t dnis_index;
ftdm_size_t ani_index;
char logname[255];
char name[10];
unsigned long txdrops;
} ftdm_r2_call_t;
@ -88,13 +89,13 @@ typedef struct ft_r2_conf_s {
int32_t max_dnis;
int32_t mfback_timeout;
int32_t metering_pulse_timeout;
int32_t mf_dump_size;
/* booleans */
int immediate_accept;
int skip_category;
int get_ani_first;
int call_files;
int mf_files;
int double_answer;
int charge_calls;
int forced_release;
@ -117,6 +118,8 @@ typedef struct ftdm_r2_data_s {
int forced_release:1;
/* whether accept the call when offered, or wait until the user decides to accept */
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 */
int32_t jobmax;
/* Total number of loops performed so far */
@ -125,6 +128,8 @@ typedef struct ftdm_r2_data_s {
uint64_t loops[11];
/* LWP */
uint32_t monitor_thread_id;
/* Logging directory */
char logdir[512];
} ftdm_r2_data_t;
/* 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 */
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_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");
@ -441,12 +447,15 @@ static void ftdm_r2_on_call_init(openr2_chan_t *r2chan)
ft_r2_clean_call(ftdmchan->call_data);
r2call = R2CALL(ftdmchan);
if (ftdmchan->state == FTDM_CHANNEL_STATE_DOWN) {
R2CALL(ftdmchan)->chanstate = FTDM_CHANNEL_STATE_DOWN;
} else {
R2CALL(ftdmchan)->chanstate = FTDM_CHANNEL_STATE_DIALING;
snprintf(r2call->logname, sizeof(r2call->logname), "%s", logname);
/* start io dump */
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);
}
@ -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)
{
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_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)
{
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 */
openr2_chan_disable_read(r2chan);
R2CALL(ftdmchan)->accepted = 1;
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;
}
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Protocol error\n");
dump_mf(r2chan);
clear_accept_pending(ftdmchan);
@ -1042,24 +1085,28 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_r2_configure_span_signaling)
/* .variant */ OR2_VAR_ITU,
/* .category */ OR2_CALLING_PARTY_CATEGORY_NATIONAL_SUBSCRIBER,
/* .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,
/* .max_ani */ 10,
/* .max_dnis */ 4,
/* .mfback_timeout */ -1,
/* .metering_pulse_timeout */ -1,
/* .mf_dump_size */ 0,
/* .immediate_accept */ -1,
/* .skip_category */ -1,
/* .get_ani_first */ -1,
/* .call_files */ 0,
/* .mf_files */ 0,
/* .double_answer */ -1,
/* .charge_calls */ -1,
/* .forced_release */ -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) {
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;
}
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")) {
if (!val) {
break;
@ -1128,46 +1176,51 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_r2_configure_span_signaling)
continue;
}
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")) {
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")) {
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")) {
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")) {
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")) {
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")) {
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")) {
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")) {
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);
} 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);
ftdm_log(FTDM_LOG_DEBUG, "Configuring R2 span %s with call files = %d\n", span->name, r2conf.call_files);
} else if (!strcasecmp(var, "mfback_timeout")) {
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")) {
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")) {
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")) {
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 {
snprintf(span->last_error, sizeof(span->last_error), "Unknown R2 parameter [%s]", var);
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_span_id(r2data->r2context, span->span_id);
if (r2conf.logdir && r2conf.logdir[0]) {
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);
}
ftdm_log(FTDM_LOG_DEBUG, "Setting span %s logdir to %s\n", span->name, 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) {
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);
}
r2data->mf_dump_size = r2conf.mf_dump_size;
r2data->flags = 0;
spanpvt->r2context = r2data->r2context;

View File

@ -351,6 +351,7 @@ typedef struct ftdm_channel_config {
ftdm_chan_type_t type;
float rxgain;
float txgain;
uint8_t debugdtmf;
} ftdm_channel_config_t;
/*!
@ -431,9 +432,38 @@ typedef enum {
FTDM_COMMAND_WINK,
FTDM_COMMAND_ENABLE_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_OUTPUT,
/*!< Stop both Input and Output trace, closing the files */
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_DISABLE_CALLERID_DETECT,
FTDM_COMMAND_ENABLE_ECHOCANCEL,

View File

@ -342,21 +342,24 @@ typedef enum {
FTDM_TYPE_CHANNEL
} ftdm_data_type_t;
#ifdef FTDM_DEBUG_DTMF
/* number of bytes for the circular buffer (5 seconds worth of audio) */
#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
/* number of bytes for the IO dump circular buffer (5 seconds worth of audio by default) */
#define FTDM_IO_DUMP_DEFAULT_BUFF_SIZE 8 * 5000
typedef struct {
FILE *file;
char buffer[DTMF_DEBUG_SIZE];
char *buffer;
ftdm_size_t size;
int windex;
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_dtmf_debug_t;
#endif
typedef struct {
const char *file;
@ -471,9 +474,9 @@ struct ftdm_channel {
void *user_private;
ftdm_timer_id_t hangup_timer;
ftdm_channel_iostats_t iostats;
#ifdef FTDM_DEBUG_DTMF
ftdm_dtmf_debug_t dtmfdbg;
#endif
ftdm_io_dump_t rxdump;
ftdm_io_dump_t txdump;
};
struct ftdm_span {

View File

@ -58,6 +58,7 @@ typedef int ftdm_filehandle_t;
extern "C" {
#endif
#define FTDM_COMMAND_OBJ_SIZE *((ftdm_size_t *)obj)
#define FTDM_COMMAND_OBJ_INT *((int *)obj)
#define FTDM_COMMAND_OBJ_CHAR_P (char *)obj
#define FTDM_COMMAND_OBJ_FLOAT *(float *)obj