diff --git a/libs/freetdm/mod_freetdm/mod_freetdm.c b/libs/freetdm/mod_freetdm/mod_freetdm.c index 9b181d545c..4e23fd74f5 100755 --- a/libs/freetdm/mod_freetdm/mod_freetdm.c +++ b/libs/freetdm/mod_freetdm/mod_freetdm.c @@ -4346,6 +4346,76 @@ end: return SWITCH_STATUS_SUCCESS; } +static void exec_io_command(const char *cmd, switch_stream_handle_t *stream, ftdm_channel_t *fchan) +{ + int enable = 0; + ftdm_channel_iostats_t stats; + if (!strcasecmp("enable", cmd)) { + enable = 1; + ftdm_channel_command(fchan, FTDM_COMMAND_SWITCH_IOSTATS, &enable); + } else if (!strcasecmp("disable", cmd)) { + enable = 0; + ftdm_channel_command(fchan, FTDM_COMMAND_SWITCH_IOSTATS, &enable); + } else if (!strcasecmp("flush", cmd)) { + ftdm_channel_command(fchan, FTDM_COMMAND_FLUSH_IOSTATS, NULL); + } else { + ftdm_channel_command(fchan, FTDM_COMMAND_GET_IOSTATS, &stats); + stream->write_function(stream, "-- IO statistics for channel %d:%d --\n", + ftdm_channel_get_span_id(fchan), ftdm_channel_get_id(fchan)); + stream->write_function(stream, "Rx errors: %u\n", stats.rx.errors); + stream->write_function(stream, "Rx queue size: %u\n", stats.rx.queue_size); + stream->write_function(stream, "Rx queue len: %u\n", stats.rx.queue_len); + stream->write_function(stream, "Rx count: %lu\n", stats.rx.packets); + + stream->write_function(stream, "Tx errors: %u\n", stats.tx.errors); + stream->write_function(stream, "Tx queue len: %u\n", stats.tx.queue_len); + stream->write_function(stream, "Tx queue len: %u\n", stats.tx.queue_len); + stream->write_function(stream, "Tx count: %lu\n", stats.tx.packets); + stream->write_function(stream, "Tx idle: %u\n", stats.tx.idle_packets); + } +} + +static switch_status_t ftdm_cmd_iostats(const char *cmd, switch_core_session_t *session, + switch_stream_handle_t *stream, int argc, char *argv[]) +{ + uint32_t chan_id = 0; + ftdm_channel_t *chan; + ftdm_iterator_t *iter = NULL; + ftdm_iterator_t *curr = NULL; + ftdm_span_t *span = NULL; + + if (argc < 3) { + stream->write_function(stream, "-ERR Usage: ftdm iostats enable|disable|flush|print []\n"); + goto end; + } + + ftdm_span_find_by_name(argv[2], &span); + if (!span) { + stream->write_function(stream, "-ERR invalid span\n"); + goto end; + } + + if (argc > 3) { + chan_id = atoi(argv[3]); + if (chan_id > ftdm_span_get_chan_count(span)) { + stream->write_function(stream, "-ERR invalid chan\n"); + goto end; + } + chan = ftdm_span_get_channel(span, chan_id); + exec_io_command(argv[1], stream, chan); + } else { + iter = ftdm_span_get_chan_iterator(span, NULL); + for (curr = iter; curr; curr = ftdm_iterator_next(curr)) { + chan = ftdm_iterator_current(curr); + exec_io_command(argv[1], stream, chan); + } + ftdm_iterator_free(iter); + } + stream->write_function(stream, "+OK\n"); +end: + return SWITCH_STATUS_SUCCESS; +} + typedef switch_status_t (*ftdm_cli_function_t)(const char *cmd, switch_core_session_t *session, switch_stream_handle_t *stream, int argc, char *argv[]); typedef struct ftdm_cli_entry { @@ -4368,6 +4438,7 @@ static ftdm_cli_entry_t ftdm_cli_options[] = { "gains", " []", "", ftdm_cmd_gains }, { "dtmf", "on|off []", "::[on:off", ftdm_cmd_dtmf }, { "queuesize", " []", "", ftdm_cmd_queuesize }, + { "iostats", "enable|disable|flush|print ", "::[enable:disable:flush:print", ftdm_cmd_iostats }, { "voice_detect", "[on|off] []", "::[on:off", ftdm_cmd_voice_detect }, /* Fake handlers as they are handled within freetdm library, diff --git a/libs/freetdm/src/ftdm_io.c b/libs/freetdm/src/ftdm_io.c index 1e7c682e02..805c525269 100644 --- a/libs/freetdm/src/ftdm_io.c +++ b/libs/freetdm/src/ftdm_io.c @@ -2825,9 +2825,9 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_command(ftdm_channel_t *ftdmchan, ftdm_co ftdm_assert_return(ftdmchan != NULL, FTDM_FAIL, "No channel\n"); ftdm_assert_return(ftdmchan->fio != NULL, FTDM_FAIL, "No IO attached to channel\n"); - ftdm_mutex_lock(ftdmchan->mutex); + ftdm_channel_lock(ftdmchan); - switch(command) { + switch (command) { case FTDM_COMMAND_ENABLE_CALLERID_DETECT: { @@ -3278,12 +3278,31 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_command(ftdm_channel_t *ftdmchan, ftdm_co GOTO_STATUS(done, FTDM_SUCCESS); } break; + case FTDM_COMMAND_GET_IOSTATS: + { + if (!obj) { + GOTO_STATUS(done, FTDM_EINVAL); + } + memcpy(obj, &ftdmchan->iostats, sizeof(ftdmchan->iostats)); + GOTO_STATUS(done, FTDM_SUCCESS); + } + break; + case FTDM_COMMAND_SWITCH_IOSTATS: + { + ftdm_bool_t enable = *(ftdm_bool_t *)obj; + if (enable) { + ftdm_channel_set_feature(ftdmchan, FTDM_CHANNEL_FEATURE_IO_STATS); + } else { + ftdm_channel_clear_feature(ftdmchan, FTDM_CHANNEL_FEATURE_IO_STATS); + } + GOTO_STATUS(done, FTDM_SUCCESS); + } + break; default: break; } if (!ftdmchan->fio->command) { - snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "method not implemented"); ftdm_log(FTDM_LOG_ERROR, "no command function defined by the I/O freetdm module!\n"); GOTO_STATUS(done, FTDM_FAIL); } @@ -3291,11 +3310,12 @@ FT_DECLARE(ftdm_status_t) ftdm_channel_command(ftdm_channel_t *ftdmchan, ftdm_co status = ftdmchan->fio->command(ftdmchan, command, obj); if (status == FTDM_NOTIMPL) { - snprintf(ftdmchan->last_error, sizeof(ftdmchan->last_error), "I/O command %d not implemented in backend", command); ftdm_log(FTDM_LOG_ERROR, "I/O backend does not support command %d!\n", command); } + done: - ftdm_mutex_unlock(ftdmchan->mutex); + ftdm_channel_unlock(ftdmchan); + return status; } diff --git a/libs/freetdm/src/include/freetdm.h b/libs/freetdm/src/include/freetdm.h index ca16219e49..96ffdce62c 100755 --- a/libs/freetdm/src/include/freetdm.h +++ b/libs/freetdm/src/include/freetdm.h @@ -707,7 +707,10 @@ typedef enum { FTDM_COMMAND_FLUSH_TX_BUFFERS = 45, FTDM_COMMAND_FLUSH_RX_BUFFERS = 46, FTDM_COMMAND_FLUSH_BUFFERS = 47, + + /*!< Flush IO statistics */ FTDM_COMMAND_FLUSH_IOSTATS = 48, + FTDM_COMMAND_SET_PRE_BUFFER_SIZE = 49, FTDM_COMMAND_SET_LINK_STATUS = 50, FTDM_COMMAND_GET_LINK_STATUS = 51, @@ -719,6 +722,11 @@ typedef enum { FTDM_COMMAND_START_MF_PLAYBACK = 57, FTDM_COMMAND_STOP_MF_PLAYBACK = 58, + /*!< Get a copy of the current IO stats */ + FTDM_COMMAND_GET_IOSTATS = 59, + /*!< Enable/disable IO stats in the channel */ + FTDM_COMMAND_SWITCH_IOSTATS = 60, + FTDM_COMMAND_COUNT, } ftdm_command_t; @@ -903,6 +911,37 @@ typedef enum { FTDM_MF_DIRECTION_BACKWARD = (1 << 9) } ftdm_mf_direction_flag_t; +/*! \brief IO Error statistics */ +typedef enum { + FTDM_IOSTATS_ERROR_CRC = (1 << 0), + FTDM_IOSTATS_ERROR_FRAME = (1 << 1), + FTDM_IOSTATS_ERROR_ABORT = (1 << 2), + FTDM_IOSTATS_ERROR_FIFO = (1 << 3), + FTDM_IOSTATS_ERROR_DMA = (1 << 4), + FTDM_IOSTATS_ERROR_QUEUE_THRES = (1 << 5), /* Queue reached high threshold */ + FTDM_IOSTATS_ERROR_QUEUE_FULL = (1 << 6), /* Queue is full */ +} ftdm_iostats_error_type_t; + +/*! \brief IO statistics */ +typedef struct { + struct { + uint32_t errors; + uint16_t flags; + uint8_t queue_size; /*!< max queue size configured */ + uint8_t queue_len; /*!< Current number of elements in queue */ + uint64_t packets; + } rx; + + struct { + uint32_t errors; + uint16_t flags; + uint8_t idle_packets; + uint8_t queue_size; /*!< max queue size configured */ + uint8_t queue_len; /*!< Current number of elements in queue */ + uint64_t packets; + } tx; +} ftdm_channel_iostats_t; + /*! \brief Override the default queue handler */ FT_DECLARE(ftdm_status_t) ftdm_global_set_queue_handler(ftdm_queue_handler_t *handler); diff --git a/libs/freetdm/src/include/private/ftdm_core.h b/libs/freetdm/src/include/private/ftdm_core.h index e4be27ce40..9c5c9ad468 100644 --- a/libs/freetdm/src/include/private/ftdm_core.h +++ b/libs/freetdm/src/include/private/ftdm_core.h @@ -359,35 +359,6 @@ typedef struct { ftdm_mutex_t *mutex; } ftdm_dtmf_debug_t; -typedef enum { - FTDM_IOSTATS_ERROR_CRC = (1 << 0), - FTDM_IOSTATS_ERROR_FRAME = (1 << 1), - FTDM_IOSTATS_ERROR_ABORT = (1 << 2), - FTDM_IOSTATS_ERROR_FIFO = (1 << 3), - FTDM_IOSTATS_ERROR_DMA = (1 << 4), - FTDM_IOSTATS_ERROR_QUEUE_THRES = (1 << 5), /* Queue reached high threshold */ - FTDM_IOSTATS_ERROR_QUEUE_FULL = (1 << 6), /* Queue is full */ -} ftdm_iostats_error_type_t; - -typedef struct { - struct { - uint32_t errors; - uint16_t flags; - uint8_t queue_size; /* max queue size configured */ - uint8_t queue_len; /* Current number of elements in queue */ - uint64_t packets; - } rx; - - struct { - uint32_t errors; - uint16_t flags; - uint8_t idle_packets; - uint8_t queue_size; /* max queue size configured */ - uint8_t queue_len; /* Current number of elements in queue */ - uint64_t packets; - } tx; -} ftdm_channel_iostats_t; - /* 2^8 table size, one for each byte (sample) value */ #define FTDM_GAINS_TABLE_SIZE 256 struct ftdm_channel {