freetdm: - Clear the IO flag when FreeSWITCH hangs up before notifying freetdm

- Misc fixes to log messages to aid debugging
         - Added ftdm ioread command to read media from the channel
         - Modified the ftdm_channel_open() API to return FTDM_EBUSY if the channel is already open
This commit is contained in:
Moises Silva 2011-09-12 15:21:23 -04:00
parent f4facb0f65
commit 6c5132a507
2 changed files with 179 additions and 35 deletions

View File

@ -503,13 +503,16 @@ static switch_status_t channel_on_hangup(switch_core_session_t *session)
tech_pvt = switch_core_session_get_private(session); tech_pvt = switch_core_session_get_private(session);
assert(tech_pvt != NULL); assert(tech_pvt != NULL);
/* ignore any further I/O requests, we're hanging up already! */
switch_clear_flag_locked(tech_pvt, TFLAG_IO);
if (!tech_pvt->ftdmchan) { if (!tech_pvt->ftdmchan) {
goto end; goto end;
} }
ftdm_channel_clear_token(tech_pvt->ftdmchan, switch_core_session_get_uuid(session)); ftdm_channel_clear_token(tech_pvt->ftdmchan, switch_core_session_get_uuid(session));
chantype = ftdm_channel_get_type(tech_pvt->ftdmchan); chantype = ftdm_channel_get_type(tech_pvt->ftdmchan);
switch (chantype) { switch (chantype) {
case FTDM_CHAN_TYPE_FXO: case FTDM_CHAN_TYPE_FXO:
case FTDM_CHAN_TYPE_EM: case FTDM_CHAN_TYPE_EM:
@ -548,8 +551,6 @@ static switch_status_t channel_on_hangup(switch_core_session_t *session)
end: end:
switch_clear_flag_locked(tech_pvt, TFLAG_IO);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s CHANNEL HANGUP\n", switch_channel_get_name(channel)); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "%s CHANNEL HANGUP\n", switch_channel_get_name(channel));
switch_mutex_lock(globals.mutex); switch_mutex_lock(globals.mutex);
globals.calls--; globals.calls--;
@ -629,6 +630,7 @@ static switch_status_t channel_read_frame(switch_core_session_t *session, switch
int total_to; int total_to;
int chunk, do_break = 0; int chunk, do_break = 0;
uint32_t span_id, chan_id; uint32_t span_id, chan_id;
const char *name = NULL;
channel = switch_core_session_get_channel(session); channel = switch_core_session_get_channel(session);
assert(channel != NULL); assert(channel != NULL);
@ -636,17 +638,18 @@ static switch_status_t channel_read_frame(switch_core_session_t *session, switch
tech_pvt = switch_core_session_get_private(session); tech_pvt = switch_core_session_get_private(session);
assert(tech_pvt != NULL); assert(tech_pvt != NULL);
if (switch_test_flag(tech_pvt, TFLAG_DEAD)) { name = switch_channel_get_name(channel);
ftdm_log(FTDM_LOG_DEBUG, "TFLAG_DEAD is set\n");
return SWITCH_STATUS_FALSE;
}
if (!tech_pvt->ftdmchan) { if (!tech_pvt->ftdmchan) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "no ftdmchan set in channel %s!\n", name);
return SWITCH_STATUS_FALSE; return SWITCH_STATUS_FALSE;
} }
span_id = ftdm_channel_get_span_id(tech_pvt->ftdmchan); span_id = ftdm_channel_get_span_id(tech_pvt->ftdmchan);
chan_id = ftdm_channel_get_id(tech_pvt->ftdmchan); chan_id = ftdm_channel_get_id(tech_pvt->ftdmchan);
if (switch_test_flag(tech_pvt, TFLAG_DEAD)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "TFLAG_DEAD is set in channel %s device %d:%d!\n", name, span_id, chan_id);
return SWITCH_STATUS_FALSE;
}
/* Digium Cards sometimes timeout several times in a row here. /* Digium Cards sometimes timeout several times in a row here.
Yes, we support digium cards, ain't we nice....... Yes, we support digium cards, ain't we nice.......
@ -678,7 +681,7 @@ static switch_status_t channel_read_frame(switch_core_session_t *session, switch
} }
if (!switch_test_flag(tech_pvt, TFLAG_IO)) { if (!switch_test_flag(tech_pvt, TFLAG_IO)) {
ftdm_log(FTDM_LOG_DEBUG, "TFLAG_IO is not set\n"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "TFLAG_IO is not set in channel %s device %d:%d!\n", name, span_id, chan_id);
goto fail; goto fail;
} }
@ -686,7 +689,7 @@ static switch_status_t channel_read_frame(switch_core_session_t *session, switch
status = ftdm_channel_wait(tech_pvt->ftdmchan, &wflags, chunk); status = ftdm_channel_wait(tech_pvt->ftdmchan, &wflags, chunk);
if (status == FTDM_FAIL) { if (status == FTDM_FAIL) {
ftdm_log(FTDM_LOG_ERROR, "Failed to wait for I/O\n"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to read from channel %s device %d:%d!\n", name, span_id, chan_id);
goto fail; goto fail;
} }
@ -694,7 +697,7 @@ static switch_status_t channel_read_frame(switch_core_session_t *session, switch
if (!switch_test_flag(tech_pvt, TFLAG_HOLD)) { if (!switch_test_flag(tech_pvt, TFLAG_HOLD)) {
total_to -= chunk; total_to -= chunk;
if (total_to <= 0) { if (total_to <= 0) {
ftdm_log(FTDM_LOG_WARNING, "Too many timeouts while waiting for I/O\n"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Too many timeouts while waiting I/O in channel %s device %d:%d!\n", name, span_id, chan_id);
goto fail; goto fail;
} }
} }
@ -707,9 +710,9 @@ static switch_status_t channel_read_frame(switch_core_session_t *session, switch
len = tech_pvt->read_frame.buflen; len = tech_pvt->read_frame.buflen;
if (ftdm_channel_read(tech_pvt->ftdmchan, tech_pvt->read_frame.data, &len) != FTDM_SUCCESS) { if (ftdm_channel_read(tech_pvt->ftdmchan, tech_pvt->read_frame.data, &len) != FTDM_SUCCESS) {
ftdm_log(FTDM_LOG_WARNING, "failed to read from device %d:%d\n", span_id, chan_id); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to read from channel %s device %d:%d!\n", name, span_id, chan_id);
if (++tech_pvt->read_error > FTDM_MAX_READ_WRITE_ERRORS) { if (++tech_pvt->read_error > FTDM_MAX_READ_WRITE_ERRORS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "too many I/O read errors on device %d:%d!\n", span_id, chan_id); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "too many I/O read errors on channel %s device %d:%d!\n", name, span_id, chan_id);
goto fail; goto fail;
} }
} else { } else {
@ -730,18 +733,18 @@ static switch_status_t channel_read_frame(switch_core_session_t *session, switch
for (p = dtmf; p && *p; p++) { for (p = dtmf; p && *p; p++) {
if (is_dtmf(*p)) { if (is_dtmf(*p)) {
_dtmf.digit = *p; _dtmf.digit = *p;
ftdm_log(FTDM_LOG_DEBUG, "Queuing DTMF [%c] in channel %s\n", *p, switch_channel_get_name(channel)); ftdm_log(FTDM_LOG_DEBUG, "Queuing DTMF [%c] in channel %s device %d:%d\n", *p, name, span_id, chan_id);
switch_channel_queue_dtmf(channel, &_dtmf); switch_channel_queue_dtmf(channel, &_dtmf);
} }
} }
} }
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
fail: fail:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "clearing IO in channel %s device %d:%d!\n", name, span_id, chan_id);
switch_clear_flag_locked(tech_pvt, TFLAG_IO); switch_clear_flag_locked(tech_pvt, TFLAG_IO);
return SWITCH_STATUS_GENERR; return SWITCH_STATUS_GENERR;
} }
static switch_status_t channel_write_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id) static switch_status_t channel_write_frame(switch_core_session_t *session, switch_frame_t *frame, switch_io_flag_t flags, int stream_id)
@ -750,8 +753,9 @@ static switch_status_t channel_write_frame(switch_core_session_t *session, switc
private_t *tech_pvt = NULL; private_t *tech_pvt = NULL;
ftdm_size_t len; ftdm_size_t len;
unsigned char data[SWITCH_RECOMMENDED_BUFFER_SIZE] = {0}; unsigned char data[SWITCH_RECOMMENDED_BUFFER_SIZE] = {0};
const char *name = "(none)";
ftdm_wait_flag_t wflags = FTDM_WRITE; ftdm_wait_flag_t wflags = FTDM_WRITE;
uint32_t span_id, chan_id; uint32_t span_id = 0, chan_id = 0;
channel = switch_core_session_get_channel(session); channel = switch_core_session_get_channel(session);
assert(channel != NULL); assert(channel != NULL);
@ -759,7 +763,17 @@ static switch_status_t channel_write_frame(switch_core_session_t *session, switc
tech_pvt = switch_core_session_get_private(session); tech_pvt = switch_core_session_get_private(session);
assert(tech_pvt != NULL); assert(tech_pvt != NULL);
name = switch_channel_get_name(channel);
if (!tech_pvt->ftdmchan) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "no ftdmchan set in channel %s!\n", name);
return SWITCH_STATUS_FALSE;
}
span_id = ftdm_channel_get_span_id(tech_pvt->ftdmchan);
chan_id = ftdm_channel_get_id(tech_pvt->ftdmchan);
if (switch_test_flag(tech_pvt, TFLAG_DEAD)) { if (switch_test_flag(tech_pvt, TFLAG_DEAD)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "TFLAG_DEAD is set in channel %s device %d:%d!\n", name, span_id, chan_id);
return SWITCH_STATUS_FALSE; return SWITCH_STATUS_FALSE;
} }
@ -768,17 +782,10 @@ static switch_status_t channel_write_frame(switch_core_session_t *session, switc
} }
if (!switch_test_flag(tech_pvt, TFLAG_IO)) { if (!switch_test_flag(tech_pvt, TFLAG_IO)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "TFLAG_IO is not set in channel %s device %d:%d!\n", name, span_id, chan_id);
goto fail; goto fail;
} }
if (!tech_pvt->ftdmchan) {
return SWITCH_STATUS_FALSE;
}
span_id = ftdm_channel_get_span_id(tech_pvt->ftdmchan);
chan_id = ftdm_channel_get_id(tech_pvt->ftdmchan);
if (switch_test_flag(frame, SFF_CNG)) { if (switch_test_flag(frame, SFF_CNG)) {
frame->data = data; frame->data = data;
frame->buflen = sizeof(data); frame->buflen = sizeof(data);
@ -793,16 +800,16 @@ static switch_status_t channel_write_frame(switch_core_session_t *session, switc
ftdm_channel_wait(tech_pvt->ftdmchan, &wflags, ftdm_channel_get_io_interval(tech_pvt->ftdmchan) * 10); ftdm_channel_wait(tech_pvt->ftdmchan, &wflags, ftdm_channel_get_io_interval(tech_pvt->ftdmchan) * 10);
if (!(wflags & FTDM_WRITE)) { if (!(wflags & FTDM_WRITE)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Dropping frame! (write not ready)\n"); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Dropping frame! (write note ready) in channel %s device %d:%d!\n", name, span_id, chan_id);
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
} }
len = frame->datalen; len = frame->datalen;
if (ftdm_channel_write(tech_pvt->ftdmchan, frame->data, frame->buflen, &len) != FTDM_SUCCESS) { if (ftdm_channel_write(tech_pvt->ftdmchan, frame->data, frame->buflen, &len) != FTDM_SUCCESS) {
ftdm_log(FTDM_LOG_WARNING, "failed to write to device %d:%d\n", span_id, chan_id); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Failed to write to channel %s device %d:%d!\n", name, span_id, chan_id);
if (++tech_pvt->write_error > FTDM_MAX_READ_WRITE_ERRORS) { if (++tech_pvt->write_error > FTDM_MAX_READ_WRITE_ERRORS) {
switch_log_printf(SWITCH_CHANNEL_LOG, switch_log_printf(SWITCH_CHANNEL_LOG,
SWITCH_LOG_ERROR, "too many I/O write errors on device %d:%d!\n", span_id, chan_id); SWITCH_LOG_ERROR, "Too many I/O write errors on channel %s device %d:%d!\n", name, span_id, chan_id);
goto fail; goto fail;
} }
} else { } else {
@ -812,7 +819,7 @@ static switch_status_t channel_write_frame(switch_core_session_t *session, switc
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
fail: fail:
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Error writing to channel %s device %d:%d!\n", name, span_id, chan_id);
switch_clear_flag_locked(tech_pvt, TFLAG_IO); switch_clear_flag_locked(tech_pvt, TFLAG_IO);
return SWITCH_STATUS_GENERR; return SWITCH_STATUS_GENERR;
@ -3802,9 +3809,139 @@ typedef switch_status_t (*ftdm_cli_function_t)(ftdm_cli_entry_t *cli, const char
int argc, char *argv[]) int argc, char *argv[])
static void print_usage(switch_stream_handle_t *stream, ftdm_cli_entry_t *cli); static void print_usage(switch_stream_handle_t *stream, ftdm_cli_entry_t *cli);
FTDM_CLI_DECLARE(ftdm_cmd_voice_detect)
typedef struct cmd_ioread_data {
int num_times;
uint32_t interval;
ftdm_span_t *span;
ftdm_channel_t *fchan;
switch_memory_pool_t *pool;
int already_open;
} cmd_ioread_data_t;
static void *SWITCH_THREAD_FUNC ioread_thread(switch_thread_t *thread, void *obj)
{ {
print_usage(stream, cli); ftdm_wait_flag_t wflags = FTDM_READ;
ftdm_status_t status = FTDM_FAIL;
unsigned char iobuf[SWITCH_RECOMMENDED_BUFFER_SIZE];
cmd_ioread_data_t *data = obj;
int span_id = ftdm_span_get_id(data->span);
int chan_id = ftdm_channel_get_id(data->fchan);
uint32_t len = ftdm_channel_get_io_packet_len(data->fchan);
uint32_t origlen = len;
unsigned int pbuf[5];
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,
"Started ioread thread (times = %d, interval = %ums, len = %u, span = %d, chan = %d\n",
data->num_times, data->interval, len, span_id, chan_id);
while (ftdm_running() && data->num_times > 0) {
data->num_times--;
wflags = FTDM_READ;
status = ftdm_channel_wait(data->fchan, &wflags, (data->interval * 10));
if (status == FTDM_FAIL) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Failed to wait for IO in device %d:%d!\n", span_id, chan_id);
continue;
}
if (status == FTDM_TIMEOUT) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Timed out while waiting I/O in device %d:%d!\n", span_id, chan_id);
continue;
}
len = origlen;
if (ftdm_channel_read(data->fchan, iobuf, &len) != FTDM_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Failed to read from device %d:%d!\n", span_id, chan_id);
continue;
}
pbuf[0] = iobuf[0];
pbuf[1] = iobuf[1];
pbuf[2] = iobuf[2];
pbuf[3] = iobuf[3];
pbuf[4] = iobuf[4];
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Read 0x%1X 0x%1X 0x%1X 0x%1X 0x%1X\n",
pbuf[0], pbuf[1], pbuf[2], pbuf[3], pbuf[4]);
}
if (!data->already_open) {
ftdm_channel_close(&data->fchan);
}
switch_core_destroy_memory_pool(&data->pool);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG,
"Done ioread thread (times = %d, interval = %ums, len = %u, span = %d, chan = %d\n",
data->num_times, data->interval, origlen, span_id, chan_id);
return NULL;
}
FTDM_CLI_DECLARE(ftdm_cmd_ioread)
{
char *span_name = NULL;
int channo = 0;
ftdm_status_t status = FTDM_SUCCESS;
switch_threadattr_t *attr = NULL;
switch_thread_t *thread = NULL;
cmd_ioread_data_t *thdata;
cmd_ioread_data_t data;
memset(&data, 0, sizeof(data));
data.num_times = 1;
if (argc < 3) {
print_usage(stream, cli);
goto end;
}
span_name = argv[1];
ftdm_span_find_by_name(span_name, &data.span);
if (!data.span) {
stream->write_function(stream, "-ERR span %s not found\n", span_name);
goto end;
}
channo = atoi(argv[2]);
status = ftdm_channel_open(ftdm_span_get_id(data.span), channo, &data.fchan);
if (!data.fchan || (status != FTDM_SUCCESS && status != FTDM_EBUSY)) {
stream->write_function(stream, "-ERR Failed to open channel %d in span %s\n", channo, span_name);
goto end;
}
if (status == FTDM_EBUSY) {
data.already_open = 1;
}
if (argc > 3) {
data.num_times = atoi(argv[3]);
if (data.num_times < 1) {
data.num_times = 1;
}
}
if (argc > 4) {
data.interval = atoi(argv[4]);
}
if (data.interval <= 0 || data.interval > 10000) {
data.interval = ftdm_channel_get_io_interval(data.fchan);
}
switch_core_new_memory_pool(&data.pool);
thdata = switch_core_alloc(data.pool, sizeof(data));
memcpy(thdata, &data, sizeof(*thdata));
switch_threadattr_create(&attr, data.pool);
switch_threadattr_detach_set(attr, 1);
switch_threadattr_stacksize_set(attr, SWITCH_THREAD_STACKSIZE);
switch_thread_create(&thread, attr, ioread_thread, thdata, data.pool);
end:
return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_SUCCESS;
} }
@ -4452,7 +4589,7 @@ static ftdm_cli_entry_t ftdm_cli_options[] =
{ "dtmf", "on|off <span_id|span_name> [<chan_id>]", "::[on:off", ftdm_cmd_dtmf }, { "dtmf", "on|off <span_id|span_name> [<chan_id>]", "::[on:off", ftdm_cmd_dtmf },
{ "queuesize", "<rxsize> <txsize> <span_id|span_name> [<chan_id>]", "", ftdm_cmd_queuesize }, { "queuesize", "<rxsize> <txsize> <span_id|span_name> [<chan_id>]", "", ftdm_cmd_queuesize },
{ "iostats", "enable|disable|flush|print <span_id|span_name> <chan_id>", "::[enable:disable:flush:print", ftdm_cmd_iostats }, { "iostats", "enable|disable|flush|print <span_id|span_name> <chan_id>", "::[enable:disable:flush:print", ftdm_cmd_iostats },
{ "voice_detect", "[on|off] <span_id|span_name> [<chan_id>]", "::[on:off", ftdm_cmd_voice_detect }, { "ioread", "<span_id|span_name> <chan_id> [num_times] [interval]", "", ftdm_cmd_ioread },
/* Fake handlers as they are handled within freetdm library, /* Fake handlers as they are handled within freetdm library,
* we should provide a way inside freetdm to query for completions from signaling modules */ * we should provide a way inside freetdm to query for completions from signaling modules */

View File

@ -1855,7 +1855,14 @@ static ftdm_status_t _ftdm_channel_open(uint32_t span_id, uint32_t chan_id, ftdm
goto done; goto done;
} }
ftdm_mutex_lock(check->mutex); ftdm_channel_lock(check);
if (ftdm_test_flag(check, FTDM_CHANNEL_OPEN)) {
/* let them know is already open, but return the channel anyway */
status = FTDM_EBUSY;
*ftdmchan = check;
goto unlockchan;
}
/* The following if's and gotos replace a big if (this || this || this || this) else { nothing; } */ /* The following if's and gotos replace a big if (this || this || this || this) else { nothing; } */
@ -1902,7 +1909,7 @@ openchan:
goto done; goto done;
unlockchan: unlockchan:
ftdm_mutex_unlock(check->mutex); ftdm_channel_unlock(check);
done: done:
ftdm_mutex_unlock(globals.mutex); ftdm_mutex_unlock(globals.mutex);