Merge branch 'moy.nonblocking-api'

Conflicts:
	libs/freetdm/src/ftmod/ftmod_r2/ftmod_r2.c
This commit is contained in:
Moises Silva 2011-01-04 12:32:30 -05:00
commit 1334d02ac0
32 changed files with 1614 additions and 2549 deletions

View File

@ -73,6 +73,7 @@ libfreetdm_la_SOURCES = \
$(SRC)/hashtable.c \
$(SRC)/hashtable_itr.c \
$(SRC)/ftdm_io.c \
$(SRC)/ftdm_state.c \
$(SRC)/ftdm_queue.c \
$(SRC)/ftdm_sched.c \
$(SRC)/ftdm_call_utils.c \

View File

@ -1,38 +0,0 @@
;M3UA SS7 Links Config
;
;ss7box-m3ua_mode => true
;local_sctp_ip => localhost
;local sctp_port => 30000
;remote_sctp_ip => localhost
;remote_sctp_port => 30001
;opc => 0-0-0
;dpc => 0-0-0
; AP Specific Stuff. This will likely move later.
; CNAM Gateways
cnam1_dpc => 0-0-0
cnam1_ssn => 253
cnam2_dpc => 0-0-0
cnam2_ssn => 253
cnam3_dpc => 0-0-0
cnam3_ssn => 253
;LNP Gateways
lnp1_dpc => 0-0-0
lnp1_ssn => 253
lnp2_dpc => 0-0-0
lnp2_ssn => 253
lnp3_dpc => 0-0-0
lnp3_ssn => 253
;LNP Gateways
sms8001_dpc => 0-0-0
sms8001_ssn => 253
sms8002_dpc => 0-0-0
sms8002_ssn => 253
sms8003_dpc => 0-0-0
sms8003_ssn => 253

View File

@ -160,7 +160,7 @@ AC_ARG_WITH([pritap],
[AS_HELP_STRING([--with-pritap], [Install ftmod_pritap])],
[case "${withval}" in
no) enable_pritap="no" ;;
*) enable_pritab="yes" ;;
*) enable_pritap="yes" ;;
esac],
[enable_pritap="no"]
)

View File

@ -0,0 +1,125 @@
Last Updated: Fri 30 Dec, 2010
== Background ==
FreeTDM is a threaded library. As such, locking considerations must be taken when using it and when writing code inside the library.
At the moment locks are not exposed to users. This means API users cannot acquire a lock on a channel or span structure. There is no
need for users to lock channels or spans since all their interactions with those structures should be done thru the FreeTDM API which
can (and in most cases must) internally lock on their behalf.
Internally, locking can be done either by the core or the signaling modules. To better understand the locking considerations we must
understand first the threading model of FreeTDM.
== Threading Model ==
At startup, when the user calls ftdm_global_init(), just one timing thread is created to dispatch internal timers. If you write
a signaling module or any other code using the scheduling API, you can choose to run your schedule in this timing thread or in
a thread of your choice. This is the only thread launched at initialization.
If the application decides to use ftdm_global_configuration(), which reads freetdm.conf to create the spans and channels data
structures, then possibly another thread will be launched for CPU usage monitoring (only if enabled in the configuration cpu_monitor=yes
This thread sole purpose is to check the CPU and raise an alarm if reaches a configurable threshold, the alarm then is checked to avoid
placing or receiving further calls.
At this point FreeTDM has initialized and configured its channels input output configuration.
The user is then supposed to configure the signaling via ftdm_configure_span_signaling() and then start the signaling work
using ftdm_span_start(). This will typically launch at least 1 thread per span. Some signaling modules (actually just the analog one)
launches another thread per channel when receiving a call. The rest of the signaling modules currently launch only one thread per
span and the signaling for all channels within the span is handled in that thread. We call that thread 'the signaling thread'.
At this point the user can start placing calls using the FreeTDM call API ftdm_channel_call_place(). Any of the possible threads in
which the user calls the FreeTDM API is called 'the user thread', depending on the application thread model (the application using FreeTDM)
this user thread may be different each time or the same all the time, we cannot make any assumptions. In the case of FreeSWITCH, the most
common user of FreeTDM, the user thread is most of the cases a thread for each new call leg.
At this point we have identified 4 types of threads.
1. The timing thread (the core thread that triggers timers).
Its responsibility is simply check for timers that were scheduled and trigger them when the time comes. This means that if you decide
to use the scheduling API in freerun mode (where you use the core timer thread) you callbacks will be executed in this global thread
and you MUST not block at all since there might be other events waiting.
2. The CPU thread (we don't really care about this one as it does not interact with channels or spans).
3. The signaling thread.
There is one thread of this per span. This thread takes care of reading signaling specific messages from the network (ISDN network, etc) and
changing the channel call state accordingly and processing state changes caused by user API calls (like ftdm_channel_call_hangup for example).
4. The user thread.
This is a thread in which the user decides to execute FreeTDM APIs, in some cases it might even be the same than the signaling thread (because
most SIGEVENT notifications are delivered by the signaling thread, however we are advicing users to not use FreeTDM unsafe APIs from the
thread where they receive SIGEVENT notifications as some APIs may block for a few milliseconds, effectively blocking the whole signaling thread
that is servicing a span.
== Application Locking ==
Users of the FreeTDM API will typically have locking of their own for their own application-specific data structures (in the case of FreeSWITCH, the
session lock for example). Other application-specific locks may be involved.
== DeadLocks ==
As soon as we think of application locks, and we mix them with the FreeTDM internal locks, the possibility of deadlocks arise.
A typical deadlock scenario when 2 locks are involved is:
- User Thread - - Signaling Thread -
1. Application locks applock. 1. A network message is received for a channel.
2. Aplication invokes a FreeTDM call API (ie: ftdm_channel_call_hangup()). 2. The involved channel is locked.
3. The FreeTDM API attempts to acquire the channel lock and stalls because 3. The message processing results in a notification
the signaling thread just acquired it. to be delivered to the user via the callback function
provided for that purpose. The callback is then called.
4. The thread is now deadlocked because the signaling thread will never 4. The application callback attempts to acquire its application
release the channel lock. lock but deadlocks because the user thread already has it.
To avoid this signaling modules should not deliver signals to the user while holding the channel lock. An easy way to avoid this is
to not deliver signals while processing a state change, but rather defer them until the channel lock is released. Most new signaling modules
accomplish this by setting the span flag FTDM_SPAN_USE_SIGNALS_QUEUE, this flag tells the core to enqueue signals (ie FTDM_SIGEVENT_START)
when ftdm_span_send_signal() is called and not deliver them until ftdm_span_trigger_signals() is called, which is done by the signaling module
in its signaling thread when no channel lock is being held.
== State changes while locking ==
Only 2 types of threads should be performing state changes.
User threads.
The user thread is a random thread that was crated by the API user. We do not know what threading model users of FreeTDM will follow
and therefore cannot make assumptions about it. The user should be free to call FreeTDM APIs from any thread, except threads that
are under our control, like the signaling threads. Although it may work in most situations, is discouraged for users to try
to use FreeTDM APIs from the signaling thread, that is, the thread where the signaling callback provided during configuration
is called (the callback where FTDM_SIGEVENT_XXX signals are delivered).
A user thread may request state changes implicitly through calls to FreeTDM API's. The idea of state changes is internal to freetdm
and should not be exposed to users of the API (except for debugging purposes, like the ftdm_channel_get_state, ftdm_channel_get_state_str etc)
This is an example of the API's that implicitly request a state change.
ftdm_channel_call_answer()
Signaling modules should guarantee that upon releasing a lock on a channel, any state changes will be already processed and
not deferred to other threads, otherwise that leads to a situation where a state change requested by the signaling module is pending
to be serviced by another signaling module thread but a user thread wins the channel lock and attempts to perform a state change which will
fail because another state change is pending (and user threads are not meant to process signaling states).
ONLY one signaling thread per channel should try to perform state changes and processing of the states,
otherwise complexity arises and is not worth it!
At some point before we stablished this policies we could have 3 different threads doing state changes.
1. A user random thread could implcitly try to change the state in response to a call API.
2. The ftmod signaling thread could try to change the state in response to other state changes.
3. The lower level signaling stack threads could try to change the state in response to stack events.
As a result, lower level signaling stack thread could set a state and then let the signaling thread to
process it, but when unlocking the channel, the user thread may win the lock over the signaling thread and
may try to set a state change of its own and fail (due to the unprocessed state change)!
The rule is, the signaling module should never unlock a channel with states pending to process this way the user,
when acquiring a channel lock (inside ftdm_channel_call_answer for example) it will always find a consistent state
for the channel and not in the middle of state processing.

View File

@ -421,16 +421,18 @@ static switch_status_t channel_on_routing(switch_core_session_t *session)
private_t *tech_pvt = NULL;
channel = switch_core_session_get_channel(session);
assert(channel != NULL);
switch_assert(channel != NULL);
tech_pvt = switch_core_session_get_private(session);
assert(tech_pvt != NULL);
switch_assert(tech_pvt != NULL);
assert(tech_pvt->ftdmchan != NULL);
switch_assert(tech_pvt->ftdmchan != NULL);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s CHANNEL ROUTING\n", switch_channel_get_name(channel));
ftdm_channel_call_indicate(tech_pvt->ftdmchan, FTDM_CHANNEL_INDICATE_PROCEED);
if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_INBOUND) {
ftdm_channel_call_indicate(tech_pvt->ftdmchan, FTDM_CHANNEL_INDICATE_PROCEED);
}
return SWITCH_STATUS_SUCCESS;
}
@ -441,10 +443,10 @@ static switch_status_t channel_on_execute(switch_core_session_t *session)
private_t *tech_pvt = NULL;
channel = switch_core_session_get_channel(session);
assert(channel != NULL);
switch_assert(channel != NULL);
tech_pvt = switch_core_session_get_private(session);
assert(tech_pvt != NULL);
switch_assert(tech_pvt != NULL);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s CHANNEL EXECUTE\n", switch_channel_get_name(channel));
@ -1640,6 +1642,14 @@ static FIO_SIGNAL_CB_FUNCTION(on_common_signal)
}
return FTDM_SUCCESS;
}
case FTDM_SIGEVENT_RELEASED:
case FTDM_SIGEVENT_INDICATION_COMPLETED:
{
/* Swallow these events */
return FTDM_BREAK;
}
break;
default:
return FTDM_SUCCESS;
break;
@ -1730,7 +1740,6 @@ static FIO_SIGNAL_CB_FUNCTION(on_fxo_signal)
}
}
break;
case FTDM_SIGEVENT_RELEASED: { /* twiddle */ } break;
case FTDM_SIGEVENT_SIGSTATUS_CHANGED: { /* twiddle */ } break;
default:
@ -1786,7 +1795,6 @@ static FIO_SIGNAL_CB_FUNCTION(on_fxs_signal)
}
}
break;
case FTDM_SIGEVENT_RELEASED: { /* twiddle */ } break;
case FTDM_SIGEVENT_STOP:
{
@ -2013,8 +2021,6 @@ static FIO_SIGNAL_CB_FUNCTION(on_r2_signal)
}
break;
case FTDM_SIGEVENT_RELEASED: { /* twiddle */ } break;
/* on DNIS received from the R2 forward side, return status == FTDM_BREAK to stop requesting DNIS */
case FTDM_SIGEVENT_COLLECTED_DIGIT:
{
@ -2094,6 +2100,7 @@ static FIO_SIGNAL_CB_FUNCTION(on_r2_signal)
break;
case FTDM_SIGEVENT_PROCEED:{} break;
case FTDM_SIGEVENT_INDICATION_COMPLETED:{} break;
default:
{
@ -2132,8 +2139,6 @@ static FIO_SIGNAL_CB_FUNCTION(on_clear_channel_signal)
}
break;
case FTDM_SIGEVENT_RELEASED: { /* twiddle */ } break;
case FTDM_SIGEVENT_STOP:
case FTDM_SIGEVENT_RESTART:
{

View File

@ -94,78 +94,6 @@
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="2"
CharacterSet="2"
WholeProgramOptimization="1"
BuildLogFile="$(IntDir)\BuildLog-freetdm.htm"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories="../src/include;../src/include/private;../src/isdn/include"
PreprocessorDefinitions="WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FREETDM_EXPORTS;TELETONE_EXPORTS"
RuntimeLibrary="2"
DisableLanguageExtensions="false"
RuntimeTypeInfo="false"
UsePrecompiledHeader="0"
WarningLevel="4"
WarnAsError="true"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="3"
CompileAs="1"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Debug|x64"
OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)"
@ -243,6 +171,78 @@
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="2"
CharacterSet="2"
WholeProgramOptimization="1"
BuildLogFile="$(IntDir)\BuildLog-freetdm.htm"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories="../src/include;../src/include/private;../src/isdn/include"
PreprocessorDefinitions="WIN32;NDEBUG;_LIB;_CRT_SECURE_NO_WARNINGS;FREETDM_EXPORTS;TELETONE_EXPORTS"
RuntimeLibrary="2"
DisableLanguageExtensions="false"
RuntimeTypeInfo="false"
UsePrecompiledHeader="0"
WarningLevel="4"
WarnAsError="true"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="3"
CompileAs="1"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|x64"
OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)"
@ -366,6 +366,10 @@
RelativePath="..\src\include\private\ftdm_sched.h"
>
</File>
<File
RelativePath="..\src\include\private\ftdm_state.h"
>
</File>
<File
RelativePath="..\src\include\ftdm_threadmutex.h"
>
@ -452,6 +456,10 @@
RelativePath="..\src\ftdm_sched.c"
>
</File>
<File
RelativePath="..\src\ftdm_state.c"
>
</File>
<File
RelativePath="..\src\ftdm_threadmutex.c"
>

View File

