1561 lines
53 KiB
C
1561 lines
53 KiB
C
/*
|
|
* Copyright (c) 2010, Sangoma Technologies
|
|
* David Yat Sin <davidy@sangoma.com>
|
|
* 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 "ftmod_sangoma_isdn.h"
|
|
|
|
#ifdef FTDM_DEBUG_CHAN_MEMORY
|
|
#include <sys/mman.h>
|
|
#endif
|
|
|
|
|
|
static void *ftdm_sangoma_isdn_run(ftdm_thread_t *me, void *obj);
|
|
static ftdm_status_t ftdm_sangoma_isdn_stop(ftdm_span_t *span);
|
|
static ftdm_status_t ftdm_sangoma_isdn_start(ftdm_span_t *span);
|
|
static ftdm_status_t ftdm_sangoma_isdn_dtmf(ftdm_channel_t *ftdmchan, const char* dtmf);
|
|
|
|
ftdm_channel_t* ftdm_sangoma_isdn_process_event_states(ftdm_span_t *span, sngisdn_event_data_t *sngisdn_event);
|
|
static void ftdm_sangoma_isdn_process_phy_events(ftdm_span_t *span, ftdm_oob_event_t event);
|
|
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);
|
|
|
|
static ftdm_io_interface_t g_sngisdn_io_interface;
|
|
static sng_isdn_event_interface_t g_sngisdn_event_interface;
|
|
|
|
ftdm_sngisdn_data_t g_sngisdn_data;
|
|
|
|
SNGISDN_ENUM_NAMES(SNGISDN_TRANSFER_TYPE_NAMES, SNGISDN_TRANSFER_TYPE_STRINGS)
|
|
SNGISDN_STR2ENUM(ftdm_str2sngisdn_transfer_type, sngisdn_transfer_type2str, sngisdn_transfer_type_t, SNGISDN_TRANSFER_TYPE_NAMES, SNGISDN_TRANSFER_INVALID)
|
|
|
|
ftdm_state_map_t sangoma_isdn_state_map = {
|
|
{
|
|
{
|
|
ZSD_INBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_ANY_STATE, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_RESET, FTDM_CHANNEL_STATE_RESTART, FTDM_END}
|
|
},
|
|
{
|
|
ZSD_INBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_CHANNEL_STATE_RESET, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_DOWN, FTDM_END}
|
|
},
|
|
{
|
|
ZSD_INBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_CHANNEL_STATE_RESTART, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_DOWN, FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_HANGUP, FTDM_END}
|
|
},
|
|
{
|
|
ZSD_INBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_CHANNEL_STATE_CANCEL, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_HANGUP_COMPLETE, FTDM_END}
|
|
},
|
|
{
|
|
ZSD_INBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_CHANNEL_STATE_SUSPENDED, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_DOWN, FTDM_CHANNEL_STATE_COLLECT, FTDM_CHANNEL_STATE_RING, FTDM_CHANNEL_STATE_DIALING,
|
|
FTDM_CHANNEL_STATE_RESTART, FTDM_CHANNEL_STATE_PROGRESS, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_CHANNEL_STATE_UP,
|
|
FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_CANCEL, FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_HANGUP_COMPLETE, FTDM_END}
|
|
},
|
|
{
|
|
ZSD_INBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_CHANNEL_STATE_DOWN, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_COLLECT, FTDM_CHANNEL_STATE_RING, FTDM_CHANNEL_STATE_GET_CALLERID, FTDM_CHANNEL_STATE_IN_LOOP, FTDM_END}
|
|
},
|
|
{
|
|
ZSD_INBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_CHANNEL_STATE_GET_CALLERID, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_RING, FTDM_CHANNEL_STATE_CANCEL, FTDM_CHANNEL_STATE_IN_LOOP, FTDM_END}
|
|
},
|
|
{
|
|
ZSD_INBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_CHANNEL_STATE_COLLECT, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_RING, FTDM_CHANNEL_STATE_CANCEL, FTDM_CHANNEL_STATE_IN_LOOP, FTDM_END}
|
|
},
|
|
{
|
|
ZSD_INBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_CHANNEL_STATE_IN_LOOP, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_COLLECT, FTDM_CHANNEL_STATE_DOWN, FTDM_END}
|
|
},
|
|
{
|
|
ZSD_INBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_CHANNEL_STATE_RING, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_PROCEED, FTDM_CHANNEL_STATE_RINGING, FTDM_CHANNEL_STATE_PROGRESS, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_END}
|
|
},
|
|
{
|
|
ZSD_INBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_CHANNEL_STATE_PROCEED, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_RINGING, FTDM_CHANNEL_STATE_PROGRESS, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_CHANNEL_STATE_UP, FTDM_CHANNEL_STATE_TRANSFER, FTDM_END}
|
|
},
|
|
{
|
|
ZSD_INBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_CHANNEL_STATE_RINGING, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_PROGRESS, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_CHANNEL_STATE_UP, FTDM_END},
|
|
},
|
|
{
|
|
ZSD_INBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_CHANNEL_STATE_PROGRESS, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_CHANNEL_STATE_UP, FTDM_END},
|
|
},
|
|
{
|
|
ZSD_INBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_UP, FTDM_END},
|
|
},
|
|
{
|
|
ZSD_INBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_CHANNEL_STATE_UP, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_TRANSFER, FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_END},
|
|
},
|
|
|
|
{
|
|
ZSD_INBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_CHANNEL_STATE_TRANSFER, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_PROCEED, FTDM_CHANNEL_STATE_PROGRESS, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_CHANNEL_STATE_UP, FTDM_CHANNEL_STATE_TERMINATING,FTDM_END},
|
|
},
|
|
{
|
|
ZSD_INBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_CHANNEL_STATE_TERMINATING, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_HANGUP, FTDM_END},
|
|
},
|
|
{
|
|
ZSD_INBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_CHANNEL_STATE_HANGUP, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_HANGUP_COMPLETE, FTDM_END},
|
|
},
|
|
{
|
|
ZSD_INBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_CHANNEL_STATE_HANGUP_COMPLETE, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_DOWN, FTDM_END},
|
|
},
|
|
{
|
|
ZSD_OUTBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_ANY_STATE, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_RESET, FTDM_CHANNEL_STATE_RESTART, FTDM_CHANNEL_STATE_TERMINATING, FTDM_END}
|
|
},
|
|
{
|
|
ZSD_OUTBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_CHANNEL_STATE_RESET, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_DOWN, FTDM_END}
|
|
},
|
|
{
|
|
ZSD_OUTBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_CHANNEL_STATE_RESTART, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_DOWN, FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_HANGUP, FTDM_END}
|
|
},
|
|
{
|
|
ZSD_OUTBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_CHANNEL_STATE_CANCEL, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_HANGUP, FTDM_END}
|
|
},
|
|
{
|
|
ZSD_OUTBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_CHANNEL_STATE_SUSPENDED, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_DOWN, FTDM_CHANNEL_STATE_COLLECT, FTDM_CHANNEL_STATE_RING, FTDM_CHANNEL_STATE_DIALING,
|
|
FTDM_CHANNEL_STATE_RESTART, FTDM_CHANNEL_STATE_PROGRESS, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_CHANNEL_STATE_UP,
|
|
FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_CANCEL, FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_HANGUP_COMPLETE, FTDM_END}
|
|
},
|
|
{
|
|
ZSD_OUTBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_CHANNEL_STATE_DOWN, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_DIALING, FTDM_CHANNEL_STATE_HANGUP_COMPLETE, FTDM_END}
|
|
},
|
|
{
|
|
ZSD_OUTBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_CHANNEL_STATE_DIALING, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_HANGUP,
|
|
FTDM_CHANNEL_STATE_PROCEED, FTDM_CHANNEL_STATE_RINGING, FTDM_CHANNEL_STATE_PROGRESS, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_CHANNEL_STATE_UP,
|
|
FTDM_CHANNEL_STATE_DOWN, FTDM_END}
|
|
},
|
|
{
|
|
ZSD_OUTBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_CHANNEL_STATE_PROCEED, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_HANGUP,
|
|
FTDM_CHANNEL_STATE_RINGING, FTDM_CHANNEL_STATE_PROGRESS, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_CHANNEL_STATE_UP, FTDM_END},
|
|
},
|
|
{
|
|
ZSD_OUTBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_CHANNEL_STATE_PROGRESS, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_CHANNEL_STATE_UP, FTDM_END},
|
|
},
|
|
{
|
|
ZSD_OUTBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_CHANNEL_STATE_RINGING, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_PROGRESS, FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_CHANNEL_STATE_UP, FTDM_END},
|
|
},
|
|
{
|
|
ZSD_OUTBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_CHANNEL_STATE_PROGRESS_MEDIA, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_TERMINATING, FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_UP, FTDM_END},
|
|
},
|
|
{
|
|
ZSD_OUTBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_CHANNEL_STATE_UP, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, FTDM_END},
|
|
},
|
|
{
|
|
ZSD_OUTBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_CHANNEL_STATE_TERMINATING, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_HANGUP, FTDM_END},
|
|
},
|
|
{
|
|
ZSD_OUTBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_CHANNEL_STATE_HANGUP, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_HANGUP_COMPLETE, FTDM_END},
|
|
},
|
|
{
|
|
ZSD_OUTBOUND,
|
|
ZSM_UNACCEPTABLE,
|
|
{FTDM_CHANNEL_STATE_HANGUP_COMPLETE, FTDM_END},
|
|
{FTDM_CHANNEL_STATE_DOWN, FTDM_END},
|
|
}
|
|
}
|
|
};
|
|
|
|
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;
|
|
sngisdn_snd_event(signal_data, event);
|
|
|
|
switch (event) {
|
|
/* Check if the span woke up from power-saving mode */
|
|
case FTDM_OOB_ALARM_CLEAR:
|
|
if (FTDM_SPAN_IS_BRI(span)) {
|
|
ftdm_channel_t *ftdmchan;
|
|
sngisdn_chan_data_t *sngisdn_info;
|
|
ftdm_iterator_t *chaniter = NULL;
|
|
ftdm_iterator_t *curr = NULL;
|
|
|
|
chaniter = ftdm_span_get_chan_iterator(span, NULL);
|
|
for (curr = chaniter; curr; curr = ftdm_iterator_next(curr)) {
|
|
ftdmchan = (ftdm_channel_t*)ftdm_iterator_current(curr);
|
|
sngisdn_info = (sngisdn_chan_data_t*)ftdmchan->call_data;
|
|
|
|
if (ftdm_test_flag(sngisdn_info, FLAG_ACTIVATING)) {
|
|
ftdm_clear_flag(sngisdn_info, FLAG_ACTIVATING);
|
|
|
|
ftdm_sched_timer(signal_data->sched, "delayed_setup", 1000, sngisdn_delayed_setup, (void*) ftdmchan->call_data, NULL);
|
|
}
|
|
}
|
|
ftdm_iterator_free(chaniter);
|
|
}
|
|
break;
|
|
default:
|
|
/* Ignore other events for now */
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void ftdm_sangoma_isdn_dchan_set_queue_size(ftdm_channel_t *dchan)
|
|
{
|
|
ftdm_status_t ret_status;
|
|
uint32_t queue_size;
|
|
|
|
queue_size = SNGISDN_DCHAN_QUEUE_LEN;
|
|
ret_status = ftdm_channel_command(dchan, FTDM_COMMAND_SET_RX_QUEUE_SIZE, &queue_size);
|
|
ftdm_assert(ret_status == FTDM_SUCCESS, "Failed to set Rx Queue size");
|
|
|
|
queue_size = SNGISDN_DCHAN_QUEUE_LEN;
|
|
ret_status = ftdm_channel_command(dchan, FTDM_COMMAND_SET_TX_QUEUE_SIZE, &queue_size);
|
|
ftdm_assert(ret_status == FTDM_SUCCESS, "Failed to set Tx Queue size");
|
|
|
|
RETVOID;
|
|
}
|
|
|
|
static void ftdm_sangoma_isdn_wakeup_phy(ftdm_channel_t *dchan)
|
|
{
|
|
ftdm_status_t ret_status;
|
|
ftdm_channel_hw_link_status_t status = FTDM_HW_LINK_CONNECTED;
|
|
ret_status = ftdm_channel_command(dchan, FTDM_COMMAND_SET_LINK_STATUS, &status);
|
|
if (ret_status != FTDM_SUCCESS) {
|
|
ftdm_log_chan_msg(dchan, FTDM_LOG_WARNING, "Failed to wake-up link\n");
|
|
}
|
|
return;
|
|
}
|
|
|
|
static void *ftdm_sangoma_isdn_io_run(ftdm_thread_t *me, void *obj)
|
|
{
|
|
uint8_t data[1000];
|
|
unsigned i = 0;
|
|
ftdm_status_t status = FTDM_SUCCESS;
|
|
ftdm_span_t *span = (ftdm_span_t*) obj;
|
|
ftdm_size_t len = 0;
|
|
ftdm_channel_t *ftdmchan = NULL;
|
|
unsigned waitms = 10000;
|
|
ftdm_iterator_t *chaniter = NULL;
|
|
ftdm_iterator_t *citer = NULL;
|
|
ftdm_event_t *event;
|
|
short *poll_events = ftdm_malloc(sizeof(short) * span->chan_count);
|
|
|
|
/* Initialize the d-channel */
|
|
chaniter = ftdm_span_get_chan_iterator(span, NULL);
|
|
if (!chaniter) {
|
|
ftdm_log(FTDM_LOG_CRIT, "Failed to allocate channel iterator for span %s!\n", span->name);
|
|
goto done;
|
|
}
|
|
|
|
while (ftdm_running() && !(ftdm_test_flag(span, FTDM_SPAN_STOP_THREAD))) {
|
|
len = 1000;
|
|
waitms = 1000;
|
|
memset(poll_events, 0, sizeof(short)*span->chan_count);
|
|
|
|
for (i = 0, citer = ftdm_span_get_chan_iterator(span, chaniter); citer; citer = ftdm_iterator_next(citer), i++) {
|
|
ftdmchan = ftdm_iterator_current(citer);
|
|
|
|
poll_events[i] |= FTDM_EVENTS;
|
|
if (FTDM_IS_VOICE_CHANNEL(ftdmchan)) {
|
|
if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_RX_DISABLED)) {
|
|
poll_events[i] |= FTDM_READ;
|
|
waitms = 20;
|
|
}
|
|
} else {
|
|
/* We always read the d-channel */
|
|
poll_events[i] |= FTDM_READ;
|
|
}
|
|
}
|
|
|
|
status = ftdm_span_poll_event(span, waitms, poll_events);
|
|
switch (status) {
|
|
case FTDM_FAIL:
|
|
ftdm_log(FTDM_LOG_CRIT, "Failed to poll span for IO\n");
|
|
break;
|
|
case FTDM_TIMEOUT:
|
|
break;
|
|
case FTDM_SUCCESS:
|
|
/* Check if there are any channels that have data available */
|
|
for (citer = ftdm_span_get_chan_iterator(span, chaniter); citer; citer = ftdm_iterator_next(citer)) {
|
|
len = sizeof(data);
|
|
ftdmchan = ftdm_iterator_current(citer);
|
|
if (FTDM_IS_VOICE_CHANNEL(ftdmchan)) {
|
|
if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_RX_DISABLED)) {
|
|
if (ftdm_test_io_flag(ftdmchan, FTDM_CHANNEL_IO_READ)) {
|
|
status = ftdm_raw_read(ftdmchan, data, &len);
|
|
if (status != FTDM_SUCCESS) {
|
|
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "raw I/O read failed\n");
|
|
continue;
|
|
}
|
|
|
|
status = ftdm_channel_process_media(ftdmchan, data, &len);
|
|
if (status != FTDM_SUCCESS) {
|
|
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Failed to process media\n");
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (ftdm_test_io_flag(ftdmchan, FTDM_CHANNEL_IO_READ)) {
|
|
status = ftdm_channel_read(ftdmchan, data, &len);
|
|
if (status == FTDM_SUCCESS) {
|
|
sngisdn_snd_data(ftdmchan, data, len);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Check if there are any channels that have events available */
|
|
while (ftdm_span_next_event(span, &event) == FTDM_SUCCESS) {
|
|
ftdm_sangoma_isdn_process_phy_events(span, event->enum_id);
|
|
}
|
|
|
|
break;
|
|
default:
|
|
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_CRIT, "Unhandled IO event\n");
|
|
}
|
|
}
|
|
done:
|
|
ftdm_iterator_free(chaniter);
|
|
ftdm_safe_free(poll_events);
|
|
return NULL;
|
|
}
|
|
|
|
static void *ftdm_sangoma_isdn_run(ftdm_thread_t *me, void *obj)
|
|
{
|
|
ftdm_interrupt_t *ftdm_sangoma_isdn_int[3];
|
|
ftdm_status_t ret_status;
|
|
ftdm_span_t *span = (ftdm_span_t *) obj;
|
|
ftdm_channel_t *ftdmchan = NULL;
|
|
sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*)span->signal_data;
|
|
sngisdn_event_data_t *sngisdn_event = NULL;
|
|
int32_t sleep = SNGISDN_EVENT_POLL_RATE;
|
|
|
|
ftdm_log(FTDM_LOG_INFO, "ftmod_sangoma_isdn monitor thread for span=%u started.\n", span->span_id);
|
|
|
|
/* set IN_THREAD flag so that we know this thread is running */
|
|
ftdm_set_flag(span, FTDM_SPAN_IN_THREAD);
|
|
|
|
/* get an interrupt queue for this span */
|
|
if (ftdm_queue_get_interrupt(span->pendingchans, &ftdm_sangoma_isdn_int[0]) != FTDM_SUCCESS) {
|
|
ftdm_log(FTDM_LOG_CRIT, "Failed to get a ftdm_interrupt for span = %s!\n", span->name);
|
|
goto ftdm_sangoma_isdn_run_exit;
|
|
}
|
|
|
|
if (ftdm_queue_get_interrupt(span->pendingsignals, &ftdm_sangoma_isdn_int[1]) != FTDM_SUCCESS) {
|
|
ftdm_log(FTDM_LOG_CRIT, "Failed to get a signal interrupt for span = %s!\n", span->name);
|
|
goto ftdm_sangoma_isdn_run_exit;
|
|
}
|
|
|
|
if (ftdm_queue_get_interrupt(signal_data->event_queue, &ftdm_sangoma_isdn_int[2]) != FTDM_SUCCESS) {
|
|
ftdm_log(FTDM_LOG_CRIT, "Failed to get a event interrupt for span = %s!\n", span->name);
|
|
goto ftdm_sangoma_isdn_run_exit;
|
|
}
|
|
|
|
while (ftdm_running() && !(ftdm_test_flag(span, FTDM_SPAN_STOP_THREAD))) {
|
|
/* Check if there are any timers to process */
|
|
ftdm_sched_run(signal_data->sched);
|
|
ftdm_span_trigger_signals(span);
|
|
|
|
if (ftdm_sched_get_time_to_next_timer(signal_data->sched, &sleep) == FTDM_SUCCESS) {
|
|
if (sleep < 0 || sleep > SNGISDN_EVENT_POLL_RATE) {
|
|
sleep = SNGISDN_EVENT_POLL_RATE;
|
|
}
|
|
}
|
|
ret_status = ftdm_interrupt_multiple_wait(ftdm_sangoma_isdn_int, 3, sleep);
|
|
/* find out why we returned from the interrupt queue */
|
|
switch (ret_status) {
|
|
case FTDM_SUCCESS: /* there was a state change on the span */
|
|
/* process all pending state changes */
|
|
while ((ftdmchan = ftdm_queue_dequeue(span->pendingchans))) {
|
|
/* double check that this channel has a state change pending */
|
|
ftdm_channel_lock(ftdmchan);
|
|
ftdm_channel_advance_states(ftdmchan);
|
|
ftdm_channel_unlock(ftdmchan);
|
|
}
|
|
|
|
while ((sngisdn_event = ftdm_queue_dequeue(signal_data->event_queue))) {
|
|
ftdm_sangoma_isdn_process_stack_event(span, sngisdn_event);
|
|
ftdm_safe_free(sngisdn_event);
|
|
}
|
|
break;
|
|
case FTDM_TIMEOUT:
|
|
/* twiddle */
|
|
break;
|
|
case FTDM_FAIL:
|
|
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);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* clear the IN_THREAD flag so that we know the thread is done */
|
|
ftdm_clear_flag(span, FTDM_SPAN_IN_THREAD);
|
|
|
|
ftdm_log(FTDM_LOG_INFO, "ftmod_sangoma_isdn monitor thread for span %s stopping.\n", span->name);
|
|
|
|
return NULL;
|
|
|
|
ftdm_sangoma_isdn_run_exit:
|
|
|
|
/* clear the IN_THREAD flag so that we know the thread is done */
|
|
ftdm_clear_flag(span, FTDM_SPAN_IN_THREAD);
|
|
|
|
ftdm_log(FTDM_LOG_INFO, "ftmod_sangoma_isdn monitor thread for span %s stopping due to error.\n", span->name);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/**
|
|
* \brief Checks if span has state changes pending and processes
|
|
* \param span Span where event was fired
|
|
* \param sngisdn_event Event to handle
|
|
* \return The locked FTDM channel associated to the event if any, NULL otherwise
|
|
*/
|
|
|
|
ftdm_channel_t* ftdm_sangoma_isdn_process_event_states(ftdm_span_t *span, sngisdn_event_data_t *sngisdn_event)
|
|
{
|
|
ftdm_channel_t *ftdmchan = NULL;
|
|
switch (sngisdn_event->event_id) {
|
|
/* Events that do not have a channel associated to them */
|
|
case SNGISDN_EVENT_SRV_IND:
|
|
case SNGISDN_EVENT_SRV_CFM:
|
|
case SNGISDN_EVENT_RST_CFM:
|
|
case SNGISDN_EVENT_RST_IND:
|
|
return NULL;
|
|
break;
|
|
case SNGISDN_EVENT_CON_IND:
|
|
case SNGISDN_EVENT_CON_CFM:
|
|
case SNGISDN_EVENT_CNST_IND:
|
|
case SNGISDN_EVENT_DISC_IND:
|
|
case SNGISDN_EVENT_REL_IND:
|
|
case SNGISDN_EVENT_DAT_IND:
|
|
case SNGISDN_EVENT_SSHL_IND:
|
|
case SNGISDN_EVENT_SSHL_CFM:
|
|
case SNGISDN_EVENT_RMRT_IND:
|
|
case SNGISDN_EVENT_RMRT_CFM:
|
|
case SNGISDN_EVENT_FLC_IND:
|
|
case SNGISDN_EVENT_FAC_IND:
|
|
case SNGISDN_EVENT_STA_CFM:
|
|
ftdmchan = sngisdn_event->sngisdn_info->ftdmchan;
|
|
ftdm_assert_return(ftdmchan, NULL,"Event should have a channel associated\n");
|
|
break;
|
|
}
|
|
ftdm_channel_lock(ftdmchan);
|
|
ftdm_channel_advance_states(ftdmchan);
|
|
return ftdmchan;
|
|
}
|
|
|
|
|
|
|
|
static void ftdm_sangoma_isdn_process_stack_event (ftdm_span_t *span, sngisdn_event_data_t *sngisdn_event)
|
|
{
|
|
ftdm_channel_t *ftdmchan = NULL;
|
|
|
|
ftdmchan = ftdm_sangoma_isdn_process_event_states(span, sngisdn_event);
|
|
switch(sngisdn_event->event_id) {
|
|
case SNGISDN_EVENT_CON_IND:
|
|
sngisdn_process_con_ind(sngisdn_event);
|
|
break;
|
|
case SNGISDN_EVENT_CON_CFM:
|
|
sngisdn_process_con_cfm(sngisdn_event);
|
|
break;
|
|
case SNGISDN_EVENT_CNST_IND:
|
|
sngisdn_process_cnst_ind(sngisdn_event);
|
|
break;
|
|
case SNGISDN_EVENT_DISC_IND:
|
|
sngisdn_process_disc_ind(sngisdn_event);
|
|
break;
|
|
case SNGISDN_EVENT_REL_IND:
|
|
sngisdn_process_rel_ind(sngisdn_event);
|
|
break;
|
|
case SNGISDN_EVENT_DAT_IND:
|
|
sngisdn_process_dat_ind(sngisdn_event);
|
|
break;
|
|
case SNGISDN_EVENT_SSHL_IND:
|
|
sngisdn_process_sshl_ind(sngisdn_event);
|
|
break;
|
|
case SNGISDN_EVENT_SSHL_CFM:
|
|
sngisdn_process_sshl_cfm(sngisdn_event);
|
|
break;
|
|
case SNGISDN_EVENT_RMRT_IND:
|
|
sngisdn_process_rmrt_ind(sngisdn_event);
|
|
break;
|
|
case SNGISDN_EVENT_RMRT_CFM:
|
|
sngisdn_process_rmrt_cfm(sngisdn_event);
|
|
break;
|
|
case SNGISDN_EVENT_FLC_IND:
|
|
sngisdn_process_flc_ind(sngisdn_event);
|
|
break;
|
|
case SNGISDN_EVENT_FAC_IND:
|
|
sngisdn_process_fac_ind(sngisdn_event);
|
|
break;
|
|
case SNGISDN_EVENT_STA_CFM:
|
|
sngisdn_process_sta_cfm(sngisdn_event);
|
|
break;
|
|
case SNGISDN_EVENT_SRV_IND:
|
|
sngisdn_process_srv_ind(sngisdn_event);
|
|
break;
|
|
case SNGISDN_EVENT_SRV_CFM:
|
|
sngisdn_process_srv_cfm(sngisdn_event);
|
|
break;
|
|
case SNGISDN_EVENT_RST_CFM:
|
|
sngisdn_process_rst_cfm(sngisdn_event);
|
|
break;
|
|
case SNGISDN_EVENT_RST_IND:
|
|
sngisdn_process_rst_ind(sngisdn_event);
|
|
break;
|
|
}
|
|
if (ftdmchan != NULL) {
|
|
ftdm_channel_advance_states(ftdmchan);
|
|
ftdm_channel_unlock(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;
|
|
sngisdn_chan_data_t *sngisdn_info = ftdmchan->call_data;
|
|
uint8_t state_change = 0;
|
|
|
|
memset(&sigev, 0, sizeof(sigev));
|
|
|
|
sigev.chan_id = ftdmchan->chan_id;
|
|
sigev.span_id = ftdmchan->span_id;
|
|
sigev.channel = 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");
|
|
}
|
|
#endif
|
|
|
|
/* Only needed for debugging */
|
|
initial_state = ftdmchan->state;
|
|
|
|
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "processing state change to %s\n", ftdm_channel_state2str(ftdmchan->state));
|
|
|
|
switch (ftdmchan->state) {
|
|
case FTDM_CHANNEL_STATE_COLLECT: /* SETUP received but waiting on digits */
|
|
{
|
|
/* TODO: Re-implement this. There is a way to re-evaluate new incoming digits from dialplan as they come */
|
|
sngisdn_snd_setup_ack(ftdmchan);
|
|
/* Just wait in this state until we get enough digits or T302 timeout */
|
|
}
|
|
break;
|
|
case FTDM_CHANNEL_STATE_GET_CALLERID:
|
|
{
|
|
/* By default, we do not send a progress indicator in the proceed */
|
|
ftdm_sngisdn_progind_t prog_ind = {SNGISDN_PROGIND_LOC_USER, SNGISDN_PROGIND_DESCR_INVALID};
|
|
sngisdn_snd_proceed(ftdmchan, prog_ind);
|
|
|
|
/* Wait in this state until we get FACILITY msg */
|
|
}
|
|
break;
|
|
case FTDM_CHANNEL_STATE_RING: /* incoming call request */
|
|
{
|
|
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Sending incoming call from %s to %s to FTDM core\n", ftdmchan->caller_data.ani.digits, ftdmchan->caller_data.dnis.digits);
|
|
|
|
/* we have enough information to inform FTDM of the call*/
|
|
sngisdn_send_signal(sngisdn_info, FTDM_SIGEVENT_START);
|
|
}
|
|
break;
|
|
case FTDM_CHANNEL_STATE_DIALING: /* outgoing call request */
|
|
{
|
|
if (FTDM_SPAN_IS_BRI(ftdmchan->span) && ftdm_test_flag(ftdmchan->span, FTDM_SPAN_PWR_SAVING)) {
|
|
ftdm_signaling_status_t sigstatus;
|
|
ftdm_span_get_sig_status(ftdmchan->span, &sigstatus);
|
|
if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_IN_ALARM)) {
|
|
sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*)ftdmchan->span->signal_data;
|
|
|
|
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Requesting Physical Line activation\n");
|
|
sngisdn_set_flag(sngisdn_info, FLAG_ACTIVATING);
|
|
ftdm_sangoma_isdn_wakeup_phy(ftdmchan);
|
|
ftdm_sched_timer(signal_data->sched, "timer_t3", signal_data->timer_t3*1000, sngisdn_t3_timeout, (void*) sngisdn_info, NULL);
|
|
} else if (sigstatus == FTDM_SIG_STATE_DOWN) {
|
|
sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*)ftdmchan->span->signal_data;
|
|
|
|
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Requesting Q.921 Line activation\n");
|
|
sngisdn_set_flag(sngisdn_info, FLAG_ACTIVATING);
|
|
sngisdn_snd_info_req(ftdmchan);
|
|
ftdm_sched_timer(signal_data->sched, "timer_t3", signal_data->timer_t3*1000, sngisdn_t3_timeout, (void*) sngisdn_info, NULL);
|
|
} else {
|
|
sngisdn_snd_setup(ftdmchan);
|
|
}
|
|
} else {
|
|
sngisdn_snd_setup(ftdmchan);
|
|
}
|
|
}
|
|
break;
|
|
case FTDM_CHANNEL_STATE_PROCEED:
|
|
{
|
|
if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
|
|
/*OUTBOUND...so we were told by the line of this so noifiy the user*/
|
|
sngisdn_send_signal(sngisdn_info, FTDM_SIGEVENT_PROCEED);
|
|
|
|
if (sngisdn_test_flag(sngisdn_info, FLAG_MEDIA_READY)) {
|
|
state_change++;
|
|
ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA);
|
|
}
|
|
} else {
|
|
ftdm_sngisdn_progind_t prog_ind = {SNGISDN_PROGIND_LOC_USER, SNGISDN_PROGIND_DESCR_INVALID};
|
|
sngisdn_snd_proceed(ftdmchan, prog_ind);
|
|
}
|
|
}
|
|
break;
|
|
case FTDM_CHANNEL_STATE_RINGING:
|
|
{
|
|
if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
|
|
/* OUTBOUND...so we were told by the line of this so notify the user */
|
|
sngisdn_send_signal(sngisdn_info, FTDM_SIGEVENT_RINGING);
|
|
|
|
if (sngisdn_test_flag(sngisdn_info, FLAG_MEDIA_READY)) {
|
|
state_change++;
|
|
ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA);
|
|
}
|
|
} else {
|
|
ftdm_sngisdn_progind_t prog_ind = {SNGISDN_PROGIND_LOC_USER, SNGISDN_PROGIND_DESCR_NETE_ISDN};
|
|
sngisdn_snd_alert(ftdmchan, prog_ind);
|
|
}
|
|
}
|
|
break;
|
|
case FTDM_CHANNEL_STATE_PROGRESS:
|
|
{
|
|
/*check if the channel is inbound or outbound*/
|
|
if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
|
|
/*OUTBOUND...so we were told by the line of this so noifiy the user*/
|
|
|
|
sngisdn_send_signal(sngisdn_info, FTDM_SIGEVENT_PROGRESS);
|
|
} else {
|
|
/* Send a progress message, indicating: Call is not end-to-end ISDN, further call progress may be available */
|
|
ftdm_sngisdn_progind_t prog_ind = {SNGISDN_PROGIND_LOC_USER, SNGISDN_PROGIND_DESCR_NETE_ISDN};
|
|
sngisdn_snd_progress(ftdmchan, prog_ind);
|
|
}
|
|
}
|
|
break;
|
|
case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
|
|
{
|
|
if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
|
|
sngisdn_send_signal(sngisdn_info, FTDM_SIGEVENT_PROGRESS_MEDIA);
|
|
} else {
|
|
/* Send a progress message, indicating: In-band information/pattern available */
|
|
ftdm_sngisdn_progind_t prog_ind = {SNGISDN_PROGIND_LOC_USER, SNGISDN_PROGIND_DESCR_IB_AVAIL};
|
|
sngisdn_snd_progress(ftdmchan, prog_ind);
|
|
}
|
|
}
|
|
break;
|
|
case FTDM_CHANNEL_STATE_UP: /* call is answered */
|
|
{
|
|
/* check if the channel is inbound or outbound */
|
|
if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
|
|
/* OUTBOUND ... so we were told by the line that the other side answered */
|
|
|
|
sngisdn_send_signal(sngisdn_info, FTDM_SIGEVENT_UP);
|
|
|
|
if (ftdmchan->span->trunk_type == FTDM_TRUNK_BRI_PTMP &&
|
|
((sngisdn_span_data_t*)ftdmchan->span->signal_data)->signalling == SNGISDN_SIGNALING_NET) {
|
|
/* Assign the call to a specific equipment */
|
|
sngisdn_snd_con_complete(ftdmchan);
|
|
}
|
|
} else {
|
|
/* INBOUND ... so FS told us it just answered ... tell the stack */
|
|
sngisdn_snd_connect(ftdmchan);
|
|
}
|
|
}
|
|
break;
|
|
case FTDM_CHANNEL_STATE_CANCEL:
|
|
{
|
|
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Hanging up call before informing user!\n");
|
|
|
|
/* Send a release complete */
|
|
sngisdn_snd_release(ftdmchan, 0);
|
|
/*now go to the HANGUP complete state*/
|
|
state_change++;
|
|
ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE);
|
|
}
|
|
break;
|
|
case FTDM_CHANNEL_STATE_TERMINATING: /* call is hung up by the remote end */
|
|
{
|
|
/* this state is set when the line is hanging up */
|
|
sngisdn_send_signal(sngisdn_info, FTDM_SIGEVENT_STOP);
|
|
}
|
|
break;
|
|
case FTDM_CHANNEL_STATE_HANGUP: /* call is hung up locally */
|
|
{
|
|
if (sngisdn_test_flag(sngisdn_info, FLAG_REMOTE_ABORT)) {
|
|
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Acknowledging remote abort\n");
|
|
} else if (sngisdn_test_flag(sngisdn_info, FLAG_REMOTE_REL)) {
|
|
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Acknowledging remote hangup\n");
|
|
sngisdn_snd_release(ftdmchan, 0);
|
|
} else if (sngisdn_test_flag(sngisdn_info, FLAG_LOCAL_ABORT)) {
|
|
/* We aborted this call before sending anything to the stack, so nothing to do anymore */
|
|
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Clearing local states from local abort\n");
|
|
} else if (sngisdn_test_flag(sngisdn_info, FLAG_GLARE)) {
|
|
/* We are hangup local call because there was a glare, we are waiting for a
|
|
RELEASE on this call, before we can process the saved call */
|
|
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Waiting for RELEASE on hungup glared call\n");
|
|
} else if (sngisdn_test_flag(sngisdn_info, FLAG_SEND_DISC)) {
|
|
/* Remote side sent a PROGRESS message, but cause indicates disconnect or T310 expired*/
|
|
sngisdn_snd_disconnect(ftdmchan);
|
|
} else {
|
|
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Hanging up call upon local request!\n");
|
|
|
|
/* set the flag to indicate this hangup is started from the local side */
|
|
sngisdn_set_flag(sngisdn_info, FLAG_LOCAL_REL);
|
|
|
|
switch(ftdmchan->last_state) {
|
|
case FTDM_CHANNEL_STATE_RING:
|
|
/* If we never sent PROCEED/ALERT/PROGRESS/CONNECT on an incoming call, we need to send release instead of disconnect */
|
|
sngisdn_set_flag(sngisdn_info, FLAG_LOCAL_ABORT);
|
|
sngisdn_snd_release(ftdmchan, 0);
|
|
break;
|
|
case FTDM_CHANNEL_STATE_DIALING:
|
|
/* If we never received a PROCEED/ALERT/PROGRESS/CONNECT on an outgoing call, we need to send release instead of disconnect */
|
|
sngisdn_snd_release(ftdmchan, 0);
|
|
break;
|
|
case FTDM_CHANNEL_STATE_PROCEED:
|
|
if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND)) {
|
|
if (((sngisdn_span_data_t*)(ftdmchan->span->signal_data))->switchtype == SNGISDN_SWITCH_4ESS ||
|
|
((sngisdn_span_data_t*)(ftdmchan->span->signal_data))->switchtype == SNGISDN_SWITCH_5ESS) {
|
|
|
|
/* When using 5ESS, if the user wants to clear an inbound call, the correct procedure is to send a PROGRESS with in-band info available, and play tones. Then send a DISCONNECT. If we reached this point, it means user did not try to play-tones, so send a RELEASE because remote side does not expect DISCONNECT in state 3 */
|
|
sngisdn_snd_release(ftdmchan, 0);
|
|
break;
|
|
}
|
|
}
|
|
/* fall-through */
|
|
default:
|
|
sngisdn_snd_disconnect(ftdmchan);
|
|
break;
|
|
}
|
|
}
|
|
/* now go to the HANGUP complete state */
|
|
state_change++;
|
|
ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE);
|
|
}
|
|
break;
|
|
case FTDM_CHANNEL_STATE_HANGUP_COMPLETE:
|
|
{
|
|
if (sngisdn_test_flag(sngisdn_info, FLAG_REMOTE_ABORT) ||
|
|
sngisdn_test_flag(sngisdn_info, FLAG_LOCAL_ABORT)) {
|
|
/* If the remote side aborted, we will not get anymore message for this call */
|
|
state_change++;
|
|
ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_DOWN);
|
|
} else {
|
|
/* waiting on remote confirmation before moving to down */
|
|
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Waiting for release from stack\n");
|
|
}
|
|
}
|
|
break;
|
|
case FTDM_CHANNEL_STATE_DOWN: /* the call is finished and removed */
|
|
{
|
|
uint8_t glare = sngisdn_test_flag(sngisdn_info, FLAG_GLARE);
|
|
/* clear all of the call specific data store in the channel structure */
|
|
clear_call_data(sngisdn_info);
|
|
|
|
/* Close the channel even if we had a glare, we will re-open it when processing state COLLECT for the
|
|
"glared call" */
|
|
if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OPEN)) {
|
|
ftdm_channel_t *close_chan = ftdmchan;
|
|
/* close the channel */
|
|
ftdm_channel_close(&close_chan);
|
|
}
|
|
if (glare) {
|
|
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_DEBUG, "Glare detected, processing saved call\n");
|
|
/* We are calling sngisdn_rcv_con_ind with ftdmchan->mutex being locked,
|
|
so no other threads will be able to touch this channel. The next time we will
|
|
process this channel is in this function, and it should be in state COLLECT (set inside
|
|
sngisdn_rcv_con_ind)*/
|
|
sngisdn_rcv_con_ind(sngisdn_info->glare.suId, sngisdn_info->glare.suInstId, sngisdn_info->glare.spInstId, &sngisdn_info->glare.setup, sngisdn_info->glare.dChan, sngisdn_info->glare.ces);
|
|
}
|
|
}
|
|
break;
|
|
case FTDM_CHANNEL_STATE_TRANSFER:
|
|
{
|
|
/* sngisdn_transfer function will always result in a state change */
|
|
sngisdn_transfer(ftdmchan);
|
|
state_change++;
|
|
}
|
|
break;
|
|
case FTDM_CHANNEL_STATE_RESTART:
|
|
{
|
|
/* IMPLEMENT ME */
|
|
}
|
|
break;
|
|
case FTDM_CHANNEL_STATE_SUSPENDED:
|
|
{
|
|
/* IMPLEMENT ME */
|
|
}
|
|
break;
|
|
case FTDM_CHANNEL_STATE_RESET:
|
|
{
|
|
sngisdn_snd_restart(ftdmchan);
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
ftdm_log_chan(ftdmchan, FTDM_LOG_CRIT, "unsupported sngisdn_rcvd state %s\n", ftdm_channel_state2str(ftdmchan->state));
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (!state_change) {
|
|
/* Acknowledge the state change */
|
|
ftdm_channel_complete_state(ftdmchan);
|
|
}
|
|
|
|
/* If sngisdn_info->variables is not NULL, it means did not send any
|
|
* sigevent to the user, therefore we have to free that hashtable */
|
|
if (sngisdn_info->variables) {
|
|
hashtable_destroy(sngisdn_info->variables);
|
|
sngisdn_info->variables = NULL;
|
|
}
|
|
|
|
/* If sngisdn_info->raw_data is not NULL, it means did not send any
|
|
* sigevent to the user, therefore we have to free that raw data */
|
|
if (sngisdn_info->raw_data) {
|
|
ftdm_safe_free(sngisdn_info->raw_data);
|
|
sngisdn_info->raw_data = NULL;
|
|
sngisdn_info->raw_data_len = 0;
|
|
}
|
|
|
|
if (ftdmchan->state == initial_state) {
|
|
ftdm_assert(!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE), "state change flag is still set, but we did not change state\n");
|
|
}
|
|
#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");
|
|
}
|
|
#endif
|
|
return FTDM_SUCCESS;
|
|
}
|
|
|
|
static FIO_CHANNEL_INDICATE_FUNCTION(ftdm_sangoma_isdn_indicate)
|
|
{
|
|
ftdm_status_t status = FTDM_FAIL;
|
|
|
|
switch (indication) {
|
|
case FTDM_CHANNEL_INDICATE_FACILITY:
|
|
sngisdn_snd_fac_req(ftdmchan);
|
|
status = FTDM_SUCCESS;
|
|
break;
|
|
default:
|
|
status = FTDM_NOTIMPL;
|
|
}
|
|
return status;
|
|
}
|
|
|
|
static FIO_CHANNEL_OUTGOING_CALL_FUNCTION(ftdm_sangoma_isdn_outgoing_call)
|
|
{
|
|
sngisdn_chan_data_t *sngisdn_info = ftdmchan->call_data;
|
|
ftdm_status_t status = FTDM_FAIL;
|
|
|
|
if (ftdmchan->state == FTDM_CHANNEL_STATE_DOWN) {
|
|
if (sngisdn_test_flag(sngisdn_info, FLAG_GLARE)) {
|
|
/* A call came in after we called ftdm_channel_open_chan for this call, but before we got here */
|
|
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Glare detected - aborting outgoing call\n");
|
|
|
|
sngisdn_set_flag(sngisdn_info, FLAG_LOCAL_ABORT);
|
|
ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE);
|
|
|
|
status = FTDM_BREAK;
|
|
} else {
|
|
status = FTDM_SUCCESS;
|
|
}
|
|
} else {
|
|
/* the channel is already used...this can't be, end the request */
|
|
ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "Outgoing call requested channel in already in use (%s)\n", ftdm_channel_state2str(ftdmchan->state));
|
|
status = FTDM_BREAK;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
static FIO_CHANNEL_GET_SIG_STATUS_FUNCTION(ftdm_sangoma_isdn_get_chan_sig_status)
|
|
{
|
|
if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_SIG_UP)) {
|
|
*status = FTDM_SIG_STATE_UP;
|
|
} else {
|
|
*status = FTDM_SIG_STATE_DOWN;
|
|
}
|
|
|
|
return FTDM_SUCCESS;
|
|
}
|
|
|
|
static FIO_CHANNEL_SET_SIG_STATUS_FUNCTION(ftdm_sangoma_isdn_set_chan_sig_status)
|
|
{
|
|
ftdm_log(FTDM_LOG_ERROR,"Cannot set channel status in this module\n");
|
|
return FTDM_NOTIMPL;
|
|
}
|
|
|
|
static FIO_SPAN_GET_SIG_STATUS_FUNCTION(ftdm_sangoma_isdn_get_span_sig_status)
|
|
{
|
|
if (ftdm_test_flag(span->channels[1], FTDM_CHANNEL_SIG_UP)) {
|
|
*status = FTDM_SIG_STATE_UP;
|
|
} else {
|
|
*status = FTDM_SIG_STATE_DOWN;
|
|
}
|
|
|
|
return FTDM_SUCCESS;
|
|
}
|
|
|
|
static FIO_SPAN_SET_SIG_STATUS_FUNCTION(ftdm_sangoma_isdn_set_span_sig_status)
|
|
{
|
|
ftdm_log(FTDM_LOG_ERROR,"Cannot set span status in this module\n");
|
|
return FTDM_NOTIMPL;
|
|
}
|
|
|
|
static ftdm_status_t ftdm_sangoma_isdn_dtmf(ftdm_channel_t *ftdmchan, const char* dtmf)
|
|
{
|
|
sngisdn_chan_data_t *sngisdn_info = ftdmchan->call_data;
|
|
switch(sngisdn_info->transfer_data.type) {
|
|
case SNGISDN_TRANSFER_ATT_COURTESY_VRU:
|
|
case SNGISDN_TRANSFER_ATT_COURTESY_VRU_DATA:
|
|
return sngisdn_att_transfer_process_dtmf(ftdmchan, dtmf);
|
|
default:
|
|
/* We do not care about DTMF events, do nothing */
|
|
break;
|
|
}
|
|
|
|
return FTDM_SUCCESS;
|
|
}
|
|
|
|
static ftdm_status_t ftdm_sangoma_isdn_perform_start(ftdm_span_t *span)
|
|
{
|
|
sngisdn_span_data_t *signal_data = span->signal_data;
|
|
|
|
ftdm_log(FTDM_LOG_DEBUG, "Actually starting span:%s\n", span->name);
|
|
/* clear the monitor thread stop flag */
|
|
ftdm_clear_flag(span, FTDM_SPAN_STOP_THREAD);
|
|
ftdm_clear_flag(span, FTDM_SPAN_IN_THREAD);
|
|
|
|
if (signal_data->trace_q921 == SNGISDN_OPT_TRUE ||
|
|
signal_data->raw_trace_q921 == SNGISDN_OPT_TRUE) {
|
|
|
|
sngisdn_activate_trace(span, SNGISDN_TRACE_Q921);
|
|
}
|
|
|
|
if (signal_data->trace_q931 == SNGISDN_OPT_TRUE ||
|
|
signal_data->raw_trace_q931 == SNGISDN_OPT_TRUE) {
|
|
|
|
sngisdn_activate_trace(span, SNGISDN_TRACE_Q931);
|
|
}
|
|
|
|
/*start the span monitor thread*/
|
|
if (ftdm_thread_create_detached(ftdm_sangoma_isdn_run, span) != FTDM_SUCCESS) {
|
|
ftdm_log(FTDM_LOG_CRIT,"Failed to start Sangoma ISDN Span Monitor Thread!\n");
|
|
return FTDM_FAIL;
|
|
}
|
|
|
|
/*start the dchan monitor thread*/
|
|
if (ftdm_thread_create_detached(ftdm_sangoma_isdn_io_run, span) != FTDM_SUCCESS) {
|
|
ftdm_log(FTDM_LOG_CRIT,"Failed to start Sangoma ISDN d-channel Monitor Thread!\n");
|
|
return FTDM_FAIL;
|
|
}
|
|
|
|
if (signal_data->restart_timeout) {
|
|
ftdm_log(FTDM_LOG_DEBUG, "%s:Scheduling Restart timeout\n", signal_data->ftdm_span->name);
|
|
ftdm_sched_timer(signal_data->sched, "restart_timeout", signal_data->restart_timeout,
|
|
sngisdn_restart_timeout, (void*) signal_data, &signal_data->timers[SNGISDN_SPAN_TIMER_RESTART]);
|
|
}
|
|
|
|
ftdm_log(FTDM_LOG_DEBUG,"Finished starting span %s\n", span->name);
|
|
return FTDM_SUCCESS;
|
|
}
|
|
|
|
|
|
static ftdm_status_t ftdm_sangoma_isdn_start(ftdm_span_t *span)
|
|
{
|
|
sngisdn_span_data_t *signal_data = span->signal_data;
|
|
|
|
ftdm_log(FTDM_LOG_INFO,"Starting span %s:%u.\n",span->name,span->span_id);
|
|
|
|
if (signal_data->dchan) {
|
|
ftdm_channel_set_feature(signal_data->dchan, FTDM_CHANNEL_FEATURE_IO_STATS);
|
|
ftdm_channel_open_chan(signal_data->dchan);
|
|
ftdm_sangoma_isdn_dchan_set_queue_size(signal_data->dchan);
|
|
}
|
|
|
|
if (signal_data->nfas.trunk) {
|
|
if (signal_data->nfas.trunk->num_spans == signal_data->nfas.trunk->num_spans_configured) {
|
|
int i;
|
|
ftdm_log(FTDM_LOG_DEBUG, "Starting span for all spans within trunkgroup:%s\n", signal_data->nfas.trunk->name);
|
|
|
|
sngisdn_stack_start(signal_data->nfas.trunk->dchan->ftdm_span);
|
|
ftdm_sangoma_isdn_perform_start(signal_data->nfas.trunk->dchan->ftdm_span);
|
|
|
|
if (signal_data->nfas.trunk->backup) {
|
|
sngisdn_stack_start(signal_data->nfas.trunk->backup->ftdm_span);
|
|
ftdm_sangoma_isdn_perform_start(signal_data->nfas.trunk->backup->ftdm_span);
|
|
}
|
|
|
|
for (i = 0; i < signal_data->nfas.trunk->num_spans; i++) {
|
|
if (signal_data->nfas.trunk->spans[i] &&
|
|
signal_data->nfas.trunk->spans[i]->nfas.sigchan == SNGISDN_NFAS_DCHAN_NONE) {
|
|
sngisdn_stack_start(signal_data->nfas.trunk->spans[i]->ftdm_span);
|
|
ftdm_sangoma_isdn_perform_start(signal_data->nfas.trunk->spans[i]->ftdm_span);
|
|
}
|
|
}
|
|
|
|
return FTDM_SUCCESS;
|
|
} else {
|
|
ftdm_log(FTDM_LOG_DEBUG, "Delaying span start until all spans within trunkgroup are started: %s\n", signal_data->nfas.trunk->name);
|
|
return FTDM_SUCCESS;
|
|
}
|
|
}
|
|
|
|
if (sngisdn_stack_start(span) != FTDM_SUCCESS) {
|
|
ftdm_log(FTDM_LOG_CRIT, "Failed to start span %s\n", span->name);
|
|
return FTDM_FAIL;
|
|
}
|
|
|
|
ftdm_sangoma_isdn_perform_start(span);
|
|
|
|
return FTDM_SUCCESS;
|
|
}
|
|
|
|
static ftdm_status_t ftdm_sangoma_isdn_stop(ftdm_span_t *span)
|
|
{
|
|
ftdm_iterator_t *chaniter = NULL;
|
|
ftdm_iterator_t *curr = NULL;
|
|
unsigned i;
|
|
sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) span->signal_data;
|
|
ftdm_log(FTDM_LOG_INFO, "Stopping span %s\n", span->name);
|
|
|
|
/* throw the STOP_THREAD flag to signal monitor thread stop */
|
|
ftdm_set_flag(span, FTDM_SPAN_STOP_THREAD);
|
|
|
|
/* wait for the thread to stop */
|
|
while (ftdm_test_flag(span, FTDM_SPAN_IN_THREAD)) {
|
|
ftdm_log(FTDM_LOG_DEBUG, "Waiting for monitor thread to end for span %s\n", span->name);
|
|
ftdm_sleep(10);
|
|
}
|
|
|
|
if (sngisdn_stack_stop(span) != FTDM_SUCCESS) {
|
|
ftdm_log(FTDM_LOG_CRIT, "Failed to stop span %s\n", span->name);
|
|
}
|
|
|
|
chaniter = ftdm_span_get_chan_iterator(span, NULL);
|
|
for (curr = chaniter; curr; curr = ftdm_iterator_next(curr)) {
|
|
ftdm_safe_free(((ftdm_channel_t*)ftdm_iterator_current(curr))->call_data);
|
|
((ftdm_channel_t*)ftdm_iterator_current(curr))->call_data = NULL;
|
|
}
|
|
ftdm_iterator_free(chaniter);
|
|
|
|
ftdm_sched_destroy(&signal_data->sched);
|
|
ftdm_queue_destroy(&signal_data->event_queue);
|
|
for (i = 0 ; i < signal_data->num_local_numbers ; i++) {
|
|
if (signal_data->local_numbers[i] != NULL) {
|
|
ftdm_safe_free(signal_data->local_numbers[i]);
|
|
}
|
|
}
|
|
ftdm_safe_free(span->signal_data);
|
|
|
|
ftdm_log(FTDM_LOG_DEBUG, "Finished stopping span %s\n", span->name);
|
|
|
|
return FTDM_SUCCESS;
|
|
}
|
|
|
|
static FIO_CONFIGURE_SPAN_SIGNALING_FUNCTION(ftdm_sangoma_isdn_span_config)
|
|
{
|
|
ftdm_iterator_t *chaniter = NULL;
|
|
ftdm_iterator_t *curr = NULL;
|
|
|
|
sngisdn_span_data_t *signal_data;
|
|
|
|
ftdm_log(FTDM_LOG_INFO, "Configuring ftmod_sangoma_isdn span = %s\n", span->name);
|
|
|
|
signal_data = ftdm_calloc(1, sizeof(sngisdn_span_data_t));
|
|
signal_data->ftdm_span = span;
|
|
span->signal_data = signal_data;
|
|
|
|
chaniter = ftdm_span_get_chan_iterator(span, NULL);
|
|
for (curr = chaniter; curr; curr = ftdm_iterator_next(curr)) {
|
|
sngisdn_chan_data_t *chan_data = ftdm_calloc(1, sizeof(sngisdn_chan_data_t));
|
|
chan_data->ftdmchan = ((ftdm_channel_t*)ftdm_iterator_current(curr));
|
|
((ftdm_channel_t*)ftdm_iterator_current(curr))->call_data = chan_data;
|
|
|
|
}
|
|
ftdm_iterator_free(chaniter);
|
|
|
|
if (ftmod_isdn_parse_cfg(ftdm_parameters, span) != FTDM_SUCCESS) {
|
|
ftdm_log(FTDM_LOG_ERROR, "Failed to parse configuration\n");
|
|
return FTDM_FAIL;
|
|
}
|
|
|
|
if (signal_data->nfas.trunk) {
|
|
if (signal_data->nfas.trunk->num_spans == ++signal_data->nfas.trunk->num_spans_configured) {
|
|
int i;
|
|
ftdm_log(FTDM_LOG_DEBUG, "Starting stack configuration for all spans within trunkgroup:%s\n", signal_data->nfas.trunk->name);
|
|
|
|
sngisdn_stack_cfg(signal_data->nfas.trunk->dchan->ftdm_span);
|
|
if (signal_data->nfas.trunk->backup) {
|
|
sngisdn_stack_cfg(signal_data->nfas.trunk->backup->ftdm_span);
|
|
}
|
|
|
|
for (i = 0; i < signal_data->nfas.trunk->num_spans; i++) {
|
|
if (signal_data->nfas.trunk->spans[i] &&
|
|
signal_data->nfas.trunk->spans[i]->nfas.sigchan == SNGISDN_NFAS_DCHAN_NONE) {
|
|
sngisdn_stack_cfg(signal_data->nfas.trunk->spans[i]->ftdm_span);
|
|
}
|
|
}
|
|
} else {
|
|
ftdm_log(FTDM_LOG_DEBUG, "Delaying span stack configuration until all spans within trunkgroup are started:%s\n", signal_data->nfas.trunk->name);
|
|
}
|
|
} else if (sngisdn_stack_cfg(span) != FTDM_SUCCESS) {
|
|
ftdm_log(FTDM_LOG_CRIT, "Sangoma ISDN Stack configuration failed\n");
|
|
return FTDM_FAIL;
|
|
}
|
|
|
|
if (signal_data->cid_name_method == SNGISDN_CID_NAME_AUTO) {
|
|
switch (signal_data->switchtype) {
|
|
case SNGISDN_SWITCH_EUROISDN:
|
|
if (FTDM_SPAN_IS_BRI(span)) {
|
|
signal_data->cid_name_method = SNGISDN_CID_NAME_USR_USR_IE;
|
|
} else {
|
|
signal_data->cid_name_method = SNGISDN_CID_NAME_DISPLAY_IE;
|
|
}
|
|
break;
|
|
case SNGISDN_SWITCH_DMS100:
|
|
signal_data->cid_name_method = SNGISDN_CID_NAME_DISPLAY_IE;
|
|
break;
|
|
case SNGISDN_SWITCH_NI2:
|
|
case SNGISDN_SWITCH_5ESS:
|
|
case SNGISDN_SWITCH_4ESS:
|
|
signal_data->cid_name_method = SNGISDN_CID_NAME_FACILITY_IE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (signal_data->send_cid_name == SNGISDN_OPT_DEFAULT) {
|
|
switch (signal_data->switchtype) {
|
|
case SNGISDN_SWITCH_EUROISDN:
|
|
#ifdef SNGISDN_SUPPORT_CALLING_NAME_IN_FACILITY
|
|
case SNGISDN_SWITCH_NI2:
|
|
case SNGISDN_SWITCH_5ESS:
|
|
case SNGISDN_SWITCH_4ESS:
|
|
#endif
|
|
if (signal_data->signalling == SNGISDN_SIGNALING_NET) {
|
|
signal_data->send_cid_name = SNGISDN_OPT_TRUE;
|
|
} else {
|
|
signal_data->send_cid_name = SNGISDN_OPT_FALSE;
|
|
}
|
|
break;
|
|
case SNGISDN_SWITCH_DMS100:
|
|
signal_data->send_cid_name = SNGISDN_OPT_TRUE;
|
|
break;
|
|
#ifndef SNGISDN_SUPPORT_CALLING_NAME_IN_FACILITY
|
|
case SNGISDN_SWITCH_NI2:
|
|
case SNGISDN_SWITCH_5ESS:
|
|
case SNGISDN_SWITCH_4ESS:
|
|
signal_data->send_cid_name = SNGISDN_OPT_FALSE;
|
|
break;
|
|
#endif
|
|
default:
|
|
signal_data->send_cid_name = SNGISDN_OPT_FALSE;
|
|
break;
|
|
}
|
|
} else if (signal_data->send_cid_name == SNGISDN_OPT_TRUE) {
|
|
switch (signal_data->switchtype) {
|
|
case SNGISDN_SWITCH_NI2:
|
|
case SNGISDN_SWITCH_5ESS:
|
|
case SNGISDN_SWITCH_4ESS:
|
|
#ifndef SNGISDN_SUPPORT_CALLING_NAME_IN_FACILITY
|
|
ftdm_log(FTDM_LOG_WARNING, "Sending Calling Name in Facility IE not supported, please update your libsng_isdn library\n");
|
|
signal_data->send_cid_name = SNGISDN_OPT_FALSE;
|
|
#endif
|
|
break;
|
|
case SNGISDN_SWITCH_INSNET: /* Don't know how to transmit caller ID name on INSNET */
|
|
case SNGISDN_SWITCH_QSIG: /* It seems like QSIG does not support Caller ID */
|
|
signal_data->send_cid_name = SNGISDN_OPT_FALSE;
|
|
break;
|
|
case SNGISDN_SWITCH_EUROISDN:
|
|
break;
|
|
default:
|
|
signal_data->send_cid_name = SNGISDN_OPT_FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
span->start = ftdm_sangoma_isdn_start;
|
|
span->stop = ftdm_sangoma_isdn_stop;
|
|
span->signal_type = FTDM_SIGTYPE_ISDN;
|
|
span->outgoing_call = ftdm_sangoma_isdn_outgoing_call;
|
|
span->indicate = ftdm_sangoma_isdn_indicate;
|
|
span->channel_request = NULL;
|
|
span->signal_cb = sig_cb;
|
|
span->sig_dtmf = ftdm_sangoma_isdn_dtmf;
|
|
span->get_channel_sig_status = ftdm_sangoma_isdn_get_chan_sig_status;
|
|
span->set_channel_sig_status = ftdm_sangoma_isdn_set_chan_sig_status;
|
|
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);
|
|
ftdm_set_flag(span, FTDM_SPAN_USE_SKIP_STATES);
|
|
ftdm_set_flag(span, FTDM_SPAN_NON_STOPPABLE);
|
|
ftdm_set_flag(span, FTDM_SPAN_USE_TRANSFER);
|
|
|
|
if (FTDM_SPAN_IS_BRI(span)) {
|
|
sngisdn_set_span_avail_rate(span, SNGISDN_AVAIL_PWR_SAVING);
|
|
}
|
|
|
|
/* Initialize scheduling context */
|
|
ftdm_assert(ftdm_sched_create(&((sngisdn_span_data_t*)span->signal_data)->sched, "sngisdn_schedule") == FTDM_SUCCESS, "Failed to create a new schedule!!");
|
|
|
|
/* Initialize the event queue */
|
|
ftdm_assert(ftdm_queue_create(&((sngisdn_span_data_t*)span->signal_data)->event_queue, SNGISDN_EVENT_QUEUE_SIZE) == FTDM_SUCCESS, "Failed to create a new queue!!");
|
|
|
|
ftdm_log(FTDM_LOG_INFO, "Finished configuring ftmod_sangoma_isdn span = %s\n", span->name);
|
|
return FTDM_SUCCESS;
|
|
}
|
|
|
|
static FIO_SIG_LOAD_FUNCTION(ftdm_sangoma_isdn_init)
|
|
{
|
|
unsigned i;
|
|
ftdm_log(FTDM_LOG_INFO, "Loading ftmod_sangoma_isdn...\n");
|
|
|
|
memset(&g_sngisdn_data, 0, sizeof(g_sngisdn_data));
|
|
memset(&g_sngisdn_event_interface, 0, sizeof(g_sngisdn_event_interface));
|
|
/* set callbacks */
|
|
g_sngisdn_event_interface.cc.sng_con_ind = sngisdn_rcv_con_ind;
|
|
g_sngisdn_event_interface.cc.sng_con_cfm = sngisdn_rcv_con_cfm;
|
|
g_sngisdn_event_interface.cc.sng_cnst_ind = sngisdn_rcv_cnst_ind;
|
|
g_sngisdn_event_interface.cc.sng_disc_ind = sngisdn_rcv_disc_ind;
|
|
g_sngisdn_event_interface.cc.sng_rel_ind = sngisdn_rcv_rel_ind;
|
|
g_sngisdn_event_interface.cc.sng_dat_ind = sngisdn_rcv_dat_ind;
|
|
g_sngisdn_event_interface.cc.sng_sshl_ind = sngisdn_rcv_sshl_ind;
|
|
g_sngisdn_event_interface.cc.sng_sshl_cfm = sngisdn_rcv_sshl_cfm;
|
|
g_sngisdn_event_interface.cc.sng_rmrt_ind = sngisdn_rcv_rmrt_ind;
|
|
g_sngisdn_event_interface.cc.sng_rmrt_cfm = sngisdn_rcv_rmrt_cfm;
|
|
g_sngisdn_event_interface.cc.sng_flc_ind = sngisdn_rcv_flc_ind;
|
|
g_sngisdn_event_interface.cc.sng_fac_ind = sngisdn_rcv_fac_ind;
|
|
g_sngisdn_event_interface.cc.sng_sta_cfm = sngisdn_rcv_sta_cfm;
|
|
g_sngisdn_event_interface.cc.sng_srv_ind = sngisdn_rcv_srv_ind;
|
|
g_sngisdn_event_interface.cc.sng_srv_cfm = sngisdn_rcv_srv_cfm;
|
|
g_sngisdn_event_interface.cc.sng_rst_ind = sngisdn_rcv_rst_ind;
|
|
g_sngisdn_event_interface.cc.sng_rst_cfm = sngisdn_rcv_rst_cfm;
|
|
|
|
g_sngisdn_event_interface.lg.sng_log = sngisdn_rcv_sng_log;
|
|
g_sngisdn_event_interface.lg.sng_assert = sngisdn_rcv_sng_assert;
|
|
|
|
g_sngisdn_event_interface.sta.sng_phy_sta_ind = sngisdn_rcv_phy_ind;
|
|
g_sngisdn_event_interface.sta.sng_q921_sta_ind = sngisdn_rcv_q921_ind;
|
|
g_sngisdn_event_interface.sta.sng_q921_trc_ind = sngisdn_rcv_q921_trace;
|
|
g_sngisdn_event_interface.sta.sng_q931_sta_ind = sngisdn_rcv_q931_ind;
|
|
g_sngisdn_event_interface.sta.sng_q931_trc_ind = sngisdn_rcv_q931_trace;
|
|
g_sngisdn_event_interface.sta.sng_cc_sta_ind = sngisdn_rcv_cc_ind;
|
|
|
|
g_sngisdn_event_interface.io.sng_l1_data_req = sngisdn_rcv_l1_data_req;
|
|
g_sngisdn_event_interface.io.sng_l1_cmd_req = sngisdn_rcv_l1_cmd_req;
|
|
|
|
for(i=1;i<=MAX_VARIANTS;i++) {
|
|
ftdm_mutex_create(&g_sngisdn_data.ccs[i].mutex);
|
|
}
|
|
|
|
/* initalize sng_isdn library */
|
|
|
|
ftdm_assert_return(!sng_isdn_init(&g_sngisdn_event_interface), FTDM_FAIL, "Failed to initialize stack\n");
|
|
return FTDM_SUCCESS;
|
|
}
|
|
|
|
static FIO_SIG_UNLOAD_FUNCTION(ftdm_sangoma_isdn_unload)
|
|
{
|
|
unsigned i;
|
|
ftdm_log(FTDM_LOG_INFO, "Starting ftmod_sangoma_isdn unload...\n");
|
|
|
|
sng_isdn_free();
|
|
|
|
for(i=1;i<=MAX_VARIANTS;i++) {
|
|
ftdm_mutex_destroy(&g_sngisdn_data.ccs[i].mutex);
|
|
}
|
|
|
|
ftdm_log(FTDM_LOG_INFO, "Finished ftmod_sangoma_isdn unload!\n");
|
|
return FTDM_SUCCESS;
|
|
}
|
|
|
|
#define SANGOMA_ISDN_API_USAGE_TRACE "ftdm sangoma_isdn trace <q921|q931> <span name>\n"
|
|
#define SANGOMA_ISDN_API_USAGE_SHOW_L1_STATS "ftdm sangoma_isdn l1_stats <span name>\n"
|
|
#define SANGOMA_ISDN_API_USAGE_SHOW_SPANS "ftdm sangoma_isdn show_spans [<span name>]\n"
|
|
|
|
#define SANGOMA_ISDN_API_USAGE "\t"SANGOMA_ISDN_API_USAGE_TRACE \
|
|
"\t"SANGOMA_ISDN_API_USAGE_SHOW_L1_STATS \
|
|
"\t"SANGOMA_ISDN_API_USAGE_SHOW_SPANS
|
|
|
|
static FIO_API_FUNCTION(ftdm_sangoma_isdn_api)
|
|
{
|
|
ftdm_status_t status = FTDM_EINVAL;
|
|
char *mycmd = NULL, *argv[10] = { 0 };
|
|
int argc = 0;
|
|
|
|
if (data) {
|
|
mycmd = ftdm_strdup(data);
|
|
argc = ftdm_separate_string(mycmd, ' ', argv, (sizeof(argv) / sizeof(argv[0])));
|
|
}
|
|
|
|
/*ftdm_log(FTDM_LOG_DEBUG, "Sangoma argc:%d argv[0]:%s argv[1]:%s argv[2]:%s \n", argc, argv[0], argv[1], argv[2]);*/
|
|
if (argc <= 0) {
|
|
ftdm_log(FTDM_LOG_ERROR, "No parameters provided\n");
|
|
goto done;
|
|
}
|
|
|
|
/* TODO: Move functions to table + function pointers */
|
|
if (!strcasecmp(argv[0], "trace")) {
|
|
char *trace_opt;
|
|
|
|
ftdm_span_t *span;
|
|
|
|
if (argc < 3) {
|
|
ftdm_log(FTDM_LOG_ERROR, "Usage: %s\n", SANGOMA_ISDN_API_USAGE_TRACE);
|
|
status = FTDM_FAIL;
|
|
goto done;
|
|
}
|
|
trace_opt = argv[1];
|
|
|
|
status = ftdm_span_find_by_name(argv[2], &span);
|
|
if (FTDM_SUCCESS != status) {
|
|
stream->write_function(stream, "-ERR failed to find span by name %s\n", argv[2]);
|
|
|
|
status = FTDM_FAIL;
|
|
goto done;
|
|
}
|
|
|
|
if (!strcasecmp(trace_opt, "q921")) {
|
|
status = sngisdn_activate_trace(span, SNGISDN_TRACE_Q921);
|
|
} else if (!strcasecmp(trace_opt, "q931")) {
|
|
status = sngisdn_activate_trace(span, SNGISDN_TRACE_Q931);
|
|
} else if (!strcasecmp(trace_opt, "disable")) {
|
|
status = sngisdn_activate_trace(span, SNGISDN_TRACE_DISABLE);
|
|
} else {
|
|
stream->write_function(stream, "-ERR invalid trace option <q921|q931> <span name>\n");
|
|
status = FTDM_FAIL;
|
|
}
|
|
goto done;
|
|
}
|
|
|
|
if (!strcasecmp(argv[0], "l1_stats")) {
|
|
ftdm_span_t *span;
|
|
if (argc < 2) {
|
|
stream->write_function(stream, "Usage: %s\n", SANGOMA_ISDN_API_USAGE_SHOW_L1_STATS);
|
|
status = FTDM_FAIL;
|
|
goto done;
|
|
}
|
|
status = ftdm_span_find_by_name(argv[1], &span);
|
|
if (FTDM_SUCCESS != status) {
|
|
stream->write_function(stream, "-ERR failed to find span with name %s\n", argv[1]);
|
|
|
|
status = FTDM_FAIL;
|
|
goto done;
|
|
}
|
|
status = sngisdn_show_l1_stats(stream, span);
|
|
goto done;
|
|
}
|
|
|
|
if (!strcasecmp(argv[0], "show_spans")) {
|
|
ftdm_span_t *span = NULL;
|
|
if (argc == 2) {
|
|
status = ftdm_span_find_by_name(argv[1], &span);
|
|
if (FTDM_SUCCESS != status) {
|
|
stream->write_function(stream, "-ERR failed to find span with name %s\n", argv[1]);
|
|
|
|
stream->write_function(stream, "Usage: %s\n", SANGOMA_ISDN_API_USAGE_SHOW_SPANS);
|
|
status = FTDM_FAIL;
|
|
goto done;
|
|
}
|
|
status = sngisdn_show_span(stream, span);
|
|
goto done;
|
|
}
|
|
status = sngisdn_show_spans(stream);
|
|
goto done;
|
|
}
|
|
|
|
if (!strcasecmp(argv[0], "check_ids")) {
|
|
status = sngisdn_check_free_ids();
|
|
goto done;
|
|
}
|
|
if (!strcasecmp(argv[0], "check_mem")) {
|
|
sngisdn_get_memory_info();
|
|
}
|
|
done:
|
|
switch (status) {
|
|
case FTDM_SUCCESS:
|
|
stream->write_function(stream, "Command executed OK\n");
|
|
break;
|
|
case FTDM_EINVAL:
|
|
stream->write_function(stream, "Invalid arguments [%s]\n", mycmd);
|
|
stream->write_function(stream, "Usage:\n%s\n", SANGOMA_ISDN_API_USAGE);
|
|
break;
|
|
default:
|
|
/* FTDM_FAIL - Do nothing since we already printed the cause of the error */
|
|
break;
|
|
}
|
|
|
|
/* Return SUCCESS because we do not want to print the general FTDM usage list */
|
|
status = FTDM_SUCCESS;
|
|
|
|
ftdm_safe_free(mycmd);
|
|
return status;
|
|
}
|
|
|
|
|
|
static FIO_IO_LOAD_FUNCTION(ftdm_sangoma_isdn_io_init)
|
|
{
|
|
memset(&g_sngisdn_io_interface, 0, sizeof(g_sngisdn_io_interface));
|
|
|
|
g_sngisdn_io_interface.name = "sangoma_isdn";
|
|
g_sngisdn_io_interface.api = ftdm_sangoma_isdn_api;
|
|
|
|
*fio = &g_sngisdn_io_interface;
|
|
|
|
return FTDM_SUCCESS;
|
|
}
|
|
|
|
EX_DECLARE_DATA ftdm_module_t ftdm_module =
|
|
{
|
|
"sangoma_isdn", /* char name[256]; */
|
|
ftdm_sangoma_isdn_io_init, /* fio_io_load_t */
|
|
NULL, /* fio_io_unload_t */
|
|
ftdm_sangoma_isdn_init, /* fio_sig_load_t */
|
|
NULL, /* fio_sig_configure_t */
|
|
ftdm_sangoma_isdn_unload, /* fio_sig_unload_t */
|
|
ftdm_sangoma_isdn_span_config /* fio_configure_span_signaling_t */
|
|
};
|
|
|
|
|
|
/* 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:
|
|
*/
|
|
|
|
/******************************************************************************/
|
|
|
|
|