$(SRC)/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_cfg.c \
$(SRC)/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_cntrl.c \
$(SRC)/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_trace.c \
+ $(SRC)/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_transfer.c \
$(SRC)/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_support.c \
$(SRC)/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_cntrl.c \
$(SRC)/ftmod/ftmod_sangoma_isdn/ftmod_sangoma_isdn_stack_cfg.c \
ftdm_channel_call_answer()
ftdm_channel_call_indicate()
+ftdm_channel_call_transfer()
FTDM_SIGEVENT_INDICATION_COMPLETED
*note that FTDM_SIGEVENT_INDICATION_COMPLETED has associated data to indicate the result of the indication
*note this event is only delivered on non-blocking channels
TFLAG_CODEC = (1 << 2),
TFLAG_BREAK = (1 << 3),
TFLAG_HOLD = (1 << 4),
- TFLAG_DEAD = (1 << 5)
+ TFLAG_DEAD = (1 << 5),
+ TFLAG_TRANSFER = (1 << 6),
} TFLAGS;
static struct {
ftdm_channel_call_answer(tech_pvt->ftdmchan);
}
break;
+ case SWITCH_MESSAGE_INDICATE_REDIRECT:
+ case SWITCH_MESSAGE_INDICATE_DEFLECT:
+ {
+ ftdm_usrmsg_t usrmsg;
+ const char *val = NULL;
+
+ memset(&usrmsg, 0, sizeof(usrmsg));
+
+ if ((val = switch_channel_get_variable(channel, "freetdm_transfer_data"))) {
+ ftdm_usrmsg_add_var(&usrmsg, "transfer_data", val);
+ }
+
+ switch_set_flag(tech_pvt, TFLAG_TRANSFER);
+ if (ftdm_channel_call_transfer_ex(tech_pvt->ftdmchan, msg->string_arg, &usrmsg) != FTDM_SUCCESS) {
+ switch_clear_flag(tech_pvt, TFLAG_TRANSFER);
+ }
+ while (switch_test_flag(tech_pvt, TFLAG_TRANSFER)) {
+ switch_yield(100000);
+ }
+ }
default:
break;
}
-
+
return SWITCH_STATUS_SUCCESS;
}
static FIO_SIGNAL_CB_FUNCTION(on_common_signal)
{
- switch_event_t *event = NULL;
- ftdm_alarm_flag_t alarmbits = FTDM_ALARM_NONE;
uint32_t chanid, spanid;
+ switch_event_t *event = NULL;
+ ftdm_alarm_flag_t alarmbits = FTDM_ALARM_NONE;
+
chanid = ftdm_channel_get_id(sigmsg->channel);
spanid = ftdm_channel_get_span_id(sigmsg->channel);
+
switch (sigmsg->event_id) {
case FTDM_SIGEVENT_ALARM_CLEAR:
}
return FTDM_SUCCESS;
}
+ case FTDM_SIGEVENT_TRANSFER_COMPLETED:
+ {
+ switch_core_session_t *session = NULL;
+ switch_channel_t *channel = NULL;
+ private_t *tech_pvt = NULL;
- case FTDM_SIGEVENT_RELEASED:
+ if ((session = ftdm_channel_get_session(sigmsg->channel, 0))) {
+ channel = switch_core_session_get_channel(session);
+ tech_pvt = switch_core_session_get_private(session);
+
+ switch_clear_flag_locked(tech_pvt, TFLAG_TRANSFER);
+ switch_channel_set_variable(channel, "freetdm_transfer_response", ftdm_transfer_response2str(sigmsg->ev_data.transfer_completed.response));
+ switch_core_session_rwunlock(session);
+ }
+ return FTDM_SUCCESS;
+ }
+ break;
+ case FTDM_SIGEVENT_RELEASED:
case FTDM_SIGEVENT_INDICATION_COMPLETED:
case FTDM_SIGEVENT_DIALING:
- {
+ {
/* Swallow these events */
return FTDM_BREAK;
- }
+ }
+ break;
+ case FTDM_SIGEVENT_STOP:
+ case FTDM_SIGEVENT_RESTART:
+ {
+ switch_core_session_t *session = NULL;
+ private_t *tech_pvt = NULL;
+ while((session = ftdm_channel_get_session(sigmsg->channel, 0))) {
+ tech_pvt = switch_core_session_get_private(session);
+
+ switch_clear_flag_locked(tech_pvt, TFLAG_TRANSFER);
+ switch_core_session_rwunlock(session);
+ return FTDM_SUCCESS;
+ }
+ }
break;
default:
return FTDM_SUCCESS;
break;
case FTDM_SIGEVENT_PROCEED:
case FTDM_SIGEVENT_FACILITY:
+ case FTDM_SIGEVENT_TRANSFER_COMPLETED:
/* FS does not have handlers for these messages, so ignore them for now */
break;
default:
#define FTDM_READ_TRACE_INDEX 0
#define FTDM_WRITE_TRACE_INDEX 1
#define MAX_CALLIDS 6000
+#define FTDM_HALF_DTMF_PAUSE 500
+#define FTDM_FULL_DTMF_PAUSE 1000
ftdm_time_t time_last_throttle_log = 0;
ftdm_time_t time_current_throttle_log = 0;
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)
+FTDM_STR2ENUM(ftdm_str2ftdm_channel_indication, ftdm_channel_indication2str, ftdm_channel_indication_t, INDICATION_NAMES, FTDM_CHANNEL_INDICATE_INVALID)
+
+FTDM_ENUM_NAMES(TRANSFER_RESPONSE_NAMES, TRANSFER_RESPONSE_STRINGS)
+FTDM_STR2ENUM(ftdm_str2ftdm_transfer_response, ftdm_transfer_response2str, ftdm_transfer_response_t, TRANSFER_RESPONSE_NAMES, FTDM_TRANSFER_RESPONSE_INVALID)
static ftdm_status_t ftdm_group_add_channels(ftdm_span_t* span, int currindex, const char* name);
goto done;
}
+ if (ftdm_test_io_flag(ftdmchan, FTDM_CHANNEL_IO_EVENT)) {
+ ftdm_clear_io_flag(ftdmchan, FTDM_CHANNEL_IO_EVENT);
+ }
+
status = span->fio->channel_next_event(ftdmchan, event);
if (status != FTDM_SUCCESS) {
goto done;
if (!citer) {
return FTDM_ENOMEM;
}
+
for (curr = citer ; curr; curr = ftdm_iterator_next(curr)) {
fchan = ftdm_iterator_current(curr);
if (enabled) {
FT_DECLARE(void) ftdm_ack_indication(ftdm_channel_t *fchan, ftdm_channel_indication_t indication, ftdm_status_t status)
{
ftdm_sigmsg_t msg;
-
+
if (!ftdm_test_flag(fchan, FTDM_CHANNEL_IND_ACK_PENDING)) {
return;
}
- ftdm_log_chan(fchan, FTDM_LOG_DEBUG, "Acknowledging indication %s in state %s (rc = %d)\n",
+ 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));
return status;
}
+FT_DECLARE(ftdm_status_t) _ftdm_channel_call_transfer(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, const char* arg, ftdm_usrmsg_t *usrmsg)
+{
+ ftdm_status_t status;
+ ftdm_usrmsg_t *msg = NULL;
+ ftdm_bool_t free_msg = FTDM_FALSE;
+
+ if (!usrmsg) {
+ msg = ftdm_calloc(1, sizeof(*msg));
+ ftdm_assert_return(msg, FTDM_FAIL, "Failed to allocate usr msg");
+ memset(msg, 0, sizeof(*msg));
+ free_msg = FTDM_TRUE;
+ } else {
+ msg = usrmsg;
+ }
+
+ ftdm_usrmsg_add_var(msg, "transfer_arg", arg);
+ /* we leave the locking up to ftdm_channel_call_indicate, DO NOT lock here since ftdm_channel_call_indicate expects
+ * the lock recursivity to be 1 */
+ status = _ftdm_channel_call_indicate(file, func, line, ftdmchan, FTDM_CHANNEL_INDICATE_TRANSFER, msg);
+ if (free_msg == FTDM_TRUE) {
+ ftdm_safe_free(msg);
+ }
+ return status;
+}
+
/* lock must be acquired by the caller! */
static ftdm_status_t _ftdm_channel_call_hangup_nl(const char *file, const char *func, int line, ftdm_channel_t *chan, ftdm_usrmsg_t *usrmsg)
{
ftdm_status_t status = FTDM_SUCCESS;
-
+
if (chan->state != FTDM_CHANNEL_STATE_DOWN) {
if (chan->state == FTDM_CHANNEL_STATE_HANGUP) {
/* make user's life easier, and just ignore double hangup requests */
case FTDM_CHANNEL_INDICATE_ANSWER:
status = _ftdm_channel_call_answer_nl(file, func, line, ftdmchan, usrmsg);
break;
+ case FTDM_CHANNEL_INDICATE_TRANSFER:
+ if (!ftdm_test_flag(ftdmchan->span, FTDM_SPAN_USE_TRANSFER)) {
+ ftdm_log_chan_ex_msg(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "Transfer not supported\n");
+ status = FTDM_EINVAL;
+ goto done;
+ }
+ status = ftdm_channel_set_state(file, func, line, ftdmchan, FTDM_CHANNEL_STATE_TRANSFER, 1, usrmsg);
+ break;
default:
/* See if signalling module can provide this indication */
status = ftdm_channel_sig_indicate(ftdmchan, indication, usrmsg);
return status;
}
-
static ftdm_status_t ftdmchan_activate_dtmf_buffer(ftdm_channel_t *ftdmchan)
{
-
if (!ftdmchan->dtmf_buffer) {
if (ftdm_buffer_create(&ftdmchan->dtmf_buffer, 1024, 3192, 0) != FTDM_SUCCESS) {
ftdm_log(FTDM_LOG_ERROR, "Failed to allocate DTMF Buffer!\n");
return FTDM_SUCCESS;
}
+/*
+ * ftdmchan_activate_dtmf_buffer to initialize ftdmchan->dtmf_buffer should be called prior to
+ * calling ftdm_insert_dtmf_pause
+ */
+static ftdm_status_t ftdm_insert_dtmf_pause(ftdm_channel_t *ftdmchan, ftdm_size_t pausems)
+{
+ void *data = NULL;
+ unsigned int datalen = pausems * sizeof(uint16_t);
+
+ data = ftdm_malloc(datalen);
+ ftdm_assert(data, "Failed to allocate memory\n");
+
+ memset(data, FTDM_SILENCE_VALUE(ftdmchan), datalen);
+
+ ftdm_buffer_write(ftdmchan->dtmf_buffer, data, datalen);
+ ftdm_safe_free(data);
+ return FTDM_SUCCESS;
+}
+
FT_DECLARE(ftdm_status_t) ftdm_channel_command(ftdm_channel_t *ftdmchan, ftdm_command_t command, void *obj)
{
ftdm_status_t status = FTDM_FAIL;
-
+
ftdm_assert_return(ftdmchan != NULL, FTDM_FAIL, "No channel\n");
ftdm_assert_return(ftdmchan->fio != NULL, FTDM_FAIL, "No IO attached to channel\n");
status = ftdm_buffer_write(ftdmchan->digit_buffer, dtmf, wr) ? FTDM_SUCCESS : FTDM_FAIL;
ftdm_mutex_unlock(ftdmchan->mutex);
-
+
return status;
}
-static FIO_WRITE_FUNCTION(ftdm_raw_write)
+FIO_WRITE_FUNCTION(ftdm_raw_write)
{
int dlen = (int) *datalen;
+
+ if (ftdm_test_io_flag(ftdmchan, FTDM_CHANNEL_IO_WRITE)) {
+ ftdm_clear_io_flag(ftdmchan, FTDM_CHANNEL_IO_WRITE);
+ }
+
if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_TX_DISABLED)) {
ftdmchan->txdrops++;
if (ftdmchan->txdrops <= 10) {
return ftdmchan->fio->write(ftdmchan, data, datalen);
}
-static FIO_READ_FUNCTION(ftdm_raw_read)
+FIO_READ_FUNCTION(ftdm_raw_read)
{
- ftdm_status_t status = ftdmchan->fio->read(ftdmchan, data, datalen);
+ ftdm_status_t status;
+
+ if (ftdm_test_io_flag(ftdmchan, FTDM_CHANNEL_IO_READ)) {
+ ftdm_clear_io_flag(ftdmchan, FTDM_CHANNEL_IO_READ);
+ }
+ status = ftdmchan->fio->read(ftdmchan, data, datalen);
- if (status == FTDM_SUCCESS && ftdm_test_flag(ftdmchan, FTDM_CHANNEL_USE_RX_GAIN)
+ if (status == FTDM_SUCCESS && ftdm_test_flag(ftdmchan, FTDM_CHANNEL_USE_RX_GAIN)
&& (ftdmchan->native_codec == FTDM_CODEC_ALAW || ftdmchan->native_codec == FTDM_CODEC_ULAW)) {
ftdm_size_t i = 0;
unsigned char *rdata = data;
if (ftdm_buffer_read(ftdmchan->gen_dtmf_buffer, digits, dblen) && !ftdm_strlen_zero_buf(digits)) {
ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Generating DTMF [%s]\n", digits);
-
- cur = digits;
- if (*cur == 'F') {
- ftdm_channel_command(ftdmchan, FTDM_COMMAND_FLASH, NULL);
- cur++;
- }
+ cur = digits;
for (; *cur; cur++) {
- if ((wrote = teletone_mux_tones(&ftdmchan->tone_session, &ftdmchan->tone_session.TONES[(int)*cur]))) {
- ftdm_buffer_write(ftdmchan->dtmf_buffer, ftdmchan->tone_session.buffer, wrote * 2);
- x++;
+ if (*cur == 'F') {
+ ftdm_channel_command(ftdmchan, FTDM_COMMAND_FLASH, NULL);
+ } else if (*cur == 'w') {
+ ftdm_insert_dtmf_pause(ftdmchan, FTDM_HALF_DTMF_PAUSE);
+ } else if (*cur == 'W') {
+ ftdm_insert_dtmf_pause(ftdmchan, FTDM_FULL_DTMF_PAUSE);
} else {
- ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Problem adding DTMF sequence [%s]\n", digits);
- return FTDM_FAIL;
+ if ((wrote = teletone_mux_tones(&ftdmchan->tone_session, &ftdmchan->tone_session.TONES[(int)*cur]))) {
+ ftdm_buffer_write(ftdmchan->dtmf_buffer, ftdmchan->tone_session.buffer, wrote * 2);
+ x++;
+ } else {
+ ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Problem adding DTMF sequence [%s]\n", digits);
+ return FTDM_FAIL;
+ }
+ }
+ if (x) {
+ ftdmchan->skip_read_frames = (wrote / (ftdmchan->effective_interval * 8)) + 4;
}
- }
-
- if (x) {
- ftdmchan->skip_read_frames = (wrote / (ftdmchan->effective_interval * 8)) + 4;
}
}
}
-
if (!ftdmchan->buffer_delay || --ftdmchan->buffer_delay == 0) {
/* time to pick a buffer, either the dtmf or fsk buffer */
}
+
FT_DECLARE(void) ftdm_generate_sln_silence(int16_t *data, uint32_t samples, uint32_t divisor)
{
int16_t x;
}
}
-FT_DECLARE(ftdm_status_t) ftdm_channel_read(ftdm_channel_t *ftdmchan, void *data, ftdm_size_t *datalen)
+FT_DECLARE(ftdm_status_t) ftdm_channel_process_media(ftdm_channel_t *ftdmchan, void *data, ftdm_size_t *datalen)
{
ftdm_status_t status = FTDM_FAIL;
fio_codec_t codec_func = NULL;
ftdm_size_t max = *datalen;
- ftdm_assert_return(ftdmchan != NULL, FTDM_FAIL, "ftdmchan is null\n");
- ftdm_assert_return(ftdmchan->fio != NULL, FTDM_FAIL, "No I/O module attached to ftdmchan\n");
-
- ftdm_channel_lock(ftdmchan);
-
- if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OPEN)) {
- snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "channel not open");
- ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "cannot read from channel that is not open\n");
- status = FTDM_FAIL;
- goto done;
- }
-
- if (!ftdmchan->fio->read) {
- snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "method not implemented");
- ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "read method not implemented\n");
- status = FTDM_FAIL;
- goto done;
- }
-
- if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_RX_DISABLED)) {
- ftdmchan->rxdrops++;
- if (ftdmchan->rxdrops <= 10) {
- ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "cannot read from channel with rx disabled\n");
- }
- if (ftdmchan->rxdrops == 10) {
- ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "too many rx drops, not logging anymore\n");
- }
- status = FTDM_FAIL;
- goto done;
- }
-
- status = ftdm_raw_read(ftdmchan, data, datalen);
- if (status != FTDM_SUCCESS) {
- ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "raw I/O read filed\n");
- goto done;
- }
-
handle_tone_generation(ftdmchan);
if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_DIGITAL_MEDIA)) {
}
}
- if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_DTMF_DETECT) || ftdm_test_flag(ftdmchan, FTDM_CHANNEL_PROGRESS_DETECT) ||
+ if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_DTMF_DETECT) ||
+ ftdm_test_flag(ftdmchan, FTDM_CHANNEL_PROGRESS_DETECT) ||
ftdm_test_flag(ftdmchan, FTDM_CHANNEL_CALLERID_DETECT)) {
+
uint8_t sln_buf[1024] = {0};
int16_t *sln;
ftdm_size_t slen = 0;
}
}
}
-
-
+
if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_DTMF_DETECT) && !ftdm_channel_test_feature(ftdmchan, FTDM_CHANNEL_FEATURE_DTMF_DETECT)) {
teletone_dtmf_detect(&ftdmchan->dtmf_detect, sln, (int)slen);
teletone_dtmf_get(&ftdmchan->dtmf_detect, digit_str, sizeof(digit_str));
if (ftdmchan->state == FTDM_CHANNEL_STATE_CALLWAITING && (*digit_str == 'D' || *digit_str == 'A')) {
ftdmchan->detected_tones[FTDM_TONEMAP_CALLWAITING_ACK]++;
} else {
- ftdm_channel_queue_dtmf(ftdmchan, digit_str);
+ if (!ftdmchan->span->sig_dtmf || (ftdmchan->span->sig_dtmf(ftdmchan, (const char*)digit_str) != FTDM_BREAK)) {
+ ftdm_channel_queue_dtmf(ftdmchan, digit_str);
+ }
if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_SUPRESS_DTMF)) {
ftdmchan->skip_read_frames = 20;
}
if (ftdmchan->skip_read_frames > 0 || ftdm_test_flag(ftdmchan, FTDM_CHANNEL_MUTE)) {
-
+
ftdm_mutex_lock(ftdmchan->pre_buffer_mutex);
if (ftdmchan->pre_buffer && ftdm_buffer_inuse(ftdmchan->pre_buffer)) {
ftdm_buffer_zero(ftdmchan->pre_buffer);
}
ftdm_mutex_unlock(ftdmchan->pre_buffer_mutex);
-
memset(data, FTDM_SILENCE_VALUE(ftdmchan), *datalen);
if (ftdmchan->skip_read_frames > 0) {
ftdmchan->skip_read_frames--;
}
- } else {
+ } else {
ftdm_mutex_lock(ftdmchan->pre_buffer_mutex);
if (ftdmchan->pre_buffer_size && ftdmchan->pre_buffer) {
ftdm_buffer_write(ftdmchan->pre_buffer, data, *datalen);
}
done:
+ return FTDM_SUCCESS;
+}
- ftdm_channel_unlock(ftdmchan);
+FT_DECLARE(ftdm_status_t) ftdm_channel_read(ftdm_channel_t *ftdmchan, void *data, ftdm_size_t *datalen)
+{
+
+ ftdm_status_t status = FTDM_FAIL;
+
+ ftdm_assert_return(ftdmchan != NULL, FTDM_FAIL, "ftdmchan is null\n");
+ ftdm_assert_return(ftdmchan->fio != NULL, FTDM_FAIL, "No I/O module attached to ftdmchan\n");
+
+ ftdm_channel_lock(ftdmchan);
+
+ if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OPEN)) {
+ ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "cannot read from channel that is not open\n");
+ status = FTDM_FAIL;
+ goto done;
+ }
+
+ if (!ftdmchan->fio->read) {
+ ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "read method not implemented\n");
+ status = FTDM_FAIL;
+ goto done;
+ }
+
+ if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_RX_DISABLED)) {
+ ftdmchan->rxdrops++;
+ if (ftdmchan->rxdrops <= 10) {
+ ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "cannot read from channel with rx disabled\n");
+ }
+ if (ftdmchan->rxdrops == 10) {
+ ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "too many rx drops, not logging anymore\n");
+ }
+ status = FTDM_FAIL;
+ goto done;
+ }
+ status = ftdm_raw_read(ftdmchan, data, datalen);
+ if (status != FTDM_SUCCESS) {
+ ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "raw I/O read filed\n");
+ goto done;
+ }
+
+ status = ftdm_channel_process_media(ftdmchan, data, datalen);
+ if (status != FTDM_SUCCESS) {
+ ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "Failed to process media\n");
+ }
+done:
+ ftdm_channel_unlock(ftdmchan);
return status;
}
if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OPEN)) {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_WARNING, "cannot write in channel not open\n");
- snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "channel not open");
status = FTDM_FAIL;
goto done;
}
if (!ftdmchan->fio->write) {
ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "write method not implemented\n");
- snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "method not implemented");
status = FTDM_FAIL;
goto done;
}
status = codec_func(data, max, datalen);
} else {
ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Do not know how to handle transcoding from %d to %d\n",
- ftdmchan->effective_codec, ftdmchan->native_codec);
- snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "codec error!");
+ ftdmchan->effective_codec, ftdmchan->native_codec);
status = FTDM_FAIL;
goto done;
}
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_poll_events(ftdm_span_t *span);
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 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;
+ftdm_sngisdn_data_t g_sngisdn_data;
+
+FTDM_ENUM_NAMES(SNGISDN_TRANSFER_TYPE_NAMES, SNGISDN_TRANSFER_TYPE_STRINGS)
+FTDM_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_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}
+ {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,
ZSD_INBOUND,
ZSM_UNACCEPTABLE,
{FTDM_CHANNEL_STATE_UP, FTDM_END},
- {FTDM_CHANNEL_STATE_HANGUP, FTDM_CHANNEL_STATE_TERMINATING, 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,
return;
}
-static void *ftdm_sangoma_isdn_dchan_run(ftdm_thread_t *me, void *obj)
+static void *ftdm_sangoma_isdn_io_run(ftdm_thread_t *me, void *obj)
{
uint8_t data[1000];
+ unsigned i;
ftdm_status_t status = FTDM_SUCCESS;
ftdm_wait_flag_t wflags = FTDM_READ;
ftdm_span_t *span = (ftdm_span_t*) obj;
- ftdm_channel_t *dchan = ((sngisdn_span_data_t*)span->signal_data)->dchan;
ftdm_size_t len = 0;
-
- ftdm_channel_set_feature(dchan, FTDM_CHANNEL_FEATURE_IO_STATS);
- ftdm_sangoma_isdn_dchan_set_queue_size(dchan);
+ ftdm_channel_t *ftdmchan = NULL;
+ unsigned waitms = 10000;
+ ftdm_iterator_t *chaniter = NULL;
+ ftdm_iterator_t *citer = NULL;
+ short *poll_events = ftdm_malloc(sizeof(short) * span->chan_count);
+
+ /* Initialize the d-channel */
+ ftdm_assert(((sngisdn_span_data_t*)span->signal_data)->dchan, "Span does not have a dchannel");
+ ftdm_channel_set_feature(((sngisdn_span_data_t*)span->signal_data)->dchan, FTDM_CHANNEL_FEATURE_IO_STATS);
+ ftdm_sangoma_isdn_dchan_set_queue_size(((sngisdn_span_data_t*)span->signal_data)->dchan);
+ ftdm_channel_open_chan(((sngisdn_span_data_t*)span->signal_data)->dchan);
+
+ 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;
+ }
- ftdm_assert(dchan, "Span does not have a dchannel");
- ftdm_channel_open_chan(dchan);
-
while (ftdm_running() && !(ftdm_test_flag(span, FTDM_SPAN_STOP_THREAD))) {
+ len = 1000;
+ waitms = 1000;
wflags = FTDM_READ;
- status = ftdm_channel_wait(dchan, &wflags, 10000);
- switch(status) {
+ 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);
+
+ 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_chan_msg(dchan, FTDM_LOG_CRIT, "Failed to wait for d-channel\n");
+ ftdm_log(FTDM_LOG_CRIT, "Failed to poll span for IO\n");
break;
case FTDM_TIMEOUT:
break;
case FTDM_SUCCESS:
- if ((wflags & FTDM_READ)) {
+ for (citer = ftdm_span_get_chan_iterator(span, chaniter); citer; citer = ftdm_iterator_next(citer)) {
len = 1000;
- status = ftdm_channel_read(dchan, data, &len);
- if (status == FTDM_SUCCESS) {
- sngisdn_snd_data(dchan, data, len);
+ 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 {
- ftdm_log_chan_msg(dchan, FTDM_LOG_WARNING, "Failed to read from channel \n");
+ 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);
+ }
+ }
}
-#ifndef WIN32 /* It is valid on WIN32 for poll to return without errors, but no flags set */
- } else {
- ftdm_log_chan_msg(dchan, FTDM_LOG_CRIT, "Failed to poll for d-channel\n");
-#endif
}
break;
default:
- ftdm_log_chan_msg(dchan, FTDM_LOG_CRIT, "Unhandled IO event\n");
+ ftdm_log_chan_msg(ftdmchan, FTDM_LOG_CRIT, "Unhandled IO event\n");
}
- }
+ }
+done:
+ ftdm_iterator_free(chaniter);
+ ftdm_safe_free(poll_events);
return NULL;
}
/* 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;
+ 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));
break;
case FTDM_CHANNEL_STATE_GET_CALLERID:
{
- if (!sngisdn_test_flag(sngisdn_info, FLAG_SENT_PROCEED)) {
- /* 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_set_flag(sngisdn_info, FLAG_SENT_PROCEED);
- sngisdn_snd_proceed(ftdmchan, prog_ind);
- }
- /* Wait in this state until we get FACILITY msg */
+ /* 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*/
ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_PROGRESS_MEDIA);
}
} else {
- if (!sngisdn_test_flag(sngisdn_info, FLAG_SENT_PROCEED)) {
- /* 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_set_flag(sngisdn_info, FLAG_SENT_PROCEED);
-
- sngisdn_snd_proceed(ftdmchan, prog_ind);
- }
+ ftdm_sngisdn_progind_t prog_ind = {SNGISDN_PROGIND_LOC_USER, SNGISDN_PROGIND_DESCR_INVALID};
+ sngisdn_snd_proceed(ftdmchan, prog_ind);
}
}
break;
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
}
}
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:
{
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_start(ftdm_span_t *span)
{
sngisdn_span_data_t *signal_data = span->signal_data;
ftdm_log(FTDM_LOG_CRIT, "Failed to start span %s\n", span->name);
return FTDM_FAIL;
}
+
/* clear the monitor thread stop flag */
ftdm_clear_flag(span, FTDM_SPAN_STOP_THREAD);
ftdm_clear_flag(span, FTDM_SPAN_IN_THREAD);
}
/*start the dchan monitor thread*/
- if (ftdm_thread_create_detached(ftdm_sangoma_isdn_dchan_run, span) != FTDM_SUCCESS) {
+ 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;
}
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;
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 (span->trunk_type == FTDM_TRUNK_BRI_PTMP ||
- span->trunk_type == FTDM_TRUNK_BRI) {
-
+ if (FTDM_SPAN_IS_BRI(span)) {
sngisdn_set_span_avail_rate(span, SNGISDN_AVAIL_PWR_SAVING);
}
return status;
}
+
static FIO_IO_LOAD_FUNCTION(ftdm_sangoma_isdn_io_init)
{
memset(&g_sngisdn_io_interface, 0, sizeof(g_sngisdn_io_interface));
FLAG_MEDIA_READY = (1 << 11),
/* Set when we already sent a Channel ID IE */
FLAG_SENT_CHAN_ID = (1 << 12),
+ /* Set when we already sent a Connect */
+ FLAG_SENT_CONNECT = (1 << 13),
} sngisdn_flag_t;
} ftdm_sngisdn_progind_t;
/* Only timers that can be cancelled are listed here */
-#define SNGISDN_NUM_TIMERS 1
+#define SNGISDN_NUM_TIMERS 2
/* Increase NUM_TIMERS as number of ftdm_sngisdn_timer_t increases */
typedef enum {
SNGISDN_TIMER_FACILITY = 0,
+ SNGISDN_TIMER_ATT_TRANSFER,
} ftdm_sngisdn_timer_t;
typedef struct sngisdn_glare_data {
int16_t dChan;
ConEvnt setup;
uint8_t ces;
-}sngisdn_glare_data_t;
+} sngisdn_glare_data_t;
+typedef enum {
+ SNGISDN_TRANSFER_NONE = 0, /* Default value, no transfer being done */
+ SNGISDN_TRANSFER_ATT_COURTESY_VRU,
+ SNGISDN_TRANSFER_ATT_COURTESY_VRU_DATA,
+ SNGISDN_TRANSFER_INVALID,
+} sngisdn_transfer_type_t;
+#define SNGISDN_TRANSFER_TYPE_STRINGS "NONE", "ATT_COURTESY_VRU", "ATT_COURTERY_VRU_DATA", "INVALID"
+FTDM_STR2ENUM_P(ftdm_str2sngisdn_transfer_type, sngisdn_transfer_type2str, sngisdn_transfer_type_t)
+
+/* From section 4.2 of TR50075, max length of data is 100 when single UUI is sent */
+#define COURTESY_TRANSFER_MAX_DATA_SIZE 100
+
+typedef struct _att_courtesy_vru
+{
+ char dtmf_digits [20];
+ char data[COURTESY_TRANSFER_MAX_DATA_SIZE];
+} att_courtesy_vru_t;
+
+typedef struct _sngisdn_transfer_data
+{
+ sngisdn_transfer_type_t type; /* Specifies which type of transfer is being used */
+ ftdm_transfer_response_t response;
+ union
+ {
+ att_courtesy_vru_t att_courtesy_vru;
+ } tdata;
+} sngisdn_transfer_data_t;
/* Channel specific data */
typedef struct sngisdn_chan_data {
uint8_t globalFlg;
sngisdn_glare_data_t glare;
- ftdm_timer_id_t timers[SNGISDN_NUM_TIMERS];
+ ftdm_timer_id_t timers[SNGISDN_NUM_TIMERS];
+ sngisdn_transfer_data_t transfer_data;
/* variables saved here will be sent to the user application
on next SIGEVENT_XXX */
uint8_t facility_ie_decode;
uint8_t facility;
int8_t facility_timeout;
+ uint8_t att_remove_dtmf;
+ int32_t transfer_timeout;
uint8_t num_local_numbers;
- uint8_t ignore_cause_value;
+ uint8_t ignore_cause_value;
uint8_t trace_q931; /* TODO: combine with trace_flags */
uint8_t trace_q921; /* TODO: combine with trace_flags */
uint8_t raw_trace_q931; /* TODO: combine with trace_flags */
ftdm_status_t set_restart_ind_ie(ftdm_channel_t *ftdmchan, RstInd *rstInd);
ftdm_status_t set_facility_ie(ftdm_channel_t *ftdmchan, FacilityStr *facilityStr);
ftdm_status_t set_facility_ie_str(ftdm_channel_t *ftdmchan, uint8_t *data, uint8_t *data_len);
+ftdm_status_t set_user_to_user_ie(ftdm_channel_t *ftdmchan, UsrUsr *usrUsr);
+ftdm_status_t set_cause_ie(ftdm_channel_t *ftdmchan, CauseDgn *causeDgn);
ftdm_status_t sngisdn_add_var(sngisdn_chan_data_t *sngisdn_info, const char* var, const char* val);
ftdm_status_t sngisdn_add_raw_data(sngisdn_chan_data_t *sngisdn_info, uint8_t* data, ftdm_size_t data_len);
ftdm_status_t sngisdn_clear_data(sngisdn_chan_data_t *sngisdn_info);
void sngisdn_send_signal(sngisdn_chan_data_t *sngisdn_info, ftdm_signal_event_t event_id);
-
+
uint8_t sngisdn_get_infoTranCap_from_user(ftdm_bearer_cap_t bearer_capability);
uint8_t sngisdn_get_usrInfoLyr1Prot_from_user(ftdm_user_layer1_prot_t layer1_prot);
ftdm_bearer_cap_t sngisdn_get_infoTranCap_from_stack(uint8_t bearer_capability);
ftdm_user_layer1_prot_t sngisdn_get_usrInfoLyr1Prot_from_stack(uint8_t layer1_prot);
+ftdm_status_t sngisdn_transfer(ftdm_channel_t *ftdmchan);
+ftdm_status_t sngisdn_att_transfer_process_dtmf(ftdm_channel_t *ftdmchan, const char* dtmf);
+
static __inline__ uint32_t sngisdn_test_flag(sngisdn_chan_data_t *sngisdn_info, sngisdn_flag_t flag)
{
return (uint32_t) sngisdn_info->flags & flag;
signal_data->timer_t3 = 8;
signal_data->restart_opt = SNGISDN_OPT_DEFAULT;
signal_data->link_id = span->span_id;
-
+ signal_data->transfer_timeout = 20000;
+ signal_data->att_remove_dtmf = 1;
+
span->default_caller_data.dnis.plan = FTDM_NPI_INVALID;
span->default_caller_data.dnis.type = FTDM_TON_INVALID;
span->default_caller_data.cid_num.plan = FTDM_NPI_INVALID;
if (signal_data->facility_timeout < 0) {
signal_data->facility_timeout = 0;
}
+ } else if (!strcasecmp(var, "transfer-timeout")) {
+ signal_data->transfer_timeout = atoi(val);
+ if (signal_data->transfer_timeout < 0) {
+ signal_data->transfer_timeout = 0;
+ }
+ } else if (!strcasecmp(var, "att-remove-dtmf")) {
+ parse_yesno(var, val, &signal_data->att_remove_dtmf);
} else if (!strcasecmp(var, "facility-ie-decode")) {
parse_yesno(var, val, &signal_data->facility_ie_decode);
} else if (!strcasecmp(var, "ignore-cause-value")) {
}
} else {
switch(ftdmchan->state) {
+ case FTDM_CHANNEL_STATE_TRANSFER:
case FTDM_CHANNEL_STATE_UP:
- /* This is the only valid state we should get a CONNECT ACK on */
+ /* These are the only valid states we should get a CONNECT ACK on */
/* do nothing */
break;
case FTDM_CHANNEL_STATE_HANGUP_COMPLETE:
/* TODO: readjust this when NFAS is implemented as signal_data will not always be the first
* span for that d-channel */
+ if (!rstEvnt->rstInd.eh.pres || !rstEvnt->rstInd.rstClass.pres) {
+ ftdm_log(FTDM_LOG_DEBUG, "Received RESTART IND, but Restart Indicator IE not present\n");
+ return;
+ }
signal_data = g_sngisdn_data.dchans[dChan].spans[1];
sngisdn_chan_data_t *sngisdn_info = (sngisdn_chan_data_t*) ftdmchan->call_data;
sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data;
- if (!sngisdn_info->suInstId || !sngisdn_info->spInstId) {
+ if (sngisdn_test_flag(sngisdn_info, FLAG_SENT_PROCEED)) {
+ return;
+ }
+ sngisdn_set_flag(sngisdn_info, FLAG_SENT_PROCEED);
+
+ if (!sngisdn_info->suInstId || !sngisdn_info->spInstId) {
ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Sending PROGRESS, but no call data, aborting (suId:%d suInstId:%u spInstId:%u)\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId);
sngisdn_set_flag(sngisdn_info, FLAG_LOCAL_ABORT);
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING);
return;
}
-
+
memset(&cnStEvnt, 0, sizeof(cnStEvnt));
/* Indicate channel ID only in first response */
}
set_prog_ind_ie(ftdmchan, &cnStEvnt.progInd, prog_ind);
set_facility_ie(ftdmchan, &cnStEvnt.facilityStr);
-
+
ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Sending PROCEED (suId:%d suInstId:%u spInstId:%u dchan:%d ces:%d)\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, signal_data->dchan_id, sngisdn_info->ces);
if(sng_isdn_con_status(signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, &cnStEvnt, MI_CALLPROC, signal_data->dchan_id, sngisdn_info->ces)) {
void sngisdn_snd_connect(ftdm_channel_t *ftdmchan)
{
- CnStEvnt cnStEvnt;
+ CnStEvnt cnStEvnt;
sngisdn_chan_data_t *sngisdn_info = (sngisdn_chan_data_t*) ftdmchan->call_data;
sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data;
ftdm_sngisdn_progind_t prog_ind = {SNGISDN_PROGIND_LOC_USER, SNGISDN_PROGIND_DESCR_NETE_ISDN};
- if (!sngisdn_info->suInstId || !sngisdn_info->spInstId) {
+ if (sngisdn_test_flag(sngisdn_info, FLAG_SENT_CONNECT)) {
+ return;
+ }
+ sngisdn_set_flag(sngisdn_info, FLAG_SENT_CONNECT);
+
+ if (!sngisdn_info->suInstId || !sngisdn_info->spInstId) {
ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Sending CONNECT, but no call data, aborting (suId:%d suInstId:%u spInstId:%u)\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId);
sngisdn_set_flag(sngisdn_info, FLAG_LOCAL_ABORT);
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_TERMINATING);
ftdm_set_state_locked(ftdmchan, FTDM_CHANNEL_STATE_HANGUP_COMPLETE);
return;
}
-
+
memset(&discEvnt, 0, sizeof(discEvnt));
-
- /* Fill discEvnt here */
- /* TODO move this to set_cause_ie function */
- discEvnt.causeDgn[0].eh.pres = PRSNT_NODEF;
- discEvnt.causeDgn[0].location.pres = PRSNT_NODEF;
- discEvnt.causeDgn[0].location.val = IN_LOC_PRIVNETLU;
- discEvnt.causeDgn[0].codeStand3.pres = PRSNT_NODEF;
- discEvnt.causeDgn[0].codeStand3.val = IN_CSTD_CCITT;
- discEvnt.causeDgn[0].causeVal.pres = PRSNT_NODEF;
- discEvnt.causeDgn[0].causeVal.val = ftdmchan->caller_data.hangup_cause;
- discEvnt.causeDgn[0].recommend.pres = NOTPRSNT;
- discEvnt.causeDgn[0].dgnVal.pres = NOTPRSNT;
-
- set_facility_ie(ftdmchan, &discEvnt.facilityStr);
+
+ set_cause_ie(ftdmchan, &discEvnt.causeDgn[0]);
+ set_facility_ie(ftdmchan, &discEvnt.facilityStr);
+ set_user_to_user_ie(ftdmchan, &discEvnt.usrUsr);
ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Sending DISCONNECT (suId:%d suInstId:%u spInstId:%u)\n", signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId);
if (sng_isdn_disc_request(signal_data->cc_id, sngisdn_info->suInstId, sngisdn_info->spInstId, &discEvnt)) {
g_sngisdn_data.ccs[cc_id].active_spInstIds[sngisdn_info->spInstId]=NULL;
g_sngisdn_data.ccs[cc_id].active_suInstIds[sngisdn_info->suInstId]=NULL;
ftdm_mutex_unlock(g_sngisdn_data.ccs[cc_id].mutex);
-
+
sngisdn_info->suInstId = 0;
sngisdn_info->spInstId = 0;
sngisdn_info->globalFlg = 0;
sngisdn_info->flags = 0;
+ sngisdn_info->transfer_data.type = SNGISDN_TRANSFER_NONE;
return;
}
return FTDM_SUCCESS;
}
+ftdm_status_t set_user_to_user_ie(ftdm_channel_t *ftdmchan, UsrUsr *usrUsr)
+{
+ sngisdn_chan_data_t *sngisdn_info = ftdmchan->call_data;
+
+ if (sngisdn_info->transfer_data.type == SNGISDN_TRANSFER_ATT_COURTESY_VRU_DATA) {
+ usrUsr->eh.pres = PRSNT_NODEF;
+
+ usrUsr->protocolDisc.pres = PRSNT_NODEF;
+ usrUsr->protocolDisc.val = 0x08;
+ usrUsr->usrInfo.pres = PRSNT_NODEF;
+ usrUsr->usrInfo.len = strlen(sngisdn_info->transfer_data.tdata.att_courtesy_vru.data);
+ memcpy(usrUsr->usrInfo.val, sngisdn_info->transfer_data.tdata.att_courtesy_vru.data, usrUsr->usrInfo.len);
+ ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Sending AT&T Transfer data len:%d\n", usrUsr->usrInfo.len);
+
+ return FTDM_SUCCESS;
+ }
+
+ return FTDM_SUCCESS;
+}
+
+ftdm_status_t set_cause_ie(ftdm_channel_t *ftdmchan, CauseDgn *causeDgn)
+{
+
+ causeDgn->eh.pres = PRSNT_NODEF;
+ causeDgn->location.pres = PRSNT_NODEF;
+ causeDgn->location.val = IN_LOC_PRIVNETLU;
+ causeDgn->codeStand3.pres = PRSNT_NODEF;
+ causeDgn->codeStand3.val = IN_CSTD_CCITT;
+ causeDgn->causeVal.pres = PRSNT_NODEF;
+ causeDgn->causeVal.val = ftdmchan->caller_data.hangup_cause;
+ causeDgn->recommend.pres = NOTPRSNT;
+ causeDgn->dgnVal.pres = NOTPRSNT;
+ return FTDM_SUCCESS;
+}
+
ftdm_status_t set_chan_id_ie(ftdm_channel_t *ftdmchan, ChanId *chanId)
{
sngisdn_chan_data_t *sngisdn_info = (sngisdn_chan_data_t*)ftdmchan->call_data;
}
ftdm_set_flag(sngisdn_info, FLAG_SENT_CHAN_ID);
-
+
chanId->eh.pres = PRSNT_NODEF;
chanId->prefExc.pres = PRSNT_NODEF;
chanId->prefExc.val = IN_PE_EXCLSVE;
chanId->intIdentPres.pres = PRSNT_NODEF;
chanId->intIdentPres.val = IN_IIP_IMPLICIT;
- if (ftdmchan->span->trunk_type == FTDM_TRUNK_BRI ||
- ftdmchan->span->trunk_type == FTDM_TRUNK_BRI_PTMP) {
+ if (FTDM_SPAN_IS_BRI(ftdmchan->span)) {
/* BRI only params */
chanId->intType.pres = PRSNT_NODEF;
sigev.raw.data = sngisdn_info->raw_data;
sigev.raw.len = sngisdn_info->raw_data_len;
-
+
sngisdn_info->raw_data = NULL;
sngisdn_info->raw_data_len = 0;
}
+ if (event_id == FTDM_SIGEVENT_TRANSFER_COMPLETED) {
+ sigev.ev_data.transfer_completed.response = sngisdn_info->transfer_data.response;
+ }
ftdm_span_send_signal(ftdmchan->span, &sigev);
}
--- /dev/null
+/*
+ * Copyright (c) 2011, Sangoma Technologies
+ * David Yat Sin <davidy@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"
+
+#define TRANSFER_FUNC(name) ftdm_status_t (name)(ftdm_channel_t *ftdmchan, sngisdn_transfer_type_t type, char* target)
+
+#define SNGISDN_ATT_TRANSFER_RESPONSE_CP_DROP_OFF "**1"
+#define SNGISDN_ATT_TRANSFER_RESPONSE_LIMITS_EXCEEDED "**5"
+#define SNGISDN_ATT_TRANSFER_RESPONSE_OK "**6"
+#define SNGISDN_ATT_TRANSFER_RESPONSE_INVALID_NUM "**7"
+#define SNGISDN_ATT_TRANSFER_RESPONSE_INVALID_COMMAND "**8"
+
+
+void att_courtesy_transfer_complete(sngisdn_chan_data_t *sngisdn_info, ftdm_transfer_response_t response);
+void att_courtesy_transfer_timeout(void* p_sngisdn_info);
+static ftdm_status_t att_courtesy_vru(ftdm_channel_t *ftdmchan, sngisdn_transfer_type_t type, char* target);
+
+typedef struct transfer_interfaces {
+ const char *name;
+ sngisdn_transfer_type_t type;
+ TRANSFER_FUNC(*func);
+}transfer_interface_t;
+
+static transfer_interface_t transfer_interfaces[] = {
+ /* AT&T TR-50075 Courtesy Transfer - VRU -- No Data (Section 4.3) */
+ { "ATT_COURTESY_TRANSFER_V", SNGISDN_TRANSFER_ATT_COURTESY_VRU, att_courtesy_vru},
+ /* AT&T TR-50075 Courtesy Transfer - VRU --Data (Section 4.4) */
+ { "ATT_COURTESY_TRANSFER_V_DATA", SNGISDN_TRANSFER_ATT_COURTESY_VRU_DATA, att_courtesy_vru},
+};
+
+void att_courtesy_transfer_complete(sngisdn_chan_data_t *sngisdn_info, ftdm_transfer_response_t response)
+{
+ ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan;
+ ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_RX_DISABLED);
+ ftdm_channel_command(ftdmchan, FTDM_COMMAND_DISABLE_DTMF_DETECT, NULL);
+
+ sngisdn_info->transfer_data.type = SNGISDN_TRANSFER_NONE;
+ sngisdn_info->transfer_data.response = response;
+
+ ftdm_log_chan(ftdmchan, FTDM_LOG_INFO, "Transfer Complete:%s\n", ftdm_transfer_response2str(sngisdn_info->transfer_data.response));
+ sngisdn_send_signal(sngisdn_info, FTDM_SIGEVENT_TRANSFER_COMPLETED);
+
+ ftdm_channel_command(ftdmchan, FTDM_COMMAND_FLUSH_RX_BUFFERS, NULL);
+ return;
+}
+
+void att_courtesy_transfer_timeout(void* p_sngisdn_info)
+{
+ sngisdn_chan_data_t *sngisdn_info = (sngisdn_chan_data_t*)p_sngisdn_info;
+ ftdm_channel_t *ftdmchan = sngisdn_info->ftdmchan;
+ sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data;
+
+ ftdm_mutex_lock(ftdmchan->mutex);
+ if (sngisdn_info->transfer_data.type == SNGISDN_TRANSFER_NONE) {
+ /* Call was already cleared */
+ ftdm_mutex_unlock(ftdmchan->mutex);
+ return;
+ }
+
+ ftdm_log_chan(ftdmchan, FTDM_LOG_WARNING, "AT&T Courtesy Transfer timeout (%d)\n", signal_data->transfer_timeout);
+ att_courtesy_transfer_complete(sngisdn_info, FTDM_TRANSFER_RESPONSE_TIMEOUT);
+ ftdm_mutex_unlock(ftdmchan->mutex);
+ return;
+}
+
+static ftdm_status_t att_courtesy_vru(ftdm_channel_t *ftdmchan, sngisdn_transfer_type_t type, char* args)
+{
+ char dtmf_digits[64];
+ ftdm_status_t status = FTDM_FAIL;
+ sngisdn_chan_data_t *sngisdn_info = ftdmchan->call_data;
+ sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data;
+ char *p = args;
+ uint8_t forced_answer = 0;
+
+ switch (signal_data->switchtype) {
+ case SNGISDN_SWITCH_5ESS:
+ case SNGISDN_SWITCH_4ESS:
+ break;
+ default:
+ ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "AT&T Courtesy Transfer not supported for switchtype\n");
+ return FTDM_FAIL;
+ }
+
+ while (!ftdm_strlen_zero(p)) {
+ if (!isdigit(*p) && *p != 'w' && *p != 'W') {
+ ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Cannot transfer to non-numeric number:%s\n", args);
+ return FTDM_FAIL;
+ }
+ p++;
+ }
+
+ ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Performing AT&T Courtesy Transfer-VRU%s to %s\n", (type == SNGISDN_TRANSFER_ATT_COURTESY_VRU_DATA) ?"--data" : "", args);
+ sprintf(dtmf_digits, "*8w%s", args);
+ ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Sending digits %s\n", dtmf_digits);
+
+ switch (ftdmchan->last_state) {
+ case FTDM_CHANNEL_STATE_PROCEED:
+ case FTDM_CHANNEL_STATE_PROGRESS:
+ case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
+ /* Call has to be in answered state - so send a CONNECT message if we did not answer this call yet */
+ forced_answer++;
+ sngisdn_snd_connect(ftdmchan);
+ /* fall-through */
+ case FTDM_CHANNEL_STATE_UP:
+ memset(&sngisdn_info->transfer_data.tdata.att_courtesy_vru.dtmf_digits, 0, sizeof(sngisdn_info->transfer_data.tdata.att_courtesy_vru.dtmf_digits));
+ sngisdn_info->transfer_data.type = type;
+
+ /* We will be polling the channel for IO so that we can receive the DTMF events,
+ * Disable user RX otherwise it is a race between who calls channel_read */
+ ftdm_set_flag(ftdmchan, FTDM_CHANNEL_RX_DISABLED);
+
+ ftdm_channel_command(ftdmchan, FTDM_COMMAND_ENABLE_DTMF_DETECT, NULL);
+ ftdm_channel_command(ftdmchan, FTDM_COMMAND_SEND_DTMF, dtmf_digits);
+
+ if (type == SNGISDN_TRANSFER_ATT_COURTESY_VRU_DATA) {
+ /* We need to save transfer data, so we can send it in the disconnect msg */
+ const char *val = ftdm_usrmsg_get_var(ftdmchan->usrmsg, "transfer_data");
+ if (ftdm_strlen_zero(val)) {
+ ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Cannot perform data transfer because transfer_data variable is not set\n");
+ goto done;
+ }
+ if (strlen(val) > COURTESY_TRANSFER_MAX_DATA_SIZE) {
+ ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Data exceeds max size (len:%d max:%d), cannot perform transfer\n", strlen(val), COURTESY_TRANSFER_MAX_DATA_SIZE);
+ goto done;
+ }
+ memcpy(sngisdn_info->transfer_data.tdata.att_courtesy_vru.data, val, strlen(val));
+ }
+
+ ftdm_set_state(ftdmchan, FTDM_CHANNEL_STATE_UP);
+ if (forced_answer) {
+ /* Notify the user that we answered the call */
+ sngisdn_send_signal(sngisdn_info, FTDM_SIGEVENT_UP);
+ }
+ if (signal_data->transfer_timeout) {
+ ftdm_sched_timer(((sngisdn_span_data_t*)ftdmchan->span->signal_data)->sched, "courtesy_transfer_timeout", signal_data->transfer_timeout, att_courtesy_transfer_timeout, (void*) sngisdn_info, &sngisdn_info->timers[SNGISDN_TIMER_ATT_TRANSFER]);
+ }
+
+ status = FTDM_SUCCESS;
+ break;
+ default:
+ ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Cannot perform transfer in state %s\n", ftdm_channel_state2str(ftdmchan->state));
+ break;
+
+ }
+done:
+ return status;
+}
+
+
+ftdm_status_t sngisdn_transfer(ftdm_channel_t *ftdmchan)
+{
+ const char* args;
+ char *p;
+ char *type = NULL;
+ char *target = NULL;
+ ftdm_status_t status = FTDM_FAIL;
+ sngisdn_chan_data_t *sngisdn_info = ftdmchan->call_data;
+ unsigned i;
+
+ args = ftdm_usrmsg_get_var(ftdmchan->usrmsg, "transfer_arg");
+ if (ftdm_strlen_zero(args)) {
+ ftdm_log_chan_msg(ftdmchan, FTDM_LOG_ERROR, "Cannot perform transfer because call_transfer_arg variable is not set\n");
+ goto done;
+ }
+
+ type = ftdm_strdup(args);
+ if ((p = strchr(type, '/'))) {
+ target = ftdm_strdup(p+1);
+ *p = '\0';
+ }
+
+ if (ftdm_strlen_zero(type) || ftdm_strlen_zero(target)) {
+ ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Invalid parameters for transfer %s, expected <type>/<target>\n", args);
+ goto done;
+ }
+
+ if (sngisdn_info->transfer_data.type != SNGISDN_TRANSFER_NONE) {
+ ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Cannot perform transfer because an existing transfer transfer is pending (%s)\n", sngisdn_transfer_type2str(sngisdn_info->transfer_data.type));
+ goto done;
+ }
+
+ ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Transfer requested type:%s target:%s\n", type, target);
+ for (i = 0; i < ftdm_array_len(transfer_interfaces); i++ ) {
+ if (!strcasecmp(transfer_interfaces[i].name, type)) {
+ /* Depending on the transfer type, the transfer function may change the
+ * channel state to UP, or last_state, but the transfer function will always result in
+ * an immediate state change if FTDM_SUCCESS is returned */
+
+ status = transfer_interfaces[i].func(ftdmchan, transfer_interfaces[i].type, target);
+ goto done;
+ }
+ }
+
+ ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Invalid transfer type:%s\n", type);
+
+done:
+ if (status != FTDM_SUCCESS) {
+ ftdm_set_state(ftdmchan, ftdmchan->last_state);
+ }
+
+ ftdm_safe_free(type);
+ ftdm_safe_free(target);
+ return status;
+}
+
+ftdm_status_t sngisdn_att_transfer_process_dtmf(ftdm_channel_t *ftdmchan, const char* dtmf)
+{
+ ftdm_status_t status = FTDM_SUCCESS;
+ sngisdn_chan_data_t *sngisdn_info = ftdmchan->call_data;
+ sngisdn_span_data_t *signal_data = (sngisdn_span_data_t*) ftdmchan->span->signal_data;
+ char *dtmf_digits = sngisdn_info->transfer_data.tdata.att_courtesy_vru.dtmf_digits;
+ ftdm_size_t dtmf_digits_len = strlen(dtmf_digits);
+
+ dtmf_digits_len += sprintf(&dtmf_digits[dtmf_digits_len], "%s", dtmf);
+ ftdm_log_chan(ftdmchan, FTDM_LOG_DEBUG, "Transfer response digits:%s\n", dtmf_digits);
+ if (dtmf_digits_len == 3) {
+ if (!strcmp(dtmf_digits, SNGISDN_ATT_TRANSFER_RESPONSE_CP_DROP_OFF)) {
+ sngisdn_info->transfer_data.response = FTDM_TRANSFER_RESPONSE_CP_DROP_OFF;
+ } else if (!strcmp(dtmf_digits, SNGISDN_ATT_TRANSFER_RESPONSE_LIMITS_EXCEEDED)) {
+ sngisdn_info->transfer_data.response = FTDM_TRANSFER_RESPONSE_LIMITS_EXCEEDED;
+ } else if (!strcmp(dtmf_digits, SNGISDN_ATT_TRANSFER_RESPONSE_OK)) {
+ sngisdn_info->transfer_data.response = FTDM_TRANSFER_RESPONSE_OK;
+ } else if (!strcmp(dtmf_digits, SNGISDN_ATT_TRANSFER_RESPONSE_INVALID_NUM)) {
+ sngisdn_info->transfer_data.response = FTDM_TRANSFER_RESPONSE_INVALID_NUM;
+ } else if (!strcmp(dtmf_digits, SNGISDN_ATT_TRANSFER_RESPONSE_INVALID_COMMAND)) {
+ sngisdn_info->transfer_data.response = FTDM_TRANSFER_RESPONSE_INVALID_COMMAND;
+ } else {
+ sngisdn_info->transfer_data.response = FTDM_TRANSFER_RESPONSE_INVALID;
+ }
+ if (signal_data->transfer_timeout) {
+ ftdm_sched_cancel_timer(signal_data->sched, sngisdn_info->timers[SNGISDN_TIMER_ATT_TRANSFER]);
+ }
+
+ if (sngisdn_info->transfer_data.response == FTDM_TRANSFER_RESPONSE_OK &&
+ sngisdn_info->transfer_data.type == SNGISDN_TRANSFER_ATT_COURTESY_VRU_DATA) {
+ sngisdn_set_flag(sngisdn_info, FLAG_SEND_DISC);
+ ftdmchan->caller_data.hangup_cause = FTDM_CAUSE_NORMAL_CLEARING;
+ sngisdn_snd_disconnect(ftdmchan);
+ }
+ /* Network side will send disconnect in case of NO-DATA Transfer */
+ att_courtesy_transfer_complete(sngisdn_info, sngisdn_info->transfer_data.response);
+ }
+
+ if (signal_data->att_remove_dtmf) {
+ /* If we return FTDM_BREAK, dtmf event is not queue'ed to user */
+ status = FTDM_BREAK;
+ }
+ return status;
+}
#else
if (pfds[i-1].revents & POLLPRI) {
#endif
- ftdm_set_flag(ftdmchan, FTDM_CHANNEL_EVENT);
+ ftdm_set_io_flag(ftdmchan, FTDM_CHANNEL_IO_EVENT);
ftdmchan->last_event_time = ftdm_current_time_in_ms();
k++;
}
+#ifdef LIBSANGOMA_VERSION
+ if (outflags[i-1] & POLLIN) {
+#else
+ if (pfds[i-1].revents & POLLIN) {
+#endif
+ ftdm_set_io_flag(ftdmchan, FTDM_CHANNEL_IO_READ);
+ }
+#ifdef LIBSANGOMA_VERSION
+ if (outflags[i-1] & POLLOUT) {
+#else
+ if (pfds[i-1].revents & POLLOUT) {
+#endif
+ ftdm_set_io_flag(ftdmchan, FTDM_CHANNEL_IO_WRITE);
+ }
}
- /* when k is 0 it might be that an async wanpipe device signal was delivered */
+ /* when k is 0 it might be that an async wanpipe device signal was delivered */
return FTDM_SUCCESS;
}
wanpipe_tdm_api_t tdm_api;
ftdm_span_t *span = ftdmchan->span;
- if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_EVENT)) {
- ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_EVENT);
- }
-
memset(&tdm_api, 0, sizeof(tdm_api));
status = sangoma_tdm_read_event(ftdmchan->sockfd, &tdm_api);
if (status != FTDM_SUCCESS) {
- snprintf(span->last_error, sizeof(span->last_error), "%s", strerror(errno));
ftdm_log_chan(ftdmchan, FTDM_LOG_ERROR, "Failed to read event from channel: %s\n", strerror(errno));
return FTDM_FAIL;
}
for(i = 1; i <= span->chan_count; i++) {
/* as a hack for wink/flash detection, wanpipe_poll_event overrides the timeout parameter
* to force the user to call this function each 5ms or so to detect the timeout of our wink/flash */
- if (span->channels[i]->last_event_time && !ftdm_test_flag(span->channels[i], FTDM_CHANNEL_EVENT)) {
+ if (span->channels[i]->last_event_time && !ftdm_test_io_flag(span->channels[i], FTDM_CHANNEL_IO_EVENT)) {
ftdm_time_t diff = ftdm_current_time_in_ms() - span->channels[i]->last_event_time;
/* XX printf("%u %u %u\n", diff, (unsigned)ftdm_current_time_in_ms(), (unsigned)span->channels[i]->last_event_time); */
if (ftdm_test_flag(span->channels[i], FTDM_CHANNEL_WINK)) {
goto event;
}
}
- }
- if (ftdm_test_flag(span->channels[i], FTDM_CHANNEL_EVENT)) {
+ }
+ if (ftdm_test_io_flag(span->channels[i], FTDM_CHANNEL_IO_EVENT)) {
ftdm_status_t status;
wanpipe_tdm_api_t tdm_api;
ftdm_channel_t *ftdmchan = span->channels[i];
memset(&tdm_api, 0, sizeof(tdm_api));
- ftdm_clear_flag(span->channels[i], FTDM_CHANNEL_EVENT);
+ ftdm_clear_io_flag(span->channels[i], FTDM_CHANNEL_IO_EVENT);
err = sangoma_tdm_read_event(ftdmchan->sockfd, &tdm_api);
if (err != FTDM_SUCCESS) {
snprintf(span->last_error, sizeof(span->last_error), "%s", strerror(errno));
return FTDM_FAIL;
}
-
+
for(i = 1; i <= span->chan_count; i++) {
if (pfds[i-1].revents & POLLPRI) {
- ftdm_set_flag(span->channels[i], FTDM_CHANNEL_EVENT);
+ ftdm_set_io_flag(span->channels[i], FTDM_CHANNEL_IO_EVENT);
span->channels[i]->last_event_time = ftdm_current_time_in_ms();
k++;
}
+ if (pfds[i-1].revents & POLLIN) {
+ ftdm_set_io_flag(span->channels[i], FTDM_CHANNEL_IO_READ);
+ }
+ if (pfds[i-1].revents & POLLOUT) {
+ ftdm_set_io_flag(span->channels[i], FTDM_CHANNEL_IO_WRITE);
+ }
}
if (!k) {
zt_event_t zt_event_id = 0;
ftdm_span_t *span = ftdmchan->span;
- if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_EVENT)) {
- ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_EVENT);
+ if (ftdm_test_io_flag(ftdmchan, FTDM_CHANNEL_IO_EVENT)) {
+ ftdm_clear_io_flag(ftdmchan, FTDM_CHANNEL_IO_EVENT);
}
if (ioctl(ftdmchan->sockfd, codes.GETEVENT, &zt_event_id) == -1) {
for(i = 1; i <= span->chan_count; i++) {
ftdm_channel_t *fchan = span->channels[i];
- if (ftdm_test_flag(fchan, FTDM_CHANNEL_EVENT)) {
- ftdm_clear_flag(fchan, FTDM_CHANNEL_EVENT);
+ if (ftdm_test_io_flag(fchan, FTDM_CHANNEL_IO_EVENT)) {
+ ftdm_clear_io_flag(fchan, FTDM_CHANNEL_IO_EVENT);
if (ioctl(fchan->sockfd, codes.GETEVENT, &zt_event_id) == -1) {
snprintf(span->last_error, sizeof(span->last_error), "%s", strerror(errno));
return FTDM_FAIL;
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
- * Contributors:
+ * Contributors:
*
* Moises Silva <moy@sangoma.com>
+ * David Yat Sin <dyatsin@sangoma.com>
*
*/
#define CALLING_PARTY_CATEGORY_STRINGS "unknown", "operator", "operator-french", "operator-english", "operator-german", "operator-russian", "operator-spanish", "ordinary", "priority", "data-call", "test-call", "payphone", "invalid"
FTDM_STR2ENUM_P(ftdm_str2ftdm_calling_party_category, ftdm_calling_party_category2str, ftdm_calling_party_category_t)
+/*! Network responses to transfer requests */
+typedef enum {
+ FTDM_TRANSFER_RESPONSE_OK, /* Call is being transferred */
+ FTDM_TRANSFER_RESPONSE_CP_DROP_OFF, /* Calling Party drop off */
+ FTDM_TRANSFER_RESPONSE_LIMITS_EXCEEDED, /* Cannot redirect, limits exceeded */
+ FTDM_TRANSFER_RESPONSE_INVALID_NUM, /* Network did not receive or recognize dialed number */
+ FTDM_TRANSFER_RESPONSE_INVALID_COMMAND, /* Network received an invalid command */
+ FTDM_TRANSFER_RESPONSE_TIMEOUT, /* We did not receive a response from Network */
+ FTDM_TRANSFER_RESPONSE_INVALID,
+} ftdm_transfer_response_t;
+#define TRANSFER_RESPONSE_STRINGS "transfer-ok", "cp-drop-off", "limits-exceeded", "invalid-num", "invalid-command", "timeout", "invalid"
+FTDM_STR2ENUM_P(ftdm_str2ftdm_transfer_response, ftdm_transfer_response2str, ftdm_transfer_response_t)
+
/*! \brief Digit limit used in DNIS/ANI */
#define FTDM_DIGITS_LIMIT 25
FTDM_SIGEVENT_TRACE, /*!<Interpreted trace event */
FTDM_SIGEVENT_TRACE_RAW, /*!<Raw trace event */
FTDM_SIGEVENT_INDICATION_COMPLETED, /*!< Last requested indication was completed */
- FTDM_SIGEVENT_DIALING, /* Outgoing call just started */
+ FTDM_SIGEVENT_DIALING, /*!< Outgoing call just started */
+ FTDM_SIGEVENT_TRANSFER_COMPLETED, /*!< Transfer request is 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", "FACILITY", \
- "TRACE", "TRACE_RAW", "INDICATION_COMPLETED", "DIALING", "INVALID"
+ "TRACE", "TRACE_RAW", "INDICATION_COMPLETED", "DIALING", "TRANSFER_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)
/* Using this indication is equivalent to call ftdm_channel_call_answer API */
FTDM_CHANNEL_INDICATE_ANSWER,
FTDM_CHANNEL_INDICATE_FACILITY,
+ FTDM_CHANNEL_INDICATE_TRANSFER,
FTDM_CHANNEL_INDICATE_INVALID,
} ftdm_channel_indication_t;
-#define INDICATION_STRINGS "NONE", "RINGING", "PROCEED", "PROGRESS", "PROGRESS_MEDIA", "BUSY", "ANSWER", "FACILITY", "INVALID"
+#define INDICATION_STRINGS "NONE", "RINGING", "PROCEED", "PROGRESS", "PROGRESS_MEDIA", "BUSY", "ANSWER", "FACILITY", "TRANSFER", "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)
+FTDM_STR2ENUM_P(ftdm_str2ftdm_channel_indication, ftdm_channel_indication2str, ftdm_channel_indication_t)
typedef struct {
/* The indication that was completed */
ftdm_status_t status;
} ftdm_event_indication_completed_t;
+typedef struct {
+ ftdm_transfer_response_t response;
+} ftdm_event_transfer_completed_t;
+
typedef void * ftdm_variable_container_t;
typedef struct {
ftdm_event_trace_t trace; /*!< valid if event_id is FTDM_SIGEVENT_TRACE or FTDM_SIGEVENT_TRACE_RAW */
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 */
+ ftdm_event_transfer_completed_t transfer_completed;
} ev_data;
ftdm_raw_data_t raw;
};
#define ftdm_channel_call_place(ftdmchan) _ftdm_channel_call_place(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), NULL)
#define ftdm_channel_call_place_ex(ftdmchan, usrmsg) _ftdm_channel_call_place_ex(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), (usrmsg))
-/*! \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)
+/*! \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)
* \deprecated This function is deprecated since leaves the door open to glare issues, use ftdm_call_place instead
*/
FT_DECLARE(ftdm_status_t) _ftdm_channel_call_place(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_usrmsg_t *usrmsg);
/*! \brief Hangup the call with cause recording the source code point where it was called (see ftdm_channel_call_hangup_with_cause for an easy to use macro) */
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, ftdm_usrmsg_t *usrmsg);
+/*! \brief Transfer call. This can also be accomplished by ftdm_channel_call_indicate with FTDM_CHANNEL_INDICATE_TRANSFER, in both
+ * cases you will get a FTDM_SIGEVENT_INDICATION_COMPLETED when the indication is sent (or an error occurs).
+ * Just as with ftdm_channel_call_indicate you won't receive FTDM_SIGEVENT_INDICATION_COMPLETED when this function
+ * returns anything else than FTDM_SUCCESS
+ * \note Although this API may 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_transfer
+ */
+#define ftdm_channel_call_transfer(ftdmchan, arg) _ftdm_channel_call_transfer(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), (arg), NULL)
+#define ftdm_channel_call_transfer_ex(ftdmchan, arg, usrmsg) _ftdm_channel_call_transfer(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), (arg), (usrmsg))
+
+/*! \brief Answer call recording the source code point where the it was called (see ftdm_channel_call_tranasfer for an easy to use macro) */
+FT_DECLARE(ftdm_status_t) _ftdm_channel_call_transfer(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, const char* arg, ftdm_usrmsg_t *usrmsg);
+
/*! \brief Reset the channel */
#define ftdm_channel_reset(ftdmchan) _ftdm_channel_reset(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), NULL)
#define ftdm_channel_reset_ex(ftdmchan, usrmsg) _ftdm_channel_reset(__FILE__, __FUNCTION__, __LINE__, (ftdmchan), usrmsg)
/*! \brief Remove the channel from a hunt group */
FT_DECLARE(ftdm_status_t) ftdm_channel_remove_from_group(ftdm_group_t* group, ftdm_channel_t* ftdmchan);
-/*!
+/*!
* \brief Retrieves an event from the span
*
* \note
#define ftdm_clear_alarm_flag(obj, flag) (obj)->alarm_flags &= ~(flag)
#define ftdm_test_alarm_flag(obj, flag) ((obj)->alarm_flags & flag)
+#define ftdm_set_io_flag(obj, flag) (obj)->io_flags |= (flag)
+#define ftdm_clear_io_flag(obj, flag) (obj)->io_flags &= ~(flag)
+#define ftdm_test_io_flag(obj, flag) ((obj)->io_flags & flag)
+
/*!
\brief Set a flag on an arbitrary object
\command obj the object to set the flags on
uint64_t flags;
uint32_t pflags;
uint32_t sflags;
+ uint8_t io_flags;
ftdm_alarm_flag_t alarm_flags;
ftdm_channel_feature_t features;
ftdm_codec_t effective_codec;
ftdm_span_stop_t stop;
ftdm_channel_sig_read_t sig_read;
ftdm_channel_sig_write_t sig_write;
+ ftdm_channel_sig_dtmf_t sig_dtmf;
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;
FT_DECLARE(ftdm_iterator_t *) ftdm_get_iterator(ftdm_iterator_type_t type, ftdm_iterator_t *iter);
+FT_DECLARE(ftdm_status_t) ftdm_channel_process_media(ftdm_channel_t *ftdmchan, void *data, ftdm_size_t *datalen);
+
+FIO_WRITE_FUNCTION(ftdm_raw_write);
+FIO_READ_FUNCTION(ftdm_raw_read);
/*!
* \brief Retrieves an event from the span
FTDM_CHANNEL_STATE_PROGRESS,
FTDM_CHANNEL_STATE_PROGRESS_MEDIA,
FTDM_CHANNEL_STATE_UP,
+ FTDM_CHANNEL_STATE_TRANSFER,
FTDM_CHANNEL_STATE_IDLE,
FTDM_CHANNEL_STATE_TERMINATING,
FTDM_CHANNEL_STATE_CANCEL,
} 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", \
+ "RESTART", "PROCEED", "PROGRESS", "PROGRESS_MEDIA", "UP", "TRANSFER", "IDLE", "TERMINATING", "CANCEL", \
"HANGUP", "HANGUP_COMPLETE", "IN_LOOP", "RESET", "INVALID"
FTDM_STR2ENUM_P(ftdm_str2ftdm_channel_state, ftdm_channel_state2str, ftdm_channel_state_t)
/* If this flag is set, then this span cannot be stopped individually, it can only be stopped
on freetdm unload */
FTDM_SPAN_NON_STOPPABLE = (1 << 13),
+ /* If this flag is set, then this span supports TRANSFER state */
+ FTDM_SPAN_USE_TRANSFER = (1 << 14),
} ftdm_span_flag_t;
/*! \brief Channel supported features */
FTDM_CHANNEL_FEATURE_MF_GENERATE = (1<<10), /*!< Channel can generate R2 MF tones (read-only) */
} ftdm_channel_feature_t;
+/*! \brief Channel IO pending flags */
+typedef enum {
+ FTDM_CHANNEL_IO_EVENT = (1 << 0),
+ FTDM_CHANNEL_IO_READ = (1 << 1),
+ FTDM_CHANNEL_IO_WRITE = (1 << 2),
+} ftdm_channel_io_flags_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)
#define FTDM_CHANNEL_SUPRESS_DTMF (1ULL << 4)
#define FTDM_CHANNEL_TRANSCODE (1ULL << 5)
#define FTDM_CHANNEL_BUFFER (1ULL << 6)
-#define FTDM_CHANNEL_EVENT (1ULL << 7)
#define FTDM_CHANNEL_INTHREAD (1ULL << 8)
#define FTDM_CHANNEL_WINK (1ULL << 9)
#define FTDM_CHANNEL_FLASH (1ULL << 10)
typedef ftdm_status_t (*ftdm_span_stop_t)(ftdm_span_t *span);
typedef ftdm_status_t (*ftdm_channel_sig_read_t)(ftdm_channel_t *ftdmchan, void *data, ftdm_size_t size);
typedef ftdm_status_t (*ftdm_channel_sig_write_t)(ftdm_channel_t *ftdmchan, void *data, ftdm_size_t size);
+typedef ftdm_status_t (*ftdm_channel_sig_dtmf_t)(ftdm_channel_t *ftdmchan, const char *dtmf);
typedef enum {
FTDM_ITERATOR_VARS = 1,
- FTDM_ITERATOR_CHANS,
+ FTDM_ITERATOR_CHANS,
} ftdm_iterator_type_t;
struct ftdm_iterator {