@ -268,9 +268,6 @@ FTDM_STR2ENUM(ftdm_str2ftdm_analog_start_type, ftdm_analog_start_type2str, ftdm_
FTDM_ENUM_NAMES(SIGNAL_NAMES, SIGNAL_STRINGS)
FTDM_STR2ENUM(ftdm_str2ftdm_signal_event, ftdm_signal_event2str, ftdm_signal_event_t, SIGNAL_NAMES, FTDM_SIGEVENT_INVALID)
FTDM_ENUM_NAMES(CHANNEL_STATE_NAMES, CHANNEL_STATE_STRINGS)
FTDM_STR2ENUM(ftdm_str2ftdm_channel_state, ftdm_channel_state2str, ftdm_channel_state_t, CHANNEL_STATE_NAMES, FTDM_CHANNEL_STATE_INVALID)
FTDM_ENUM_NAMES(MDMF_TYPE_NAMES, MDMF_STRINGS)
FTDM_STR2ENUM(ftdm_str2ftdm_mdmf_type, ftdm_mdmf_type2str, ftdm_mdmf_type_t, MDMF_TYPE_NAMES, MDMF_INVALID)
@ -304,6 +301,9 @@ FTDM_STR2ENUM(ftdm_str2ftdm_usr_layer1_prot, ftdm_user_layer1_prot2str, ftdm_use
FTDM_ENUM_NAMES(CALLING_PARTY_CATEGORY_NAMES, CALLING_PARTY_CATEGORY_STRINGS)
FTDM_STR2ENUM(ftdm_str2ftdm_calling_party_category, ftdm_calling_party_category2str, ftdm_calling_party_category_t, CALLING_PARTY_CATEGORY_NAMES, FTDM_CPC_INVALID)
FTDM_ENUM_NAMES(INDICATION_NAMES, INDICATION_STRINGS)
FTDM_STR2ENUM(ftdm_str2channel_indication, ftdm_channel_indication2str, ftdm_channel_indication_t, INDICATION_NAMES, FTDM_CHANNEL_INDICATE_INVALID)
static ftdm_status_t ftdm_group_add_channels(ftdm_span_t* span, int currindex, const char* name);
static const char *cut_path(const char *in)
@ -615,6 +615,9 @@ static ftdm_status_t ftdm_channel_destroy(ftdm_channel_t *ftdmchan)
ftdm_mutex_destroy(&ftdmchan->mutex);
ftdm_mutex_destroy(&ftdmchan->pre_buffer_mutex);
if (ftdmchan->state_completed_interrupt) {
ftdm_interrupt_destroy(&ftdmchan->state_completed_interrupt);
}
}
return FTDM_SUCCESS;
@ -1022,6 +1025,8 @@ FT_DECLARE(ftdm_status_t) ftdm_span_add_channel(ftdm_span_t *span, ftdm_socket_t
}
ftdm_set_flag(new_chan, FTDM_CHANNEL_CONFIGURED | FTDM_CHANNEL_READY);
new_chan->state = FTDM_CHANNEL_STATE_DOWN;
new_chan->state_status = FTDM_STATE_STATUS_COMPLETED;
*chan = new_chan;
return FTDM_SUCCESS;
}
@ -1340,237 +1345,6 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_add_token(ftdm_channel_t *ftdmchan, char
}
FT_DECLARE(ftdm_status_t) ftdm_channel_complete_state(ftdm_channel_t *ftdmchan)
{
ftdm_channel_state_t state = ftdmchan->state;
if (state == FTDM_CHANNEL_STATE_PROGRESS) {
ftdm_set_flag(ftdmchan, FTDM_CHANNEL_PROGRESS);
} else if (state == FTDM_CHANNEL_STATE_UP) {
ftdm_set_flag(ftdmchan, FTDM_CHANNEL_PROGRESS);
ftdm_set_flag(ftdmchan, FTDM_CHANNEL_MEDIA);
ftdm_set_flag(ftdmchan, FTDM_CHANNEL_ANSWERED);
} else if (state == FTDM_CHANNEL_STATE_PROGRESS_MEDIA) {
ftdm_set_flag(ftdmchan, FTDM_CHANNEL_PROGRESS);
ftdm_set_flag(ftdmchan, FTDM_CHANNEL_MEDIA);
}
return FTDM_SUCCESS;
}
static int ftdm_parse_state_map(ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, ftdm_state_map_t *state_map)
{
int x = 0, ok = 0;
ftdm_state_direction_t direction = ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND) ? ZSD_OUTBOUND : ZSD_INBOUND;
for(x = 0; x < FTDM_MAP_NODE_SIZE; x++) {
int i = 0, proceed = 0;
if (!state_map->nodes[x].type) {
break;
}
if (state_map->nodes[x].direction != direction) {
continue;
}
if (state_map->nodes[x].check_states[0] == FTDM_ANY_STATE) {
proceed = 1;
} else {
for(i = 0; i < FTDM_MAP_MAX; i++) {
if (state_map->nodes[x].check_states[i] == ftdmchan->state) {
proceed = 1;
break;
}
}
}
if (!proceed) {
continue;
}
for(i = 0; i < FTDM_MAP_MAX; i++) {
ok = (state_map->nodes[x].type == ZSM_ACCEPTABLE);
if (state_map->nodes[x].states[i] == FTDM_END) {
break;
}
if (state_map->nodes[x].states[i] == state) {
ok = !ok;
goto end;
}
}
}
end:
return ok;
}
/* this function MUST be called with the channel lock held. If waitrq == 1, the channel will be unlocked/locked (never call it with waitrq == 1 with an lock recursivity > 1) */
#define DEFAULT_WAIT_TIME 1000
FT_DECLARE(ftdm_status_t) ftdm_channel_set_state(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, int waitrq)
{
int ok = 1;
int waitms = DEFAULT_WAIT_TIME;
if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_READY)) {
ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_ERROR, "Ignored state change request from %s to %s, the channel is not ready\n",
ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
return FTDM_FAIL;
}
if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) {
ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_ERROR, "Ignored state change request from %s to %s, the previous state change has not been processed yet\n",
ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
return FTDM_FAIL;
}
if (ftdmchan->state == state) {
ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "Why bother changing state from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
return FTDM_FAIL;
}
if (ftdmchan->span->state_map) {
ok = ftdm_parse_state_map(ftdmchan, state, ftdmchan->span->state_map);
goto end;
}
/* basic core state validation (by-passed if the signaling module provides a state_map) */
switch(ftdmchan->state) {
case FTDM_CHANNEL_STATE_HANGUP:
case FTDM_CHANNEL_STATE_TERMINATING:
{
ok = 0;
switch(state) {
case FTDM_CHANNEL_STATE_DOWN:
case FTDM_CHANNEL_STATE_BUSY:
case FTDM_CHANNEL_STATE_RESTART:
ok = 1;
break;
default:
break;
}
}
break;
case FTDM_CHANNEL_STATE_UP:
{
ok = 1;
switch(state) {
case FTDM_CHANNEL_STATE_PROGRESS:
case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
case FTDM_CHANNEL_STATE_RING:
ok = 0;
break;
default:
break;
}
}
break;
case FTDM_CHANNEL_STATE_DOWN:
{
ok = 0;
switch(state) {
case FTDM_CHANNEL_STATE_DIALTONE:
case FTDM_CHANNEL_STATE_COLLECT:
case FTDM_CHANNEL_STATE_DIALING:
case FTDM_CHANNEL_STATE_RING:
case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
case FTDM_CHANNEL_STATE_PROGRESS:
case FTDM_CHANNEL_STATE_IDLE:
case FTDM_CHANNEL_STATE_GET_CALLERID:
case FTDM_CHANNEL_STATE_GENRING:
ok = 1;
break;
default:
break;
}
}
break;
case FTDM_CHANNEL_STATE_BUSY:
{
switch(state) {
case FTDM_CHANNEL_STATE_UP:
ok = 0;
break;
default:
break;
}
}
break;
case FTDM_CHANNEL_STATE_RING:
{
switch(state) {
case FTDM_CHANNEL_STATE_UP:
ok = 1;
break;
default:
break;
}
}
break;
default:
break;
}
end:
if (ok) {
ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_DEBUG, "Changed state from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
ftdmchan->last_state = ftdmchan->state;
ftdmchan->state = state;
ftdmchan->history[ftdmchan->hindex].file = file;
ftdmchan->history[ftdmchan->hindex].func = func;
ftdmchan->history[ftdmchan->hindex].line = line;
ftdmchan->history[ftdmchan->hindex].state = ftdmchan->state;
ftdmchan->history[ftdmchan->hindex].last_state = ftdmchan->last_state;
ftdmchan->history[ftdmchan->hindex].time = ftdm_current_time_in_ms();
ftdmchan->hindex++;
if (ftdmchan->hindex == ftdm_array_len(ftdmchan->history)) {
ftdmchan->hindex = 0;
}
ftdm_set_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE);
ftdm_mutex_lock(ftdmchan->span->mutex);
ftdm_set_flag(ftdmchan->span, FTDM_SPAN_STATE_CHANGE);
if (ftdmchan->span->pendingchans) {
ftdm_queue_enqueue(ftdmchan->span->pendingchans, ftdmchan);
}
ftdm_mutex_unlock(ftdmchan->span->mutex);
} else {
ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "VETO state change from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
goto done;
}
/* there is an inherent race here between set and check of the change flag but we do not care because
* the flag should never last raised for more than a few ms for any state change */
while (waitrq && waitms > 0) {
/* give a chance to the signaling stack to process it */
ftdm_mutex_unlock(ftdmchan->mutex);
ftdm_sleep(10);
waitms -= 10;
ftdm_mutex_lock(ftdmchan->mutex);
/* if the flag is no longer set, the state change was processed (or is being processed) */
if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) {
break;
}
/* if the state is no longer what we set, the state change was
* obviously processed (and the current state change flag is for other state change) */
if (ftdmchan->state != state) {
break;
}
}
if (waitms <= 0) {
ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "state change from %s to %s was most likely not processed after aprox %dms\n",
ftdm_channel_state2str(ftdmchan->last_state), ftdm_channel_state2str(state), DEFAULT_WAIT_TIME);
}
done:
return ok ? FTDM_SUCCESS : FTDM_FAIL;
}
FT_DECLARE(uint32_t) ftdm_group_get_id(const ftdm_group_t *group)
{
return group->group_id;
@ -1930,17 +1704,6 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_open_by_span(uint32_t span_id, ftdm_direc
return status;
}
FT_DECLARE(ftdm_status_t) ftdm_channel_init(ftdm_channel_t *ftdmchan)
{
if (ftdmchan->init_state != FTDM_CHANNEL_STATE_DOWN) {
ftdm_set_state(ftdmchan, ftdmchan->init_state);
ftdmchan->init_state = FTDM_CHANNEL_STATE_DOWN;
}
return FTDM_SUCCESS;
}
FT_DECLARE(ftdm_status_t) ftdm_channel_open_chan(ftdm_channel_t *ftdmchan)
{
ftdm_status_t status = FTDM_FAIL;
@ -2115,6 +1878,28 @@ FT_DECLARE(void) ftdm_span_set_trunk_type(ftdm_span_t *span, ftdm_trunk_type_t t
span->trunk_type = type;
}
FT_DECLARE(ftdm_status_t) ftdm_span_set_blocking_mode(const ftdm_span_t *span, ftdm_bool_t enabled)
{
ftdm_channel_t *fchan = NULL;
ftdm_iterator_t *citer = NULL;
ftdm_iterator_t *curr = NULL;
citer = ftdm_span_get_chan_iterator(span, NULL);
if (!citer) {
return FTDM_ENOMEM;
}
for (curr = citer ; curr; curr = ftdm_iterator_next(curr)) {
fchan = ftdm_iterator_current(curr);
if (enabled) {
ftdm_clear_flag_locked(fchan, FTDM_CHANNEL_NONBLOCK);
} else {
ftdm_set_flag_locked(fchan, FTDM_CHANNEL_NONBLOCK);
}
}
ftdm_iterator_free(citer);
return FTDM_SUCCESS;
}
FT_DECLARE(ftdm_trunk_type_t) ftdm_span_get_trunk_type(const ftdm_span_t *span)
{
return span->trunk_type;
@ -2201,19 +1986,107 @@ FT_DECLARE(ftdm_bool_t) ftdm_channel_call_check_done(const ftdm_channel_t *ftdmc
FT_DECLARE(ftdm_status_t) _ftdm_channel_call_hold(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan)
{
ftdm_status_t status;
ftdm_channel_lock(ftdmchan);
ftdm_set_flag(ftdmchan, FTDM_CHANNEL_HOLD);
ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_DIALTONE, 0);
status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_DIALTONE, 0);
ftdm_channel_unlock(ftdmchan);
return FTDM_SUCCESS;
return status;
}
FT_DECLARE(ftdm_status_t) _ftdm_channel_call_unhold(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan)
{
ftdm_status_t status;
ftdm_channel_lock(ftdmchan);
ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_UP, 0);
status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_UP, 0);
ftdm_channel_unlock(ftdmchan);
return FTDM_SUCCESS;
return status;
}
FT_DECLARE(void) ftdm_ack_indication(ftdm_channel_t *fchan, ftdm_channel_indication_t indication, ftdm_status_t status)
{
ftdm_sigmsg_t msg;
ftdm_log_chan(fchan, FTDM_LOG_DEBUG, "Acknowledging indication %s in state %s (rc = %d)\n",
ftdm_channel_indication2str(indication), ftdm_channel_state2str(fchan->state), status);
ftdm_clear_flag(fchan, FTDM_CHANNEL_IND_ACK_PENDING);
memset(&msg, 0, sizeof(msg));
msg.channel = fchan;
msg.event_id = FTDM_SIGEVENT_INDICATION_COMPLETED;
msg.ev_data.indication_completed.indication = indication;
msg.ev_data.indication_completed.status = status;
ftdm_span_send_signal(fchan->span, &msg);
}
/*! \brief Answer call without locking the channel. The caller must have locked first
* \note This function was added because ftdm_channel_call_indicate needs to answer the call
* when its already locking the channel, ftdm_channel_set_state cannot be called with the same
* lock locked once or more (recursive lock) and wait for the result */
static ftdm_status_t _ftdm_channel_call_answer_nl(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan)
{
ftdm_status_t status = FTDM_SUCCESS;
if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
status = FTDM_EINVAL;
goto done;
}
if (ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING) {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Ignoring answer because the call is already TERMINATING\n");
status = FTDM_ECANCELED;
goto done;
}
if (!ftdm_test_flag(ftdmchan->span, FTDM_SPAN_USE_SKIP_STATES)) {
/* We will fail RFC's if we not skip states, but some modules apart from ftmod_sangoma_isdn
* expect the call to always to go PROGRESS and PROGRESS MEDIA state before going to UP, so
* use FTDM_SPAN_USE_SKIP_STATES for now while we update the sig modules */
if (ftdmchan->state < FTDM_CHANNEL_STATE_PROGRESS) {
status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS, 1);
if (status != FTDM_SUCCESS) {
status = FTDM_ECANCELED;
goto done;
}
}
/* set state unlocks the channel so we need to re-confirm that the channel hasn't gone to hell */
if (ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING) {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Ignoring answer because the call has moved to TERMINATING while we're moving to PROGRESS\n");
status = FTDM_ECANCELED;
goto done;
}
if (ftdmchan->state < FTDM_CHANNEL_STATE_PROGRESS_MEDIA) {
status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, 1);
if (status != FTDM_SUCCESS) {
status = FTDM_ECANCELED;
goto done;
}
}
/* set state unlocks the channel so we need to re-confirm that the channel hasn't gone to hell */
if (ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING) {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Ignoring answer because the call has moved to TERMINATING while we're moving to UP\n");
status = FTDM_ECANCELED;
goto done;
}
}
status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_UP, 1);
if (status != FTDM_SUCCESS) {
status = FTDM_ECANCELED;
goto done;
}
done:
return status;
}
FT_DECLARE(ftdm_status_t) _ftdm_channel_call_answer(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan)
@ -2222,55 +2095,17 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_answer(const char *file, const char
ftdm_channel_lock(ftdmchan);
if (ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING) {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Ignoring answer because the call is already TERMINATING\n");
goto done;
}
ftdm_set_flag(ftdmchan, FTDM_CHANNEL_ANSWERED);
ftdm_set_flag(ftdmchan, FTDM_CHANNEL_PROGRESS);
ftdm_set_flag(ftdmchan, FTDM_CHANNEL_MEDIA);
if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
goto done;
}
if (!ftdm_test_flag(ftdmchan->span, FTDM_SPAN_USE_SKIP_STATES)) {
/* We will fail RFC's if we not skip states, but some modules apart from ftmod_sangoma_isdn
* expect the call to always to go PROGRESS and PROGRESS MEDIA state before going to UP, so
* use FTDM_SPAN_USE_SKIP_STATESfor now while we update the sig modules */
if (ftdmchan->state < FTDM_CHANNEL_STATE_PROGRESS) {
ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS, 1);
}
/* set state unlocks the channel so we need to re-confirm that the channel hasn't gone to hell */
if (ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING) {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Ignoring answer because the call has moved to TERMINATING while we're moving to PROGRESS\n");
goto done;
}
if (ftdmchan->state < FTDM_CHANNEL_STATE_PROGRESS_MEDIA) {
ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, 1);
}
/* set state unlocks the channel so we need to re-confirm that the channel hasn't gone to hell */
if (ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING) {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Ignoring answer because the call has moved to TERMINATING while we're moving to UP\n");
goto done;
}
}
ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_UP, 1);
done:
status = _ftdm_channel_call_answer_nl(file, func, line, ftdmchan);
ftdm_channel_unlock(ftdmchan);
return status;
}
/* lock must be acquired by the caller! */
static ftdm_status_t call_hangup(ftdm_channel_t *chan, const char *file, const char *func, int line)
static ftdm_status_t _ftdm_channel_call_hangup_nl(ftdm_channel_t *chan, const char *file, const char *func, int line)
{
ftdm_status_t status = FTDM_SUCCESS;
ftdm_set_flag(chan, FTDM_CHANNEL_USER_HANGUP);
ftdm_set_echocancel_call_end(chan);
@ -2283,7 +2118,7 @@ static ftdm_status_t call_hangup(ftdm_channel_t *chan, const char *file, const c
if (chan->hangup_timer) {
ftdm_sched_cancel_timer(globals.timingsched, chan->hangup_timer);
}
ftdm_channel_set_state(file, func, line, chan, FTDM_CHANNEL_STATE_HANGUP, 1);
status = ftdm_channel_set_state(file, func, line, chan, FTDM_CHANNEL_STATE_HANGUP, 1);
} else {
/* the signaling stack did not touch the state,
* core is responsible from clearing flags and stuff, however, because ftmod_analog
@ -2296,28 +2131,34 @@ static ftdm_status_t call_hangup(ftdm_channel_t *chan, const char *file, const c
ftdm_channel_close(&chan);
}
}
return FTDM_SUCCESS;
return status;
}
FT_DECLARE(ftdm_status_t) _ftdm_channel_call_hangup_with_cause(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_call_cause_t cause)
{
ftdm_status_t status = FTDM_SUCCESS;
ftdm_channel_lock(ftdmchan);
ftdmchan->caller_data.hangup_cause = cause;
call_hangup(ftdmchan, file, func, line);
status = _ftdm_channel_call_hangup_nl(ftdmchan, file, func, line);
ftdm_channel_unlock(ftdmchan);
return FTDM_SUCCESS;
return status;
}
FT_DECLARE(ftdm_status_t) _ftdm_channel_call_hangup(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan)
{
ftdm_status_t status = FTDM_SUCCESS;
ftdm_channel_lock(ftdmchan);
ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_NORMAL_CLEARING;
call_hangup(ftdmchan, file, func, line);
status = _ftdm_channel_call_hangup_nl(ftdmchan, file, func, line);
ftdm_channel_unlock(ftdmchan);
return FTDM_SUCCESS;
return status;
}
FT_DECLARE(const char *) ftdm_channel_get_last_error(const ftdm_channel_t *ftdmchan)
@ -2335,42 +2176,6 @@ FT_DECLARE(ftdm_caller_data_t *) ftdm_channel_get_caller_data(ftdm_channel_t *ft
return &ftdmchan->caller_data;
}
FT_DECLARE(int) ftdm_channel_get_state(const ftdm_channel_t *ftdmchan)
{
int state;
ftdm_channel_lock(ftdmchan);
state = ftdmchan->state;
ftdm_channel_unlock(ftdmchan);
return state;
}
FT_DECLARE(const char *) ftdm_channel_get_state_str(const ftdm_channel_t *ftdmchan)
{
const char *state;
ftdm_channel_lock(ftdmchan);
state = ftdm_channel_state2str(ftdmchan->state);
ftdm_channel_unlock(ftdmchan);
return state;
}
FT_DECLARE(int) ftdm_channel_get_last_state(const ftdm_channel_t *ftdmchan)
{
int last_state;
ftdm_channel_lock(ftdmchan);
last_state = ftdmchan->last_state;
ftdm_channel_unlock(ftdmchan);
return last_state;
}
FT_DECLARE(const char *) ftdm_channel_get_last_state_str(const ftdm_channel_t *ftdmchan)
{
const char *state;
ftdm_channel_lock(ftdmchan);
state = ftdm_channel_state2str(ftdmchan->last_state);
ftdm_channel_unlock(ftdmchan);
return state;
}
FT_DECLARE(ftdm_channel_t *) ftdm_span_get_channel(const ftdm_span_t *span, uint32_t chanid)
{
ftdm_channel_t *chan;
@ -2402,13 +2207,48 @@ FT_DECLARE(uint32_t) ftdm_channel_get_ph_span_id(const ftdm_channel_t *ftdmchan)
return id;
}
/*
* Every user requested indication *MUST* be acknowledged with the proper status (ftdm_status_t)
* However, if the indication fails before we notify the signaling stack, we don't need to ack
* but if we already notified the signaling stack about the indication, the signaling stack is
* responsible for the acknowledge. Bottom line is, whenever this function returns FTDM_SUCCESS
* someone *MUST* acknowledge the indication, either the signaling stack, this function or the core
* at some later point
* */
FT_DECLARE(ftdm_status_t) _ftdm_channel_call_indicate(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_channel_indication_t indication)
{
ftdm_status_t status = FTDM_SUCCESS;
ftdm_assert_return(ftdmchan, FTDM_FAIL, "Null channel\n");
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Indicating %s in state %s\n",
ftdm_channel_indication2str(indication), ftdm_channel_state2str(ftdmchan->state));
ftdm_channel_lock(ftdmchan);
if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_IND_ACK_PENDING)) {
ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Cannot indicate %s in channel with indication %s still pending in state %s\n",
ftdm_channel_indication2str(indication),
ftdm_channel_indication2str(ftdmchan->indication),
ftdm_channel_state2str(ftdmchan->state));
status = FTDM_EBUSY;
goto done;
}
ftdmchan->indication = indication;
ftdm_set_flag(ftdmchan, FTDM_CHANNEL_IND_ACK_PENDING);
if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Cannot indicate %s in outgoing channel in state %s\n",
ftdm_channel_indication2str(indication), ftdm_channel_state2str(ftdmchan->state));
status = FTDM_EINVAL;
goto done;
}
if (ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING) {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Ignoring answer because the call has moved to TERMINATING while we're moving to PROGRESS\n");
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Ignoring indication %s because the call is in %s state\n",
ftdm_channel_indication2str(indication), ftdm_channel_state2str(ftdmchan->state));
status = FTDM_ECANCELED;
goto done;
}
@ -2416,55 +2256,52 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_indicate(const char *file, const ch
/* FIXME: ring and busy cannot be used with all signaling stacks
* (particularly isdn stacks I think, we should emulate or just move to hangup with busy cause) */
case FTDM_CHANNEL_INDICATE_RINGING:
ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_RINGING, 1);
status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_RINGING, 1);
break;
case FTDM_CHANNEL_INDICATE_BUSY:
ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_BUSY, 1);
status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_BUSY, 1);
break;
case FTDM_CHANNEL_INDICATE_PROCEED:
if (ftdm_test_flag(ftdmchan->span, FTDM_SPAN_USE_PROCEED_STATE)) {
if (ftdmchan->state == FTDM_CHANNEL_STATE_RING) {
ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROCEED, 1);
}
if (!ftdm_test_flag(ftdmchan->span, FTDM_SPAN_USE_PROCEED_STATE)) {
ftdm_ack_indication(ftdmchan, indication, status);
goto done;
}
status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROCEED, 1);
break;
case FTDM_CHANNEL_INDICATE_PROGRESS:
if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
ftdm_set_flag(ftdmchan, FTDM_CHANNEL_PROGRESS);
} else {
ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS, 1);
}
status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS, 1);
break;
case FTDM_CHANNEL_INDICATE_PROGRESS_MEDIA:
if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
ftdm_set_flag(ftdmchan, FTDM_CHANNEL_PROGRESS);
ftdm_set_flag(ftdmchan, FTDM_CHANNEL_MEDIA);
} else {
if (!ftdm_test_flag(ftdmchan->span, FTDM_SPAN_USE_SKIP_STATES)) {
if (ftdmchan->state < FTDM_CHANNEL_STATE_PROGRESS) {
ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS, 1);
}
/* set state unlocks the channel so we need to re-confirm that the channel hasn't gone to hell */
if (ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING) {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Ignoring answer because the call has moved to TERMINATING while we're moving to PROGRESS\n");
if (!ftdm_test_flag(ftdmchan->span, FTDM_SPAN_USE_SKIP_STATES)) {
if (ftdmchan->state < FTDM_CHANNEL_STATE_PROGRESS) {
status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS, 1);
if (status != FTDM_SUCCESS) {
goto done;
}
}
ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, 1);
/* set state unlocks the channel so we need to re-confirm that the channel hasn't gone to hell */
if (ftdmchan->state == FTDM_CHANNEL_STATE_TERMINATING) {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Ignoring progress media because the call is terminating\n");
goto done;
}
}
status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, 1);
break;
case FTDM_CHANNEL_INDICATE_ANSWER:
/* _ftdm_channel_call_answer takes care of the indication ack */
status = _ftdm_channel_call_answer_nl(file, func, line, ftdmchan);
break;
default:
ftdm_log(file, func, line, FTDM_LOG_LEVEL_WARNING, "Do not know how to indicate %d\n", indication);
status = FTDM_FAIL;
status = FTDM_EINVAL;
break;
}
done:
ftdm_channel_unlock(ftdmchan);
return FTDM_SUCCESS;
return status;
}
FT_DECLARE(ftdm_status_t) _ftdm_channel_call_send_msg(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_sigmsg_t *sigmsg)
@ -2636,6 +2473,7 @@ static ftdm_status_t ftdm_channel_done(ftdm_channel_t *ftdmchan)
ftdmchan->init_state = FTDM_CHANNEL_STATE_DOWN;
ftdmchan->state = FTDM_CHANNEL_STATE_DOWN;
ftdmchan->state_status = FTDM_STATE_STATUS_COMPLETED;
ftdm_channel_command(ftdmchan, FTDM_COMMAND_DISABLE_DEBUG_DTMF, NULL);
ftdm_channel_command(ftdmchan, FTDM_COMMAND_DISABLE_INPUT_DUMP, NULL);
@ -4397,7 +4235,7 @@ static void print_core_usage(ftdm_stream_handle_t *stream)
stream->write_function(stream,
"--------------------------------------------------------------------------------\n"
"ftdm core state [!]<state_name> - List all channels in or not in the given state\n"
"ftdm core flag <flag-int-value> - List all channels with the fiven flag value set\n"
"ftdm core flag <flag-int-value> - List all channels with the given flag value set\n"
"ftdm core calls - List all known calls to the FreeTDM core\n"
"--------------------------------------------------------------------------------\n");
}
@ -5195,7 +5033,7 @@ FT_DECLARE(ftdm_status_t) ftdm_configure_span_signaling(ftdm_span_t *span, const
ftdm_assert_return(parameters != NULL, FTDM_FAIL, "No parameters");
if (!span->chan_count) {
ftdm_log(FTDM_LOG_WARNING, "Cannot configure signaling on span with no channels\n");
ftdm_log(FTDM_LOG_WARNING, "Cannot configure signaling on span %s with no channels\n", span->name);
return FTDM_FAIL;
}
@ -5455,7 +5293,7 @@ static void execute_safety_hangup(void *data)
fchan->hangup_timer = 0;
if (fchan->state == FTDM_CHANNEL_STATE_TERMINATING) {
ftdm_log_chan(fchan, FTDM_LOG_CRIT, "Forcing hangup since the user did not confirmed our hangup after %dms\n", FORCE_HANGUP_TIMER);
call_hangup(fchan, __FILE__, __FUNCTION__, __LINE__);
_ftdm_channel_call_hangup_nl(fchan, __FILE__, __FUNCTION__, __LINE__);
} else {
ftdm_log_chan(fchan, FTDM_LOG_CRIT, "Not performing safety hangup, channel state is %s\n", ftdm_channel_state2str(fchan->state));
}
@ -6069,63 +5907,6 @@ FT_DECLARE(char *) ftdm_strndup(const char *str, ftdm_size_t inlen)
return new;
}
static void write_history_entry(const ftdm_channel_t *fchan, ftdm_stream_handle_t *stream, int i, ftdm_time_t *prevtime)
{
char func[255];
char line[255];
char states[255];
const char *filename = NULL;
snprintf(states, sizeof(states), "%-5.15s => %-5.15s", ftdm_channel_state2str(fchan->history[i].last_state), ftdm_channel_state2str(fchan->history[i].state));
snprintf(func, sizeof(func), "[%s]", fchan->history[i].func);
filename = strrchr(fchan->history[i].file, *FTDM_PATH_SEPARATOR);
if (!filename) {
filename = fchan->history[i].file;
} else {
filename++;
}
if (!(*prevtime)) {
*prevtime = fchan->history[i].time;
}
snprintf(line, sizeof(func), "[%s:%d]", filename, fchan->history[i].line);
stream->write_function(stream, "%-30.30s %-30.30s %-30.30s %lums\n", states, func, line, (fchan->history[i].time - *prevtime));
*prevtime = fchan->history[i].time;
}
FT_DECLARE(char *) ftdm_channel_get_history_str(const ftdm_channel_t *fchan)
{
uint8_t i = 0;
ftdm_time_t currtime = 0;
ftdm_time_t prevtime = 0;
ftdm_stream_handle_t stream = { 0 };
FTDM_STANDARD_STREAM(stream);
if (!fchan->history[0].file) {
stream.write_function(&stream, "-- No state history --\n");
return stream.data;
}
stream.write_function(&stream, "%-30.30s %-30.30s %-30.30s %s",
"-- States --", "-- Function --", "-- Location --", "-- Time Offset --\n");
for (i = fchan->hindex; i < ftdm_array_len(fchan->history); i++) {
if (!fchan->history[i].file) {
break;
}
write_history_entry(fchan, &stream, i, &prevtime);
}
for (i = 0; i < fchan->hindex; i++) {
write_history_entry(fchan, &stream, i, &prevtime);
}
currtime = ftdm_current_time_in_ms();
stream.write_function(&stream, "\nTime since last state change: %lums\n", (currtime - prevtime));
return stream.data;
}
static ftdm_status_t ftdm_call_set_call_id(ftdm_channel_t *fchan, ftdm_caller_data_t *caller_data)
{
uint32_t current_call_id;

View File

@ -1,692 +0,0 @@
/*
* ftdm_m3ua.c
* freetdm
*
* Created by Shane Burrell on 4/3/08.
* Copyright 2008 Shane Burrell. All rights reserved.
*
*
* Copyright (c) 2007, Anthony Minessale II, Nenad Corbic *
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of the original author; nor the names of any contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "freetdm.h"
#include "m3ua_client.h"
#include "ftdm_m3ua.h"
#define MAX_REQ_ID MAX_PENDING_CALLS
typedef uint16_t m3ua_request_id_t;
typedef enum {
BST_FREE,
BST_WAITING,
BST_READY,
BST_FAIL
} m3ua_request_status_t;
typedef struct {
m3ua_request_status_t status;
m3uac_event_t event;
ftdm_span_t *span;
ftdm_channel_t *ftdmchan;
} m3ua_request_t;
struct general_config {
uint32_t region;
};
typedef struct general_config general_config_t;
struct m3ua_channel_profile {
char name[80];
int cust_span;
unsigned char opc[3];
unsigned char dpc[3];
int local_ip[4];
int local_port;
int remote_ip[4];
int remote_port;
int m3ua_mode;
};
typedef struct m3ua_channel_profile m3ua_channel_profile_t;
static struct {
ftdm_hash_t *profile_hash;
general_config_t general_config;
} globals;
struct m3ua_span_data {
uint32_t boardno;
uint32_t flags;
};
typedef struct m3ua_span_data m3ua_span_data_t;
struct m3ua_chan_data {
ftdm_buffer_t *digit_buffer;
ftdm_mutex_t *digit_mutex;
ftdm_size_t dtmf_len;
uint32_t flags;
uint32_t hdlc_bytes;
};
typedef struct m3ua_chan_data m3ua_chan_data_t;
static ftdm_mutex_t *request_mutex = NULL;
static ftdm_mutex_t *signal_mutex = NULL;
static uint8_t req_map[MAX_REQ_ID+1] = { 0 };
static void release_request_id(m3ua_request_id_t r)
{
ftdm_mutex_lock(request_mutex);
req_map[r] = 0;
ftdm_mutex_unlock(request_mutex);
}
/*static m3ua_request_id_t next_request_id(void)
{
m3ua_request_id_t r = 0;
int ok = 0;
while(!ok) {
ftdm_mutex_lock(request_mutex);
for (r = 1; r <= MAX_REQ_ID; r++) {
if (!req_map[r]) {
ok = 1;
req_map[r] = 1;
break;
}
}
ftdm_mutex_unlock(request_mutex);
if (!ok) {
ftdm_sleep(5);
}
}
return r;
}
*/
static __inline__ void state_advance(ftdm_channel_t *ftdmchan)
{
m3ua_data_t *m3ua_data = ftdmchan->span->signal_data;
m3uac_connection_t *mcon = &m3ua_data->mcon;
ftdm_sigmsg_t sig;
ftdm_status_t status;
ftdm_log(FTDM_LOG_DEBUG, "%d:%d STATE [%s]\n", ftdmchan->span_id, ftdmchan->chan_id, ftdm_channel_state2str(ftdmchan->state));
memset(&sig, 0, sizeof(sig));
sig.chan_id = ftdmchan->chan_id;
sig.span_id = ftdmchan->span_id;
sig.channel = ftdmchan;
switch (ftdmchan->state) {
case FTDM_CHANNEL_STATE_DOWN:
{
if (ftdmchan->extra_id) {
release_request_id((m3ua_request_id_t)ftdmchan->extra_id);
ftdmchan->extra_id = 0;
}
ftdm_channel_close(&ftdmchan);
}
break;
case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
case FTDM_CHANNEL_STATE_PROGRESS:
{
if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
sig.event_id = FTDM_SIGEVENT_PROGRESS_MEDIA;
if ((status = m3ua_data->signal_cb(&sig) != FTDM_SUCCESS)) {
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP);
}
} else {
m3uac_exec_command(mcon,
ftdmchan->physical_span_id-1,
ftdmchan->physical_chan_id-1,
0,
SIGBOOST_EVENT_CALL_START_ACK,
0);
}
}
break;
case FTDM_CHANNEL_STATE_RING:
{
if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
sig.event_id = FTDM_SIGEVENT_START;
if ((status = m3ua_data->signal_cb(&sig) != FTDM_SUCCESS)) {
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP);
}
}
}
break;
case FTDM_CHANNEL_STATE_RESTART:
{
if (ftdmchan->last_state != FTDM_CHANNEL_STATE_HANGUP && ftdmchan->last_state != FTDM_CHANNEL_STATE_DOWN) {
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP);
} else {
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
}
}
break;
case FTDM_CHANNEL_STATE_UP:
{
if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
sig.event_id = FTDM_SIGEVENT_UP;
if ((status = m3ua_data->signal_cb(&sig) != FTDM_SUCCESS)) {
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP);
}
} else {
if (!(ftdm_test_flag(ftdmchan, FTDM_CHANNEL_PROGRESS) || ftdm_test_flag(ftdmchan, FTDM_CHANNEL_MEDIA))) {
m3uac_exec_command(mcon,
ftdmchan->physical_span_id-1,
ftdmchan->physical_chan_id-1,
0,
SIGBOOST_EVENT_CALL_START_ACK,
0);
}
m3uac_exec_command(mcon,
ftdmchan->physical_span_id-1,
ftdmchan->physical_chan_id-1,
0,
SIGBOOST_EVENT_CALL_ANSWERED,
0);
}
}
break;
case FTDM_CHANNEL_STATE_DIALING:
{
}
break;
case FTDM_CHANNEL_STATE_HANGUP_COMPLETE:
{
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
}
break;
case FTDM_CHANNEL_STATE_HANGUP:
{
if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_ANSWERED) || ftdm_test_flag(ftdmchan, FTDM_CHANNEL_PROGRESS) || ftdm_test_flag(ftdmchan, FTDM_CHANNEL_MEDIA)) {
m3uac_exec_command(mcon,
ftdmchan->physical_span_id-1,
ftdmchan->physical_chan_id-1,
0,
SIGBOOST_EVENT_CALL_STOPPED,
ftdmchan->caller_data.hangup_cause);
} else {
m3uac_exec_command(mcon,
ftdmchan->physical_span_id-1,
ftdmchan->physical_chan_id-1,
0,
SIGBOOST_EVENT_CALL_START_NACK,
ftdmchan->caller_data.hangup_cause);
}
}
break;
case FTDM_CHANNEL_STATE_CANCEL:
{
sig.event_id = FTDM_SIGEVENT_STOP;
status = m3ua_data->signal_cb(&sig);
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
m3uac_exec_command(mcon,
ftdmchan->physical_span_id-1,
ftdmchan->physical_chan_id-1,
0,
SIGBOOST_EVENT_CALL_START_NACK_ACK,
0);
}
break;
case FTDM_CHANNEL_STATE_TERMINATING:
{
sig.event_id = FTDM_SIGEVENT_STOP;
status = m3ua_data->signal_cb(&sig);
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
m3uac_exec_command(mcon,
ftdmchan->physical_span_id-1,
ftdmchan->physical_chan_id-1,
0,
SIGBOOST_EVENT_CALL_STOPPED_ACK,
0);
}
break;
default:
break;
}
}
static __inline__ void check_state(ftdm_span_t *span)
{
if (ftdm_test_flag(span, FTDM_SPAN_STATE_CHANGE)) {
uint32_t j;
ftdm_clear_flag_locked(span, FTDM_SPAN_STATE_CHANGE);
for(j = 1; j <= span->chan_count; j++) {
if (ftdm_test_flag((&span->channels[j]), FTDM_CHANNEL_STATE_CHANGE)) {
ftdm_clear_flag_locked((&span->channels[j]), FTDM_CHANNEL_STATE_CHANGE);
state_advance(&span->channels[j]);
ftdm_channel_complete_state(&span->channels[j]);
}
}
}
}
static int parse_ss7_event(ftdm_span_t *span, m3uac_connection_t *mcon, m3uac_event_t *event)
{
ftdm_mutex_lock(signal_mutex);
if (!ftdm_running()) {
ftdm_log(FTDM_LOG_WARNING, "System is shutting down.\n");
goto end;
}
if (ftdm_test_flag(span, FTDM_SPAN_SUSPENDED) &&
event->event_id != SIGBOOST_EVENT_SYSTEM_RESTART_ACK && event->event_id != SIGBOOST_EVENT_HEARTBEAT) {
ftdm_log(FTDM_LOG_WARNING,
"INVALID EVENT: %s:(%X) [w%dg%d] Rc=%i CSid=%i Seq=%i Cd=[%s] Ci=[%s]\n",
m3uac_event_id_name(event->event_id),
event->event_id,
event->span+1,
event->chan+1,
event->release_cause,
event->call_setup_id,
event->fseqno,
(event->called_number_digits_count ? (char *) event->called_number_digits : "N/A"),
(event->calling_number_digits_count ? (char *) event->calling_number_digits : "N/A")
);
goto end;
}
ftdm_log(FTDM_LOG_DEBUG,
"RX EVENT: %s:(%X) [w%dg%d] Rc=%i CSid=%i Seq=%i Cd=[%s] Ci=[%s]\n",
m3uac_event_id_name(event->event_id),
event->event_id,
event->span+1,
event->chan+1,
event->release_cause,
event->call_setup_id,
event->fseqno,
(event->called_number_digits_count ? (char *) event->called_number_digits : "N/A"),
(event->calling_number_digits_count ? (char *) event->calling_number_digits : "N/A")
);
switch(event->event_id) {
case SIGBOOST_EVENT_CALL_START:
//handle_call_start(span, mcon, event);
break;
case SIGBOOST_EVENT_CALL_STOPPED:
//handle_call_stop(span, mcon, event);
break;
case SIGBOOST_EVENT_CALL_START_ACK:
//handle_call_start_ack(mcon, event);
break;
case SIGBOOST_EVENT_CALL_START_NACK:
//handle_call_start_nack(span, mcon, event);
break;
case SIGBOOST_EVENT_CALL_ANSWERED:
//handle_call_answer(span, mcon, event);
break;
case SIGBOOST_EVENT_HEARTBEAT:
//handle_heartbeat(mcon, event);
break;
case SIGBOOST_EVENT_CALL_STOPPED_ACK:
case SIGBOOST_EVENT_CALL_START_NACK_ACK:
//handle_call_done(span, mcon, event);
break;
case SIGBOOST_EVENT_INSERT_CHECK_LOOP:
//handle_call_loop_start(event);
break;
case SIGBOOST_EVENT_REMOVE_CHECK_LOOP:
//handle_call_stop(event);
break;
case SIGBOOST_EVENT_SYSTEM_RESTART_ACK:
//handle_restart_ack(mcon, span, event);
break;
case SIGBOOST_EVENT_AUTO_CALL_GAP_ABATE:
//handle_gap_abate(event);
break;
default:
ftdm_log(FTDM_LOG_WARNING, "No handler implemented for [%s]\n", m3uac_event_id_name(event->event_id));
break;
}
end:
ftdm_mutex_unlock(signal_mutex);
return 0;
}
static FIO_CONFIGURE_FUNCTION(m3ua_configure)
{
m3ua_channel_profile_t *profile = NULL;
int ok = 1;
if (!(profile = (m3ua_channel_profile_t *) hashtable_search(globals.profile_hash, (char *)category))) {
profile = ftdm_malloc(sizeof(*profile));
memset(profile, 0, sizeof(*profile));
ftdm_set_string(profile->name, category);
hashtable_insert(globals.profile_hash, (void *)profile->name, profile);
ftdm_log(FTDM_LOG_INFO, "creating profile [%s]\n", category);
}
// ftdm_set_string(m3ua_data->mcon. cfg.local_ip, local_ip);
if (!strcasecmp(var, "local_sctp_port")) {
profile->local_port = 30000 ;
profile->remote_port = 30000;
profile->cust_span++;
}
ok = 1;
if (ok) {
ftdm_log(FTDM_LOG_INFO, "setting param [%s]=[%s] for profile [%s]\n", var, val, category);
} else {
ftdm_log(FTDM_LOG_ERROR, "unknown param [%s]\n", var);
}
return FTDM_SUCCESS;
}
static FIO_CONFIGURE_SPAN_FUNCTION(m3ua_configure_span)
{
return FTDM_FAIL;
}
static FIO_OPEN_FUNCTION(m3ua_open)
{
return FTDM_FAIL;
}
static FIO_CLOSE_FUNCTION(m3ua_close)
{
return FTDM_FAIL;
}
/*static FIO_SET_INTERVAL_FUNCTION(m3ua_set_interval)
{
return 0;
}*/
static FIO_WAIT_FUNCTION(m3ua_wait)
{
return FTDM_FAIL;
}
static FIO_READ_FUNCTION(m3ua_read)
{
return FTDM_FAIL;
}
static FIO_WRITE_FUNCTION(m3ua_write)
{
return FTDM_FAIL;
}
static FIO_COMMAND_FUNCTION(m3ua_command)
{
return FTDM_FAIL;
}
static FIO_SPAN_POLL_EVENT_FUNCTION(m3ua_poll_event)
{
return FTDM_FAIL;
}
static FIO_SPAN_NEXT_EVENT_FUNCTION(m3ua_next_event)
{
return FTDM_FAIL;
}
static FIO_SPAN_DESTROY_FUNCTION(m3ua_span_destroy)
{
m3ua_span_data_t *span_data = (m3ua_span_data_t *) span->io_data;
if (span_data) {
ftdm_safe_free(span_data);
}
return FTDM_SUCCESS;
}
static FIO_CHANNEL_DESTROY_FUNCTION(m3ua_channel_destroy)
{
m3ua_chan_data_t *chan_data = (m3ua_chan_data_t *) ftdmchan->io_data;
m3ua_span_data_t *span_data = (m3ua_span_data_t *) ftdmchan->span->io_data;
if (!chan_data) {
return FTDM_FAIL;
}
ftdm_mutex_destroy(&chan_data->digit_mutex);
ftdm_buffer_destroy(&chan_data->digit_buffer);
ftdm_safe_free(chan_data);
if (span_data) {
ftdm_safe_free(span_data);
}
return FTDM_SUCCESS;
}
static FIO_GET_ALARMS_FUNCTION(m3ua_get_alarms)
{
return FTDM_FAIL;
}
static ftdm_io_interface_t m3ua_interface;
ftdm_status_t m3ua_init(ftdm_io_interface_t **zint)
{
assert(zint != NULL);
memset(&m3ua_interface, 0, sizeof(m3ua_interface));
m3ua_interface.name = "m3ua";
m3ua_interface.configure = m3ua_configure;
m3ua_interface.configure_span = m3ua_configure_span;
m3ua_interface.open = m3ua_open;
m3ua_interface.close = m3ua_close;
m3ua_interface.wait = m3ua_wait;
m3ua_interface.read = m3ua_read;
m3ua_interface.write = m3ua_write;
m3ua_interface.command = m3ua_command;
m3ua_interface.poll_event = m3ua_poll_event;
m3ua_interface.next_event = m3ua_next_event;
m3ua_interface.channel_destroy = m3ua_channel_destroy;
m3ua_interface.span_destroy = m3ua_span_destroy;
m3ua_interface.get_alarms = m3ua_get_alarms;
*zint = &m3ua_interface;
return FTDM_FAIL;
}
ftdm_status_t m3ua_destroy(void)
{
return FTDM_FAIL;
}
static void *m3ua_run(ftdm_thread_t *me, void *obj)
{
ftdm_span_t *span = (ftdm_span_t *) obj;
m3ua_data_t *m3ua_data = span->signal_data;
m3uac_connection_t *mcon, *pcon;
uint32_t ms = 10, too_long = 60000;
m3ua_data->pcon = m3ua_data->mcon;
if (m3uac_connection_open(&m3ua_data->mcon,
m3ua_data->mcon.cfg.local_ip,
m3ua_data->mcon.cfg.local_port,
m3ua_data->mcon.cfg.remote_ip,
m3ua_data->mcon.cfg.remote_port) < 0) {
ftdm_log(FTDM_LOG_DEBUG, "Error: Opening MCON Socket [%d] %s\n", m3ua_data->mcon.socket, strerror(errno));
goto end;
}
if (m3uac_connection_open(&m3ua_data->pcon,
m3ua_data->pcon.cfg.local_ip,
++m3ua_data->pcon.cfg.local_port,
m3ua_data->pcon.cfg.remote_ip,
m3ua_data->pcon.cfg.remote_port) < 0) {
ftdm_log(FTDM_LOG_DEBUG, "Error: Opening PCON Socket [%d] %s\n", m3ua_data->pcon.socket, strerror(errno));
goto end;
}
mcon = &m3ua_data->mcon;
pcon = &m3ua_data->pcon;
top:
//init_outgoing_array();
m3uac_exec_command(mcon,
0,
0,
-1,
SIGBOOST_EVENT_SYSTEM_RESTART,
0);
while (ftdm_test_flag(m3ua_data, FTDM_M3UA_RUNNING)) {
fd_set rfds, efds;
struct timeval tv = { 0, ms * 1000 };
int max, activity, i = 0;
m3uac_event_t *event = NULL;
if (!ftdm_running()) {
m3uac_exec_command(mcon,
0,
0,
-1,
SIGBOOST_EVENT_SYSTEM_RESTART,
0);
break;
}
FD_ZERO(&rfds);
FD_ZERO(&efds);
FD_SET(mcon->socket, &rfds);
FD_SET(mcon->socket, &efds);
FD_SET(pcon->socket, &rfds);
FD_SET(pcon->socket, &efds);
max = ((pcon->socket > mcon->socket) ? pcon->socket : mcon->socket) + 1;
if ((activity = select(max, &rfds, NULL, &efds, &tv)) < 0) {
goto error;
}
if (activity) {
if (FD_ISSET(pcon->socket, &efds) || FD_ISSET(mcon->socket, &efds)) {
goto error;
}
if (FD_ISSET(pcon->socket, &rfds)) {
if ((event = m3uac_connection_readp(pcon, i))) {
parse_ss7_event(span, mcon, event);
} else goto top;
}
if (FD_ISSET(mcon->socket, &rfds)) {
if ((event = m3uac_connection_read(mcon, i))) {
parse_ss7_event(span, mcon, event);
} else goto top;
}
}
check_state(span);
mcon->hb_elapsed += ms;
if (mcon->hb_elapsed >= too_long && (mcon->up || !ftdm_test_flag(span, FTDM_SPAN_SUSPENDED))) {
ftdm_set_state_all(span, FTDM_CHANNEL_STATE_RESTART);
ftdm_set_flag_locked(span, FTDM_SPAN_SUSPENDED);
mcon->up = 0;
ftdm_log(FTDM_LOG_CRIT, "Lost Heartbeat!\n");
}
}
goto end;
error:
ftdm_log(FTDM_LOG_CRIT, "Socket Error!\n");
end:
m3uac_connection_close(&m3ua_data->mcon);
m3uac_connection_close(&m3ua_data->pcon);
ftdm_clear_flag(m3ua_data, FTDM_M3UA_RUNNING);
ftdm_log(FTDM_LOG_DEBUG, "M3UA thread ended.\n");
return NULL;
}
ftdm_status_t m3ua_start(ftdm_span_t *span)
{
m3ua_data_t *m3ua_data = span->signal_data;
ftdm_set_flag(m3ua_data, FTDM_M3UA_RUNNING);
return ftdm_thread_create_detached(m3ua_run, span);
}
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
*/

