Michael Collins e4ed6a4fa3 Add Doxygen comments. Thanks Francois.
git-svn-id: http://svn.openzap.org/svn/openzap/trunk@741 a93c3328-9c30-0410-af19-c9cd2b2d52af
2009-06-08 20:01:29 +00:00

1013 lines
27 KiB
C

/*
* Copyright (c) 2007, Anthony Minessale II
* 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.
*/
#ifdef __sun
#include <unistd.h>
#include <stropts.h>
#endif
#include "openzap.h"
#ifndef __WINDOWS__
#include <poll.h>
#include <sys/socket.h>
#endif
#include "libsangoma.h"
#if defined(__WINDOWS__)
/*! Backward compatible defines - current code is all using the old names*/
#define sangoma_open_tdmapi_span_chan sangoma_open_api_span_chan
#define sangoma_open_tdmapi_span sangoma_open_api_span
#define sangoma_open_tdmapi_ctrl sangoma_open_api_ctrl
#define sangoma_tdm_get_fe_status sangoma_get_fe_status
#define sangoma_socket_close sangoma_close
#define sangoma_tdm_get_hw_coding sangoma_get_hw_coding
#define sangoma_tdm_set_fe_status sangoma_set_fe_status
#define sangoma_tdm_get_link_status sangoma_get_link_status
#define sangoma_tdm_flush_bufs sangoma_flush_bufs
#define sangoma_tdm_cmd_exec sangoma_cmd_exec
#define sangoma_tdm_read_event sangoma_read_event
#define sangoma_readmsg_tdm sangoma_readmsg
#define sangoma_readmsg_socket sangoma_readmsg
#define sangoma_sendmsg_socket sangoma_writemsg
#define sangoma_writemsg_tdm sangoma_writemsg
#define sangoma_create_socket_intr sangoma_open_api_span_chan
#define EX_DECLARE_DATA __declspec(dllexport)
#else
#define EX_DECLARE_DATA
#endif
/**
* \brief Wanpipe flags
*/
typedef enum {
WP_RINGING = (1 << 0)
} wp_flag_t;
/**
* \brief Wanpipe globals
*/
static struct {
uint32_t codec_ms;
uint32_t wink_ms;
uint32_t flash_ms;
uint32_t ring_on_ms;
uint32_t ring_off_ms;
} wp_globals;
/* a bunch of this stuff should go into the wanpipe_tdm_api_iface.h */
ZIO_SPAN_POLL_EVENT_FUNCTION(wanpipe_poll_event);
ZIO_SPAN_NEXT_EVENT_FUNCTION(wanpipe_next_event);
#define WP_INVALID_SOCKET -1
/**
* \brief Poll for event on a wanpipe socket
* \param fd Wanpipe socket descriptor
* \param timeout Time to wait for event
* \param flags Sangoma event flags
* \return -1 on failure, wanpipe event flags on success
*
* a cross platform way to poll on an actual pollset (span and/or list of spans) will probably also be needed for analog
* so we can have one analong handler thread that will deal with all the idle analog channels for events
* the alternative would be for the driver to provide one socket for all of the oob events for all analog channels
*/
static __inline__ int tdmv_api_wait_socket(sng_fd_t fd, int timeout, int *flags)
{
#ifdef LIBSANGOMA_VERSION
int err;
sangoma_wait_obj_t sangoma_wait_obj;
sangoma_init_wait_obj(&sangoma_wait_obj, fd, 1, 1, *flags, SANGOMA_WAIT_OBJ);
err=sangoma_socket_waitfor_many(&sangoma_wait_obj,1 , timeout);
if (err > 0) {
*flags=sangoma_wait_obj.flags_out;
}
return err;
#else
struct pollfd pfds[1];
int res;
memset(&pfds[0], 0, sizeof(pfds[0]));
pfds[0].fd = fd;
pfds[0].events = *flags;
res = poll(pfds, 1, timeout);
*flags = 0;
if (pfds[0].revents & POLLERR) {
res = -1;
}
if (res > 0) {
*flags = pfds[0].revents;
}
return res;
#endif
}
/**
* \brief Opens a sangoma channel socket (TDM API)
* \param span Span number
* \param chan Channel number
* \return 0 on success, wanpipe error code on failure
*/
static __inline__ sng_fd_t tdmv_api_open_span_chan(int span, int chan)
{
return sangoma_open_tdmapi_span_chan(span, chan);
}
static zap_io_interface_t wanpipe_interface;
/**
* \brief Inverts bit string
* \param cas_bits CAS bit string
* \return Swapped bits
*/
static unsigned char wanpipe_swap_bits(unsigned char cas_bits)
{
unsigned char swapped_bits = 0x0;
if (cas_bits & 0x8) {
swapped_bits |= 0x1;
}
if (cas_bits & 0x4) {
swapped_bits |= 0x2;
}
if (cas_bits & 0x2) {
swapped_bits |= 0x4;
}
if (cas_bits & 0x1) {
swapped_bits |= 0x8;
}
return swapped_bits;
}
/**
* \brief Initialises a range of wanpipe channels
* \param span Openzap span
* \param spanno Wanpipe span number
* \param start Initial wanpipe channel number
* \param end Final wanpipe channel number
* \param type Openzap channel type
* \param name Openzap span name
* \param number Openzap span number
* \param cas_bits CAS bits
* \return number of spans configured
*/
static unsigned wp_open_range(zap_span_t *span, unsigned spanno, unsigned start, unsigned end, zap_chan_type_t type, char *name, char *number, unsigned char cas_bits)
{
unsigned configured = 0, x;
if (type == ZAP_CHAN_TYPE_CAS) {
zap_log(ZAP_LOG_DEBUG, "Configuring CAS channels with abcd == 0x%X\n", cas_bits);
}
for(x = start; x < end; x++) {
zap_channel_t *chan;
zap_socket_t sockfd = WP_INVALID_SOCKET;
const char *dtmf = "none";
sockfd = tdmv_api_open_span_chan(spanno, x);
if (sockfd != WP_INVALID_SOCKET && zap_span_add_channel(span, sockfd, type, &chan) == ZAP_SUCCESS) {
wanpipe_tdm_api_t tdm_api;
memset(&tdm_api,0,sizeof(tdm_api));
chan->physical_span_id = spanno;
chan->physical_chan_id = x;
chan->rate = 8000;
if (type == ZAP_CHAN_TYPE_FXS || type == ZAP_CHAN_TYPE_FXO || type == ZAP_CHAN_TYPE_B) {
int err;
dtmf = "software";
/* FIXME: Handle Error Conditino Check for return code */
err= sangoma_tdm_get_hw_coding(chan->sockfd, &tdm_api);
if (tdm_api.wp_tdm_cmd.hw_tdm_coding) {
chan->native_codec = chan->effective_codec = ZAP_CODEC_ALAW;
} else {
chan->native_codec = chan->effective_codec = ZAP_CODEC_ULAW;
}
err = sangoma_tdm_get_hw_dtmf(chan->sockfd, &tdm_api);
if (err > 0) {
err = sangoma_tdm_enable_dtmf_events(chan->sockfd, &tdm_api);
if (err == 0) {
zap_channel_set_feature(chan, ZAP_CHANNEL_FEATURE_DTMF_DETECT);
dtmf = "hardware";
}
}
}
#if 0
if (type == ZAP_CHAN_TYPE_FXS || type == ZAP_CHAN_TYPE_FXO) {
/* Enable FLASH/Wink Events */
int err=sangoma_set_rm_rxflashtime(chan->sockfd, &tdm_api, wp_globals.flash_ms);
if (err == 0) {
zap_log(ZAP_LOG_ERROR, "flash enabled s%dc%d\n", spanno, x);
} else {
zap_log(ZAP_LOG_ERROR, "flash disabled s%dc%d\n", spanno, x);
}
}
#endif
if (type == ZAP_CHAN_TYPE_CAS) {
#ifdef LIBSANGOMA_VERSION
sangoma_tdm_write_rbs(chan->sockfd,&tdm_api,chan->physical_chan_id,wanpipe_swap_bits(cas_bits));
#else
sangoma_tdm_write_rbs(chan->sockfd,&tdm_api,wanpipe_swap_bits(cas_bits));
#endif
}
if (!zap_strlen_zero(name)) {
zap_copy_string(chan->chan_name, name, sizeof(chan->chan_name));
}
if (!zap_strlen_zero(number)) {
zap_copy_string(chan->chan_number, number, sizeof(chan->chan_number));
}
configured++;
zap_log(ZAP_LOG_INFO, "configuring device s%dc%d as OpenZAP device %d:%d fd:%d DTMF: %s\n",
spanno, x, chan->span_id, chan->chan_id, sockfd, dtmf);
} else {
zap_log(ZAP_LOG_ERROR, "failure configuring device s%dc%d\n", spanno, x);
}
}
return configured;
}
/**
* \brief Process configuration variable for a Wanpipe profile
* \param category Wanpipe profile name
* \param var Variable name
* \param val Variable value
* \param lineno Line number from configuration file
* \return Success
*/
static ZIO_CONFIGURE_FUNCTION(wanpipe_configure)
{
int num;
if (!strcasecmp(category, "defaults")) {
if (!strcasecmp(var, "codec_ms")) {
num = atoi(val);
if (num < 10 || num > 60) {
zap_log(ZAP_LOG_WARNING, "invalid codec ms at line %d\n", lineno);
} else {
wp_globals.codec_ms = num;
}
} else if (!strcasecmp(var, "wink_ms")) {
num = atoi(val);
if (num < 50 || num > 3000) {
zap_log(ZAP_LOG_WARNING, "invalid wink ms at line %d\n", lineno);
} else {
wp_globals.wink_ms = num;
}
} else if (!strcasecmp(var, "flash_ms")) {
num = atoi(val);
if (num < 50 || num > 3000) {
zap_log(ZAP_LOG_WARNING, "invalid flash ms at line %d\n", lineno);
} else {
wp_globals.flash_ms = num;
}
}
}
return ZAP_SUCCESS;
}
/**
* \brief Initialises an openzap Wanpipe span from a configuration string
* \param span Openzap span
* \param str Configuration string
* \param type Openzap span type
* \param name Openzap span name
* \param number Openzap span number
* \return Success or failure
*/
static ZIO_CONFIGURE_SPAN_FUNCTION(wanpipe_configure_span)
{
int items, i;
char *mydata, *item_list[10];
char *sp, *ch, *mx;
unsigned char cas_bits = 0;
int channo;
int spanno;
int top = 0;
unsigned configured = 0;
assert(str != NULL);
mydata = strdup(str);
assert(mydata != NULL);
items = zap_separate_string(mydata, ',', item_list, (sizeof(item_list) / sizeof(item_list[0])));
for(i = 0; i < items; i++) {
sp = item_list[i];
if ((ch = strchr(sp, ':'))) {
*ch++ = '\0';
}
if (!(sp && ch)) {
zap_log(ZAP_LOG_ERROR, "Invalid input\n");
continue;
}
channo = atoi(ch);
spanno = atoi(sp);
if (channo < 0) {
zap_log(ZAP_LOG_ERROR, "Invalid channel number %d\n", channo);
continue;
}
if (spanno < 0) {
zap_log(ZAP_LOG_ERROR, "Invalid span number %d\n", channo);
continue;
}
if ((mx = strchr(ch, '-'))) {
mx++;
top = atoi(mx) + 1;
} else {
top = channo + 1;
}
if (top < 0) {
zap_log(ZAP_LOG_ERROR, "Invalid range number %d\n", top);
continue;
}
if (ZAP_CHAN_TYPE_CAS == type && zap_config_get_cas_bits(ch, &cas_bits)) {
zap_log(ZAP_LOG_ERROR, "Failed to get CAS bits in CAS channel\n");
continue;
}
configured += wp_open_range(span, spanno, channo, top, type, name, number, cas_bits);
}
free(mydata);
return configured;
}
/**
* \brief Opens Wanpipe channel
* \param zchan Channel to open
* \return Success or failure
*/
static ZIO_OPEN_FUNCTION(wanpipe_open)
{
wanpipe_tdm_api_t tdm_api;
memset(&tdm_api,0,sizeof(tdm_api));
if (zchan->type == ZAP_CHAN_TYPE_DQ921 || zchan->type == ZAP_CHAN_TYPE_DQ931) {
zchan->native_codec = zchan->effective_codec = ZAP_CODEC_NONE;
} else {
zchan->effective_codec = zchan->native_codec;
sangoma_tdm_set_usr_period(zchan->sockfd, &tdm_api, wp_globals.codec_ms);
zap_channel_set_feature(zchan, ZAP_CHANNEL_FEATURE_INTERVAL);
zchan->effective_interval = zchan->native_interval = wp_globals.codec_ms;
zchan->packet_len = zchan->native_interval * 8;
}
return ZAP_SUCCESS;
}
/**
* \brief Closes Wanpipe channel
* \param zchan Channel to close
* \return Success
*/
static ZIO_CLOSE_FUNCTION(wanpipe_close)
{
return ZAP_SUCCESS;
}
/**
* \brief Executes an Openzap command on a Wanpipe channel
* \param zchan Channel to execute command on
* \param command Openzap command to execute
* \param obj Object (unused)
* \return Success or failure
*/
static ZIO_COMMAND_FUNCTION(wanpipe_command)
{
wanpipe_tdm_api_t tdm_api;
int err = 0;
memset(&tdm_api, 0, sizeof(tdm_api));
switch(command) {
case ZAP_COMMAND_OFFHOOK:
{
err=sangoma_tdm_txsig_offhook(zchan->sockfd,&tdm_api);
if (err) {
snprintf(zchan->last_error, sizeof(zchan->last_error), "OFFHOOK Failed");
return ZAP_FAIL;
}
zap_set_flag_locked(zchan, ZAP_CHANNEL_OFFHOOK);
}
break;
case ZAP_COMMAND_ONHOOK:
{
err=sangoma_tdm_txsig_onhook(zchan->sockfd,&tdm_api);
if (err) {
snprintf(zchan->last_error, sizeof(zchan->last_error), "ONHOOK Failed");
return ZAP_FAIL;
}
zap_clear_flag_locked(zchan, ZAP_CHANNEL_OFFHOOK);
}
break;
case ZAP_COMMAND_GENERATE_RING_ON:
{
err=sangoma_tdm_txsig_start(zchan->sockfd,&tdm_api);
if (err) {
snprintf(zchan->last_error, sizeof(zchan->last_error), "Ring Failed");
return ZAP_FAIL;
}
zap_set_flag_locked(zchan, ZAP_CHANNEL_RINGING);
zap_set_pflag_locked(zchan, WP_RINGING);
zchan->ring_time = zap_current_time_in_ms() + wp_globals.ring_on_ms;
}
break;
case ZAP_COMMAND_GENERATE_RING_OFF:
{
err=sangoma_tdm_txsig_offhook(zchan->sockfd,&tdm_api);
if (err) {
snprintf(zchan->last_error, sizeof(zchan->last_error), "Ring-off Failed");
return ZAP_FAIL;
}
zap_clear_pflag_locked(zchan, WP_RINGING);
zap_clear_flag_locked(zchan, ZAP_CHANNEL_RINGING);
}
break;
case ZAP_COMMAND_GET_INTERVAL:
{
err=sangoma_tdm_get_usr_period(zchan->sockfd, &tdm_api);
if (err > 0 ) {
ZAP_COMMAND_OBJ_INT = err;
err=0;
}
}
break;
case ZAP_COMMAND_SET_INTERVAL:
{
err=sangoma_tdm_set_usr_period(zchan->sockfd, &tdm_api, ZAP_COMMAND_OBJ_INT);
zchan->packet_len = zchan->native_interval * (zchan->effective_codec == ZAP_CODEC_SLIN ? 16 : 8);
}
break;
case ZAP_COMMAND_SET_CAS_BITS:
{
#ifdef LIBSANGOMA_VERSION
err=sangoma_tdm_write_rbs(zchan->sockfd,&tdm_api,zchan->physical_chan_id,wanpipe_swap_bits(ZAP_COMMAND_OBJ_INT));
#else
err=sangoma_tdm_write_rbs(zchan->sockfd,&tdm_api,wanpipe_swap_bits(ZAP_COMMAND_OBJ_INT));
#endif
}
break;
case ZAP_COMMAND_GET_CAS_BITS:
{
/* wanpipe does not has a command to get the CAS bits so we emulate it */
ZAP_COMMAND_OBJ_INT = zchan->cas_bits;
}
break;
default:
break;
};
if (err) {
snprintf(zchan->last_error, sizeof(zchan->last_error), "%s", strerror(errno));
return ZAP_FAIL;
}
return ZAP_SUCCESS;
}
/**
* \brief Reads data from a Wanpipe channel
* \param zchan Channel to read from
* \param data Data buffer
* \param datalen Size of data buffer
* \return Success, failure or timeout
*/
static ZIO_READ_FUNCTION(wanpipe_read)
{
int rx_len = 0;
wp_tdm_api_rx_hdr_t hdrframe;
memset(&hdrframe, 0, sizeof(hdrframe));
rx_len = sangoma_readmsg_tdm(zchan->sockfd, &hdrframe, (int)sizeof(hdrframe), data, (int)*datalen,0);
*datalen = rx_len;
if (rx_len == 0 || rx_len == -17) {
return ZAP_TIMEOUT;
}
if (rx_len < 0) {
snprintf(zchan->last_error, sizeof(zchan->last_error), "%s", strerror(errno));
return ZAP_FAIL;
}
return ZAP_SUCCESS;
}
/**
* \brief Writes data to a Wanpipe channel
* \param zchan Channel to write to
* \param data Data buffer
* \param datalen Size of data buffer
* \return Success or failure
*/
static ZIO_WRITE_FUNCTION(wanpipe_write)
{
int bsent;
wp_tdm_api_tx_hdr_t hdrframe;
/* Do we even need the headerframe here? on windows, we don't even pass it to the driver */
memset(&hdrframe, 0, sizeof(hdrframe));
bsent = sangoma_writemsg_tdm(zchan->sockfd, &hdrframe, (int)sizeof(hdrframe), data, (unsigned short)(*datalen),0);
/* should we be checking if bsent == *datalen here? */
if (bsent > 0) {
*datalen = bsent;
return ZAP_SUCCESS;
}
return ZAP_FAIL;
}
/**
* \brief Waits for an event on a Wanpipe channel
* \param zchan Channel to open
* \param flags Type of event to wait for
* \param to Time to wait (in ms)
* \return Success, failure or timeout
*/
static ZIO_WAIT_FUNCTION(wanpipe_wait)
{
int32_t inflags = 0;
int result;
if (*flags & ZAP_READ) {
inflags |= POLLIN;
}
if (*flags & ZAP_WRITE) {
inflags |= POLLOUT;
}
if (*flags & ZAP_EVENTS) {
inflags |= POLLPRI;
}
result = tdmv_api_wait_socket(zchan->sockfd, to, &inflags);
*flags = ZAP_NO_FLAGS;
if (result < 0){
snprintf(zchan->last_error, sizeof(zchan->last_error), "Poll failed");
return ZAP_FAIL;
}
if (result == 0) {
return ZAP_TIMEOUT;
}
if (inflags & POLLIN) {
*flags |= ZAP_READ;
}
if (inflags & POLLOUT) {
*flags |= ZAP_WRITE;
}
if (inflags & POLLPRI) {
*flags |= ZAP_EVENTS;
}
return ZAP_SUCCESS;
}
/**
* \brief Checks for events on a Wanpipe span
* \param span Span to check for events
* \param ms Time to wait for event
* \return Success if event is waiting or failure if not
*/
ZIO_SPAN_POLL_EVENT_FUNCTION(wanpipe_poll_event)
{
#ifdef LIBSANGOMA_VERSION
sangoma_wait_obj_t pfds[ZAP_MAX_CHANNELS_SPAN];
#else
struct pollfd pfds[ZAP_MAX_CHANNELS_SPAN];
#endif
uint32_t i, j = 0, k = 0, l = 0;
int objects=0;
int r;
for(i = 1; i <= span->chan_count; i++) {
zap_channel_t *zchan = span->channels[i];
#ifdef LIBSANGOMA_VERSION
sangoma_init_wait_obj(&pfds[j], zchan->sockfd , 1, 1, POLLPRI, SANGOMA_WAIT_OBJ);
#else
memset(&pfds[j], 0, sizeof(pfds[j]));
pfds[j].fd = span->channels[i]->sockfd;
pfds[j].events = POLLPRI;
#endif
objects++;
/* The driver probably should be able to do this wink/flash/ringing by itself this is sort of a hack to make it work! */
if (zap_test_flag(zchan, ZAP_CHANNEL_WINK) || zap_test_flag(zchan, ZAP_CHANNEL_FLASH)) {
l = 5;
}
j++;
if (zap_test_flag(zchan, ZAP_CHANNEL_RINGING)) {
l = 5;
}
if (zap_test_flag(zchan, ZAP_CHANNEL_RINGING) && zap_current_time_in_ms() >= zchan->ring_time) {
wanpipe_tdm_api_t tdm_api;
int err;
memset(&tdm_api, 0, sizeof(tdm_api));
if (zap_test_pflag(zchan, WP_RINGING)) {
err=sangoma_tdm_txsig_offhook(zchan->sockfd,&tdm_api);
if (err) {
snprintf(zchan->last_error, sizeof(zchan->last_error), "Ring-off Failed");
return ZAP_FAIL;
}
zap_clear_pflag_locked(zchan, WP_RINGING);
zchan->ring_time = zap_current_time_in_ms() + wp_globals.ring_off_ms;
} else {
err=sangoma_tdm_txsig_start(zchan->sockfd,&tdm_api);
if (err) {
snprintf(zchan->last_error, sizeof(zchan->last_error), "Ring Failed");
return ZAP_FAIL;
}
zap_set_pflag_locked(zchan, WP_RINGING);
zchan->ring_time = zap_current_time_in_ms() + wp_globals.ring_on_ms;
}
}
}
if (l) {
ms = l;
}
#ifdef LIBSANGOMA_VERSION
r = sangoma_socket_waitfor_many(pfds,objects,ms);
#else
r = poll(pfds, j, ms);
#endif
if (r == 0) {
return l ? ZAP_SUCCESS : ZAP_TIMEOUT;
} else if (r < 0) {
snprintf(span->last_error, sizeof(span->last_error), "%s", strerror(errno));
return ZAP_FAIL;
}
for(i = 1; i <= span->chan_count; i++) {
zap_channel_t *zchan = span->channels[i];
#ifdef LIBSANGOMA_VERSION
if (pfds[i-1].flags_out & POLLPRI) {
#else
if (pfds[i-1].revents & POLLPRI) {
#endif
zap_set_flag(zchan, ZAP_CHANNEL_EVENT);
zchan->last_event_time = zap_current_time_in_ms();
k++;
}
}
return k ? ZAP_SUCCESS : ZAP_FAIL;
}
/**
* \brief Gets alarms from a Wanpipe Channel
* \param zchan Channel to get alarms from
* \return Success or failure
*/
static ZIO_GET_ALARMS_FUNCTION(wanpipe_get_alarms)
{
wanpipe_tdm_api_t tdm_api;
unsigned int alarms = 0;
int err;
memset(&tdm_api,0,sizeof(tdm_api));
#ifdef LIBSANGOMA_VERSION
if ((err = sangoma_tdm_get_fe_alarms(zchan->sockfd, &tdm_api, &alarms))) {
snprintf(zchan->last_error, sizeof(zchan->last_error), "ioctl failed (%s)", strerror(errno));
snprintf(zchan->span->last_error, sizeof(zchan->span->last_error), "ioctl failed (%s)", strerror(errno));
return ZAP_FAIL;
}
#else
if ((err = sangoma_tdm_get_fe_alarms(zchan->sockfd, &tdm_api)) < 0){
snprintf(zchan->last_error, sizeof(zchan->last_error), "ioctl failed (%s)", strerror(errno));
snprintf(zchan->span->last_error, sizeof(zchan->span->last_error), "ioctl failed (%s)", strerror(errno));
return ZAP_FAIL;
}
alarms = tdm_api.wp_tdm_cmd.fe_alarms;
#endif
zchan->alarm_flags = alarms ? ZAP_ALARM_RED : ZAP_ALARM_NONE;
return ZAP_SUCCESS;
}
/**
* \brief Retrieves an event from a wanpipe span
* \param span Span to retrieve event from
* \param event Openzap event to return
* \return Success or failure
*/
ZIO_SPAN_NEXT_EVENT_FUNCTION(wanpipe_next_event)
{
uint32_t i,err;
zap_oob_event_t event_id;
for(i = 1; i <= span->chan_count; i++) {
if (span->channels[i]->last_event_time && !zap_test_flag(span->channels[i], ZAP_CHANNEL_EVENT)) {
uint32_t diff = (uint32_t)(zap_current_time_in_ms() - span->channels[i]->last_event_time);
/* XX printf("%u %u %u\n", diff, (unsigned)zap_current_time_in_ms(), (unsigned)span->channels[i]->last_event_time); */
if (zap_test_flag(span->channels[i], ZAP_CHANNEL_WINK)) {
if (diff > wp_globals.wink_ms) {
zap_clear_flag_locked(span->channels[i], ZAP_CHANNEL_WINK);
zap_clear_flag_locked(span->channels[i], ZAP_CHANNEL_FLASH);
zap_set_flag_locked(span->channels[i], ZAP_CHANNEL_OFFHOOK);
event_id = ZAP_OOB_OFFHOOK;
goto event;
}
}
if (zap_test_flag(span->channels[i], ZAP_CHANNEL_FLASH)) {
if (diff > wp_globals.flash_ms) {
zap_clear_flag_locked(span->channels[i], ZAP_CHANNEL_FLASH);
zap_clear_flag_locked(span->channels[i], ZAP_CHANNEL_WINK);
zap_clear_flag_locked(span->channels[i], ZAP_CHANNEL_OFFHOOK);
event_id = ZAP_OOB_ONHOOK;
if (span->channels[i]->type == ZAP_CHAN_TYPE_FXO) {
zap_channel_t *zchan = span->channels[i];
wanpipe_tdm_api_t tdm_api;
memset(&tdm_api, 0, sizeof(tdm_api));
sangoma_tdm_txsig_onhook(zchan->sockfd,&tdm_api);
}
goto event;
}
}
}
if (zap_test_flag(span->channels[i], ZAP_CHANNEL_EVENT)) {
wanpipe_tdm_api_t tdm_api;
zap_channel_t *zchan = span->channels[i];
memset(&tdm_api, 0, sizeof(tdm_api));
zap_clear_flag(span->channels[i], ZAP_CHANNEL_EVENT);
err=sangoma_tdm_read_event(zchan->sockfd,&tdm_api);
if (err != ZAP_SUCCESS) {
snprintf(span->last_error, sizeof(span->last_error), "%s", strerror(errno));
return ZAP_FAIL;
}
switch(tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_type) {
case WP_TDMAPI_EVENT_LINK_STATUS:
{
switch(tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_link_status) {
case WP_TDMAPI_EVENT_LINK_STATUS_CONNECTED:
event_id = ZAP_OOB_ALARM_CLEAR;
break;
default:
event_id = ZAP_OOB_ALARM_TRAP;
break;
};
}
break;
case WP_TDMAPI_EVENT_RXHOOK:
{
if (span->channels[i]->type == ZAP_CHAN_TYPE_FXS) {
event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_hook_state & WP_TDMAPI_EVENT_RXHOOK_OFF ? ZAP_OOB_OFFHOOK : ZAP_OOB_ONHOOK;
if (event_id == ZAP_OOB_OFFHOOK) {
if (zap_test_flag(span->channels[i], ZAP_CHANNEL_FLASH)) {
zap_clear_flag_locked(span->channels[i], ZAP_CHANNEL_FLASH);
zap_clear_flag_locked(span->channels[i], ZAP_CHANNEL_WINK);
event_id = ZAP_OOB_FLASH;
goto event;
} else {
zap_set_flag_locked(span->channels[i], ZAP_CHANNEL_WINK);
}
} else {
if (zap_test_flag(span->channels[i], ZAP_CHANNEL_WINK)) {
zap_clear_flag_locked(span->channels[i], ZAP_CHANNEL_WINK);
zap_clear_flag_locked(span->channels[i], ZAP_CHANNEL_FLASH);
event_id = ZAP_OOB_WINK;
goto event;
} else {
zap_set_flag_locked(span->channels[i], ZAP_CHANNEL_FLASH);
}
}
continue;
} else {
int err;
zap_channel_t *zchan = span->channels[i];
err=sangoma_tdm_txsig_onhook(zchan->sockfd,&tdm_api);
if (err) {
snprintf(span->channels[i]->last_error, sizeof(span->channels[i]->last_error), "ONHOOK Failed");
return ZAP_FAIL;
}
event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_hook_state & WP_TDMAPI_EVENT_RXHOOK_OFF ? ZAP_OOB_ONHOOK : ZAP_OOB_NOOP;
}
}
break;
case WP_TDMAPI_EVENT_RING_DETECT:
{
event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_ring_state == WP_TDMAPI_EVENT_RING_PRESENT ? ZAP_OOB_RING_START : ZAP_OOB_RING_STOP;
}
break;
case WP_TDMAPI_EVENT_RING_TRIP_DETECT:
{
event_id = tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_ring_state == WP_TDMAPI_EVENT_RING_PRESENT ? ZAP_OOB_ONHOOK : ZAP_OOB_OFFHOOK;
}
break;
case WP_TDMAPI_EVENT_RBS:
{
event_id = ZAP_OOB_CAS_BITS_CHANGE;
/* save the CAS bits, user should retrieve it with ZAP_COMMAND_GET_CAS_BITS
is there a best play to store this? instead of adding cas_bits member to zap_chan? */
span->channels[i]->cas_bits = wanpipe_swap_bits(tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_rbs_bits);
}
break;
case WP_TDMAPI_EVENT_DTMF:
{
char tmp_dtmf[2] = { tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_dtmf_digit, 0 };
event_id = ZAP_OOB_NOOP;
//zap_log(ZAP_LOG_DEBUG, "%d:%d queue hardware dtmf %s\n", zchan->span_id, zchan->chan_id, tmp_dtmf);
if (tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_dtmf_type == WAN_EC_TONE_STOP) {
zap_channel_queue_dtmf(zchan, tmp_dtmf);
}
}
break;
case WP_TDMAPI_EVENT_ALARM:
event_id = ZAP_OOB_NOOP;
break;
default:
{
zap_log(ZAP_LOG_WARNING, "Unhandled event %d\n", tdm_api.wp_tdm_cmd.event.wp_tdm_api_event_type);
event_id = ZAP_OOB_INVALID;
}
break;
}
event:
span->channels[i]->last_event_time = 0;
span->event_header.e_type = ZAP_EVENT_OOB;
span->event_header.enum_id = event_id;
span->event_header.channel = span->channels[i];
*event = &span->event_header;
return ZAP_SUCCESS;
}
}
return ZAP_FAIL;
}
/**
* \brief Destroys a Wanpipe Channel
* \param zchan Channel to destroy
* \return Success
*/
static ZIO_CHANNEL_DESTROY_FUNCTION(wanpipe_channel_destroy)
{
if (zchan->sockfd > -1) {
close(zchan->sockfd);
zchan->sockfd = WP_INVALID_SOCKET;
}
return ZAP_SUCCESS;
}
/**
* \brief Loads wanpipe IO module
* \param zio Openzap IO interface
* \return Success
*/
static ZIO_IO_LOAD_FUNCTION(wanpipe_init)
{
assert(zio != NULL);
memset(&wanpipe_interface, 0, sizeof(wanpipe_interface));
wp_globals.codec_ms = 20;
wp_globals.wink_ms = 150;
wp_globals.flash_ms = 750;
wp_globals.ring_on_ms = 2000;
wp_globals.ring_off_ms = 4000;
wanpipe_interface.name = "wanpipe";
wanpipe_interface.configure_span = wanpipe_configure_span;
wanpipe_interface.configure = wanpipe_configure;
wanpipe_interface.open = wanpipe_open;
wanpipe_interface.close = wanpipe_close;
wanpipe_interface.command = wanpipe_command;
wanpipe_interface.wait = wanpipe_wait;
wanpipe_interface.read = wanpipe_read;
wanpipe_interface.write = wanpipe_write;
#ifndef WIN32
wanpipe_interface.poll_event = wanpipe_poll_event;
#endif
wanpipe_interface.next_event = wanpipe_next_event;
wanpipe_interface.channel_destroy = wanpipe_channel_destroy;
wanpipe_interface.get_alarms = wanpipe_get_alarms;
*zio = &wanpipe_interface;
return ZAP_SUCCESS;
}
/**
* \brief Unloads wanpipe IO module
* \return Success
*/
static ZIO_IO_UNLOAD_FUNCTION(wanpipe_destroy)
{
memset(&wanpipe_interface, 0, sizeof(wanpipe_interface));
return ZAP_SUCCESS;
}
/**
* \brief Openzap wanpipe IO module definition
*/
EX_DECLARE_DATA zap_module_t zap_module = {
"wanpipe",
wanpipe_init,
wanpipe_destroy,
};
/* 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 expandtab:
*/