freeswitch/libs/openzap/src/ozmod/ozmod_zt/ozmod_zt.c

1254 lines
32 KiB
C

/*
* Copyright (c) 2007-2014, 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.
*/
#include "openzap.h"
#include "ozmod_zt.h"
/**
* \brief Zaptel globals
*/
static struct {
uint32_t codec_ms;
uint32_t wink_ms;
uint32_t flash_ms;
uint32_t eclevel;
uint32_t etlevel;
float rxgain;
float txgain;
} zt_globals;
/**
* \brief General IOCTL codes
*/
struct ioctl_codes {
int GET_BLOCKSIZE;
int SET_BLOCKSIZE;
int FLUSH;
int SYNC;
int GET_PARAMS;
int SET_PARAMS;
int HOOK;
int GETEVENT;
int IOMUX;
int SPANSTAT;
int MAINT;
int GETCONF;
int SETCONF;
int CONFLINK;
int CONFDIAG;
int GETGAINS;
int SETGAINS;
int SPANCONFIG;
int CHANCONFIG;
int SET_BUFINFO;
int GET_BUFINFO;
int AUDIOMODE;
int ECHOCANCEL;
int HDLCRAWMODE;
int HDLCFCSMODE;
int SPECIFY;
int SETLAW;
int SETLINEAR;
int GETCONFMUTE;
int ECHOTRAIN;
int SETTXBITS;
int GETRXBITS;
};
/**
* \brief Zaptel IOCTL codes
*/
static struct ioctl_codes zt_ioctl_codes = {
.GET_BLOCKSIZE = ZT_GET_BLOCKSIZE,
.SET_BLOCKSIZE = ZT_SET_BLOCKSIZE,
.FLUSH = ZT_FLUSH,
.SYNC = ZT_SYNC,
.GET_PARAMS = ZT_GET_PARAMS,
.SET_PARAMS = ZT_SET_PARAMS,
.HOOK = ZT_HOOK,
.GETEVENT = ZT_GETEVENT,
.IOMUX = ZT_IOMUX,
.SPANSTAT = ZT_SPANSTAT,
.MAINT = ZT_MAINT,
.GETCONF = ZT_GETCONF,
.SETCONF = ZT_SETCONF,
.CONFLINK = ZT_CONFLINK,
.CONFDIAG = ZT_CONFDIAG,
.GETGAINS = ZT_GETGAINS,
.SETGAINS = ZT_SETGAINS,
.SPANCONFIG = ZT_SPANCONFIG,
.CHANCONFIG = ZT_CHANCONFIG,
.SET_BUFINFO = ZT_SET_BUFINFO,
.GET_BUFINFO = ZT_GET_BUFINFO,
.AUDIOMODE = ZT_AUDIOMODE,
.ECHOCANCEL = ZT_ECHOCANCEL,
.HDLCRAWMODE = ZT_HDLCRAWMODE,
.HDLCFCSMODE = ZT_HDLCFCSMODE,
.SPECIFY = ZT_SPECIFY,
.SETLAW = ZT_SETLAW,
.SETLINEAR = ZT_SETLINEAR,
.GETCONFMUTE = ZT_GETCONFMUTE,
.ECHOTRAIN = ZT_ECHOTRAIN,
.SETTXBITS = ZT_SETTXBITS,
.GETRXBITS = ZT_GETRXBITS
};
/**
* \brief Dahdi IOCTL codes
*/
static struct ioctl_codes dahdi_ioctl_codes = {
.GET_BLOCKSIZE = DAHDI_GET_BLOCKSIZE,
.SET_BLOCKSIZE = DAHDI_SET_BLOCKSIZE,
.FLUSH = DAHDI_FLUSH,
.SYNC = DAHDI_SYNC,
.GET_PARAMS = DAHDI_GET_PARAMS,
.SET_PARAMS = DAHDI_SET_PARAMS,
.HOOK = DAHDI_HOOK,
.GETEVENT = DAHDI_GETEVENT,
.IOMUX = DAHDI_IOMUX,
.SPANSTAT = DAHDI_SPANSTAT,
.MAINT = DAHDI_MAINT,
.GETCONF = DAHDI_GETCONF,
.SETCONF = DAHDI_SETCONF,
.CONFLINK = DAHDI_CONFLINK,
.CONFDIAG = DAHDI_CONFDIAG,
.GETGAINS = DAHDI_GETGAINS,
.SETGAINS = DAHDI_SETGAINS,
.SPANCONFIG = DAHDI_SPANCONFIG,
.CHANCONFIG = DAHDI_CHANCONFIG,
.SET_BUFINFO = DAHDI_SET_BUFINFO,
.GET_BUFINFO = DAHDI_GET_BUFINFO,
.AUDIOMODE = DAHDI_AUDIOMODE,
.ECHOCANCEL = DAHDI_ECHOCANCEL,
.HDLCRAWMODE = DAHDI_HDLCRAWMODE,
.HDLCFCSMODE = DAHDI_HDLCFCSMODE,
.SPECIFY = DAHDI_SPECIFY,
.SETLAW = DAHDI_SETLAW,
.SETLINEAR = DAHDI_SETLINEAR,
.GETCONFMUTE = DAHDI_GETCONFMUTE,
.ECHOTRAIN = DAHDI_ECHOTRAIN,
.SETTXBITS = DAHDI_SETTXBITS,
.GETRXBITS = DAHDI_GETRXBITS
};
#define ZT_INVALID_SOCKET -1
static struct ioctl_codes codes;
static const char *ctlpath = NULL;
static const char *chanpath = NULL;
static const char dahdi_ctlpath[] = "/dev/dahdi/ctl";
static const char dahdi_chanpath[] = "/dev/dahdi/channel";
static const char zt_ctlpath[] = "/dev/zap/ctl";
static const char zt_chanpath[] = "/dev/zap/channel";
static zap_socket_t CONTROL_FD = ZT_INVALID_SOCKET;
ZIO_SPAN_NEXT_EVENT_FUNCTION(zt_next_event);
ZIO_SPAN_POLL_EVENT_FUNCTION(zt_poll_event);
/**
* \brief Initialises codec, and rx/tx gains
* \param g Structure for gains to be initialised
* \param rxgain RX gain value
* \param txgain TX gain value
* \param codec Codec
*/
static void zt_build_gains(struct zt_gains *g, float rxgain, float txgain, int codec)
{
int j;
int k;
float linear_rxgain = pow(10.0, rxgain / 20.0);
float linear_txgain = pow(10.0, txgain / 20.0);
switch (codec) {
case ZAP_CODEC_ALAW:
for (j = 0; j < (sizeof(g->receive_gain) / sizeof(g->receive_gain[0])); j++) {
if (rxgain) {
k = (int) (((float) alaw_to_linear(j)) * linear_rxgain);
if (k > 32767) k = 32767;
if (k < -32767) k = -32767;
g->receive_gain[j] = linear_to_alaw(k);
} else {
g->receive_gain[j] = j;
}
if (txgain) {
k = (int) (((float) alaw_to_linear(j)) * linear_txgain);
if (k > 32767) k = 32767;
if (k < -32767) k = -32767;
g->transmit_gain[j] = linear_to_alaw(k);
} else {
g->transmit_gain[j] = j;
}
}
break;
case ZAP_CODEC_ULAW:
for (j = 0; j < (sizeof(g->receive_gain) / sizeof(g->receive_gain[0])); j++) {
if (rxgain) {
k = (int) (((float) ulaw_to_linear(j)) * linear_rxgain);
if (k > 32767) k = 32767;
if (k < -32767) k = -32767;
g->receive_gain[j] = linear_to_ulaw(k);
} else {
g->receive_gain[j] = j;
}
if (txgain) {
k = (int) (((float) ulaw_to_linear(j)) * linear_txgain);
if (k > 32767) k = 32767;
if (k < -32767) k = -32767;
g->transmit_gain[j] = linear_to_ulaw(k);
} else {
g->transmit_gain[j] = j;
}
}
break;
}
}
/**
* \brief Initialises a range of zaptel channels
* \param span Openzap span
* \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 zt_open_range(zap_span_t *span, unsigned start, unsigned end, zap_chan_type_t type, char *name, char *number, unsigned char cas_bits)
{
unsigned configured = 0, x;
zt_params_t ztp;
memset(&ztp, 0, sizeof(ztp));
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 *zchan;
zap_socket_t sockfd = ZT_INVALID_SOCKET;
int len;
sockfd = open(chanpath, O_RDWR);
if (sockfd != ZT_INVALID_SOCKET && zap_span_add_channel(span, sockfd, type, &zchan) == ZAP_SUCCESS) {
if (ioctl(sockfd, codes.SPECIFY, &x)) {
zap_log(ZAP_LOG_ERROR, "failure configuring device %s chan %d fd %d (%s)\n", chanpath, x, sockfd, strerror(errno));
close(sockfd);
continue;
}
if (zchan->type == ZAP_CHAN_TYPE_DQ921) {
struct zt_bufferinfo binfo;
memset(&binfo, 0, sizeof(binfo));
binfo.txbufpolicy = 0;
binfo.rxbufpolicy = 0;
binfo.numbufs = 32;
binfo.bufsize = 1024;
if (ioctl(sockfd, codes.SET_BUFINFO, &binfo)) {
zap_log(ZAP_LOG_ERROR, "failure configuring device %s as OpenZAP device %d:%d fd:%d\n", chanpath, zchan->span_id, zchan->chan_id, sockfd);
close(sockfd);
continue;
}
}
if (type == ZAP_CHAN_TYPE_FXS || type == ZAP_CHAN_TYPE_FXO) {
struct zt_chanconfig cc;
memset(&cc, 0, sizeof(cc));
cc.chan = cc.master = x;
switch(type) {
case ZAP_CHAN_TYPE_FXS:
{
switch(span->start_type) {
case ZAP_ANALOG_START_KEWL:
cc.sigtype = ZT_SIG_FXOKS;
break;
case ZAP_ANALOG_START_LOOP:
cc.sigtype = ZT_SIG_FXOLS;
break;
case ZAP_ANALOG_START_GROUND:
cc.sigtype = ZT_SIG_FXOGS;
break;
default:
break;
}
}
break;
case ZAP_CHAN_TYPE_FXO:
{
switch(span->start_type) {
case ZAP_ANALOG_START_KEWL:
cc.sigtype = ZT_SIG_FXSKS;
break;
case ZAP_ANALOG_START_LOOP:
cc.sigtype = ZT_SIG_FXSLS;
break;
case ZAP_ANALOG_START_GROUND:
cc.sigtype = ZT_SIG_FXSGS;
break;
default:
break;
}
}
break;
default:
break;
}
if (ioctl(CONTROL_FD, codes.CHANCONFIG, &cc)) {
zap_log(ZAP_LOG_WARNING, "this ioctl fails on older zaptel but is harmless if you used ztcfg\n[device %s chan %d fd %d (%s)]\n", chanpath, x, CONTROL_FD, strerror(errno));
}
}
if (type == ZAP_CHAN_TYPE_CAS) {
struct zt_chanconfig cc;
memset(&cc, 0, sizeof(cc));
cc.chan = cc.master = x;
cc.sigtype = ZT_SIG_CAS;
cc.idlebits = cas_bits;
if (ioctl(CONTROL_FD, codes.CHANCONFIG, &cc)) {
zap_log(ZAP_LOG_ERROR, "failure configuring device %s as OpenZAP device %d:%d fd:%d err:%s", chanpath, zchan->span_id, zchan->chan_id, sockfd, strerror(errno));
close(sockfd);
continue;
}
}
if (zchan->type != ZAP_CHAN_TYPE_DQ921 && zchan->type != ZAP_CHAN_TYPE_DQ931) {
len = zt_globals.codec_ms * 8;
if (ioctl(zchan->sockfd, codes.SET_BLOCKSIZE, &len)) {
zap_log(ZAP_LOG_ERROR, "failure configuring device %s as OpenZAP device %d:%d fd:%d err:%s\n",
chanpath, zchan->span_id, zchan->chan_id, sockfd, strerror(errno));
close(sockfd);
continue;
}
zchan->packet_len = len;
zchan->effective_interval = zchan->native_interval = zchan->packet_len / 8;
if (zchan->effective_codec == ZAP_CODEC_SLIN) {
zchan->packet_len *= 2;
}
}
if (ioctl(sockfd, codes.GET_PARAMS, &ztp) < 0) {
zap_log(ZAP_LOG_ERROR, "failure configuring device %s as OpenZAP device %d:%d fd:%d\n", chanpath, zchan->span_id, zchan->chan_id, sockfd);
close(sockfd);
continue;
}
if (zchan->type == ZAP_CHAN_TYPE_DQ921) {
if (
(ztp.sig_type != ZT_SIG_HDLCRAW) &&
(ztp.sig_type != ZT_SIG_HDLCFCS) &&
(ztp.sig_type != ZT_SIG_HARDHDLC)
) {
zap_log(ZAP_LOG_ERROR, "Failure configuring device %s as OpenZAP device %d:%d fd:%d, hardware signaling is not HDLC, fix your Zap/DAHDI configuration!\n", chanpath, zchan->span_id, zchan->chan_id, sockfd);
close(sockfd);
continue;
}
}
zap_log(ZAP_LOG_INFO, "configuring device %s channel %d as OpenZAP device %d:%d fd:%d\n", chanpath, x, zchan->span_id, zchan->chan_id, sockfd);
zchan->rate = 8000;
zchan->physical_span_id = ztp.span_no;
zchan->physical_chan_id = ztp.chan_no;
if (type == ZAP_CHAN_TYPE_FXS || type == ZAP_CHAN_TYPE_FXO || type == ZAP_CHAN_TYPE_EM || type == ZAP_CHAN_TYPE_B) {
if (ztp.g711_type == ZT_G711_ALAW) {
zchan->native_codec = zchan->effective_codec = ZAP_CODEC_ALAW;
} else if (ztp.g711_type == ZT_G711_MULAW) {
zchan->native_codec = zchan->effective_codec = ZAP_CODEC_ULAW;
} else {
int type;
if (zchan->span->trunk_type == ZAP_TRUNK_E1) {
type = ZAP_CODEC_ALAW;
} else {
type = ZAP_CODEC_ULAW;
}
zchan->native_codec = zchan->effective_codec = type;
}
}
ztp.wink_time = zt_globals.wink_ms;
ztp.flash_time = zt_globals.flash_ms;
if (ioctl(sockfd, codes.SET_PARAMS, &ztp) < 0) {
zap_log(ZAP_LOG_ERROR, "failure configuring device %s as OpenZAP device %d:%d fd:%d\n", chanpath, zchan->span_id, zchan->chan_id, sockfd);
close(sockfd);
continue;
}
if (!zap_strlen_zero(name)) {
zap_copy_string(zchan->chan_name, name, sizeof(zchan->chan_name));
}
if (!zap_strlen_zero(number)) {
zap_copy_string(zchan->chan_number, number, sizeof(zchan->chan_number));
}
configured++;
} else {
zap_log(ZAP_LOG_ERROR, "failure configuring device %s\n", chanpath);
}
}
return configured;
}
/**
* \brief Initialises an openzap zaptel 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(zt_configure_span)
{
int items, i;
char *mydata, *item_list[10];
char *ch, *mx;
unsigned char cas_bits = 0;
int channo;
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++) {
ch = item_list[i];
if (!(ch)) {
zap_log(ZAP_LOG_ERROR, "Invalid input\n");
continue;
}
channo = atoi(ch);
if (channo < 0) {
zap_log(ZAP_LOG_ERROR, "Invalid channel 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 += zt_open_range(span, channo, top, type, name, number, cas_bits);
}
free(mydata);
return configured;
}
/**
* \brief Process configuration variable for a zaptel 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(zt_configure)
{
int num;
float fnum;
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 {
zt_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 {
zt_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 {
zt_globals.flash_ms = num;
}
} else if (!strcasecmp(var, "echo_cancel_level")) {
num = atoi(val);
if (num < 0 || num > 256) {
zap_log(ZAP_LOG_WARNING, "invalid echo can val at line %d\n", lineno);
} else {
zt_globals.eclevel = num;
}
} else if (!strcasecmp(var, "echo_train_level")) {
if (zt_globals.eclevel < 1) {
zap_log(ZAP_LOG_WARNING, "can't set echo train level without setting echo cancel level first at line %d\n", lineno);
} else {
num = atoi(val);
if (num < 0 || num > 256) {
zap_log(ZAP_LOG_WARNING, "invalid echo can val at line %d\n", lineno);
} else {
zt_globals.etlevel = num;
}
}
} else if (!strcasecmp(var, "rxgain")) {
fnum = (float)atof(val);
if (fnum < -100.0 || fnum > 100.0) {
zap_log(ZAP_LOG_WARNING, "invalid rxgain val at line %d\n", lineno);
} else {
zt_globals.rxgain = fnum;
zap_log(ZAP_LOG_INFO, "Setting rxgain val to %f\n", fnum);
}
} else if (!strcasecmp(var, "txgain")) {
fnum = (float)atof(val);
if (fnum < -100.0 || fnum > 100.0) {
zap_log(ZAP_LOG_WARNING, "invalid txgain val at line %d\n", lineno);
} else {
zt_globals.txgain = fnum;
zap_log(ZAP_LOG_INFO, "Setting txgain val to %f\n", fnum);
}
}
}
return ZAP_SUCCESS;
}
/**
* \brief Opens a zaptel channel
* \param zchan Channel to open
* \return Success or failure
*/
static ZIO_OPEN_FUNCTION(zt_open)
{
zap_channel_set_feature(zchan, ZAP_CHANNEL_FEATURE_INTERVAL);
if (zchan->type == ZAP_CHAN_TYPE_DQ921 || zchan->type == ZAP_CHAN_TYPE_DQ931) {
zchan->native_codec = zchan->effective_codec = ZAP_CODEC_NONE;
} else {
int blocksize = zt_globals.codec_ms * (zchan->rate / 1000);
int err;
if ((err = ioctl(zchan->sockfd, codes.SET_BLOCKSIZE, &blocksize))) {
snprintf(zchan->last_error, sizeof(zchan->last_error), "%s", strerror(errno));
return ZAP_FAIL;
} else {
zchan->effective_interval = zchan->native_interval;
zchan->packet_len = blocksize;
zchan->native_codec = zchan->effective_codec;
}
if (zchan->type == ZAP_CHAN_TYPE_B) {
int one = 1;
if (ioctl(zchan->sockfd, codes.AUDIOMODE, &one)) {
snprintf(zchan->last_error, sizeof(zchan->last_error), "%s", strerror(errno));
zap_log(ZAP_LOG_ERROR, "%s\n", zchan->last_error);
return ZAP_FAIL;
}
}
if (zt_globals.rxgain || zt_globals.txgain) {
struct zt_gains gains;
memset(&gains, 0, sizeof(gains));
gains.chan_no = zchan->physical_chan_id;
zt_build_gains(&gains, zt_globals.rxgain, zt_globals.txgain, zchan->native_codec);
if(zt_globals.rxgain)
zap_log(ZAP_LOG_INFO, "Setting rxgain to %f on channel %d\n", zt_globals.rxgain, gains.chan_no);
if(zt_globals.txgain)
zap_log(ZAP_LOG_INFO, "Setting txgain to %f on channel %d\n", zt_globals.txgain, gains.chan_no);
if (ioctl(zchan->sockfd, codes.SETGAINS, &gains) < 0) {
zap_log(ZAP_LOG_ERROR, "failure configuring device %s as OpenZAP device %d:%d fd:%d\n", chanpath, zchan->span_id, zchan->chan_id, zchan->sockfd);
}
}
if (zt_globals.eclevel >= 0) {
int len = zt_globals.eclevel;
if (len) {
zap_log(ZAP_LOG_INFO, "Setting echo cancel to %d taps for %d:%d\n", len, zchan->span_id, zchan->chan_id);
} else {
zap_log(ZAP_LOG_INFO, "Disable echo cancel for %d:%d\n", zchan->span_id, zchan->chan_id);
}
if (ioctl(zchan->sockfd, codes.ECHOCANCEL, &len)) {
zap_log(ZAP_LOG_WARNING, "Echo cancel not available for %d:%d\n", zchan->span_id, zchan->chan_id);
} else if (zt_globals.etlevel > 0) {
len = zt_globals.etlevel;
if (ioctl(zchan->sockfd, codes.ECHOTRAIN, &len)) {
zap_log(ZAP_LOG_WARNING, "Echo training not available for %d:%d\n", zchan->span_id, zchan->chan_id);
}
}
}
}
return ZAP_SUCCESS;
}
/**
* \brief Closes zaptel channel
* \param zchan Channel to close
* \return Success
*/
static ZIO_CLOSE_FUNCTION(zt_close)
{
return ZAP_SUCCESS;
}
/**
* \brief Executes an Openzap command on a zaptel 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(zt_command)
{
zt_params_t ztp;
int err = 0;
memset(&ztp, 0, sizeof(ztp));
switch(command) {
case ZAP_COMMAND_ENABLE_ECHOCANCEL:
{
int level = ZAP_COMMAND_OBJ_INT;
err = ioctl(zchan->sockfd, codes.ECHOCANCEL, &level);
ZAP_COMMAND_OBJ_INT = level;
}
case ZAP_COMMAND_DISABLE_ECHOCANCEL:
{
int level = 0;
err = ioctl(zchan->sockfd, codes.ECHOCANCEL, &level);
ZAP_COMMAND_OBJ_INT = level;
}
break;
case ZAP_COMMAND_ENABLE_ECHOTRAIN:
{
int level = ZAP_COMMAND_OBJ_INT;
err = ioctl(zchan->sockfd, codes.ECHOTRAIN, &level);
ZAP_COMMAND_OBJ_INT = level;
}
case ZAP_COMMAND_DISABLE_ECHOTRAIN:
{
int level = 0;
err = ioctl(zchan->sockfd, codes.ECHOTRAIN, &level);
ZAP_COMMAND_OBJ_INT = level;
}
break;
case ZAP_COMMAND_OFFHOOK:
{
int command = ZT_OFFHOOK;
if (ioctl(zchan->sockfd, codes.HOOK, &command)) {
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:
{
int command = ZT_ONHOOK;
if (ioctl(zchan->sockfd, codes.HOOK, &command)) {
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_FLASH:
{
int command = ZT_FLASH;
if (ioctl(zchan->sockfd, codes.HOOK, &command)) {
snprintf(zchan->last_error, sizeof(zchan->last_error), "FLASH Failed");
return ZAP_FAIL;
}
}
break;
case ZAP_COMMAND_WINK:
{
int command = ZT_WINK;
if (ioctl(zchan->sockfd, codes.HOOK, &command)) {
snprintf(zchan->last_error, sizeof(zchan->last_error), "WINK Failed");
return ZAP_FAIL;
}
}
break;
case ZAP_COMMAND_GENERATE_RING_ON:
{
int command = ZT_RING;
if (ioctl(zchan->sockfd, codes.HOOK, &command)) {
snprintf(zchan->last_error, sizeof(zchan->last_error), "Ring Failed");
return ZAP_FAIL;
}
zap_set_flag_locked(zchan, ZAP_CHANNEL_RINGING);
}
break;
case ZAP_COMMAND_GENERATE_RING_OFF:
{
int command = ZT_RINGOFF;
if (ioctl(zchan->sockfd, codes.HOOK, &command)) {
snprintf(zchan->last_error, sizeof(zchan->last_error), "Ring-off failed");
return ZAP_FAIL;
}
zap_clear_flag_locked(zchan, ZAP_CHANNEL_RINGING);
}
break;
case ZAP_COMMAND_GET_INTERVAL:
{
if (!(err = ioctl(zchan->sockfd, codes.GET_BLOCKSIZE, &zchan->packet_len))) {
zchan->native_interval = zchan->packet_len / 8;
if (zchan->effective_codec == ZAP_CODEC_SLIN) {
zchan->packet_len *= 2;
}
ZAP_COMMAND_OBJ_INT = zchan->native_interval;
}
}
break;
case ZAP_COMMAND_SET_INTERVAL:
{
int interval = ZAP_COMMAND_OBJ_INT;
int len = interval * 8;
if (!(err = ioctl(zchan->sockfd, codes.SET_BLOCKSIZE, &len))) {
zchan->packet_len = len;
zchan->effective_interval = zchan->native_interval = zchan->packet_len / 8;
if (zchan->effective_codec == ZAP_CODEC_SLIN) {
zchan->packet_len *= 2;
}
}
}
break;
case ZAP_COMMAND_SET_CAS_BITS:
{
int bits = ZAP_COMMAND_OBJ_INT;
err = ioctl(zchan->sockfd, codes.SETTXBITS, &bits);
}
break;
case ZAP_COMMAND_GET_CAS_BITS:
{
err = ioctl(zchan->sockfd, codes.GETRXBITS, &zchan->rx_cas_bits);
if (!err) {
ZAP_COMMAND_OBJ_INT = zchan->rx_cas_bits;
}
}
break;
case ZAP_COMMAND_FLUSH_TX_BUFFERS:
{
int flushmode = ZT_FLUSH_WRITE;
err = ioctl(zchan->sockfd, codes.FLUSH, &flushmode);
}
break;
case ZAP_COMMAND_FLUSH_RX_BUFFERS:
{
int flushmode = ZT_FLUSH_READ;
err = ioctl(zchan->sockfd, codes.FLUSH, &flushmode);
}
break;
case ZAP_COMMAND_FLUSH_BUFFERS:
{
int flushmode = ZT_FLUSH_BOTH;
err = ioctl(zchan->sockfd, codes.FLUSH, &flushmode);
}
break;
default:
err = ZAP_NOTIMPL;
break;
};
if (err && err != ZAP_NOTIMPL) {
snprintf(zchan->last_error, sizeof(zchan->last_error), "%s", strerror(errno));
return ZAP_FAIL;
}
return err == 0 ? ZAP_SUCCESS : err;
}
/**
* \brief Gets alarms from a zaptel Channel
* \param zchan Channel to get alarms from
* \return Success or failure
*/
static ZIO_GET_ALARMS_FUNCTION(zt_get_alarms)
{
struct zt_spaninfo info;
memset(&info, 0, sizeof(info));
info.span_no = zchan->physical_span_id;
if (ioctl(CONTROL_FD, codes.SPANSTAT, &info)) {
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;
}
zchan->alarm_flags = info.alarms;
return ZAP_SUCCESS;
}
/**
* \brief Waits for an event on a zaptel 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(zt_wait)
{
int32_t inflags = 0;
int result;
struct pollfd pfds[1];
if (*flags & ZAP_READ) {
inflags |= POLLIN;
}
if (*flags & ZAP_WRITE) {
inflags |= POLLOUT;
}
if (*flags & ZAP_EVENTS) {
inflags |= POLLPRI;
}
memset(&pfds[0], 0, sizeof(pfds[0]));
pfds[0].fd = zchan->sockfd;
pfds[0].events = inflags;
result = poll(pfds, 1, to);
*flags = 0;
if (pfds[0].revents & POLLERR) {
result = -1;
}
if (result > 0) {
inflags = pfds[0].revents;
}
*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 zaptel 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(zt_poll_event)
{
struct pollfd pfds[ZAP_MAX_CHANNELS_SPAN];
uint32_t i, j = 0, k = 0;
int r;
for(i = 1; i <= span->chan_count; i++) {
memset(&pfds[j], 0, sizeof(pfds[j]));
pfds[j].fd = span->channels[i]->sockfd;
pfds[j].events = POLLPRI;
j++;
}
r = poll(pfds, j, ms);
if (r == 0) {
return ZAP_TIMEOUT;
} else if (r < 0 || (pfds[i-1].revents & POLLERR)) {
snprintf(span->last_error, sizeof(span->last_error), "%s", strerror(errno));
return ZAP_FAIL;
}
for(i = 1; i <= span->chan_count; i++) {
if (pfds[i-1].revents & POLLPRI) {
zap_set_flag(span->channels[i], ZAP_CHANNEL_EVENT);
span->channels[i]->last_event_time = zap_current_time_in_ms();
k++;
}
}
if (!k) {
snprintf(span->last_error, sizeof(span->last_error), "no matching descriptor");
}
return k ? ZAP_SUCCESS : ZAP_FAIL;
}
/**
* \brief Retrieves an event from a zaptel span
* \param span Span to retrieve event from
* \param event Openzap event to return
* \return Success or failure
*/
ZIO_SPAN_NEXT_EVENT_FUNCTION(zt_next_event)
{
uint32_t i, event_id = ZAP_OOB_INVALID;
zt_event_t zt_event_id = 0;
for(i = 1; i <= span->chan_count; i++) {
if (zap_test_flag(span->channels[i], ZAP_CHANNEL_EVENT)) {
zap_clear_flag(span->channels[i], ZAP_CHANNEL_EVENT);
if (ioctl(span->channels[i]->sockfd, codes.GETEVENT, &zt_event_id) == -1) {
snprintf(span->last_error, sizeof(span->last_error), "%s", strerror(errno));
return ZAP_FAIL;
}
switch(zt_event_id) {
case ZT_EVENT_RINGEROFF:
{
return ZAP_FAIL;
}
break;
case ZT_EVENT_RINGERON:
{
return ZAP_FAIL;
}
break;
case ZT_EVENT_RINGBEGIN:
{
event_id = ZAP_OOB_RING_START;
}
break;
case ZT_EVENT_ONHOOK:
{
event_id = ZAP_OOB_ONHOOK;
}
break;
case ZT_EVENT_WINKFLASH:
{
if (span->channels[i]->state == ZAP_CHANNEL_STATE_DOWN || span->channels[i]->state == ZAP_CHANNEL_STATE_DIALING) {
event_id = ZAP_OOB_WINK;
} else {
event_id = ZAP_OOB_FLASH;
}
}
break;
case ZT_EVENT_RINGOFFHOOK:
{
if (span->channels[i]->type == ZAP_CHAN_TYPE_FXS || (span->channels[i]->type == ZAP_CHAN_TYPE_EM && span->channels[i]->state != ZAP_CHANNEL_STATE_UP)) {
zap_set_flag_locked(span->channels[i], ZAP_CHANNEL_OFFHOOK);
event_id = ZAP_OOB_OFFHOOK;
} else if (span->channels[i]->type == ZAP_CHAN_TYPE_FXO) {
event_id = ZAP_OOB_RING_START;
} else {
event_id = ZAP_OOB_NOOP;
}
}
break;
case ZT_EVENT_ALARM:
{
zap_sigmsg_t sigmsg;
zap_channel_t *zchan = span->channels[i];
event_id = ZAP_OOB_ALARM_TRAP;
memset(&sigmsg, 0, sizeof(sigmsg));
sigmsg.chan_id = zchan->chan_id;
sigmsg.span_id = zchan->span_id;
sigmsg.channel = zchan;
sigmsg.event_id = ZAP_SIGEVENT_ALARM_TRAP;
zap_span_send_signal(zchan->span, &sigmsg);
}
break;
case ZT_EVENT_NOALARM:
{
zap_sigmsg_t sigmsg;
zap_channel_t *zchan = span->channels[i];
event_id = ZAP_OOB_ALARM_CLEAR;
memset(&sigmsg, 0, sizeof(sigmsg));
sigmsg.chan_id = zchan->chan_id;
sigmsg.span_id = zchan->span_id;
sigmsg.channel = zchan;
sigmsg.event_id = ZAP_SIGEVENT_ALARM_CLEAR;
zap_span_send_signal(zchan->span, &sigmsg);
}
break;
case ZT_EVENT_BITSCHANGED:
{
event_id = ZAP_OOB_CAS_BITS_CHANGE;
int bits = 0;
int err = ioctl(span->channels[i]->sockfd, codes.GETRXBITS, &bits);
if (err) {
return ZAP_FAIL;
}
span->channels[i]->rx_cas_bits = bits;
}
break;
default:
{
zap_log(ZAP_LOG_WARNING, "Unhandled event %d for %d:%d\n", zt_event_id, span->span_id, i);
event_id = ZAP_OOB_INVALID;
}
break;
}
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 Reads data from a zaptel 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(zt_read)
{
zap_ssize_t r = 0;
int errs = 0;
while (errs++ < 30) {
if ((r = read(zchan->sockfd, data, *datalen)) > 0) {
break;
}
zap_sleep(10);
if (r == 0) {
errs--;
}
}
if (r > 0) {
*datalen = r;
if (zchan->type == ZAP_CHAN_TYPE_DQ921) {
*datalen -= 2;
}
return ZAP_SUCCESS;
}
return r == 0 ? ZAP_TIMEOUT : ZAP_FAIL;
}
/**
* \brief Writes data to a zaptel 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(zt_write)
{
zap_ssize_t w = 0;
zap_size_t bytes = *datalen;
if (zchan->type == ZAP_CHAN_TYPE_DQ921) {
memset(data+bytes, 0, 2);
bytes += 2;
}
w = write(zchan->sockfd, data, bytes);
if (w >= 0) {
*datalen = w;
return ZAP_SUCCESS;
}
return ZAP_FAIL;
}
/**
* \brief Destroys a zaptel Channel
* \param zchan Channel to destroy
* \return Success
*/
static ZIO_CHANNEL_DESTROY_FUNCTION(zt_channel_destroy)
{
close(zchan->sockfd);
zchan->sockfd = ZT_INVALID_SOCKET;
return ZAP_SUCCESS;
}
/**
* \brief Global Openzap IO interface for zaptel
*/
static zap_io_interface_t zt_interface;
/**
* \brief Loads zaptel IO module
* \param zio Openzap IO interface
* \return Success or failure
*/
static ZIO_IO_LOAD_FUNCTION(zt_init)
{
assert(zio != NULL);
struct stat statbuf;
memset(&zt_interface, 0, sizeof(zt_interface));
memset(&zt_globals, 0, sizeof(zt_globals));
if (!stat(zt_ctlpath, &statbuf)) {
zap_log(ZAP_LOG_NOTICE, "Using Zaptel control device\n");
ctlpath = zt_ctlpath;
chanpath = zt_chanpath;
memcpy(&codes, &zt_ioctl_codes, sizeof(codes));
} else if (!stat(dahdi_ctlpath, &statbuf)) {
zap_log(ZAP_LOG_NOTICE, "Using DAHDI control device\n");
ctlpath = dahdi_ctlpath;
chanpath = dahdi_chanpath;
memcpy(&codes, &dahdi_ioctl_codes, sizeof(codes));
} else {
zap_log(ZAP_LOG_ERROR, "No DAHDI or Zap control device found in /dev/\n");
return ZAP_FAIL;
}
if ((CONTROL_FD = open(ctlpath, O_RDWR)) < 0) {
zap_log(ZAP_LOG_ERROR, "Cannot open control device %s: %s\n", ctlpath, strerror(errno));
return ZAP_FAIL;
}
zt_globals.codec_ms = 20;
zt_globals.wink_ms = 150;
zt_globals.flash_ms = 750;
zt_globals.eclevel = 0;
zt_globals.etlevel = 0;
zt_interface.name = "zt";
zt_interface.configure = zt_configure;
zt_interface.configure_span = zt_configure_span;
zt_interface.open = zt_open;
zt_interface.close = zt_close;
zt_interface.command = zt_command;
zt_interface.wait = zt_wait;
zt_interface.read = zt_read;
zt_interface.write = zt_write;
zt_interface.poll_event = zt_poll_event;
zt_interface.next_event = zt_next_event;
zt_interface.channel_destroy = zt_channel_destroy;
zt_interface.get_alarms = zt_get_alarms;
*zio = &zt_interface;
return ZAP_SUCCESS;
}
/**
* \brief Unloads zaptel IO module
* \return Success
*/
static ZIO_IO_UNLOAD_FUNCTION(zt_destroy)
{
close(CONTROL_FD);
memset(&zt_interface, 0, sizeof(zt_interface));
return ZAP_SUCCESS;
}
/**
* \brief Openzap zaptel IO module definition
*/
zap_module_t zap_module = {
"zt",
zt_init,
zt_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 noet:
*/