View File

@ -0,0 +1,485 @@
/*
* Copyright (c) 2010, Sangoma Technologies
* Moises Silva <moy@sangoma.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of the original author; nor the names of any contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "private/ftdm_core.h"
FTDM_ENUM_NAMES(CHANNEL_STATE_NAMES, CHANNEL_STATE_STRINGS)
FTDM_STR2ENUM(ftdm_str2ftdm_channel_state, ftdm_channel_state2str, ftdm_channel_state_t, CHANNEL_STATE_NAMES, FTDM_CHANNEL_STATE_INVALID)
FTDM_ENUM_NAMES(CHANNEL_STATE_STATUS_NAMES, CHANNEL_STATE_STATUS_STRINGS)
FTDM_STR2ENUM(ftdm_str2ftdm_state_status, ftdm_state_status2str, ftdm_state_status_t, CHANNEL_STATE_STATUS_NAMES, FTDM_STATE_STATUS_INVALID)
/* This function is only needed for boost and we should get rid of it at the next refactoring */
FT_DECLARE(ftdm_status_t) ftdm_channel_init(ftdm_channel_t *fchan)
{
ftdm_channel_lock(fchan);
if (fchan->init_state != FTDM_CHANNEL_STATE_DOWN) {
ftdm_channel_set_state(__FILE__, __FUNCTION__, __LINE__, fchan, fchan->init_state, 1);
fchan->init_state = FTDM_CHANNEL_STATE_DOWN;
}
ftdm_channel_unlock(fchan);
return FTDM_SUCCESS;
}
FT_DECLARE(ftdm_status_t) _ftdm_channel_complete_state(const char *file, const char *func, int line, ftdm_channel_t *fchan)
{
uint8_t hindex = 0;
ftdm_time_t diff = 0;
ftdm_channel_state_t state = fchan->state;
if (fchan->state_status == FTDM_STATE_STATUS_COMPLETED) {
ftdm_assert_return(!ftdm_test_flag(fchan, FTDM_CHANNEL_STATE_CHANGE), FTDM_FAIL,
"State change flag set but state is not completed\n");
return FTDM_SUCCESS;
}
ftdm_clear_flag(fchan, FTDM_CHANNEL_STATE_CHANGE);
if (state == FTDM_CHANNEL_STATE_PROGRESS) {
ftdm_set_flag(fchan, FTDM_CHANNEL_PROGRESS);
} else if (state == FTDM_CHANNEL_STATE_UP) {
ftdm_set_flag(fchan, FTDM_CHANNEL_PROGRESS);
ftdm_set_flag(fchan, FTDM_CHANNEL_MEDIA);
ftdm_set_flag(fchan, FTDM_CHANNEL_ANSWERED);
} else if (state == FTDM_CHANNEL_STATE_PROGRESS_MEDIA) {
ftdm_set_flag(fchan, FTDM_CHANNEL_PROGRESS);
ftdm_set_flag(fchan, FTDM_CHANNEL_MEDIA);
}
/* if there is a pending ack for an indication
* MAINTENANCE WARNING: we're assuming an indication performed
* via state change will involve a single state change
*/
if (ftdm_test_flag(fchan, FTDM_CHANNEL_IND_ACK_PENDING)) {
ftdm_ack_indication(fchan, fchan->indication, FTDM_SUCCESS);
}
hindex = (fchan->hindex == 0) ? (ftdm_array_len(fchan->history) - 1) : (fchan->hindex - 1);
ftdm_assert(!fchan->history[hindex].end_time, "End time should be zero!\n");
fchan->history[hindex].end_time = ftdm_current_time_in_ms();
fchan->state_status = FTDM_STATE_STATUS_COMPLETED;
diff = fchan->history[hindex].end_time - fchan->history[hindex].time;
ftdm_log_chan_ex(fchan, file, func, line, FTDM_LOG_LEVEL_DEBUG, "Completed state change from %s to %s in %llums\n",
ftdm_channel_state2str(fchan->last_state), ftdm_channel_state2str(state), diff);
if (ftdm_test_flag(fchan, FTDM_CHANNEL_BLOCKING)) {
ftdm_clear_flag(fchan, FTDM_CHANNEL_BLOCKING);
ftdm_interrupt_signal(fchan->state_completed_interrupt);
}
return FTDM_SUCCESS;
}
FT_DECLARE(ftdm_status_t) _ftdm_set_state(const char *file, const char *func, int line,
ftdm_channel_t *fchan, ftdm_channel_state_t state)
{
if (fchan->state_status != FTDM_STATE_STATUS_COMPLETED) {
/* the current state is not completed, setting a new state from a signaling module
when the current state is not completed is equivalent to implicitly acknowledging
the current state */
_ftdm_channel_complete_state(file, func, line, fchan);
}
return ftdm_channel_set_state(file, func, line, fchan, state, 0);
}
static int ftdm_parse_state_map(ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, ftdm_state_map_t *state_map)
{
int x = 0, ok = 0;
ftdm_state_direction_t direction = ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND) ? ZSD_OUTBOUND : ZSD_INBOUND;
for(x = 0; x < FTDM_MAP_NODE_SIZE; x++) {
int i = 0, proceed = 0;
if (!state_map->nodes[x].type) {
break;
}
if (state_map->nodes[x].direction != direction) {
continue;
}
if (state_map->nodes[x].check_states[0] == FTDM_ANY_STATE) {
proceed = 1;
} else {
for(i = 0; i < FTDM_MAP_MAX; i++) {
if (state_map->nodes[x].check_states[i] == ftdmchan->state) {
proceed = 1;
break;
}
}
}
if (!proceed) {
continue;
}
for(i = 0; i < FTDM_MAP_MAX; i++) {
ok = (state_map->nodes[x].type == ZSM_ACCEPTABLE);
if (state_map->nodes[x].states[i] == FTDM_END) {
break;
}
if (state_map->nodes[x].states[i] == state) {
ok = !ok;
goto end;
}
}
}
end:
return ok;
}
/* this function MUST be called with the channel lock held. If waitrq == 1, the channel will be unlocked/locked (never call it with waitrq == 1 with an lock recursivity > 1) */
#define DEFAULT_WAIT_TIME 1000
FT_DECLARE(ftdm_status_t) ftdm_channel_set_state(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, int waitrq)
{
ftdm_status_t status;
int ok = 1;
int waitms = DEFAULT_WAIT_TIME;
if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_READY)) {
ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_ERROR, "Ignored state change request from %s to %s, the channel is not ready\n",
ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
return FTDM_FAIL;
}
if (ftdmchan->state_status != FTDM_STATE_STATUS_COMPLETED) {
ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_ERROR,
"Ignored state change request from %s to %s, the previous state change has not been processed yet (status = %s)\n",
ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state),
ftdm_state_status2str(ftdmchan->state_status));
return FTDM_FAIL;
}
if (ftdmchan->state == state) {
ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "Why bother changing state from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
return FTDM_FAIL;
}
if (!ftdmchan->state_completed_interrupt) {
status = ftdm_interrupt_create(&ftdmchan->state_completed_interrupt, FTDM_INVALID_SOCKET);
if (status != FTDM_SUCCESS) {
ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_CRIT,
"Failed to create state change interrupt when moving from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
return status;
}
}
if (ftdmchan->span->state_map) {
ok = ftdm_parse_state_map(ftdmchan, state, ftdmchan->span->state_map);
goto end;
}
/* basic core state validation (by-passed if the signaling module provides a state_map) */
switch(ftdmchan->state) {
case FTDM_CHANNEL_STATE_HANGUP:
case FTDM_CHANNEL_STATE_TERMINATING:
{
ok = 0;
switch(state) {
case FTDM_CHANNEL_STATE_DOWN:
case FTDM_CHANNEL_STATE_BUSY:
case FTDM_CHANNEL_STATE_RESTART:
ok = 1;
break;
default:
break;
}
}
break;
case FTDM_CHANNEL_STATE_UP:
{
ok = 1;
switch(state) {
case FTDM_CHANNEL_STATE_PROGRESS:
case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
case FTDM_CHANNEL_STATE_RING:
ok = 0;
break;
default:
break;
}
}
break;
case FTDM_CHANNEL_STATE_DOWN:
{
ok = 0;
switch(state) {
case FTDM_CHANNEL_STATE_DIALTONE:
case FTDM_CHANNEL_STATE_COLLECT:
case FTDM_CHANNEL_STATE_DIALING:
case FTDM_CHANNEL_STATE_RING:
case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
case FTDM_CHANNEL_STATE_PROGRESS:
case FTDM_CHANNEL_STATE_IDLE:
case FTDM_CHANNEL_STATE_GET_CALLERID:
case FTDM_CHANNEL_STATE_GENRING:
ok = 1;
break;
default:
break;
}
}
break;
case FTDM_CHANNEL_STATE_BUSY:
{
switch(state) {
case FTDM_CHANNEL_STATE_UP:
ok = 0;
break;
default:
break;
}
}
break;
case FTDM_CHANNEL_STATE_RING:
{
switch(state) {
case FTDM_CHANNEL_STATE_UP:
ok = 1;
break;
default:
break;
}
}
break;
default:
break;
}
end:
if (!ok) {
ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "VETO state change from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
goto done;
}
ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_DEBUG, "Changed state from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
ftdmchan->last_state = ftdmchan->state;
ftdmchan->state = state;
ftdmchan->state_status = FTDM_STATE_STATUS_NEW;
ftdmchan->history[ftdmchan->hindex].file = file;
ftdmchan->history[ftdmchan->hindex].func = func;
ftdmchan->history[ftdmchan->hindex].line = line;
ftdmchan->history[ftdmchan->hindex].state = ftdmchan->state;
ftdmchan->history[ftdmchan->hindex].last_state = ftdmchan->last_state;
ftdmchan->history[ftdmchan->hindex].time = ftdm_current_time_in_ms();
ftdmchan->history[ftdmchan->hindex].end_time = 0;
ftdmchan->hindex++;
if (ftdmchan->hindex == ftdm_array_len(ftdmchan->history)) {
ftdmchan->hindex = 0;
}
ftdm_set_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE);
ftdm_mutex_lock(ftdmchan->span->mutex);
ftdm_set_flag(ftdmchan->span, FTDM_SPAN_STATE_CHANGE);
if (ftdmchan->span->pendingchans) {
ftdm_queue_enqueue(ftdmchan->span->pendingchans, ftdmchan);
}
ftdm_mutex_unlock(ftdmchan->span->mutex);
if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_NONBLOCK)) {
/* the channel should not block waiting for state processing */
goto done;
}
if (!waitrq) {
/* no waiting was requested */
goto done;
}
/* let's wait for the state change to be completed by the signaling stack */
ftdm_set_flag(ftdmchan, FTDM_CHANNEL_BLOCKING);
ftdm_mutex_unlock(ftdmchan->mutex);
status = ftdm_interrupt_wait(ftdmchan->state_completed_interrupt, waitms);
ftdm_mutex_lock(ftdmchan->mutex);
if (status != FTDM_SUCCESS) {
ftdm_log_chan_ex(ftdmchan, file, func, line,
FTDM_LOG_LEVEL_WARNING, "state change from %s to %s was most likely not completed after aprox %dms\n",
ftdm_channel_state2str(ftdmchan->last_state), ftdm_channel_state2str(state), DEFAULT_WAIT_TIME);
ok = 0;
goto done;
}
done:
return ok ? FTDM_SUCCESS : FTDM_FAIL;
}
FT_DECLARE(int) ftdm_channel_get_state(const ftdm_channel_t *ftdmchan)
{
int state;
ftdm_channel_lock(ftdmchan);
state = ftdmchan->state;
ftdm_channel_unlock(ftdmchan);
return state;
}
FT_DECLARE(const char *) ftdm_channel_get_state_str(const ftdm_channel_t *ftdmchan)
{
const char *state;
ftdm_channel_lock(ftdmchan);
state = ftdm_channel_state2str(ftdmchan->state);
ftdm_channel_unlock(ftdmchan);
return state;
}
FT_DECLARE(int) ftdm_channel_get_last_state(const ftdm_channel_t *ftdmchan)
{
int last_state;
ftdm_channel_lock(ftdmchan);
last_state = ftdmchan->last_state;
ftdm_channel_unlock(ftdmchan);
return last_state;
}
FT_DECLARE(const char *) ftdm_channel_get_last_state_str(const ftdm_channel_t *ftdmchan)
{
const char *state;
ftdm_channel_lock(ftdmchan);
state = ftdm_channel_state2str(ftdmchan->last_state);
ftdm_channel_unlock(ftdmchan);
return state;
}
static void write_history_entry(const ftdm_channel_t *fchan, ftdm_stream_handle_t *stream, int i, ftdm_time_t *prevtime)
{
char func[255];
char line[255];
char states[255];
const char *filename = NULL;
snprintf(states, sizeof(states), "%-5.15s => %-5.15s", ftdm_channel_state2str(fchan->history[i].last_state), ftdm_channel_state2str(fchan->history[i].state));
snprintf(func, sizeof(func), "[%s]", fchan->history[i].func);
filename = strrchr(fchan->history[i].file, *FTDM_PATH_SEPARATOR);
if (!filename) {
filename = fchan->history[i].file;
} else {
filename++;
}
if (!(*prevtime)) {
*prevtime = fchan->history[i].time;
}
snprintf(line, sizeof(func), "[%s:%d]", filename, fchan->history[i].line);
stream->write_function(stream, "%-30.30s %-30.30s %-30.30s %lums\n", states, func, line, (fchan->history[i].time - *prevtime));
*prevtime = fchan->history[i].time;
}
FT_DECLARE(char *) ftdm_channel_get_history_str(const ftdm_channel_t *fchan)
{
uint8_t i = 0;
ftdm_time_t currtime = 0;
ftdm_time_t prevtime = 0;
ftdm_stream_handle_t stream = { 0 };
FTDM_STANDARD_STREAM(stream);
if (!fchan->history[0].file) {
stream.write_function(&stream, "-- No state history --\n");
return stream.data;
}
stream.write_function(&stream, "%-30.30s %-30.30s %-30.30s %s",
"-- States --", "-- Function --", "-- Location --", "-- Time Offset --\n");
for (i = fchan->hindex; i < ftdm_array_len(fchan->history); i++) {
if (!fchan->history[i].file) {
break;
}
write_history_entry(fchan, &stream, i, &prevtime);
}
for (i = 0; i < fchan->hindex; i++) {
write_history_entry(fchan, &stream, i, &prevtime);
}
currtime = ftdm_current_time_in_ms();
stream.write_function(&stream, "\nTime since last state change: %lums\n", (currtime - prevtime));
return stream.data;
}
FT_DECLARE(ftdm_status_t) ftdm_channel_advance_states(ftdm_channel_t *fchan)
{
ftdm_channel_state_t state;
ftdm_assert_return(fchan->span->state_processor, FTDM_FAIL, "Cannot process states without a state processor!\n");
while (fchan->state_status == FTDM_STATE_STATUS_NEW) {
state = fchan->state;
ftdm_log_chan(fchan, FTDM_LOG_DEBUG, "Executing state processor for %s\n", ftdm_channel_state2str(fchan->state));
fchan->span->state_processor(fchan);
if (state == fchan->state && fchan->state_status == FTDM_STATE_STATUS_NEW) {
/* if the state did not change and is still NEW, the state status must go to PROCESSED
* otherwise we don't touch it since is a new state and the old state was
* already completed implicitly by the state_processor() function via some internal
* call to ftdm_set_state() */
fchan->state_status = FTDM_STATE_STATUS_PROCESSED;
}
}
return FTDM_SUCCESS;
}
FT_DECLARE(int) ftdm_check_state_all(ftdm_span_t *span, ftdm_channel_state_t state)
{
uint32_t j;
for(j = 1; j <= span->chan_count; j++) {
if (span->channels[j]->state != state || ftdm_test_flag(span->channels[j], FTDM_CHANNEL_STATE_CHANGE)) {
return 0;
}
}
return 1;
}
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4:
*/

View File

@ -56,7 +56,11 @@ struct ftdm_interrupt {
/* for generic interruption */
HANDLE event;
#else
/* for generic interruption */
/* In theory we could be using thread conditions for generic interruption,
* however, Linux does not have a primitive like Windows WaitForMultipleObjects
* to wait for both thread condition and file descriptors, therefore we decided
* to use a dummy pipe for generic interruption/condition logic
* */
int readfd;
int writefd;
#endif
@ -243,6 +247,7 @@ FT_DECLARE(ftdm_status_t) _ftdm_mutex_unlock(ftdm_mutex_t *mutex)
FT_DECLARE(ftdm_status_t) ftdm_interrupt_create(ftdm_interrupt_t **ininterrupt, ftdm_socket_t device)
{
ftdm_status_t status = FTDM_SUCCESS;
ftdm_interrupt_t *interrupt = NULL;
#ifndef WIN32
int fds[2];
@ -253,7 +258,7 @@ FT_DECLARE(ftdm_status_t) ftdm_interrupt_create(ftdm_interrupt_t **ininterrupt,
interrupt = ftdm_calloc(1, sizeof(*interrupt));
if (!interrupt) {
ftdm_log(FTDM_LOG_ERROR, "Failed to allocate interrupt memory\n");
return FTDM_FAIL;
return FTDM_ENOMEM;
}
interrupt->device = device;
@ -261,11 +266,13 @@ FT_DECLARE(ftdm_status_t) ftdm_interrupt_create(ftdm_interrupt_t **ininterrupt,
interrupt->event = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!interrupt->event) {
ftdm_log(FTDM_LOG_ERROR, "Failed to allocate interrupt event\n");
status = FTDM_ENOMEM;
goto failed;
}
#else
if (pipe(fds)) {
ftdm_log(FTDM_LOG_ERROR, "Failed to allocate interrupt pipe: %s\n", strerror(errno));
status = FTDM_FAIL;
goto failed;
}
interrupt->readfd = fds[0];
@ -287,7 +294,7 @@ failed:
#endif
ftdm_safe_free(interrupt);
}
return FTDM_FAIL;
return status;
}
#define ONE_BILLION 1000000000

View File

@ -585,8 +585,8 @@ static void *ftdm_analog_channel_run(ftdm_thread_t *me, void *obj)
if (done) {
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_UP);
ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_STATE_CHANGE);
ftdm_clear_flag_locked(ftdmchan->span, FTDM_SPAN_STATE_CHANGE);
ftdm_channel_complete_state(ftdmchan);
ftdmchan->detected_tones[FTDM_TONEMAP_CALLWAITING_ACK] = 0;
}
}
@ -628,7 +628,6 @@ static void *ftdm_analog_channel_run(ftdm_thread_t *me, void *obj)
break;
}
} else {
ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_STATE_CHANGE);
ftdm_clear_flag_locked(ftdmchan->span, FTDM_SPAN_STATE_CHANGE);
ftdm_channel_complete_state(ftdmchan);
indicate = 0;
@ -672,11 +671,12 @@ static void *ftdm_analog_channel_run(ftdm_thread_t *me, void *obj)
!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND) &&
ftdm_test_flag(analog_data, FTDM_ANALOG_ANSWER_POLARITY_REVERSE)) {
ftdm_polarity_t polarity = FTDM_POLARITY_REVERSE;
if (ftdmchan->polarity != FTDM_POLARITY_FORWARD) {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Polarity is already reversed on answer??\n");
} else {
if (ftdmchan->polarity == FTDM_POLARITY_FORWARD) {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Reversing polarity on answer\n");
ftdm_channel_command(ftdmchan, FTDM_COMMAND_SET_POLARITY, &polarity);
} else {
/* the polarity may be already reversed if this is the second time we
* answer (ie, due to 2 calls being on the same line) */
}
}
@ -1036,8 +1036,8 @@ static __inline__ ftdm_status_t process_event(ftdm_span_t *span, ftdm_event_t *e
if (event->channel->state != FTDM_CHANNEL_STATE_DOWN) {
if (event->channel->state == FTDM_CHANNEL_STATE_HANGUP &&
ftdm_test_flag(event->channel, FTDM_CHANNEL_STATE_CHANGE)) {
ftdm_clear_flag(event->channel, FTDM_CHANNEL_STATE_CHANGE);
/* we do not need to process HANGUP since the device also hangup already */
ftdm_channel_complete_state(event->channel);
}
ftdm_set_state(event->channel, FTDM_CHANNEL_STATE_DOWN);
}
@ -1052,8 +1052,8 @@ static __inline__ ftdm_status_t process_event(ftdm_span_t *span, ftdm_event_t *e
{
if (event->channel->state == FTDM_CHANNEL_STATE_CALLWAITING) {
ftdm_set_state(event->channel, FTDM_CHANNEL_STATE_UP);
ftdm_clear_flag(event->channel, FTDM_CHANNEL_STATE_CHANGE);
ftdm_clear_flag(event->channel->span, FTDM_SPAN_STATE_CHANGE);
ftdm_channel_complete_state(event->channel);
event->channel->detected_tones[FTDM_TONEMAP_CALLWAITING_ACK] = 0;
}

View File

@ -355,7 +355,6 @@ static void *ftdm_analog_em_channel_run(ftdm_thread_t *me, void *obj)
break;
}
} else {
ftdm_clear_flag_locked(ftdmchan, FTDM_CHANNEL_STATE_CHANGE);
ftdm_clear_flag_locked(ftdmchan->span, FTDM_SPAN_STATE_CHANGE);
ftdm_channel_complete_state(ftdmchan);
indicate = 0;

View File

@ -497,7 +497,7 @@ static ftdm_state_map_t isdn_state_map = {
* \param ftdmchan Channel to handle
* \note This function MUST be called with the channel locked
*/
static __inline__ void state_advance(ftdm_channel_t *chan)
static ftdm_status_t state_advance(ftdm_channel_t *chan)
{
ftdm_libpri_data_t *isdn_data = chan->span->signal_data;
q931_call *call = (q931_call *)chan->call_data;
@ -511,6 +511,8 @@ static __inline__ void state_advance(ftdm_channel_t *chan)
sig.chan_id = ftdm_channel_get_id(chan);
sig.span_id = ftdm_channel_get_span_id(chan);
sig.channel = chan;
ftdm_channel_complete_state(chan);
switch (ftdm_channel_get_state(chan)) {
case FTDM_CHANNEL_STATE_DOWN:
@ -625,7 +627,7 @@ static __inline__ void state_advance(ftdm_channel_t *chan)
ftdm_channel_get_span_id(chan), ftdm_channel_get_id(chan));
/* TODO: set hangup cause? */
ftdm_set_state_locked(chan, FTDM_CHANNEL_STATE_RESTART);
return;
return FTDM_SUCCESS;
}
ton = caller_data->dnis.type;
@ -708,6 +710,7 @@ static __inline__ void state_advance(ftdm_channel_t *chan)
default:
break;
}
return FTDM_SUCCESS;
}
/**
@ -723,13 +726,8 @@ static __inline__ void check_state(ftdm_span_t *span)
for (j = 1; j <= ftdm_span_get_chan_count(span); j++) {
ftdm_channel_t *chan = ftdm_span_get_channel(span, j);
ftdm_channel_lock(chan);
while (ftdm_test_flag(chan, FTDM_CHANNEL_STATE_CHANGE)) {
ftdm_clear_flag(chan, FTDM_CHANNEL_STATE_CHANGE);
state_advance(chan);
ftdm_channel_complete_state(chan);
}
ftdm_channel_advance_states(chan);
ftdm_channel_unlock(chan);
}
}
@ -1910,6 +1908,7 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_libpri_configure_span)
span->outgoing_call = isdn_outgoing_call;
span->state_map = &isdn_state_map;
span->state_processor = state_advance;
span->get_channel_sig_status = isdn_get_channel_sig_status;
span->get_span_sig_status = isdn_get_span_sig_status;

View File

@ -265,7 +265,7 @@ static ftdm_state_map_t pritap_state_map = {
}
};
static __inline__ void state_advance(ftdm_channel_t *ftdmchan)
static ftdm_status_t state_advance(ftdm_channel_t *ftdmchan)
{
ftdm_status_t status;
ftdm_sigmsg_t sig;
@ -278,6 +278,8 @@ static __inline__ void state_advance(ftdm_channel_t *ftdmchan)
sig.span_id = ftdmchan->span_id;
sig.channel = ftdmchan;
ftdm_channel_complete_state(ftdmchan);
switch (ftdmchan->state) {
case FTDM_CHANNEL_STATE_DOWN:
{
@ -321,24 +323,20 @@ static __inline__ void state_advance(ftdm_channel_t *ftdmchan)
break;
}
return;
return FTDM_SUCCESS;
}
static __inline__ void pritap_check_state(ftdm_span_t *span)
{
if (ftdm_test_flag(span, FTDM_SPAN_STATE_CHANGE)) {
uint32_t j;
ftdm_clear_flag_locked(span, FTDM_SPAN_STATE_CHANGE);
for(j = 1; j <= span->chan_count; j++) {
if (ftdm_test_flag((span->channels[j]), FTDM_CHANNEL_STATE_CHANGE)) {
ftdm_mutex_lock(span->channels[j]->mutex);
ftdm_clear_flag((span->channels[j]), FTDM_CHANNEL_STATE_CHANGE);
state_advance(span->channels[j]);
ftdm_channel_complete_state(span->channels[j]);
ftdm_mutex_unlock(span->channels[j]->mutex);
}
}
}
if (ftdm_test_flag(span, FTDM_SPAN_STATE_CHANGE)) {
uint32_t j;
ftdm_clear_flag_locked(span, FTDM_SPAN_STATE_CHANGE);
for(j = 1; j <= span->chan_count; j++) {
ftdm_mutex_lock(span->channels[j]->mutex);
ftdm_channel_advance_states(span->channels[j]);
ftdm_mutex_unlock(span->channels[j]->mutex);
}
}
}
static int pri_io_read(struct pri *pri, void *buf, int buflen)
@ -896,6 +894,7 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_pritap_configure_span)
span->get_span_sig_status = pritap_get_span_sig_status;
span->state_map = &pritap_state_map;
span->state_processor = state_advance;
return FTDM_SUCCESS;
}

View File

@ -73,7 +73,6 @@ typedef struct ftdm_r2_call_t {
int disconnect_rcvd:1;
int ftdm_call_started:1;
int protocol_error:1;
ftdm_channel_state_t chanstate;
ftdm_size_t dnis_index;
ftdm_size_t ani_index;
char logname[255];
@ -168,8 +167,7 @@ static ftdm_hash_t *g_mod_data_hash;
/* IO interface for the command API */
static ftdm_io_interface_t g_ftdm_r2_interface;
static int ftdm_r2_state_advance(ftdm_channel_t *ftdmchan);
static void ftdm_r2_state_advance_all(ftdm_channel_t *ftdmchan);
static ftdm_status_t ftdm_r2_state_advance(ftdm_channel_t *ftdmchan);
/* whether R2 call accept process is pending */
#define IS_ACCEPTING_PENDING(ftdmchan) \
@ -349,7 +347,6 @@ static void ft_r2_clean_call(ftdm_r2_call_t *call)
call->disconnect_rcvd = 0;
call->ftdm_call_started = 0;
call->protocol_error = 0;
call->chanstate = FTDM_CHANNEL_STATE_DOWN;
call->dnis_index = 0;
call->ani_index = 0;
call->name[0] = 0;
@ -479,7 +476,6 @@ static FIO_CHANNEL_OUTGOING_CALL_FUNCTION(r2_outgoing_call)
}
R2CALL(ftdmchan)->ftdm_call_started = 1;
R2CALL(ftdmchan)->chanstate = FTDM_CHANNEL_STATE_DOWN;
ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DIALING);
ftdm_channel_set_feature(ftdmchan, FTDM_CHANNEL_FEATURE_IO_STATS);
@ -624,7 +620,7 @@ static void ftdm_r2_on_call_init(openr2_chan_t *r2chan)
ftdm_sched_cancel_timer(r2data->sched, r2call->protocol_error_recovery_timer);
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Cancelled protocol error recovery timer\n");
ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
ftdm_r2_state_advance_all(ftdmchan);
ftdm_channel_advance_states(ftdmchan);
}
}
@ -658,7 +654,6 @@ static void ftdm_r2_on_call_init(openr2_chan_t *r2chan)
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_channel_set_feature(ftdmchan, FTDM_CHANNEL_FEATURE_IO_STATS);
ftdm_channel_command(ftdmchan, FTDM_COMMAND_FLUSH_TX_BUFFERS, NULL);
@ -708,12 +703,10 @@ static void ftdm_r2_on_call_offered(openr2_chan_t *r2chan, const char *ani, cons
static void clear_accept_pending(ftdm_channel_t *fchan)
{
if (IS_ACCEPTING_PENDING(fchan)) {
ftdm_clear_flag(fchan, FTDM_CHANNEL_STATE_CHANGE);
ftdm_channel_complete_state(fchan);
} else if (ftdm_test_flag(fchan, FTDM_CHANNEL_STATE_CHANGE)) {
ftdm_log_chan(fchan, FTDM_LOG_CRIT, "State change flag set in state %s, last state = %s\n",
ftdm_channel_state2str(fchan->state), ftdm_channel_state2str(fchan->last_state));
ftdm_clear_flag(fchan, FTDM_CHANNEL_STATE_CHANGE);
ftdm_channel_complete_state(fchan);
}
}
@ -821,7 +814,7 @@ static void ftdm_r2_on_call_end(openr2_chan_t *r2chan)
ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
/* in some circumstances openr2 can call on_call_init right after this, so let's advance the state right here */
ftdm_r2_state_advance_all(ftdmchan);
ftdm_channel_advance_states(ftdmchan);
}
static void ftdm_r2_on_call_read(openr2_chan_t *r2chan, const unsigned char *buf, int buflen)
@ -853,7 +846,7 @@ static void ftdm_r2_recover_from_protocol_error(void *data)
goto done;
}
ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
ftdm_r2_state_advance_all(ftdmchan);
ftdm_channel_advance_states(ftdmchan);
done:
ftdm_channel_unlock(ftdmchan);
}
@ -1617,10 +1610,14 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_r2_configure_span_signaling)
span->set_channel_sig_status = ftdm_r2_set_channel_sig_status;
span->state_map = &r2_state_map;
span->state_processor = ftdm_r2_state_advance;
/* use signals queue */
ftdm_set_flag(span, FTDM_SPAN_USE_SIGNALS_QUEUE);
/* we can skip states (going straight from RING to UP) */
ftdm_set_flag(span, FTDM_SPAN_USE_SKIP_STATES);
/* setup the scheduler */
snprintf(schedname, sizeof(schedname), "ftmod_r2_%s", span->name);
ftdm_assert(ftdm_sched_create(&r2data->sched, schedname) == FTDM_SUCCESS, "Failed to create schedule!\n");
@ -1643,10 +1640,10 @@ fail:
}
/* the channel must be locked when calling this function */
static int ftdm_r2_state_advance(ftdm_channel_t *ftdmchan)
static ftdm_status_t ftdm_r2_state_advance(ftdm_channel_t *ftdmchan)
{
ftdm_sigmsg_t sigev;
int ret;
ftdm_status_t ret;
ftdm_r2_call_t *r2call = R2CALL(ftdmchan);
openr2_chan_t *r2chan = r2call->r2chan;
ftdm_r2_data_t *r2data = ftdmchan->span->signal_data;
@ -1656,183 +1653,173 @@ static int ftdm_r2_state_advance(ftdm_channel_t *ftdmchan)
sigev.span_id = ftdmchan->span_id;
sigev.channel = ftdmchan;
ret = 0;
ret = FTDM_SUCCESS;
/* because we do not always acknowledge the state change (clearing the FTDM_CHANNEL_STATE_CHANGE flag) due to the accept
* procedure described below, we need the chanstate member to NOT process some states twice, so is valid entering this
* function with the FTDM_CHANNEL_STATE_CHANGE flag set but with a state that was already processed and is just waiting
* to complete (the processing is media-bound)
* */
if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)
&& (r2call->chanstate != ftdmchan->state)) {
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Executing state handler for %s\n", ftdm_channel_state2str(ftdmchan->state));
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Executing state handler for %s\n", ftdm_channel_state2str(ftdmchan->state));
r2call->chanstate = ftdmchan->state;
if (IS_ACCEPTING_PENDING(ftdmchan)) {
/*
Moving to PROGRESS, PROGRESS_MEDIA or UP means that we must accept the call first, and accepting
the call in R2 means sending a tone, then waiting for the acknowledge from the other end,
since all of that requires sending and detecting tones, it takes a few milliseconds (I'd say around 100)
which means during that time the user should not try to perform any operations like answer, hangup or anything
else, therefore we DO NOT clear the FTDM_CHANNEL_STATE_CHANGE flag here, we rely on ftdm_io.c to block
the user thread until we're done with the accept (see on_call_accepted callback) and then we clear the state change flag,
otherwise we have a race condition between freetdm calling openr2_chan_answer_call and openr2 accepting the call first,
if freetdm calls openr2_chan_answer_call before the accept cycle completes, openr2 will fail to answer the call */
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "State ack for state %s will have to wait a bit\n", ftdm_channel_state2str(ftdmchan->state));
} else if (ftdmchan->state != FTDM_CHANNEL_STATE_DOWN){
ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE);
ftdm_channel_complete_state(ftdmchan);
}
switch (ftdmchan->state) {
/* starting an incoming call */
case FTDM_CHANNEL_STATE_COLLECT:
{
uint32_t interval = 0;
ftdm_channel_command(ftdmchan, FTDM_COMMAND_GET_INTERVAL, &interval);
ftdm_assert(interval != 0, "Invalid interval!");
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Starting processing of incoming call with interval %d\n", interval);
openr2_chan_enable_read(r2chan);
}
break;
/* starting an outgoing call */
case FTDM_CHANNEL_STATE_DIALING:
{
uint32_t interval = 0;
ftdm_channel_command(ftdmchan, FTDM_COMMAND_GET_INTERVAL, &interval);
ftdm_assert(interval != 0, "Invalid interval!");
ftdm_log_chan(ftdmchan,
FTDM_LOG_DEBUG, "Starting processing of outgoing call in channel with interval %d\n", interval);
openr2_chan_enable_read(r2chan);
}
break;
/* incoming call was offered */
case FTDM_CHANNEL_STATE_RING:
/* notify the user about the new call */
sigev.event_id = FTDM_SIGEVENT_START;
ftdm_span_send_signal(ftdmchan->span, &sigev);
r2call->ftdm_call_started = 1;
break;
/* the call is making progress */
case FTDM_CHANNEL_STATE_PROGRESS:
case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
{
if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
if (!r2call->accepted) {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Accepting call\n");
ft_r2_accept_call(ftdmchan);
}
} else {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Notifying progress\n");
sigev.event_id = FTDM_SIGEVENT_PROCEED;
ftdm_span_send_signal(ftdmchan->span, &sigev);
sigev.event_id = FTDM_SIGEVENT_PROGRESS_MEDIA;
ftdm_span_send_signal(ftdmchan->span, &sigev);
}
}
break;
/* the call was answered */
case FTDM_CHANNEL_STATE_UP:
{
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Call was answered\n");
if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
if (!r2call->accepted) {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Call has not been accepted, need to accept first\n");
// the answering will be done in the on_call_accepted handler
ft_r2_accept_call(ftdmchan);
r2call->answer_pending = 1;
} else {
ft_r2_answer_call(ftdmchan);
}
} else {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Notifying of call answered\n");
sigev.event_id = FTDM_SIGEVENT_UP;
ftdm_span_send_signal(ftdmchan->span, &sigev);
}
}
break;
/* just got hangup */
case FTDM_CHANNEL_STATE_HANGUP:
{
if (!r2call->disconnect_rcvd) {
openr2_call_disconnect_cause_t disconnect_cause = ftdm_r2_ftdm_cause_to_openr2_cause(ftdmchan);
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Clearing call, cause = %s\n", openr2_proto_get_disconnect_string(disconnect_cause));
/* this will disconnect the call, but need to wait for the call end before moving to DOWN */
openr2_chan_disconnect_call(r2chan, disconnect_cause);
} else if (!r2call->protocol_error) {
/* just ack the hangup, on_call_end will be called by openr2 right after */
openr2_chan_disconnect_call(r2chan, OR2_CAUSE_NORMAL_CLEARING);
} else {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Clearing call due to protocol error\n");
/* do not set to down yet, give some time for recovery */
ftdm_sched_timer(r2data->sched, "protocolerr_recover", 100,
ftdm_r2_recover_from_protocol_error, r2chan, &r2call->protocol_error_recovery_timer);
}
}
break;
case FTDM_CHANNEL_STATE_TERMINATING:
{
/* if the call has not been started yet we must go to HANGUP right here */
if (!r2call->ftdm_call_started) {
ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_HANGUP);
} else {
openr2_call_disconnect_cause_t disconnect_cause = ftdm_r2_ftdm_cause_to_openr2_cause(ftdmchan);
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Clearing call, cause = %s\n", openr2_proto_get_disconnect_string(disconnect_cause));
/* notify the user of the call terminating and we wait for the user to move us to hangup */
sigev.event_id = FTDM_SIGEVENT_STOP;
ftdm_span_send_signal(ftdmchan->span, &sigev);
}
}
break;
/* finished call for good */
case FTDM_CHANNEL_STATE_DOWN:
{
if (ftdmchan->last_state != FTDM_CHANNEL_STATE_RESET) {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "R2 Call is down\n");
}
ret = 1;
}
break;
/* INDICATE_RINGING doesn't apply to MFC/R2. maybe we could generate a tone */
case FTDM_CHANNEL_STATE_RINGING:
{
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "RINGING indicated, ignoring it as it doesn't apply to MFC/R2\n");
}
break;
/* put the r2 channel back to IDLE, close ftdmchan and set it's state as DOWN */
case FTDM_CHANNEL_STATE_RESET:
{
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "RESET indicated, putting the R2 channel back to IDLE\n");
openr2_chan_set_idle(r2chan);
ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
}
break;
default:
{
ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Unhandled channel state change: %s\n", ftdm_channel_state2str(ftdmchan->state));
}
break;
}
if (IS_ACCEPTING_PENDING(ftdmchan)) {
/*
Moving to PROGRESS, PROGRESS_MEDIA or UP means that we must accept the call first, and accepting
the call in R2 means sending a tone, then waiting for the acknowledge from the other end,
since all of that requires sending and detecting tones, it takes a few milliseconds (I'd say around 100)
which means during that time the user should not try to perform any operations like answer, hangup or anything
else, therefore we DO NOT clear the FTDM_CHANNEL_STATE_CHANGE flag here, we rely on ftdm_io.c to block
the user thread until we're done with the accept (see on_call_accepted callback) and then we clear the state change flag,
otherwise we have a race condition between freetdm calling openr2_chan_answer_call and openr2 accepting the call first,
if freetdm calls openr2_chan_answer_call before the accept cycle completes, openr2 will fail to answer the call */
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "State ack for state %s will have to wait a bit\n", ftdm_channel_state2str(ftdmchan->state));
} else if (ftdmchan->state != FTDM_CHANNEL_STATE_DOWN){
ftdm_channel_complete_state(ftdmchan);
}
if (ret) {
switch (ftdmchan->state) {
/* starting an incoming call */
case FTDM_CHANNEL_STATE_COLLECT:
{
uint32_t interval = 0;
ftdm_channel_command(ftdmchan, FTDM_COMMAND_GET_INTERVAL, &interval);
ftdm_assert(interval != 0, "Invalid interval!");
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Starting processing of incoming call with interval %d\n", interval);
openr2_chan_enable_read(r2chan);
}
break;
/* starting an outgoing call */
case FTDM_CHANNEL_STATE_DIALING:
{
uint32_t interval = 0;
ftdm_channel_command(ftdmchan, FTDM_COMMAND_GET_INTERVAL, &interval);
ftdm_assert(interval != 0, "Invalid interval!");
ftdm_log_chan(ftdmchan,
FTDM_LOG_DEBUG, "Starting processing of outgoing call in channel with interval %d\n", interval);
openr2_chan_enable_read(r2chan);
}
break;
/* incoming call was offered */
case FTDM_CHANNEL_STATE_RING:
/* notify the user about the new call */
sigev.event_id = FTDM_SIGEVENT_START;
ftdm_span_send_signal(ftdmchan->span, &sigev);
r2call->ftdm_call_started = 1;
break;
/* the call is making progress */
case FTDM_CHANNEL_STATE_PROGRESS:
case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
{
if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
if (!r2call->accepted) {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Accepting call\n");
ft_r2_accept_call(ftdmchan);
}
} else {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Notifying progress\n");
sigev.event_id = FTDM_SIGEVENT_PROCEED;
ftdm_span_send_signal(ftdmchan->span, &sigev);
sigev.event_id = FTDM_SIGEVENT_PROGRESS_MEDIA;
ftdm_span_send_signal(ftdmchan->span, &sigev);
}
}
break;
/* the call was answered */
case FTDM_CHANNEL_STATE_UP:
{
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Call was answered\n");
if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
if (!r2call->accepted) {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Call has not been accepted, need to accept first\n");
// the answering will be done in the on_call_accepted handler
ft_r2_accept_call(ftdmchan);
r2call->answer_pending = 1;
} else {
ft_r2_answer_call(ftdmchan);
}
} else {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Notifying of call answered\n");
sigev.event_id = FTDM_SIGEVENT_UP;
ftdm_span_send_signal(ftdmchan->span, &sigev);
}
}
break;
/* just got hangup */
case FTDM_CHANNEL_STATE_HANGUP:
{
if (!r2call->disconnect_rcvd) {
openr2_call_disconnect_cause_t disconnect_cause = ftdm_r2_ftdm_cause_to_openr2_cause(ftdmchan);
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Clearing call, cause = %s\n", openr2_proto_get_disconnect_string(disconnect_cause));
/* this will disconnect the call, but need to wait for the call end before moving to DOWN */
openr2_chan_disconnect_call(r2chan, disconnect_cause);
} else if (!r2call->protocol_error) {
/* just ack the hangup, on_call_end will be called by openr2 right after */
openr2_chan_disconnect_call(r2chan, OR2_CAUSE_NORMAL_CLEARING);
} else {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Clearing call due to protocol error\n");
/* do not set to down yet, give some time for recovery */
ftdm_sched_timer(r2data->sched, "protocolerr_recover", 100,
ftdm_r2_recover_from_protocol_error, r2chan, &r2call->protocol_error_recovery_timer);
}
}
break;
case FTDM_CHANNEL_STATE_TERMINATING:
{
/* if the call has not been started yet we must go to HANGUP right here */
if (!r2call->ftdm_call_started) {
ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_HANGUP);
} else {
openr2_call_disconnect_cause_t disconnect_cause = ftdm_r2_ftdm_cause_to_openr2_cause(ftdmchan);
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Clearing call, cause = %s\n", openr2_proto_get_disconnect_string(disconnect_cause));
/* notify the user of the call terminating and we wait for the user to move us to hangup */
sigev.event_id = FTDM_SIGEVENT_STOP;
ftdm_span_send_signal(ftdmchan->span, &sigev);
}
}
break;
/* finished call for good */
case FTDM_CHANNEL_STATE_DOWN:
{
if (ftdmchan->last_state != FTDM_CHANNEL_STATE_RESET) {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "R2 Call is down\n");
} else {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "R2 Reset Complete\n");
}
ret = FTDM_BREAK;
}
break;
/* INDICATE_RINGING doesn't apply to MFC/R2. maybe we could generate a tone */
case FTDM_CHANNEL_STATE_RINGING:
{
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "RINGING indicated, ignoring it as it doesn't apply to MFC/R2\n");
}
break;
/* put the r2 channel back to IDLE, close ftdmchan and set it's state as DOWN */
case FTDM_CHANNEL_STATE_RESET:
{
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "RESET indicated, putting the R2 channel back to IDLE\n");
openr2_chan_set_idle(r2chan);
ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
}
break;
default:
{
ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Unhandled channel state change: %s\n", ftdm_channel_state2str(ftdmchan->state));
}
break;
}
if (ret == FTDM_BREAK) {
ftdm_channel_t *closed_chan;
closed_chan = ftdmchan;
ftdm_channel_close(&closed_chan);
@ -1841,20 +1828,6 @@ static int ftdm_r2_state_advance(ftdm_channel_t *ftdmchan)
return ret;
}
/* the channel must be locked when calling this function */
static void ftdm_r2_state_advance_all(ftdm_channel_t *ftdmchan)
{
/* because we do not always acknowledge the state change (clearing the FTDM_CHANNEL_STATE_CHANGE flag) due to the accept
* procedure described below, we need the chanstate member to NOT process some states twice, so is valid entering this
* function with the FTDM_CHANNEL_STATE_CHANGE flag set but with a state that was already processed and is just waiting
* to complete (the processing is media-bound)
* */
while (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)
&& (R2CALL(ftdmchan)->chanstate != ftdmchan->state)) {
ftdm_r2_state_advance(ftdmchan);
}
}
static void *ftdm_r2_run(ftdm_thread_t *me, void *obj)
{
openr2_chan_t *r2chan = NULL;
@ -1983,12 +1956,12 @@ static void *ftdm_r2_run(ftdm_thread_t *me, void *obj)
ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_RX_DISABLED);
ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_TX_DISABLED);
ftdm_r2_state_advance_all(ftdmchan);
ftdm_channel_advance_states(ftdmchan);
r2chan = call->r2chan;
openr2_chan_process_signaling(r2chan);
ftdm_r2_state_advance_all(ftdmchan);
ftdm_channel_advance_states(ftdmchan);
if (!call->accepted) {
/* if the call is not accepted we do not want users reading */

View File

@ -951,7 +951,6 @@ static void handle_call_answer(ftdm_span_t *span, sangomabc_connection_t *mcon,
}
}
static __inline__ void advance_chan_states(ftdm_channel_t *ftdmchan);
static __inline__ void stop_loop(ftdm_channel_t *ftdmchan);
/**
@ -1002,7 +1001,7 @@ tryagain:
} else if (ftdmchan->state == FTDM_CHANNEL_STATE_IN_LOOP && retry) {
retry = 0;
stop_loop(ftdmchan);
advance_chan_states(ftdmchan);
ftdm_channel_advance_states(ftdmchan);
goto tryagain;
} else {
ftdm_log(FTDM_LOG_ERROR, "s%dc%d: rejecting incoming call in channel state %s\n",
@ -1267,7 +1266,7 @@ static ftdm_channel_t* event_process_states(ftdm_span_t *span, sangomabc_short_e
}
ftdm_mutex_lock(ftdmchan->mutex);
advance_chan_states(ftdmchan);
ftdm_channel_advance_states(ftdmchan);
return ftdmchan;
}
@ -1354,11 +1353,11 @@ static int parse_sangoma_event(ftdm_span_t *span, sangomabc_connection_t *mcon,
}
if(ftdmchan != NULL) {
advance_chan_states(ftdmchan);
ftdm_channel_advance_states(ftdmchan);
ftdm_mutex_unlock(ftdmchan->mutex);
}
return 0;
return 0;
}
@ -1366,7 +1365,7 @@ static int parse_sangoma_event(ftdm_span_t *span, sangomabc_connection_t *mcon,
* \brief Handler for channel state change
* \param ftdmchan Channel to handle
*/
static __inline__ ftdm_status_t state_advance(ftdm_channel_t *ftdmchan)
static ftdm_status_t state_advance(ftdm_channel_t *ftdmchan)
{
ftdm_sangoma_boost_data_t *sangoma_boost_data = ftdmchan->span->signal_data;
sangomabc_connection_t *mcon = &sangoma_boost_data->mcon;
@ -1374,12 +1373,6 @@ static __inline__ ftdm_status_t state_advance(ftdm_channel_t *ftdmchan)
ftdm_status_t status;
if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) {
ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE);
} else {
return FTDM_SUCCESS;
}
ftdm_assert_return(ftdmchan->last_state != ftdmchan->state, FTDM_FAIL, "Channel state already processed\n");
ftdm_log(FTDM_LOG_DEBUG, "%d:%d PROCESSING STATE [%s]\n", ftdmchan->span_id, ftdmchan->chan_id, ftdm_channel_state2str(ftdmchan->state));
@ -1389,6 +1382,8 @@ static __inline__ ftdm_status_t state_advance(ftdm_channel_t *ftdmchan)
sig.span_id = ftdmchan->span_id;
sig.channel = ftdmchan;
ftdm_channel_complete_state(ftdmchan);
switch (ftdmchan->state) {
case FTDM_CHANNEL_STATE_DOWN:
{
@ -1640,24 +1635,15 @@ static __inline__ ftdm_status_t state_advance(ftdm_channel_t *ftdmchan)
default:
break;
}
ftdm_channel_complete_state(ftdmchan);
return FTDM_SUCCESS;
}
static __inline__ void advance_chan_states(ftdm_channel_t *ftdmchan)
{
while (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) {
state_advance(ftdmchan);
}
}
/**
* \brief Initialises outgoing requests array
*/
static __inline__ void init_outgoing_array(void)
{
memset(&OUTBOUND_REQUESTS, 0, sizeof(OUTBOUND_REQUESTS));
}
/**
@ -1685,7 +1671,7 @@ static __inline__ void check_state(ftdm_span_t *span)
if (susp && span->channels[j]->state != FTDM_CHANNEL_STATE_DOWN) {
ftdm_set_state(span->channels[j], FTDM_CHANNEL_STATE_RESTART);
}
state_advance(span->channels[j]);
ftdm_channel_advance_states(span->channels[j]);
ftdm_mutex_unlock(span->channels[j]->mutex);
}
}
@ -1695,7 +1681,7 @@ static __inline__ void check_state(ftdm_span_t *span)
* but without taking the chan out of the queue, so check th
* flag before advancing the state */
ftdm_mutex_lock(ftdmchan->mutex);
state_advance(ftdmchan);
ftdm_channel_advance_states(ftdmchan);
ftdm_mutex_unlock(ftdmchan->mutex);
}
}
@ -2687,6 +2673,7 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_sangoma_boost_configure_span)
span->get_span_sig_status = sangoma_boost_get_span_sig_status;
span->set_span_sig_status = sangoma_boost_set_span_sig_status;
span->state_map = &boost_state_map;
span->state_processor = state_advance;
sangoma_boost_data->mcon.debuglevel = FTDM_LOG_LEVEL_DEBUG;
sangoma_boost_data->pcon.debuglevel = FTDM_LOG_LEVEL_DEBUG;
ftdm_clear_flag(span, FTDM_SPAN_SUGGEST_CHAN_ID);

View File

@ -46,10 +46,9 @@ static ftdm_status_t ftdm_sangoma_isdn_stop(ftdm_span_t *span);
static ftdm_status_t ftdm_sangoma_isdn_start(ftdm_span_t *span);
ftdm_channel_t* ftdm_sangoma_isdn_process_event_states(ftdm_span_t *span, sngisdn_event_data_t *sngisdn_event);
static void ftdm_sangoma_isdn_advance_chan_states(ftdm_channel_t *ftdmchan);
static void ftdm_sangoma_isdn_poll_events(ftdm_span_t *span);
static void ftdm_sangoma_isdn_process_phy_events(ftdm_span_t *span, ftdm_oob_event_t event);
static void ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdmchan);
static ftdm_status_t ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdmchan);
static void ftdm_sangoma_isdn_process_stack_event (ftdm_span_t *span, sngisdn_event_data_t *sngisdn_event);
static void ftdm_sangoma_isdn_wakeup_phy(ftdm_channel_t *dchan);
static void ftdm_sangoma_isdn_dchan_set_queue_size(ftdm_channel_t *ftdmchan);
@ -270,13 +269,6 @@ ftdm_state_map_t sangoma_isdn_state_map = {
}
};
static __inline__ void ftdm_sangoma_isdn_advance_chan_states(ftdm_channel_t *ftdmchan)
{
while (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) {
ftdm_sangoma_isdn_process_state_change(ftdmchan);
}
}
static void ftdm_sangoma_isdn_process_phy_events(ftdm_span_t *span, ftdm_oob_event_t event)
{
sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) span->signal_data;
@ -457,7 +449,7 @@ static void *ftdm_sangoma_isdn_run(ftdm_thread_t *me, void *obj)
while ((ftdmchan = ftdm_queue_dequeue(span->pendingchans))) {
/* double check that this channel has a state change pending */
ftdm_channel_lock(ftdmchan);
ftdm_sangoma_isdn_advance_chan_states(ftdmchan);
ftdm_channel_advance_states(ftdmchan);
ftdm_channel_unlock(ftdmchan);
}
@ -470,11 +462,11 @@ static void *ftdm_sangoma_isdn_run(ftdm_thread_t *me, void *obj)
/* twiddle */
break;
case FTDM_FAIL:
ftdm_log(FTDM_LOG_ERROR,"%s:ftdm_interrupt_wait returned error!\n", span->name);
ftdm_log(FTDM_LOG_ERROR, "%s: ftdm_interrupt_wait returned error!\n", span->name);
break;
default:
ftdm_log(FTDM_LOG_ERROR,"%s:ftdm_interrupt_wait returned with unknown code\n", span->name);
ftdm_log(FTDM_LOG_ERROR, "%s: ftdm_interrupt_wait returned with unknown code\n", span->name);
break;
}
@ -536,7 +528,7 @@ ftdm_channel_t* ftdm_sangoma_isdn_process_event_states(ftdm_span_t *span, sngisd
break;
}
ftdm_channel_lock(ftdmchan);
ftdm_sangoma_isdn_advance_chan_states(ftdmchan);
ftdm_channel_advance_states(ftdmchan);
return ftdmchan;
}
@ -600,13 +592,14 @@ static void ftdm_sangoma_isdn_process_stack_event (ftdm_span_t *span, sngisdn_ev
sngisdn_process_rst_ind(sngisdn_event);
break;
}
if(ftdmchan != NULL) {
ftdm_sangoma_isdn_advance_chan_states(ftdmchan);
if (ftdmchan != NULL) {
ftdm_channel_advance_states(ftdmchan);
ftdm_channel_unlock(ftdmchan);
}
}
static void ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdmchan)
/* this function is called with the channel already locked by the core */
static ftdm_status_t ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdmchan)
{
ftdm_sigmsg_t sigev;
ftdm_channel_state_t initial_state;
@ -618,13 +611,12 @@ static void ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdmchan)
sigev.span_id = ftdmchan->span_id;
sigev.channel = ftdmchan;
/*first lock the channel*/
ftdm_channel_lock(ftdmchan);
/*clear the state change flag...since we might be setting a new state*/
ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE);
/* Acknowledge the state change */
ftdm_channel_complete_state(ftdmchan);
#ifdef FTDM_DEBUG_CHAN_MEMORY
if (ftdmchan->state == FTDM_CHANNEL_STATE_DIALING) {
ftdm_assert(mprotect(ftdmchan, sizeof(*ftdmchan), PROT_READ)==0, "Failed to mprotect");
ftdm_assert(mprotect(ftdmchan, sizeof(*ftdmchan), PROT_READ) == 0, "Failed to mprotect");
}
#endif
@ -879,11 +871,10 @@ static void ftdm_sangoma_isdn_process_state_change(ftdm_channel_t *ftdmchan)
}
#ifdef FTDM_DEBUG_CHAN_MEMORY
if (ftdmchan->state == FTDM_CHANNEL_STATE_DIALING) {
ftdm_assert(mprotect(ftdmchan, sizeof(*ftdmchan), PROT_READ|PROT_WRITE)==0, "Failed to mprotect");
ftdm_assert(mprotect(ftdmchan, sizeof(*ftdmchan), PROT_READ|PROT_WRITE) == 0, "Failed to mprotect");
}
#endif
ftdm_channel_unlock(ftdmchan);
return;
return FTDM_SUCCESS;
}
static FIO_CHANNEL_SEND_MSG_FUNCTION(ftdm_sangoma_isdn_send_msg)
@ -1098,6 +1089,7 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_sangoma_isdn_span_config)
span->get_span_sig_status = ftdm_sangoma_isdn_get_span_sig_status;
span->set_span_sig_status = ftdm_sangoma_isdn_set_span_sig_status;
span->state_map = &sangoma_isdn_state_map;
span->state_processor = ftdm_sangoma_isdn_process_state_change;
ftdm_set_flag(span, FTDM_SPAN_USE_CHAN_QUEUE);
ftdm_set_flag(span, FTDM_SPAN_USE_SIGNALS_QUEUE);
ftdm_set_flag(span, FTDM_SPAN_USE_PROCEED_STATE);

View File

@ -46,7 +46,6 @@ ftdm_sngss7_data_t g_ftdm_sngss7_data;
/* PROTOTYPES *****************************************************************/
static void *ftdm_sangoma_ss7_run (ftdm_thread_t * me, void *obj);
void ftdm_sangoma_ss7_process_state_change (ftdm_channel_t *ftdmchan);
static void ftdm_sangoma_ss7_process_stack_event (sngss7_event_data_t *sngss7_event);
static ftdm_status_t ftdm_sangoma_ss7_stop (ftdm_span_t * span);
@ -308,9 +307,7 @@ static void *ftdm_sangoma_ss7_run(ftdm_thread_t * me, void *obj)
ftdm_mutex_lock(ftdmchan->mutex);
/* process state changes for this channel until they are all done */
while (ftdm_test_flag (ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) {
ftdm_sangoma_ss7_process_state_change (ftdmchan);
}
ftdm_channel_advance_states(ftdmchan);
/* unlock the channel */
ftdm_mutex_unlock (ftdmchan->mutex);
@ -403,9 +400,7 @@ static void ftdm_sangoma_ss7_process_stack_event (sngss7_event_data_t *sngss7_ev
ftdm_mutex_lock(ftdmchan->mutex);
/* while there's a state change present on this channel process it */
while (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) {
ftdm_sangoma_ss7_process_state_change(ftdmchan);
}
ftdm_channel_advance_states(ftdmchan);
/* figure out the type of event and send it to the right handler */
switch (sngss7_event->event_id) {
@ -468,9 +463,7 @@ static void ftdm_sangoma_ss7_process_stack_event (sngss7_event_data_t *sngss7_ev
} /* switch (sngss7_event->event_id) */
/* while there's a state change present on this channel process it */
while (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE)) {
ftdm_sangoma_ss7_process_state_change(ftdmchan);
}
ftdm_channel_advance_states(ftdmchan);
/* unlock the channel */
ftdm_mutex_unlock(ftdmchan->mutex);
@ -479,7 +472,7 @@ static void ftdm_sangoma_ss7_process_stack_event (sngss7_event_data_t *sngss7_ev
}
/******************************************************************************/
void ftdm_sangoma_ss7_process_state_change (ftdm_channel_t * ftdmchan)
ftdm_status_t ftdm_sangoma_ss7_process_state_change (ftdm_channel_t * ftdmchan)
{
sngss7_chan_data_t *sngss7_info = ftdmchan->call_data;
sng_isup_inf_t *isup_intf = NULL;
@ -495,7 +488,7 @@ void ftdm_sangoma_ss7_process_state_change (ftdm_channel_t * ftdmchan)
SS7_DEBUG_CHAN(ftdmchan, "ftmod_sangoma_ss7 processing state %s\n", ftdm_channel_state2str (ftdmchan->state));
/* clear the state change flag...since we might be setting a new state */
ftdm_clear_flag (ftdmchan, FTDM_CHANNEL_STATE_CHANGE);
ftdm_channel_complete_state(ftdmchan);
/*check what state we are supposed to be in */
switch (ftdmchan->state) {
@ -1212,7 +1205,7 @@ suspend_goto_restart:
/**************************************************************************/
}/*switch (ftdmchan->state) */
return;
return FTDM_SUCCESS;
}
/******************************************************************************/
@ -1476,6 +1469,7 @@ static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_sangoma_ss7_span_config)
span->get_channel_sig_status = ftdm_sangoma_ss7_get_sig_status;
span->set_channel_sig_status = ftdm_sangoma_ss7_set_sig_status;
span->state_map = &sangoma_ss7_state_map;
span->state_processor = ftdm_sangoma_ss7_process_state_change;
span->signal_data = ss7_span_info;
/* set the flag to indicate that this span uses channel state change queues */

View File

@ -452,7 +452,7 @@ extern int cmbLinkSetId;
/* PROTOTYPES *****************************************************************/
/* in ftmod_sangoma_ss7_main.c */
void ftdm_sangoma_ss7_process_state_change (ftdm_channel_t *ftdmchan);
ftdm_status_t ftdm_sangoma_ss7_process_state_change (ftdm_channel_t *ftdmchan);
/* in ftmod_sangoma_ss7_logger.c */
void handle_sng_log(uint8_t level, char *fmt,...);

View File

@ -349,12 +349,13 @@ typedef enum {
FTDM_SIGEVENT_FACILITY, /*!< In call facility event */
FTDM_SIGEVENT_TRACE, /*!<Interpreted trace event */
FTDM_SIGEVENT_TRACE_RAW, /*!<Raw trace event */
FTDM_SIGEVENT_INDICATION_COMPLETED, /*!< Last requested indication was completed */
FTDM_SIGEVENT_INVALID, /*!<Invalid */
} ftdm_signal_event_t;
#define SIGNAL_STRINGS "START", "STOP", "RELEASED", "UP", "FLASH", "PROCEED", "RINGING", "PROGRESS", \
"PROGRESS_MEDIA", "ALARM_TRAP", "ALARM_CLEAR", \
"COLLECTED_DIGIT", "ADD_CALL", "RESTART", "SIGSTATUS_CHANGED", "COLLISION", "FACILITY", "TRACE", "TRACE_RAW", "INVALID"
"COLLECTED_DIGIT", "ADD_CALL", "RESTART", "SIGSTATUS_CHANGED", "COLLISION", "FACILITY", \
"TRACE", "TRACE_RAW", "INDICATION_COMPLETED", "INVALID"
/*! \brief Move from string to ftdm_signal_event_t and viceversa */
FTDM_STR2ENUM_P(ftdm_str2ftdm_signal_event, ftdm_signal_event2str, ftdm_signal_event_t)
@ -434,6 +435,32 @@ typedef struct {
char digits[FTDM_DIGITS_LIMIT];
} ftdm_event_collected_t;
/*! \brief FreeTDM supported indications.
* This is used during incoming calls when you want to request the signaling stack
* to notify about indications occurring locally. See ftdm_channel_call_indicate for more info */
typedef enum {
FTDM_CHANNEL_INDICATE_NONE,
FTDM_CHANNEL_INDICATE_RINGING,
FTDM_CHANNEL_INDICATE_PROCEED,
FTDM_CHANNEL_INDICATE_PROGRESS,
FTDM_CHANNEL_INDICATE_PROGRESS_MEDIA,
FTDM_CHANNEL_INDICATE_BUSY,
/* Using this indication is equivalent to call ftdm_channel_call_answer API */
FTDM_CHANNEL_INDICATE_ANSWER,
FTDM_CHANNEL_INDICATE_INVALID,
} ftdm_channel_indication_t;
#define INDICATION_STRINGS "NONE", "RINGING", "PROCEED", "PROGRESS", "PROGRESS_MEDIA", "BUSY", "ANSWER", "INVALID"
/*! \brief Move from string to ftdm_channel_indication_t and viceversa */
FTDM_STR2ENUM_P(ftdm_str2channel_indication, ftdm_channel_indication2str, ftdm_channel_indication_t)
typedef struct {
/* The indication that was completed */
ftdm_channel_indication_t indication;
/* Completion status of the indication */
ftdm_status_t status;
} ftdm_event_indication_completed_t;
/*! \brief Generic signaling message */
struct ftdm_sigmsg {
ftdm_signal_event_t event_id; /*!< The type of message */
@ -444,7 +471,8 @@ struct ftdm_sigmsg {
union {
ftdm_event_sigstatus_t sigstatus; /*!< valid if event_id is FTDM_SIGEVENT_SIGSTATUS_CHANGED */
ftdm_event_trace_t logevent; /*!< valid if event_id is FTDM_SIGEVENT_TRACE or FTDM_SIGEVENT_TRACE_RAW */
ftdm_event_collected_t collected; /*!< valif if event_id is FTDM_SIGEVENT_COLLECTED_DIGIT */
ftdm_event_collected_t collected; /*!< valid if event_id is FTDM_SIGEVENT_COLLECTED_DIGIT */
ftdm_event_indication_completed_t indication_completed; /*!< valid if the event_id is FTDM_SIGEVENT_INDICATION_COMPLETED */
} ev_data;
struct {
uint8_t autofree; /*!< Whether the freetdm core will free it after message delivery */
@ -555,10 +583,10 @@ typedef enum {
FTDM_COMMAND_GET_LINK_STATUS,
FTDM_COMMAND_ENABLE_LOOP,
FTDM_COMMAND_DISABLE_LOOP,
FTDM_COMMAND_COUNT,
FTDM_COMMAND_SET_RX_QUEUE_SIZE,
FTDM_COMMAND_SET_TX_QUEUE_SIZE,
FTDM_COMMAND_SET_POLARITY,
FTDM_COMMAND_COUNT,
} ftdm_command_t;
typedef enum {
@ -624,7 +652,20 @@ typedef ftdm_status_t (*fio_span_get_sig_status_t) FIO_SPAN_GET_SIG_STATUS_ARGS;
typedef ftdm_status_t (*fio_span_poll_event_t) FIO_SPAN_POLL_EVENT_ARGS ;
typedef ftdm_status_t (*fio_span_next_event_t) FIO_SPAN_NEXT_EVENT_ARGS ;
typedef ftdm_status_t (*fio_channel_next_event_t) FIO_CHANNEL_NEXT_EVENT_ARGS ;
/*! \brief Callback for signal delivery (FTDM_SIGEVENT_START and friends)
* \note This callback is provided by the user during ftdm_configure_span_signaling
*
* \note You must NOT do any blocking during this callback since this function is
* most likely called in an internal signaling thread that can potentially be
* shared for all the channels in a span and blocking will delay processing
* (sometimes even audio processing) for other channels
*
* \note Although some simple FreeTDM APIs can work (ie: ftdm_span_get_id etc), the
* use of any FreeTDM call API (ie ftdm_channel_call_answer) is discouraged
*/
typedef ftdm_status_t (*fio_signal_cb_t) FIO_SIGNAL_CB_ARGS ;
typedef ftdm_status_t (*fio_event_cb_t) FIO_EVENT_CB_ARGS ;
typedef ftdm_status_t (*fio_configure_span_t) FIO_CONFIGURE_SPAN_ARGS ;
typedef ftdm_status_t (*fio_configure_t) FIO_CONFIGURE_ARGS ;
@ -708,17 +749,6 @@ typedef enum {
FTDM_CODEC_NONE = (1 << 30)
} ftdm_codec_t;
/*! \brief FreeTDM supported indications.
* This is used during incoming calls when you want to request the signaling stack
* to notify about indications occurring locally */
typedef enum {
FTDM_CHANNEL_INDICATE_RINGING,
FTDM_CHANNEL_INDICATE_PROCEED,
FTDM_CHANNEL_INDICATE_PROGRESS,
FTDM_CHANNEL_INDICATE_PROGRESS_MEDIA,
FTDM_CHANNEL_INDICATE_BUSY,
} ftdm_channel_indication_t;
/*! \brief FreeTDM supported hardware alarms. */
typedef enum {
FTDM_ALARM_NONE = 0,
@ -741,7 +771,12 @@ FT_DECLARE(ftdm_status_t) ftdm_global_set_queue_handler(ftdm_queue_handler_t *ha
*/
FT_DECLARE(int) ftdm_channel_get_availability(ftdm_channel_t *ftdmchan);
/*! \brief Answer call */
/*! \brief Answer call. This can also be accomplished by ftdm_channel_call_indicate with FTDM_CHANNEL_INDICATE_ANSWER, in both
* cases you will get a FTDM_SIGEVENT_INDICATION_COMPLETED when the indication is sent (or an error occurs)
* \note Although this API will result in FTDM_SIGEVENT_INDICATION_COMPLETED event being delivered,
* there is no guarantee of whether the event will arrive after or before your execution thread returns
* from ftdm_channel_call_answer
*/
#define ftdm_channel_call_answer(ftdmchan) _ftdm_channel_call_answer(__FILE__, __FUNCTION__, __LINE__, (ftdmchan))
/*! \brief Answer call recording the source code point where the it was called (see ftdm_channel_call_answer for an easy to use macro) */
@ -753,7 +788,19 @@ FT_DECLARE(ftdm_status_t) _ftdm_channel_call_answer(const char *file, const char
/*! \brief Place an outgoing call recording the source code point where it was called (see ftdm_channel_call_place for an easy to use macro) */
FT_DECLARE(ftdm_status_t) _ftdm_channel_call_place(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan);
/*! \brief Indicate a new condition in an incoming call */
/*! \brief Indicate a new condition in an incoming call
*
* \note Every indication request will result in FTDM_SIGEVENT_INDICATION_COMPLETED event being delivered with
* the proper status that will inform you if the request was successful or not. The exception is if this
* function returns something different to FTDM_SUCCESS, in which case the request failed right away and no
* further FTDM_SIGEVENT_INDICATION_COMPLETED will be delivered
* Be aware there is no guarantee of whether the completion event will arrive after or before your execution
* thread returns from ftdm_channel_call_indicate. This means you could get FTDM_SIGEVENT_INDICATION_COMPLETED
* even before your execution thread returns from the ftdm_channel_call_indicate() API
*
* \note You cannot send more than one indication at the time. You must wait for the completed event before
* calling this function again (unless the return code was different than FTDM_SUCCESS)
*/
#define ftdm_channel_call_indicate(ftdmchan, indication) _ftdm_channel_call_indicate(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), (indication))
/*! \brief Indicate a new condition in an incoming call recording the source code point where it was called (see ftdm_channel_call_indicate for an easy to use macro) */
@ -1494,9 +1541,14 @@ FT_DECLARE(const char *) ftdm_channel_get_last_state_str(const ftdm_channel_t *c
*/
FT_DECLARE(char *) ftdm_channel_get_history_str(const ftdm_channel_t *channel);
/*! \brief Initialize channel state for an outgoing call */
/*! \brief Initialize channel state for an outgoing call
* \note This API will eventually be deprecated, is only needed if you use boost signaling
*/
FT_DECLARE(ftdm_status_t) ftdm_channel_init(ftdm_channel_t *ftdmchan);
/*! \brief Enable/disable blocking mode in the channels for this span */
FT_DECLARE(ftdm_status_t) ftdm_span_set_blocking_mode(const ftdm_span_t *span, ftdm_bool_t enabled);
/*! \brief Initialize the library */
FT_DECLARE(ftdm_status_t) ftdm_global_init(void);

View File

@ -183,7 +183,16 @@ typedef enum {
FTDM_TIMEOUT, /*!< Operation timed out (ie: polling on a device)*/
FTDM_NOTIMPL, /*!< Operation not implemented */
FTDM_BREAK, /*!< Request the caller to perform a break (context-dependant, ie: stop getting DNIS/ANI) */
FTDM_EINVAL /*!< Invalid argument */
/*!< Any new return codes should try to mimc unix style error codes, no need to reinvent */
/* Remapping some of the codes that were before */
FTDM_ENOMEM = FTDM_MEMERR, /*!< Memory error */
FTDM_ETIMEDOUT = FTDM_TIMEOUT, /*!< Operation timedout */
FTDM_ENOSYS = FTDM_NOTIMPL, /*!< The function is not implemented */
FTDM_EINVAL, /*!< Invalid argument */
FTDM_ECANCELED, /*!< Operation cancelled */
FTDM_EBUSY, /*!< Device busy */
} ftdm_status_t;
/*! \brief FreeTDM bool type. */

View File

@ -51,6 +51,9 @@ extern "C" {
#include <unistd.h>
#endif
/*! \brief time data type */
typedef uint64_t ftdm_time_t;
/*! \brief sleep x amount of milliseconds */
#ifdef __WINDOWS__
#define ftdm_sleep(x) Sleep(x)
@ -114,6 +117,8 @@ FT_DECLARE(char *) ftdm_strdup(const char *str);
/*! \brief Duplicate string with limit */
FT_DECLARE(char *) ftdm_strndup(const char *str, ftdm_size_t inlen);
/*! \brief Get the current time in milliseconds */
FT_DECLARE(ftdm_time_t) ftdm_current_time_in_ms(void);
#ifdef __cplusplus
} /* extern C */

View File

@ -192,17 +192,6 @@ extern "C" {
#define ftdm_clear_sflag_locked(obj, flag) assert(obj->mutex != NULL); ftdm_mutex_lock(obj->mutex); (obj)->sflags &= ~(flag); ftdm_mutex_unlock(obj->mutex);
#define ftdm_set_state(obj, s) ftdm_channel_set_state(__FILE__, __FUNCTION__, __LINE__, obj, s, 0); \
#define ftdm_set_state_locked(obj, s) \
do { \
ftdm_channel_lock(obj); \
ftdm_channel_set_state(__FILE__, __FUNCTION__, __LINE__, obj, s, 0); \
ftdm_channel_unlock(obj); \
} while(0);
#define ftdm_set_state_r(obj, s, r) r = ftdm_channel_set_state(__FILE__, __FUNCTION__, __LINE__, obj, s, 0);
#ifdef _MSC_VER
/* The while(0) below throws a conditional expression is constant warning */
#pragma warning(disable:4127)
@ -363,15 +352,6 @@ typedef struct {
ftdm_mutex_t *mutex;
} ftdm_dtmf_debug_t;
typedef struct {
const char *file;
const char *func;
int line;
ftdm_channel_state_t state;
ftdm_channel_state_t last_state;
ftdm_time_t time;
} ftdm_channel_history_entry_t;
typedef enum {
FTDM_IOSTATS_ERROR_CRC = (1 << 0),
FTDM_IOSTATS_ERROR_FRAME = (1 << 1),
@ -424,9 +404,11 @@ struct ftdm_channel {
uint32_t native_interval;
uint32_t packet_len;
ftdm_channel_state_t state;
ftdm_state_status_t state_status;
ftdm_channel_state_t last_state;
ftdm_channel_state_t init_state;
ftdm_channel_history_entry_t history[10];
ftdm_channel_indication_t indication;
ftdm_state_history_entry_t history[10];
uint8_t hindex;
ftdm_mutex_t *mutex;
teletone_dtmf_detect_state_t dtmf_detect;
@ -480,6 +462,7 @@ struct ftdm_channel {
ftdm_dtmf_debug_t dtmfdbg;
ftdm_io_dump_t rxdump;
ftdm_io_dump_t txdump;
ftdm_interrupt_t *state_completed_interrupt; /*!< Notify when a state change is completed */
int32_t txdrops;
int32_t rxdrops;
};
@ -517,15 +500,15 @@ struct ftdm_span {
ftdm_span_stop_t stop;
ftdm_channel_sig_read_t sig_read;
ftdm_channel_sig_write_t sig_write;
/* Private I/O data per span. Do not touch unless you are an I/O module */
void *io_data;
ftdm_channel_state_processor_t state_processor; /*!< This guy is called whenever state processing is required */
void *io_data; /*!< Private I/O data per span. Do not touch unless you are an I/O module */
char *type;
char *dtmf_hangup;
size_t dtmf_hangup_len;
ftdm_state_map_t *state_map;
ftdm_caller_data_t default_caller_data;
ftdm_queue_t *pendingchans;
ftdm_queue_t *pendingsignals;
ftdm_queue_t *pendingchans; /*!< Channels pending of state processing */
ftdm_queue_t *pendingsignals; /*!< Signals pending from being delivered to the user */
struct ftdm_span *next;
};
@ -572,11 +555,7 @@ FT_DECLARE(ftdm_status_t) ftdm_fsk_data_add_checksum(ftdm_fsk_data_state_t *stat
FT_DECLARE(ftdm_status_t) ftdm_fsk_data_add_sdmf(ftdm_fsk_data_state_t *state, const char *date, char *number);
FT_DECLARE(ftdm_status_t) ftdm_channel_send_fsk_data(ftdm_channel_t *ftdmchan, ftdm_fsk_data_state_t *fsk_data, float db_level);
FT_DECLARE(ftdm_status_t) ftdm_channel_set_state(const char *file, const char *func, int line,
ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, int wait);
FT_DECLARE(ftdm_status_t) ftdm_span_load_tones(ftdm_span_t *span, const char *mapname);
FT_DECLARE(ftdm_time_t) ftdm_current_time_in_ms(void);
FT_DECLARE(ftdm_status_t) ftdm_channel_use(ftdm_channel_t *ftdmchan);
@ -589,8 +568,6 @@ FT_DECLARE(void) print_hex_bytes(uint8_t *data, ftdm_size_t dlen, char *buf, ftd
FT_DECLARE_NONSTD(int) ftdm_hash_equalkeys(void *k1, void *k2);
FT_DECLARE_NONSTD(uint32_t) ftdm_hash_hashfromstring(void *ky);
FT_DECLARE(ftdm_status_t) ftdm_channel_complete_state(ftdm_channel_t *ftdmchan);
FT_DECLARE(int) ftdm_load_modules(void);
FT_DECLARE(ftdm_status_t) ftdm_unload_modules(void);
@ -606,6 +583,7 @@ FT_DECLARE(int) ftdm_vasprintf(char **ret, const char *fmt, va_list ap);
FT_DECLARE(ftdm_status_t) ftdm_span_close_all(void);
FT_DECLARE(ftdm_status_t) ftdm_channel_open_chan(ftdm_channel_t *ftdmchan);
FT_DECLARE(void) ftdm_ack_indication(ftdm_channel_t *ftdmchan, ftdm_channel_indication_t indication, ftdm_status_t status);
/*!
* \brief Retrieves an event from the span
@ -706,30 +684,6 @@ static __inline__ void ftdm_abort(void)
#endif
}
static __inline__ void ftdm_set_state_all(ftdm_span_t *span, ftdm_channel_state_t state)
{
uint32_t j;
ftdm_mutex_lock(span->mutex);
for(j = 1; j <= span->chan_count; j++) {
if (!FTDM_IS_DCHAN(span->channels[j])) {
ftdm_set_state_locked((span->channels[j]), state);
}
}
ftdm_mutex_unlock(span->mutex);
}
static __inline__ int ftdm_check_state_all(ftdm_span_t *span, ftdm_channel_state_t state)
{
uint32_t j;
for(j = 1; j <= span->chan_count; j++) {
if (span->channels[j]->state != state || ftdm_test_flag(span->channels[j], FTDM_CHANNEL_STATE_CHANGE)) {
return 0;
}
}
return 1;
}
static __inline__ int16_t ftdm_saturated_add(int16_t sample1, int16_t sample2)
{
int addres;

View File

@ -1,134 +0,0 @@
/*
* ftdm_m3ua.h
* freetdm
*
* Created by Shane Burrell on 4/3/08.
* Copyright 2008 Shane Burrell. All rights reserved.
*
* Copyright (c) 2007, Anthony Minessale II, Nenad Corbic
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of the original author; nor the names of any contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
//#include "m3ua_client.h"
#include "freetdm.h"
#ifdef __cplusplus
extern "C" {
#endif
enum e_sigboost_event_id_values
{
SIGBOOST_EVENT_CALL_START = 0x80, /*128*/
SIGBOOST_EVENT_CALL_START_ACK = 0x81, /*129*/
SIGBOOST_EVENT_CALL_START_NACK = 0x82, /*130*/
SIGBOOST_EVENT_CALL_START_NACK_ACK = 0x83, /*131*/
SIGBOOST_EVENT_CALL_ANSWERED = 0x84, /*132*/
SIGBOOST_EVENT_CALL_STOPPED = 0x85, /*133*/
SIGBOOST_EVENT_CALL_STOPPED_ACK = 0x86, /*134*/
SIGBOOST_EVENT_SYSTEM_RESTART = 0x87, /*135*/
SIGBOOST_EVENT_SYSTEM_RESTART_ACK = 0x88, /*136*/
/* Following IDs are ss7boost to sangoma_mgd only. */
SIGBOOST_EVENT_HEARTBEAT = 0x89, /*137*/
SIGBOOST_EVENT_INSERT_CHECK_LOOP = 0x8a, /*138*/
SIGBOOST_EVENT_REMOVE_CHECK_LOOP = 0x8b, /*139*/
SIGBOOST_EVENT_AUTO_CALL_GAP_ABATE = 0x8c, /*140*/
};
enum e_sigboost_release_cause_values
{
SIGBOOST_RELEASE_CAUSE_UNDEFINED = 0,
SIGBOOST_RELEASE_CAUSE_NORMAL = 16,
SIGBOOST_RELEASE_CAUSE_BUSY = 17,
/* probable elimination */
//SIGBOOST_RELEASE_CAUSE_BUSY = 0x91, /* 145 */
//SIGBOOST_RELEASE_CAUSE_CALLED_NOT_EXIST = 0x92, /* 146 */
//SIGBOOST_RELEASE_CAUSE_CIRCUIT_RESET = 0x93, /* 147 */
//SIGBOOST_RELEASE_CAUSE_NOANSWER = 0x94, /* 148 */
};
enum e_sigboost_call_setup_ack_nack_cause_values
{
SIGBOOST_CALL_SETUP_NACK_ALL_CKTS_BUSY = 117, /* unused Q.850 value */
SIGBOOST_CALL_SETUP_NACK_TEST_CKT_BUSY = 118, /* unused Q.850 value */
SIGBOOST_CALL_SETUP_NACK_INVALID_NUMBER = 28,
/* probable elimination */
//SIGBOOST_CALL_SETUP_RESERVED = 0x00,
//SIGBOOST_CALL_SETUP_CIRCUIT_RESET = 0x10,
//SIGBOOST_CALL_SETUP_NACK_CKT_START_TIMEOUT = 0x11,
//SIGBOOST_CALL_SETUP_NACK_AUTO_CALL_GAP = 0x17,
};
typedef enum {
M3UA_SPAN_SIGNALING_M3UA,
M3UA_SPAN_SIGNALING_SS7BOX,
} M3UA_TSpanSignaling;
#define M3UA_SPAN_STRINGS "M3UA", "SS7BOX"
FTDM_STR2ENUM_P(m3ua_str2span, m3ua_span2str, M3UA_TSpanSignaling)
typedef enum {
FTDM_M3UA_RUNNING = (1 << 0)
} ftdm_m3uat_flag_t;
/*typedef struct m3ua_data {
m3uac_connection_t mcon;
m3uac_connection_t pcon;
fio_signal_cb_t signal_cb;
uint32_t flags;
} m3ua_data_t;
*/
/*typedef struct mu3a_link {
ss7bc_connection_t mcon;
ss7bc_connection_t pcon;
fio_signal_cb_t signal_cb;
uint32_t flags;
} ftdm_m3ua_data_t;
*/
ftdm_status_t m3ua_init(ftdm_io_interface_t **zint);
ftdm_status_t m3ua_destroy(void);
ftdm_status_t m3ua_start(ftdm_span_t *span);
#ifdef __cplusplus
}
#endif
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4:
*/

View File

@ -0,0 +1,237 @@
/*
* Copyright (c) 2010, Sangoma Technologies
* Moises Silva <moy@sangoma.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of the original author; nor the names of any contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __FTDM_STATE_H__
#define __FTDM_STATE_H__
/*! \file
* \brief State handling definitions
* \note Most, if not all of the state handling functions assume you have a lock acquired. Touching the channel
* state is a sensitive matter that requires checks and careful thought and is typically a process that
* is not encapsulated within a single function, therefore the lock must be explicitly acquired by the
* caller (most of the time, signaling modules), process states, set a new state and process it, and
* finally unlock the channel. See docs/locking.txt fore more info
*/
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
FTDM_CHANNEL_STATE_DOWN,
FTDM_CHANNEL_STATE_HOLD,
FTDM_CHANNEL_STATE_SUSPENDED,
FTDM_CHANNEL_STATE_DIALTONE,
FTDM_CHANNEL_STATE_COLLECT,
FTDM_CHANNEL_STATE_RING,
FTDM_CHANNEL_STATE_RINGING,
FTDM_CHANNEL_STATE_BUSY,
FTDM_CHANNEL_STATE_ATTN,
FTDM_CHANNEL_STATE_GENRING,
FTDM_CHANNEL_STATE_DIALING,
FTDM_CHANNEL_STATE_GET_CALLERID,
FTDM_CHANNEL_STATE_CALLWAITING,
FTDM_CHANNEL_STATE_RESTART,
FTDM_CHANNEL_STATE_PROCEED,
FTDM_CHANNEL_STATE_PROGRESS,
FTDM_CHANNEL_STATE_PROGRESS_MEDIA,
FTDM_CHANNEL_STATE_UP,
FTDM_CHANNEL_STATE_IDLE,
FTDM_CHANNEL_STATE_TERMINATING,
FTDM_CHANNEL_STATE_CANCEL,
FTDM_CHANNEL_STATE_HANGUP,
FTDM_CHANNEL_STATE_HANGUP_COMPLETE,
FTDM_CHANNEL_STATE_IN_LOOP,
FTDM_CHANNEL_STATE_RESET,
FTDM_CHANNEL_STATE_INVALID
} ftdm_channel_state_t;
#define CHANNEL_STATE_STRINGS "DOWN", "HOLD", "SUSPENDED", "DIALTONE", "COLLECT", \
"RING", "RINGING", "BUSY", "ATTN", "GENRING", "DIALING", "GET_CALLERID", "CALLWAITING", \
"RESTART", "PROCEED", "PROGRESS", "PROGRESS_MEDIA", "UP", "IDLE", "TERMINATING", "CANCEL", \
"HANGUP", "HANGUP_COMPLETE", "IN_LOOP", "RESET", "INVALID"
FTDM_STR2ENUM_P(ftdm_str2ftdm_channel_state, ftdm_channel_state2str, ftdm_channel_state_t)
typedef struct {
const char *file;
const char *func;
int line;
ftdm_channel_state_t state; /*!< Current state (processed or not) */
ftdm_channel_state_t last_state; /*!< Previous state */
ftdm_time_t time; /*!< Time the state was set */
ftdm_time_t end_time; /*!< Time the state processing was completed */
} ftdm_state_history_entry_t;
typedef ftdm_status_t (*ftdm_channel_state_processor_t)(ftdm_channel_t *fchan);
/*!
* \brief Process channel states by invoking the channel state processing routine
* it will keep calling the processing routine while the state status
* is FTDM_STATE_STATUS_NEW, it will not do anything otherwise
*/
FT_DECLARE(ftdm_status_t) ftdm_channel_advance_states(ftdm_channel_t *fchan);
FT_DECLARE(ftdm_status_t) _ftdm_channel_complete_state(const char *file, const char *function, int line, ftdm_channel_t *fchan);
#define ftdm_channel_complete_state(obj) _ftdm_channel_complete_state(__FILE__, __FUNCTION__, __LINE__, obj)
FT_DECLARE(int) ftdm_check_state_all(ftdm_span_t *span, ftdm_channel_state_t state);
/*!
* \brief Status of the current channel state
* \note A given state goes thru several status (yes, states for the state!)
* The order is always FTDM_STATE_STATUS_NEW -> FTDM_STATE_STATUS_PROCESSED -> FTDM_STATUS_COMPLETED
* However, is possible to go from NEW -> COMPLETED directly when the signaling module explicitly changes
* the state of the channel in the middle of processing the current state by calling the ftdm_set_state() API
*
* FTDM_STATE_STATUS_NEW -
* Someone just set the state of the channel, either the signaling module or the user (implicitly through a call API).
* This is accomplished by calling ftdm_channel_set_state() which changes the 'state' and 'last_state' memebers of
* the ftdm_channel_t structure.
*
* FTDM_STATE_STATUS_PROCESSED -
* The signaling module did something based on the new state.
*
* This is accomplished via ftdm_channel_advance_states()
*
* When ftdm_channel_advance_states(), at the very least, if the channel has its state in FTDM_STATE_STATUS_NEW, it
* will move to FTDM_STATE_STATUS_PROCESSED, depending on what the signaling module does during the processing
* the state may move to FTDM_STATE_STATUS_COMPLETED right after or wait for a signaling specific event to complete it.
* It is also possible that more state transitions occur during the execution of ftdm_channel_advance_states() if one
* state processing/completion leads to another state change, the function will not return until the chain of events
* lead to a state that is not in FTDM_STATE_STATUS_NEW
*
* FTDM_STATE_STATUS_COMPLETED -
* The signaling module completed the processing of the state and there is nothing further to be done for this state.
*
* This is accomplished either explicitly by the signaling module by calling ftdm_channel_complete_state() or by
* the signaling module implicitly by trying to set the state of the channel to a new state via ftdm_set_state()
*
* When working with blocking channels (FTDM_CHANNEL_NONBLOCK flag not set), the user thread is signaled and unblocked
* so it can continue.
*
* When a state moves to this status is also possible for a signal FTDM_SIGEVENT_INDICATION_COMPLETED to be delivered
* by the core if the state change was associated to an indication requested by the user,
*/
typedef enum {
FTDM_STATE_STATUS_NEW,
FTDM_STATE_STATUS_PROCESSED,
FTDM_STATE_STATUS_COMPLETED,
FTDM_STATE_STATUS_INVALID
} ftdm_state_status_t;
#define CHANNEL_STATE_STATUS_STRINGS "NEW", "PROCESSED", "COMPLETED", "INVALID"
FTDM_STR2ENUM_P(ftdm_str2ftdm_state_status, ftdm_state_status2str, ftdm_state_status_t)
typedef enum {
ZSM_NONE,
ZSM_UNACCEPTABLE,
ZSM_ACCEPTABLE
} ftdm_state_map_type_t;
typedef enum {
ZSD_INBOUND,
ZSD_OUTBOUND,
} ftdm_state_direction_t;
#define FTDM_MAP_NODE_SIZE 512
#define FTDM_MAP_MAX FTDM_CHANNEL_STATE_INVALID+2
struct ftdm_state_map_node {
ftdm_state_direction_t direction;
ftdm_state_map_type_t type;
ftdm_channel_state_t check_states[FTDM_MAP_MAX];
ftdm_channel_state_t states[FTDM_MAP_MAX];
};
typedef struct ftdm_state_map_node ftdm_state_map_node_t;
struct ftdm_state_map {
ftdm_state_map_node_t nodes[FTDM_MAP_NODE_SIZE];
};
typedef struct ftdm_state_map ftdm_state_map_t;
/*!\brief Set the state for a channel (the channel must be locked when calling this function)
* \note Signaling modules should use ftdm_set_state macro instead
* \note If this function is called with the wait parameter set to a non-zero value, the recursivity
* of the channel lock must be == 1 because the channel will be unlocked/locked when waiting */
FT_DECLARE(ftdm_status_t) ftdm_channel_set_state(const char *file, const char *func, int line,
ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, int wait);
/*!\brief Set the state of a channel immediately and implicitly complete the previous state if needed
* \note FTDM_SIGEVENT_INDICATION_COMPLETED will be sent if the state change
* is associated to some indication (ie FTDM_CHANNEL_INDICATE_PROCEED)
* \note The channel must be locked when calling this function
* */
FT_DECLARE(ftdm_status_t) _ftdm_set_state(const char *file, const char *func, int line,
ftdm_channel_t *fchan, ftdm_channel_state_t state);
#define ftdm_set_state(obj, s) _ftdm_set_state(__FILE__, __FUNCTION__, __LINE__, obj, s); \
/*!\brief This macro is deprecated, signaling modules should always lock the channel themselves anyways since they must
* process first the user pending state changes then set a new state before releasing the lock
* this macro is here for backwards compatibility, DO NOT USE IT in new code since it is *always* wrong to set
* a state in a signaling module without checking and processing the current state first (and for that you must lock the channel)
*/
#define ftdm_set_state_locked(obj, s) \
do { \
ftdm_channel_lock(obj); \
ftdm_channel_set_state(__FILE__, __FUNCTION__, __LINE__, obj, s, 0); \
ftdm_channel_unlock(obj); \
} while(0);
#define ftdm_set_state_r(obj, s, r) r = ftdm_channel_set_state(__FILE__, __FUNCTION__, __LINE__, obj, s, 0);
#define ftdm_set_state_all(span, state) \
do { \
uint32_t _j; \
ftdm_mutex_lock((span)->mutex); \
for(_j = 1; _j <= (span)->chan_count; _j++) { \
if (!FTDM_IS_DCHAN(span->channels[_j])) { \
ftdm_set_state_locked((span->channels[_j]), state); \
} \
} \
ftdm_mutex_unlock((span)->mutex); \
} while (0);
#ifdef __cplusplus
}
#endif
#endif
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4:
*/

View File

@ -69,8 +69,6 @@ extern "C" {
#define FTDM_END -1
#define FTDM_ANY_STATE -1
typedef uint64_t ftdm_time_t;
typedef enum {
FTDM_ENDIAN_BIG = 1,
FTDM_ENDIAN_LITTLE = -1
@ -204,40 +202,6 @@ typedef enum {
FTDM_CHANNEL_FEATURE_IO_STATS = (1<<9), /*!< Channel supports IO statistics (HDLC channels only) */
} ftdm_channel_feature_t;
typedef enum {
FTDM_CHANNEL_STATE_DOWN,
FTDM_CHANNEL_STATE_HOLD,
FTDM_CHANNEL_STATE_SUSPENDED,
FTDM_CHANNEL_STATE_DIALTONE,
FTDM_CHANNEL_STATE_COLLECT,
FTDM_CHANNEL_STATE_RING,
FTDM_CHANNEL_STATE_RINGING,
FTDM_CHANNEL_STATE_BUSY,
FTDM_CHANNEL_STATE_ATTN,
FTDM_CHANNEL_STATE_GENRING,
FTDM_CHANNEL_STATE_DIALING,
FTDM_CHANNEL_STATE_GET_CALLERID,
FTDM_CHANNEL_STATE_CALLWAITING,
FTDM_CHANNEL_STATE_RESTART,
FTDM_CHANNEL_STATE_PROCEED,
FTDM_CHANNEL_STATE_PROGRESS,
FTDM_CHANNEL_STATE_PROGRESS_MEDIA,
FTDM_CHANNEL_STATE_UP,
FTDM_CHANNEL_STATE_IDLE,
FTDM_CHANNEL_STATE_TERMINATING,
FTDM_CHANNEL_STATE_CANCEL,
FTDM_CHANNEL_STATE_HANGUP,
FTDM_CHANNEL_STATE_HANGUP_COMPLETE,
FTDM_CHANNEL_STATE_IN_LOOP,
FTDM_CHANNEL_STATE_RESET,
FTDM_CHANNEL_STATE_INVALID
} ftdm_channel_state_t;
#define CHANNEL_STATE_STRINGS "DOWN", "HOLD", "SUSPENDED", "DIALTONE", "COLLECT", \
"RING", "RINGING", "BUSY", "ATTN", "GENRING", "DIALING", "GET_CALLERID", "CALLWAITING", \
"RESTART", "PROCEED", "PROGRESS", "PROGRESS_MEDIA", "UP", "IDLE", "TERMINATING", "CANCEL", \
"HANGUP", "HANGUP_COMPLETE", "IN_LOOP", "RESET", "INVALID"
FTDM_STR2ENUM_P(ftdm_str2ftdm_channel_state, ftdm_channel_state2str, ftdm_channel_state_t)
/*!< Channel flags. This used to be an enum but we reached the 32bit limit for enums, is safer this way */
#define FTDM_CHANNEL_CONFIGURED (1ULL << 0)
#define FTDM_CHANNEL_READY (1ULL << 1)
@ -260,9 +224,16 @@ FTDM_STR2ENUM_P(ftdm_str2ftdm_channel_state, ftdm_channel_state2str, ftdm_channe
#define FTDM_CHANNEL_OUTBOUND (1ULL << 18)
#define FTDM_CHANNEL_SUSPENDED (1ULL << 19)
#define FTDM_CHANNEL_3WAY (1ULL << 20)
/* this 3 flags are really nonsense used by boost module only, as soon
* as we deprecate/delete boost module we can get rid of them
* ==================
* */
#define FTDM_CHANNEL_PROGRESS (1ULL << 21)
#define FTDM_CHANNEL_MEDIA (1ULL << 22)
#define FTDM_CHANNEL_ANSWERED (1ULL << 23)
/* ================== */
#define FTDM_CHANNEL_MUTE (1ULL << 24)
#define FTDM_CHANNEL_USE_RX_GAIN (1ULL << 25)
#define FTDM_CHANNEL_USE_TX_GAIN (1ULL << 26)
@ -273,33 +244,14 @@ FTDM_STR2ENUM_P(ftdm_str2ftdm_channel_state, ftdm_channel_state2str, ftdm_channe
#define FTDM_CHANNEL_TX_DISABLED (1ULL << 31)
/*!< The user knows about a call in this channel */
#define FTDM_CHANNEL_CALL_STARTED (1ULL << 32)
/*!< The user wants non-blocking operations in the channel */
#define FTDM_CHANNEL_NONBLOCK (1ULL << 33)
/*!< There is a pending acknowledge for an indication */
#define FTDM_CHANNEL_IND_ACK_PENDING (1ULL << 34)
/*!< There is someone blocking in the channel waiting for state completion */
#define FTDM_CHANNEL_BLOCKING (1ULL << 35)
typedef enum {
ZSM_NONE,
ZSM_UNACCEPTABLE,
ZSM_ACCEPTABLE
} ftdm_state_map_type_t;
typedef enum {
ZSD_INBOUND,
ZSD_OUTBOUND,
} ftdm_state_direction_t;
#define FTDM_MAP_NODE_SIZE 512
#define FTDM_MAP_MAX FTDM_CHANNEL_STATE_INVALID+2
struct ftdm_state_map_node {
ftdm_state_direction_t direction;
ftdm_state_map_type_t type;
ftdm_channel_state_t check_states[FTDM_MAP_MAX];
ftdm_channel_state_t states[FTDM_MAP_MAX];
};
typedef struct ftdm_state_map_node ftdm_state_map_node_t;
struct ftdm_state_map {
ftdm_state_map_node_t nodes[FTDM_MAP_NODE_SIZE];
};
typedef struct ftdm_state_map ftdm_state_map_t;
#include "ftdm_state.h"
typedef enum ftdm_channel_hw_link_status {
FTDM_HW_LINK_DISCONNECTED = 0,

View File

@ -1,62 +0,0 @@
/* WARNING WORK IN PROGRESS
* mstm3ua.c
* mstss7d port
*
* Created by Shane Burrell on 2/2/08.
* Copyright 2008 Shane Burrell. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of the original author; nor the names of any contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "mstm3ua.h"
int build_m3ua_hdr(unsigned char len,unsigned char *bytemsg)
{
*bytemsg++ = M_VERSION_REL1; // 1 Verison
//bytemsg[1] = 0x00; // 2 RESERVED
//bytemsg[2] = M_CLASS_XFER; // 3 Msg Class
//SS7 BOX Kludge
*bytemsg++ = 0x01; // 2 RESERVED
*bytemsg++ = 0x00; // 2 RESERVED
*bytemsg++ = M_TYPE_DATA ; // 4 Msg Type
*bytemsg++ = len; // 5 Msg LENGTH 81 32bit field
*bytemsg++ = 0x00; // 6
*bytemsg++ = 0x00; // 7
*bytemsg++ = 0x00; // 8
return(0);
};

View File

@ -1,96 +0,0 @@
/*
* mstm3ua.h
* mstss7d
*
* Created by Shane Burrell on 3/2/08.
* Copyright 2008 Shane Burrell. All rights reserved.
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of the original author; nor the names of any contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
typedef unsigned long m3ua_ulong;
typedef unsigned short m3ua_ushort;
typedef unsigned char m3ua_uchar;
typedef unsigned char u8;
typedef unsigned short u16; /* Note: multi-byte values are little-endian */
typedef unsigned long u32;
#define M_TAG_NETWORK_APPEARANCE 1
#define M_TAG_PROTOCOL_DATA 3
#define M_TAG_INFO_STRING 4
#define M_TAG_AFFECTED_DPC 5
#define M_TAG_ROUTING_CONTEXT 6
#define M_TAG_DIAGNOSTIC_INFORMATION 7
#define M_TAG_HEARTBEAT_DATA 8
#define M_TAG_UNAVAILABILITY_CAUSE 9
#define M_TAG_REASON 10
#define M_TAG_TRAFFIC_MODE_TYPE 11
#define M_TAG_ERROR_CODE 12
#define M_TAG_STATUS_TYPE 13
#define M_TAG_CONGESTED_INDICATIONS 14
#define M_VERSION_REL1 1
#define M_CLASS_MGMT 0x00
#define M_CLASS_XFER 0x01
#define M_CLASS_SSNM 0x02
#define M_CLASS_ASPSM 0x03
#define M_CLASS_ASPTM 0x04
#define M_CLASS_RKM 0x09
#define M_TYPE_ERR (0|M_CLASS_MGMT
#define M_TYPE_NTFY (1|M_CLASS_XFER)
#define M_TYPE_DATA (1|M_CLASS_XFER)
#define M_TYPE_DUNA (1|M_CLASS_SSNM)
#define M_TYPE_DAVA (2|M_CLASS_SSNM)
#define M_TYPE_DUAD (3|M_CLASS_SSNM)
#define M_TYPE_SCON (4|M_CLASS_SSNM)
#define M_TYPE_DUPU (5|M_CLASS_SSNM)
#define M_TYPE_UP (1|M_CLASS_ASPSM)
#define M_TYPE_DOWN (2|M_CLASS_ASPSM)
#define M_TYPE_BEAT (3|M_CLASS_ASPSM)
#define M_TYPE_UP_ACK (4|M_CLASS_ASPSM)
#define M_TYPE_DOWN_ACK (5|M_CLASS_ASPSM)
#define M_TYPE_BEAT_ACK (6|M_CLASS_ASPSM)
#define M_TYPE_ACTIVE (1|M_CLASS_ASPTM)
#define M_TYPE_INACTIVE (2|M_CLASS_ASPTM)
#define M_TYPE_ACTIVE_ACK (3|M_CLASS_ASPTM)
#define M_TYPE_INACTIVE_ACK (4|M_CLASS_ASPTM)
#define M_CLASS_MASK 0xff00
#define M_TYPE_MASK 0x00ff

View File

@ -1,333 +0,0 @@
/*
* m3ua_client.c
* freetdm
*
* Created by Shane Burrell on 4/3/08.
* Copyright 2008 Shane Burrell. All rights reserved.
*
*
* Copyright (c) 2007, Anthony Minessale II, Nenad Corbic
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of the original author; nor the names of any contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#if HAVE_NETDB_H
#include <netdb.h>
#endif
#include "freetdm.h"
#include <m3ua_client.h>
#ifndef HAVE_GETHOSTBYNAME_R
extern int gethostbyname_r (const char *__name,
struct hostent *__result_buf,
char *__buf, size_t __buflen,
struct hostent **__result,
int *__h_errnop);
#endif
struct m3uac_map {
uint32_t event_id;
const char *name;
};
static struct m3uac_map m3uac_table[] = {
{M3UA_EVENT_CALL_START, "CALL_START"},
{M3UA_EVENT_CALL_START_ACK, "CALL_START_ACK"},
{M3UA_EVENT_CALL_START_NACK, "CALL_START_NACK"},
{M3UA_EVENT_CALL_START_NACK_ACK, "CALL_START_NACK_ACK"},
{M3UA_EVENT_CALL_ANSWERED, "CALL_ANSWERED"},
{M3UA_EVENT_CALL_STOPPED, "CALL_STOPPED"},
{M3UA_EVENT_CALL_STOPPED_ACK, "CALL_STOPPED_ACK"},
{M3UA_EVENT_SYSTEM_RESTART, "SYSTEM_RESTART"},
{M3UA_EVENT_SYSTEM_RESTART_ACK, "SYSTEM_RESTART_ACK"},
{M3UA_EVENT_HEARTBEAT, "HEARTBEAT"},
{M3UA_EVENT_INSERT_CHECK_LOOP, "LOOP START"},
{M3UA_EVENT_REMOVE_CHECK_LOOP, "LOOP STOP"}
};
static int create_conn_socket(m3uac_connection_t *mcon, char *local_ip, int local_port, char *ip, int port)
{
int rc;
struct hostent *result, *local_result;
char buf[512], local_buf[512];
int err = 0;
memset(&mcon->remote_hp, 0, sizeof(mcon->remote_hp));
memset(&mcon->local_hp, 0, sizeof(mcon->local_hp));
mcon->socket = socket(AF_INET, SOCK_SEQPACKET, IPPROTO_SCTP);
ftdm_log(FTDM_LOG_DEBUG, "Creating L=%s:%d R=%s:%d\n",
local_ip,local_port,ip,port);
if (mcon->socket >= 0) {
int flag;
flag = 1;
gethostbyname_r(ip, &mcon->remote_hp, buf, sizeof(buf), &result, &err);
gethostbyname_r(local_ip, &mcon->local_hp, local_buf, sizeof(local_buf), &local_result, &err);
if (result && local_result) {
mcon->remote_addr.sin_family = mcon->remote_hp.h_addrtype;
memcpy((char *) &mcon->remote_addr.sin_addr.s_addr, mcon->remote_hp.h_addr_list[0], mcon->remote_hp.h_length);
mcon->remote_addr.sin_port = htons(port);
mcon->local_addr.sin_family = mcon->local_hp.h_addrtype;
memcpy((char *) &mcon->local_addr.sin_addr.s_addr, mcon->local_hp.h_addr_list[0], mcon->local_hp.h_length);
mcon->local_addr.sin_port = htons(local_port);
setsockopt(mcon->socket, IPPROTO_SCTP, SCTP_NODELAY, (char *)&flag, sizeof(int));
rc=listen(mcon->socket,100);
if (rc) {
close(mcon->socket);
mcon->socket = -1;
}
}
}
ftdm_mutex_create(&mcon->mutex);
return mcon->socket;
}
int m3uac_connection_close(m3uac_connection_t *mcon)
{
if (mcon->socket > -1) {
close(mcon->socket);
}
ftdm_mutex_lock(mcon->mutex);
ftdm_mutex_unlock(mcon->mutex);
ftdm_mutex_destroy(&mcon->mutex);
memset(mcon, 0, sizeof(*mcon));
mcon->socket = -1;
return 0;
}
int m3uac_connection_open(m3uac_connection_t *mcon, char *local_ip, int local_port, char *ip, int port)
{
create_conn_socket(mcon, local_ip, local_port, ip, port);
return mcon->socket;
}
int m3uac_exec_command(m3uac_connection_t *mcon, int span, int chan, int id, int cmd, int cause)
{
m3uac_event_t oevent;
int retry = 5;
m3uac_event_init(&oevent, cmd, chan, span);
oevent.release_cause = cause;
if (cmd == SIGBOOST_EVENT_SYSTEM_RESTART) {
mcon->rxseq_reset = 1;
mcon->txseq = 0;
mcon->rxseq = 0;
mcon->txwindow = 0;
}
if (id >= 0) {
oevent.call_setup_id = id;
}
while (m3uac_connection_write(mcon, &oevent) <= 0) {
if (--retry <= 0) {
ftdm_log(FTDM_LOG_CRIT, "Failed to tx on M3UA socket: %s\n", strerror(errno));
return -1;
} else {
ftdm_log(FTDM_LOG_WARNING, "Failed to tx on M3UA socket: %s :retry %i\n", strerror(errno), retry);
ftdm_sleep(1);
}
}
return 0;
}
m3uac_event_t *m3uac_connection_read(m3uac_connection_t *mcon, int iteration)
{
unsigned int fromlen = sizeof(struct sockaddr_in);
int bytes = 0;
bytes = recvfrom(mcon->socket, &mcon->event, sizeof(mcon->event), MSG_DONTWAIT,
(struct sockaddr *) &mcon->local_addr, &fromlen);
if (bytes == sizeof(mcon->event) || bytes == (sizeof(mcon->event)-sizeof(uint32_t))) {
if (mcon->rxseq_reset) {
if (mcon->event.event_id == SIGBOOST_EVENT_SYSTEM_RESTART_ACK) {
ftdm_log(FTDM_LOG_DEBUG, "Rx sync ok\n");
mcon->rxseq = mcon->event.fseqno;
return &mcon->event;
}
errno=EAGAIN;
ftdm_log(FTDM_LOG_DEBUG, "Waiting for rx sync...\n");
return NULL;
}
mcon->txwindow = mcon->txseq - mcon->event.bseqno;
mcon->rxseq++;
if (mcon->rxseq != mcon->event.fseqno) {
ftdm_log(FTDM_LOG_CRIT, "Invalid Sequence Number Expect=%i Rx=%i\n", mcon->rxseq, mcon->event.fseqno);
return NULL;
}
return &mcon->event;
} else {
if (iteration == 0) {
ftdm_log(FTDM_LOG_CRIT, "Invalid Event length from boost rxlen=%i evsz=%i\n", bytes, sizeof(mcon->event));
return NULL;
}
}
return NULL;
}
m3uac_event_t *m3uac_connection_readp(m3uac_connection_t *mcon, int iteration)
{
unsigned int fromlen = sizeof(struct sockaddr_in);
int bytes = 0;
bytes = recvfrom(mcon->socket, &mcon->event, sizeof(mcon->event), MSG_DONTWAIT, (struct sockaddr *) &mcon->local_addr, &fromlen);
if (bytes == sizeof(mcon->event) || bytes == (sizeof(mcon->event)-sizeof(uint32_t))) {
return &mcon->event;
} else {
if (iteration == 0) {
ftdm_log(FTDM_LOG_CRIT, "Critical Error: PQ Invalid Event lenght from boost rxlen=%i evsz=%i\n", bytes, sizeof(mcon->event));
return NULL;
}
}
return NULL;
}
int m3uac_connection_write(m3uac_connection_t *mcon, ss7bc_event_t *event)
{
int err;
if (!event || mcon->socket < 0 || !mcon->mutex) {
ftdm_log(FTDM_LOG_DEBUG, "Critical Error: No Event Device\n");
return -EINVAL;
}
if (event->span > 16 || event->chan > 31) {
ftdm_log(FTDM_LOG_CRIT, "Critical Error: TX Cmd=%s Invalid Span=%i Chan=%i\n", m3uac_event_id_name(event->event_id), event->span,event->chan);
return -1;
}
gettimeofday(&event->tv,NULL);
ftdm_mutex_lock(mcon->mutex);
event->fseqno = mcon->txseq++;
event->bseqno = mcon->rxseq;
err = sendto(mcon->socket, event, sizeof(m3uac_event_t), 0, (struct sockaddr *) &mcon->remote_addr, sizeof(mcon->remote_addr));
ftdm_mutex_unlock(mcon->mutex);
if (err != sizeof(m3uac_event_t)) {
err = -1;
}
ftdm_log(FTDM_LOG_DEBUG, "TX EVENT: %s:(%X) [w%dg%d] Rc=%i CSid=%i Seq=%i Cd=[%s] Ci=[%s]\n",
m3uac_event_id_name(event->event_id),
event->event_id,
event->span+1,
event->chan+1,
event->release_cause,
event->call_setup_id,
event->fseqno,
(event->called_number_digits_count ? (char *) event->called_number_digits : "N/A"),
(event->calling_number_digits_count ? (char *) event->calling_number_digits : "N/A")
);
return err;
}
void m3uac_call_init(m3uac_event_t *event, const char *calling, const char *called, int setup_id)
{
memset(event, 0, sizeof(m3uac_event_t));
event->event_id = M3UA_EVENT_CALL_START;
if (calling) {
strncpy((char*)event->calling_number_digits, calling, sizeof(event->calling_number_digits)-1);
event->calling_number_digits_count = strlen(calling);
}
if (called) {
strncpy((char*)event->called_number_digits, called, sizeof(event->called_number_digits)-1);
event->called_number_digits_count = strlen(called);
}
event->call_setup_id = setup_id;
}
void m3uac_event_init(m3uac_event_t *event, m3uac_event_id_t event_id, int chan, int span)
{
memset(event, 0, sizeof(ss7bc_event_t));
event->event_id = event_id;
event->chan = chan;
event->span = span;
}
const char *m3uac_event_id_name(uint32_t event_id)
{
unsigned int x;
const char *ret = NULL;
for (x = 0 ; x < sizeof(m3uac_table)/sizeof(struct m3uac_map); x++) {
if (m3uac_table[x].event_id == event_id) {
ret = m3uac_table[x].name;
break;
}
}
return ret;
}
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4:
*/

View File

@ -1,164 +0,0 @@
/*
* m3ua_client.h
* freetdm
*
* Created by Shane Burrell on 4/3/08.
* Copyright 2008 Shane Burrell. All rights reserved.
*
* Copyright (c) 2007, Anthony Minessale II, Nenad Corbic
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of the original author; nor the names of any contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <ctype.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
// Fix this for portability
#include <sctp.h>
//#include <netinet/sctp.h>
#include <arpa/inet.h>
#include <stdarg.h>
#include <netdb.h>
//#include <sigboost.h>
#include <sys/time.h>
#define MAX_DIALED_DIGITS 31
#define MAX_CALLING_NAME 31
/* Next two defines are used to create the range of values for call_setup_id
* in the t_sigboost structure.
* 0..((CORE_MAX_SPANS * CORE_MAX_CHAN_PER_SPAN) - 1) */
#define CORE_MAX_SPANS 200
#define CORE_MAX_CHAN_PER_SPAN 30
#define MAX_PENDING_CALLS CORE_MAX_SPANS * CORE_MAX_CHAN_PER_SPAN
/* 0..(MAX_PENDING_CALLS-1) is range of call_setup_id below */
#define SIZE_RDNIS 80
//#undef MSGWINDOW
#define MSGWINDOW
typedef struct
{
uint32_t event_id;
uint32_t fseqno;
#ifdef MSGWINDOW
uint32_t bseqno;
#endif
uint16_t call_setup_id;
uint32_t trunk_group;
uint32_t span;
uint32_t chan;
uint8_t called_number_digits_count;
char called_number_digits [MAX_DIALED_DIGITS + 1]; /* it's a null terminated string */
uint8_t calling_number_digits_count; /* it's an array */
char calling_number_digits [MAX_DIALED_DIGITS + 1]; /* it's a null terminated string */
uint8_t release_cause;
struct timeval tv;
/* ref. Q.931 Table 4-11 and Q.951 Section 3 */
uint8_t calling_number_screening_ind;
uint8_t calling_number_presentation;
char redirection_string [SIZE_RDNIS]; /* it's a null terminated string */
} t_m3ua;
typedef t_m3ua m3uac_event_t;
typedef uint32_t m3uac_event_id_t;
typedef struct m3uac_ip_cfg
{
char local_ip[25];
int local_port;
char remote_ip[25];
int remote_port;
}m3uac_ip_cfg_t;
struct m3uac_connection {
ftdm_socket_t socket;
struct sockaddr_in local_addr;
struct sockaddr_in remote_addr;
m3uac_event_t event;
struct hostent remote_hp;
struct hostent local_hp;
unsigned int flags;
ftdm_mutex_t *mutex;
FILE *log;
unsigned int txseq;
unsigned int rxseq;
unsigned int txwindow;
unsigned int rxseq_reset;
m3uac_ip_cfg_t cfg;
uint32_t hb_elapsed;
int up;
};
typedef enum {
MSU_FLAG_EVENT = (1 << 0)
} m3uac_flag_t;
typedef struct m3uac_connection m3uac_connection_t;
static inline void sctp_no_nagle(int socket)
{
//int flag = 1;
//setsockopt(socket, IPPROTO_SCTP, SCTP_NODELAY, (char *) &flag, sizeof(int));
}
int m3uac_connection_close(m3uac_connection_t *mcon);
int m3uac_connection_open(m3uac_connection_t *mcon, char *local_ip, int local_port, char *ip, int port);
m3uac_event_t *m3uac_connection_read(m3uac_connection_t *mcon, int iteration);
m3uac_event_t *m3uac_connection_readp(m3uac_connection_t *mcon, int iteration);
int m3uac_connection_write(m3uac_connection_t *mcon, m3uac_event_t *event);
void m3uac_event_init(m3uac_event_t *event, m3uac_event_id_t event_id, int chan, int span);
void m3uac_call_init(m3uac_event_t *event, const char *calling, const char *called, int setup_id);
const char *m3uac_event_id_name(uint32_t event_id);
int m3uac_exec_command(m3uac_connection_t *mcon, int span, int chan, int id, int cmd, int cause);
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:t
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4:
*/

View File

@ -1,60 +0,0 @@
/*
* testm3ua.c
* freetdm
*
* Created by Shane Burrell on 4/8/08.
* Copyright 2008 __MyCompanyName__. All rights reserved.
*
*/
#include "testm3ua.h"
#include "freetdm.h"
#include "ftdm_m3ua.h"
static FIO_SIGNAL_CB_FUNCTION(on_signal)
{
return FTDM_FAIL;
}
int main(int argc, char *argv[])
{
ftdm_span_t *span;
//m3ua_data_t *data;
ftdm_global_set_default_logger(FTDM_LOG_LEVEL_DEBUG);
if (argc < 5) {
printf("more args needed\n");
exit(-1);
}
if (ftdm_global_init() != FTDM_SUCCESS) {
fprintf(stderr, "Error loading FreeTDM\n");
exit(-1);
}
printf("FreeTDM loaded\n");
if (ftdm_span_find(atoi(argv[1]), &span) != FTDM_SUCCESS) {
fprintf(stderr, "Error finding FreeTDM span\n");
goto done;
}
if (ftdm_m3ua_configure_span(span) == FTDM_SUCCESS) {
//data = span->signal_data;
ftdm_m3ua_start(span);
} else {
fprintf(stderr, "Error starting M3UA\n");
goto done;
}
//while(ftdm_test_flag(data, FTDM_M3UA_RUNNING)) {
// ftdm_sleep(1 * 1000);
//}
done:
ftdm_global_destroy();
}

View File

@ -2,78 +2,158 @@
#include <signal.h>
#include <stdlib.h>
static int R = 0;
static ftdm_mutex_t *mutex = NULL;
static volatile int running = 0;
static ftdm_mutex_t *the_mutex = NULL;
static ftdm_channel_t *fchan = NULL;
static ftdm_channel_indication_t indication = FTDM_CHANNEL_INDICATE_NONE;
static FIO_SIGNAL_CB_FUNCTION(on_r2_signal)
{
int chanid = ftdm_channel_get_ph_id(sigmsg->channel);
ftdm_log(FTDM_LOG_DEBUG, "Got R2 channel sig [%s] in channel\n", ftdm_signal_event2str(sigmsg->event_id), chanid);
return FTDM_SUCCESS;
ftdm_log(FTDM_LOG_DEBUG, "Got R2 channel sig [%s] in channel\n", ftdm_signal_event2str(sigmsg->event_id), chanid);
switch (sigmsg->event_id) {
case FTDM_SIGEVENT_START:
{
ftdm_mutex_lock(the_mutex);
if (!fchan) {
fchan = sigmsg->channel;
indication = FTDM_CHANNEL_INDICATE_PROCEED;
}
ftdm_mutex_unlock(the_mutex);
}
break;
case FTDM_SIGEVENT_INDICATION_COMPLETED:
{
ftdm_channel_indication_t ind = FTDM_CHANNEL_INDICATE_NONE;
if (sigmsg->ev_data.indication_completed.indication == FTDM_CHANNEL_INDICATE_PROCEED) {
ftdm_log(FTDM_LOG_DEBUG, "Proceed indication result = %d\n", sigmsg->ev_data.indication_completed.status);
ind = FTDM_CHANNEL_INDICATE_PROGRESS;
} else if (sigmsg->ev_data.indication_completed.indication == FTDM_CHANNEL_INDICATE_PROGRESS) {
ftdm_log(FTDM_LOG_DEBUG, "Progress indication result = %d\n", sigmsg->ev_data.indication_completed.status);
ind = FTDM_CHANNEL_INDICATE_PROGRESS_MEDIA;
} else if (sigmsg->ev_data.indication_completed.indication == FTDM_CHANNEL_INDICATE_PROGRESS_MEDIA) {
ftdm_log(FTDM_LOG_DEBUG, "Progress media indication result = %d\n", sigmsg->ev_data.indication_completed.status);
ind = FTDM_CHANNEL_INDICATE_ANSWER;
} else if (sigmsg->ev_data.indication_completed.indication == FTDM_CHANNEL_INDICATE_ANSWER) {
ftdm_log(FTDM_LOG_DEBUG, "Answer indication result = %d\n", sigmsg->ev_data.indication_completed.status);
} else {
ftdm_log(FTDM_LOG_DEBUG, "Unexpected indication, result = %d\n", sigmsg->ev_data.indication_completed.status);
exit(1);
}
ftdm_mutex_lock(the_mutex);
if (fchan) {
indication = ind;
}
ftdm_mutex_unlock(the_mutex);
}
break;
case FTDM_SIGEVENT_STOP:
{
ftdm_channel_call_hangup(sigmsg->channel);
}
break;
case FTDM_SIGEVENT_RELEASED:
{
ftdm_mutex_lock(the_mutex);
if (fchan && fchan == sigmsg->channel) {
fchan = NULL;
}
ftdm_mutex_unlock(the_mutex);
}
break;
default:
break;
}
return FTDM_SUCCESS;
}
static void handle_SIGINT(int sig)
static void stop_test(int sig)
{
ftdm_mutex_lock(mutex);
R = 0;
ftdm_mutex_unlock(mutex);
return;
running = 0;
}
int main(int argc, char *argv[])
{
ftdm_span_t *span;
ftdm_mutex_create(&mutex);
ftdm_global_set_default_logger(FTDM_LOG_LEVEL_DEBUG);
ftdm_conf_parameter_t parameters[20];
ftdm_mutex_create(&the_mutex);
if (argc < 2) {
printf("umm no\n");
exit(-1);
exit(1);
}
ftdm_global_set_default_logger(FTDM_LOG_LEVEL_DEBUG);
if (ftdm_global_init() != FTDM_SUCCESS) {
fprintf(stderr, "Error loading FreeTDM\n");
exit(-1);
exit(1);
}
ftdm_global_configuration();
printf("FreeTDM loaded\n");
if (ftdm_span_find(atoi(argv[1]), &span) != FTDM_SUCCESS) {
fprintf(stderr, "Error finding FreeTDM span\n");
if (ftdm_span_find_by_name(argv[1], &span) != FTDM_SUCCESS) {
fprintf(stderr, "Error finding FreeTDM span %s\n", argv[1]);
goto done;
}
/* testing non-blocking operation */
//ftdm_span_set_blocking_mode(span, FTDM_FALSE);
parameters[0].var = "variant";
parameters[0].val = "br";
if (ftdm_configure_span(span, "r2", on_r2_signal,
"variant", "mx",
"max_ani", 10,
"max_dnis", 4,
"logging", "all",
FTDM_TAG_END) == FTDM_SUCCESS) {
parameters[1].var = "max_ani";
parameters[1].val = "4";
parameters[2].var = "max_dnis";
parameters[2].val = "4";
parameters[3].var = "logging";
parameters[3].val = "all";
parameters[4].var = NULL;
parameters[4].val = NULL;
if (ftdm_configure_span_signaling(span, "r2", on_r2_signal, parameters) == FTDM_SUCCESS) {
ftdm_span_start(span);
} else {
fprintf(stderr, "Error starting R2 span\n");
goto done;
}
signal(SIGINT, handle_SIGINT);
ftdm_mutex_lock(mutex);
R = 1;
ftdm_mutex_unlock(mutex);
while(R) {
ftdm_sleep(1 * 1000);
running = 1;
signal(SIGINT, stop_test);
while(running) {
ftdm_sleep(20);
if (fchan && indication != FTDM_CHANNEL_INDICATE_NONE) {
ftdm_channel_t *lchan = NULL;
ftdm_channel_indication_t ind = FTDM_CHANNEL_INDICATE_NONE;
ftdm_time_t start, stop, diff;
ftdm_mutex_lock(the_mutex);
ind = indication;
indication = FTDM_CHANNEL_INDICATE_NONE;
lchan = fchan;
ftdm_mutex_unlock(the_mutex);
start = ftdm_current_time_in_ms();
ftdm_channel_call_indicate(lchan, ind);
stop = ftdm_current_time_in_ms();
diff = stop - start;
ftdm_log(FTDM_LOG_DEBUG, "Setting indication %s took %llums\n",
ftdm_channel_indication2str(ind), diff);
}
}
done:
done:
ftdm_global_destroy();
return 1;
return 0;
}
/* For Emacs: