You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
chan-capi/chan_capi.c

9737 lines
268 KiB

/*
* An implementation of Common ISDN API 2.0 for Asterisk
*
* Copyright (C) 2005-2010 Cytronics & Melware
*
* Armin Schindler <armin@melware.de>
*
* Reworked, but based on the work of
* Copyright (C) 2002-2005 Junghanns.NET GmbH
*
* Klaus-Peter Junghanns <kapejod@ns1.jnetdns.de>
*
* This program is free software and may be modified and
* distributed under the terms of the GNU Public License.
*/
#include <sys/time.h>
#include <sys/signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <math.h>
#include <sys/types.h>
#include "chan_capi_platform.h"
#include "xlaw.h"
#include "chan_capi20.h"
#include "chan_capi.h"
#include "chan_capi_rtp.h"
#include "chan_capi_qsig.h"
#include "chan_capi_qsig_ecma.h"
#include "chan_capi_qsig_asn197ade.h"
#include "chan_capi_qsig_asn197no.h"
#include "chan_capi_utils.h"
#include "chan_capi_supplementary.h"
#include "chan_capi_chat.h"
#include "chan_capi_command.h"
#ifdef CC_AST_HAS_VERSION_1_8
#include <asterisk/callerid.h>
#endif
#ifdef CC_AST_HAS_VERSION_13_0
#include "asterisk/smoother.h"
#include "asterisk/pickup.h"
#include "asterisk/features_config.h"
#endif
13 years ago
struct _diva_streaming_vector* vind;
#ifdef DIVA_STREAMING
#include "platform.h"
#include "diva_streaming_result.h"
#include "diva_streaming_messages.h"
#include "diva_streaming_vector.h"
#include "diva_streaming_manager.h"
#include "chan_capi_divastreaming_utils.h"
#endif
#ifdef DIVA_STATUS
#include "divastatus_ifc.h"
#endif
12 years ago
#include "chan_capi_mwi.h"
#include "chan_capi_cli.h"
#include "chan_capi_ami.h"
#include "chan_capi_devstate.h"
#include "divaverbose.h"
/* #define CC_VERSION "x.y.z" */
#define CC_VERSION "$Revision$"
/*
* personal stuff
*/
#undef CAPI_APPLID_UNUSED
#define CAPI_APPLID_UNUSED 0xffffffff
unsigned capi_ApplID = CAPI_APPLID_UNUSED;
#define CAPI_PLCI_VAR_NAME "CAPIPLCI"
#define CAPI_ECT_PLCI_VAR_NAME "CAPIECTPLCI"
#define CAPI_DETECTED_TONE_NAME "CAPIDETECTEDTONE"
typedef struct _diva_supported_tones {
unsigned char tone;
const char* name;
} diva_supported_tones_t;
static const char* pbx_capi_map_detected_tone(unsigned char tone);
static const char tdesc[] = "Common ISDN API Driver (" CC_VERSION ")";
static const char channeltype[] = CC_MESSAGE_BIGNAME;
#ifdef CC_AST_HAS_VERSION_1_4
#define AST_MODULE "chan_capi"
#else
static char *ccdesc = "Common ISDN API for Asterisk";
#endif
static char *commandtdesc = CC_MESSAGE_BIGNAME " command interface.\n"
"The dial command:\n"
"Dial(" CC_MESSAGE_BIGNAME "/g<group>/[<callerid>:]<destination>[/<params>])\n"
"Dial(" CC_MESSAGE_BIGNAME "/contr<controller>/[<callerid>:]<destination>[/<params>])\n"
"Dial(" CC_MESSAGE_BIGNAME "/<interface-name>/[<callerid>:]<destination>[/<params>])\n"
"\"params\" can be:\n"
"early B3:\"b\"=always, \"B\"=on successful calls only\n"
"\"d\":use callerID from capi.conf, \"o\":overlap sending number\n"
"\n\"q\":disable QSIG functions on outgoing call\n"
"\n"
"capicommand() where () can be:\n"
"\"progress\" send progress (for NT mode)\n"
"\"proceeding\" send proceeding (for NT mode)\n"
"\"deflect,to_number\" forwards an unanswered call to number\n"
"\"malicous\" report a call of malicious nature\n"
"\"echocancel,<yes> or <no>\" echo-cancel provided by driver/hardware\n"
"\"echosquelch,<yes> or <no>\" very primitive echo-squelch by chan-capi\n"
"\"holdtype,<local> or <hold>\" set type of 'hold'\n"
"\"hold[,MYHOLDVAR]\" puts an answered call on hold\n"
"\"retrieve,${MYHOLDVAR}\" gets back the held call\n"
"\"ect,${MYHOLDVAR})\" explicit call transfer of call on hold\n"
"\"3pty_begin,${MYHOLDVAR})\" Three-Party-Conference (3PTY) with active and held call\n"
"\"receivefax,filename,stationID,headline,options\" receive a " CC_MESSAGE_BIGNAME " fax\n"
"\"sendfax,filename.sff,stationID,headline\" send a " CC_MESSAGE_BIGNAME " fax\n"
"\"qsig_ssct,cidsrc,ciddst\" QSIG single step call transfer\n"
"\"qsig_ct,cidsrc,ciddst,marker,waitconnect\" QSIG call transfer\n"
"\"qsig_callmark,marker\" marks a QSIG call for later identification\n"
"Variables set after fax receive:\n"
"FAXSTATUS :0=OK, 1=Error\n"
"FAXREASON :B3 disconnect reason\n"
"FAXREASONTEXT :FAXREASON as text\n"
"FAXRATE :baud rate of fax connection\n"
"FAXRESOLUTION :0=standard, 1=high\n"
"FAXFORMAT :0=SFF, 8=native\n"
"FAXPAGES :Number of pages received\n"
"FAXID :ID of the remote fax machine\n"
"Asterisk variables used/set by chan_capi:\n"
"BCHANNELINFO,CALLEDTON,_CALLERHOLDID,CALLINGSUBADDRESS,CALLEDSUBADDRESS\n"
"CONNECTEDNUMBER,FAXEXTEN,PRI_CAUSE,REDIRECTINGNUMBER,REDIRECTREASON,ISDNPI1,ISDNPI2\n"
"!!! for more details and samples, check the README of chan_capi !!!\n";
static char *commandapp = "capicommand";
static char *commandsynopsis = "Execute special chan_capi commands";
#ifndef CC_AST_HAS_VERSION_1_4
STANDARD_LOCAL_USER;
LOCAL_USER_DECL;
#endif
static int usecnt;
#ifdef CC_AST_HAS_VERSION_1_4
struct ast_module *myself;
#endif
/*
* LOCKING RULES
* =============
*
* This channel driver uses several locks. One must be
* careful not to reverse the locking order, which will
* lead to a so called deadlock. Here is the locking order
* that must be followed:
*
* struct capi_pvt *i;
*
* 1. cc_mutex_lock(&i->owner->lock); **
*
* 2. cc_mutex_lock(&i->lock);
*
* 3. cc_mutex_lock(&iflock);
* 4. cc_mutex_lock(&messagenumber_lock);
* 5. cc_mutex_lock(&usecnt_lock);
* 6. cc_mutex_lock(&capi_put_lock);
*
*
* ** the PBX will call the callback functions with
* this lock locked. This lock protects the
* structure pointed to by 'i->owner'. Also note
* that calling some PBX functions will lock
* this lock!
*/
#ifndef CC_AST_HAS_VERSION_1_4
AST_MUTEX_DEFINE_STATIC(usecnt_lock);
#endif
AST_MUTEX_DEFINE_STATIC(iflock);
16 years ago
static pthread_t capi_device_thread = (pthread_t)(0-1);
struct capi_pvt *capi_iflist = NULL;
static struct cc_capi_controller *capi_controllers[CAPI_MAX_CONTROLLERS + 1];
static int capi_num_controllers = 0;
static unsigned int capi_counter = 0;
static struct ast_channel *chan_for_task;
static int channel_task;
#define CAPI_CHANNEL_TASK_NONE 0
#define CAPI_CHANNEL_TASK_HANGUP 1
#define CAPI_CHANNEL_TASK_SOFTHANGUP 2
#define CAPI_CHANNEL_TASK_PICKUP 3
#define CAPI_CHANNEL_TASK_GOTOFAX 4
static struct capi_pvt *interface_for_task;
static int interface_task;
#define CAPI_INTERFACE_TASK_NONE 0
#define CAPI_INTERFACE_TASK_NULLIFREMOVE 1
static char capi_national_prefix[AST_MAX_EXTENSION];
static char capi_international_prefix[AST_MAX_EXTENSION];
static char capi_subscriber_prefix[AST_MAX_EXTENSION];
static char default_language[MAX_LANGUAGE] = "";
cc_format_t capi_capability = CC_FORMAT_ALAW;
static int null_plci_dtmf_support = 1;
#ifdef CC_AST_HAS_VERSION_1_4
/* Global jitterbuffer configuration - by default, jb is disabled */
static struct ast_jb_conf default_jbconf =
{
.flags = 0,
.max_size = -1,
.resync_threshold = -1,
.impl = ""
};
static struct ast_jb_conf global_jbconf;
static char global_mohinterpret[MAX_MUSICCLASS] = "default";
#endif
/* local prototypes */
#define CC_B_INTERFACE_NOT_FREE(__x__) (((__x__)->used) || ((__x__)->reserved) || \
((__x__)->channeltype != CAPI_CHANNELTYPE_B) || \
(capi_controllers[(__x__)->controller]->nfreebchannels < capi_controllers[(__x__)->controller]->nfreebchannelsHardThr))
/*!
* \brief Acquire lock in correct order. Called if locking from non
* ast_channel context (thread, ...)
*/
static struct ast_channel* capidev_acquire_locks_from_thread_context(struct capi_pvt *i);
static int pbx_capi_hold(struct ast_channel *c, char *param);
static int pbx_capi_retrieve(struct ast_channel *c, char *param);
#ifdef CC_AST_HAS_INDICATE_DATA
static int pbx_capi_indicate(struct ast_channel *c, int condition, const void *data, size_t datalen);
#else
static int pbx_capi_indicate(struct ast_channel *c, int condition);
#endif
static struct capi_pvt* get_active_plci(struct ast_channel *c);
static void clear_channel_fax_loop(struct ast_channel *c, struct capi_pvt *i);
static void pbx_capi_add_diva_protocol_independent_extension(
struct capi_pvt *i,
unsigned char *facilityarray,
struct ast_channel *c,
const char* variable);
#ifdef DIVA_STATUS
static void pbx_capi_interface_status_changed(int controller, diva_status_interface_state_t newInterfaceState);
static void pbx_capi_hw_status_changed(int controller, diva_status_hardware_state_t newHwState);
#endif
static int pbx_capi_check_controller_status(int controller);
/*
* B protocol settings
*/
static struct {
_cword b1protocol;
_cword b2protocol;
_cword b3protocol;
_cstruct b1configuration;
_cstruct b2configuration;
_cstruct b3configuration;
} b_protocol_table[] =
{
{ 0x01, 0x01, 0x00, /* 0 */
NULL,
NULL,
NULL
},
{ 0x04, 0x04, 0x05, /* 1 */
NULL,
NULL,
NULL
},
{ 0x1f, 0x1f, 0x1f, /* 2 */
(_cstruct) "\x00",
/* (_cstruct) "\x04\x01\x00\x00\x02", */
(_cstruct) "\x06\x01\x00\x58\x02\x32\x00",
(_cstruct) "\x00"
},
{ 0x1f, 1, 0, /* 3 */
NULL,
NULL,
NULL
},
{ 0x04, 0x04, 0x04, /* 4 */
NULL,
NULL,
NULL
}
};
/*
* set the global-configuration (b-channel operation)
*/
static _cstruct capi_set_global_configuration(struct capi_pvt *i)
{
unsigned short dtedce = 0;
unsigned char *buf = i->tmpbuf;
buf[0] = 2; /* len */
if (i->FaxState & CAPI_FAX_STATE_ACTIVE) {
if ((i->outgoing) && (!(i->FaxState & CAPI_FAX_STATE_SENDMODE)))
dtedce = 2;
if ((!(i->outgoing)) && ((i->FaxState & CAPI_FAX_STATE_SENDMODE)))
dtedce = 1;
}
write_capi_word(&buf[1], dtedce);
if (dtedce == 0)
buf = NULL;
return (_cstruct)buf;
}
/*
* command to string function
*/
static const char * capi_command_to_string(unsigned short wCmd)
{
enum { lowest_value = CAPI_P_MIN,
end_value = CAPI_P_MAX,
range = end_value - lowest_value,
};
#undef CHAN_CAPI_COMMAND_DESC
#define CHAN_CAPI_COMMAND_DESC(n, ENUM, value) \
[CAPI_P_REQ(ENUM)-(n)] = #ENUM "_REQ", \
[CAPI_P_CONF(ENUM)-(n)] = #ENUM "_CONF", \
[CAPI_P_IND(ENUM)-(n)] = #ENUM "_IND", \
[CAPI_P_RESP(ENUM)-(n)] = #ENUM "_RESP",
static const char * const table[range] = {
CAPI_COMMANDS(CHAN_CAPI_COMMAND_DESC, lowest_value)
};
wCmd -= lowest_value;
if (wCmd >= range) {
goto error;
}
if (table[wCmd] == NULL) {
goto error;
}
return table[wCmd];
error:
return "UNDEFINED";
}
/*
* wait for B3 up
*/
int capi_wait_for_b3_up(struct capi_pvt *i)
{
struct timespec abstime;
int ret = 1;
cc_mutex_lock(&i->lock);
if (!(i->isdnstate & CAPI_ISDN_STATE_B3_UP)) {
i->waitevent = CAPI_WAITEVENT_B3_UP;
abstime.tv_sec = time(NULL) + 2;
abstime.tv_nsec = 0;
cc_verbose(4, 1, "%s: wait for b3 up.\n",
i->vname);
if (ast_cond_timedwait(&i->event_trigger, &i->lock, &abstime) != 0) {
cc_log(LOG_WARNING, "%s: timed out waiting for b3 up.\n",
i->vname);
ret = 0;
} else {
cc_verbose(4, 1, "%s: cond signal received for b3 up.\n",
i->vname);
}
}
cc_mutex_unlock(&i->lock);
return ret;
}
/*
* wait for finishing answering state
*/
void capi_wait_for_answered(struct capi_pvt *i)
{
struct timespec abstime;
cc_mutex_lock(&i->lock);
if (i->state == CAPI_STATE_ANSWERING) {
i->waitevent = CAPI_WAITEVENT_ANSWER_FINISH;
abstime.tv_sec = time(NULL) + 2;
abstime.tv_nsec = 0;
cc_verbose(4, 1, "%s: wait for finish answer.\n",
i->vname);
if (ast_cond_timedwait(&i->event_trigger, &i->lock, &abstime) != 0) {
cc_log(LOG_WARNING, "%s: timed out waiting for finish answer.\n",
i->vname);
} else {
cc_verbose(4, 1, "%s: cond signal received for finish answer.\n",
i->vname);
}
}
cc_mutex_unlock(&i->lock);
}
/*
* function to tell if fax activity has finished
*/
static int capi_tell_fax_finish(void *data)
{
struct capi_pvt *i = (struct capi_pvt *)data;
if (i->FaxState & CAPI_FAX_STATE_ACTIVE) {
return 1;
}
return 0;
}
/*
* TCAP -> CIP Translation Table (TransferCapability->CommonIsdnProfile)
*/
static struct {
unsigned short tcap;
unsigned short cip;
unsigned char digital;
} translate_tcap2cip[] = {
{ PRI_TRANS_CAP_SPEECH, CAPI_CIPI_SPEECH, 0 },
{ PRI_TRANS_CAP_DIGITAL, CAPI_CIPI_DIGITAL, 1 },
{ PRI_TRANS_CAP_RESTRICTED_DIGITAL, CAPI_CIPI_RESTRICTED_DIGITAL, 1 },
{ PRI_TRANS_CAP_3K1AUDIO, CAPI_CIPI_3K1AUDIO, 0 },
{ PRI_TRANS_CAP_DIGITAL_W_TONES, CAPI_CIPI_DIGITAL_W_TONES, 1 },
{ PRI_TRANS_CAP_VIDEO, CAPI_CIPI_VIDEO, 1 }
};
static int tcap2cip(unsigned short tcap)
{
int x;
for (x = 0; x < sizeof(translate_tcap2cip) / sizeof(translate_tcap2cip[0]); x++) {
if (translate_tcap2cip[x].tcap == tcap)
return (int)translate_tcap2cip[x].cip;
}
return CAPI_CIPI_SPEECH;
}
unsigned char capi_tcap_is_digital(unsigned short tcap)
{
int x;
for (x = 0; x < sizeof(translate_tcap2cip) / sizeof(translate_tcap2cip[0]); x++) {
if (translate_tcap2cip[x].tcap == tcap)
return translate_tcap2cip[x].digital;
}
return 0;
}
/*
* CIP -> TCAP Translation Table (CommonIsdnProfile->TransferCapability)
*/
static struct {
unsigned short cip;
unsigned short tcap;
} translate_cip2tcap[] = {
{ CAPI_CIPI_SPEECH, PRI_TRANS_CAP_SPEECH },
{ CAPI_CIPI_DIGITAL, PRI_TRANS_CAP_DIGITAL },
{ CAPI_CIPI_RESTRICTED_DIGITAL, PRI_TRANS_CAP_RESTRICTED_DIGITAL },
{ CAPI_CIPI_3K1AUDIO, PRI_TRANS_CAP_3K1AUDIO },
{ CAPI_CIPI_7KAUDIO, PRI_TRANS_CAP_DIGITAL_W_TONES },
{ CAPI_CIPI_VIDEO, PRI_TRANS_CAP_VIDEO },
{ CAPI_CIPI_PACKET_MODE, PRI_TRANS_CAP_DIGITAL },
{ CAPI_CIPI_56KBIT_RATE_ADAPTION, PRI_TRANS_CAP_DIGITAL },
{ CAPI_CIPI_DIGITAL_W_TONES, PRI_TRANS_CAP_DIGITAL_W_TONES },
{ CAPI_CIPI_TELEPHONY, PRI_TRANS_CAP_SPEECH },
{ CAPI_CIPI_FAX_G2_3, PRI_TRANS_CAP_3K1AUDIO },
{ CAPI_CIPI_FAX_G4C1, PRI_TRANS_CAP_DIGITAL },
{ CAPI_CIPI_FAX_G4C2_3, PRI_TRANS_CAP_DIGITAL },
{ CAPI_CIPI_TELETEX_PROCESSABLE, PRI_TRANS_CAP_DIGITAL },
{ CAPI_CIPI_TELETEX_BASIC, PRI_TRANS_CAP_DIGITAL },
{ CAPI_CIPI_VIDEOTEX, PRI_TRANS_CAP_DIGITAL },
{ CAPI_CIPI_TELEX, PRI_TRANS_CAP_DIGITAL },
{ CAPI_CIPI_X400, PRI_TRANS_CAP_DIGITAL },
{ CAPI_CIPI_X200, PRI_TRANS_CAP_DIGITAL },
{ CAPI_CIPI_7K_TELEPHONY, PRI_TRANS_CAP_DIGITAL_W_TONES },
{ CAPI_CIPI_VIDEO_TELEPHONY_C1, PRI_TRANS_CAP_DIGITAL_W_TONES },
{ CAPI_CIPI_VIDEO_TELEPHONY_C2, PRI_TRANS_CAP_DIGITAL }
};
static unsigned short cip2tcap(int cip)
{
int x;
for (x = 0;x < sizeof(translate_cip2tcap) / sizeof(translate_cip2tcap[0]); x++) {
if (translate_cip2tcap[x].cip == (unsigned short)cip)
return translate_cip2tcap[x].tcap;
}
return 0;
}
/*
* TransferCapability to String conversion
*/
static char *transfercapability2str(int transfercapability)
{
switch(transfercapability) {
case PRI_TRANS_CAP_SPEECH:
return "SPEECH";
case PRI_TRANS_CAP_DIGITAL:
return "DIGITAL";
case PRI_TRANS_CAP_RESTRICTED_DIGITAL:
return "RESTRICTED_DIGITAL";
case PRI_TRANS_CAP_3K1AUDIO:
return "3K1AUDIO";
case PRI_TRANS_CAP_DIGITAL_W_TONES:
return "DIGITAL_W_TONES";
case PRI_TRANS_CAP_VIDEO:
return "VIDEO";
default:
return "UNKNOWN";
}
}
/*
* set task for an interface which need to be done out of lock
* ( after the capi thread loop )
*/
static void capi_interface_task(struct capi_pvt *i, int task)
{
interface_for_task = i;
interface_task = task;
cc_verbose(4, 1, VERBOSE_PREFIX_4 "%s: set interface task to %d\n",
i->name, task);
}
/*
* set task for a channel which need to be done out of lock
* ( after the capi thread loop )
*/
static void capi_channel_task(struct ast_channel *c, int task)
{
chan_for_task = c;
channel_task = task;
#ifdef CC_AST_HAS_VERSION_11_0
const char *cur_name = ast_channel_name(c);
#else /* !defined(CC_AST_HAS_VERSION_11_0) */
const char *cur_name = c->name;
#endif /* defined(CC_AST_HAS_VERSION_11_0) */
cc_verbose(4, 1, VERBOSE_PREFIX_4 "%s: set channel task to %d\n",
cur_name, task);
}
/*
* Added date/time IE to facility structure
*/
static void capi_facility_add_datetime(unsigned char *facilityarray)
{
unsigned int idx;
time_t current_time;
struct tm *time_local;
unsigned char year;
if (!facilityarray)
return;
current_time = time(NULL);
time_local = localtime(&current_time);
year = time_local->tm_year;
while (year > 99) {
year -= 100;
}
idx = facilityarray[0] + 1;
facilityarray[idx++] = 0x29; /* date/time IE */
facilityarray[idx++] = 5; /* length */
facilityarray[idx++] = year;
facilityarray[idx++] = time_local->tm_mon + 1;
facilityarray[idx++] = time_local->tm_mday;
facilityarray[idx++] = time_local->tm_hour;
facilityarray[idx++] = time_local->tm_min;
facilityarray[0] = idx - 1;
return;
}
/*
* Echo cancellation is for cards w/ integrated echo cancellation only
*/
void capi_echo_canceller(struct capi_pvt *i, int function)
{
int ecAvail = 0;
if ((i->isdnstate & CAPI_ISDN_STATE_DISCONNECT))
return;
if ((i->channeltype == CAPI_CHANNELTYPE_NULL) &&
(i->line_plci == NULL)) {
return;
}
if (((function == EC_FUNCTION_ENABLE) && (i->isdnstate & CAPI_ISDN_STATE_EC)) ||
((function != EC_FUNCTION_ENABLE) && (!(i->isdnstate & CAPI_ISDN_STATE_EC)))) {
cc_verbose(3, 1, VERBOSE_PREFIX_4 "%s: echo canceller (PLCI=%#x, function=%d) unchanged\n",
i->vname, i->PLCI, function);
/* nothing to do */
return;
}
/* check for old echo-cancel configuration */
if ((i->ecSelector != FACILITYSELECTOR_ECHO_CANCEL) &&
(capi_controllers[i->controller]->broadband)) {
ecAvail = 1;
}
if ((i->ecSelector == FACILITYSELECTOR_ECHO_CANCEL) &&
(capi_controllers[i->controller]->echocancel)) {
ecAvail = 1;
}
if ((i->channeltype == CAPI_CHANNELTYPE_NULL) &&
(i->line_plci == NULL)) {
return;
}
if ((i->channeltype == CAPI_CHANNELTYPE_NULL) &&
(capi_controllers[i->controller]->ecPath & EC_ECHOCANCEL_PATH_IP) == 0) {
return;
}
if ((i->channeltype != CAPI_CHANNELTYPE_NULL) &&
(capi_controllers[i->controller]->ecPath & EC_ECHOCANCEL_PATH_IFC) == 0) {
return;
}
/* If echo cancellation is not requested or supported, don't attempt to enable it */
if (!ecAvail || !i->doEC) {
return;
}
if (capi_tcap_is_digital(i->transfercapability)) {
cc_verbose(3, 1, VERBOSE_PREFIX_2 "%s: No echo canceller in digital mode (PLCI=%#x)\n",
i->vname, i->PLCI);
return;
}
cc_verbose(3, 0, VERBOSE_PREFIX_2 "%s: Setting up echo canceller (PLCI=%#x, function=%d, options=%d, tail=%d)\n",
i->vname, i->PLCI, function, i->ecOption, i->ecTail);
if (function == EC_FUNCTION_ENABLE) {
i->isdnstate |= CAPI_ISDN_STATE_EC;
} else {
i->isdnstate &= ~CAPI_ISDN_STATE_EC;
}
capi_sendf(i, 0, CAPI_FACILITY_REQ, i->PLCI, get_capi_MessageNumber(),
"w(w(www))",
i->ecSelector,
function,
i->ecOption, /* bit field - ignore echo canceller disable tone */
i->ecTail, /* Tail length, ms */
0
);
return;
}
static int capi_check_diva_tone_function_allowed(struct capi_pvt *i, int useLinePLCI)
{
int ecAvail = 0;
if ((i->isdnstate & CAPI_ISDN_STATE_DISCONNECT))
return -1;
if ((i->channeltype == CAPI_CHANNELTYPE_NULL) &&
(i->line_plci == NULL)) {
return -1;
}
if ((i->channeltype == CAPI_CHANNELTYPE_NULL) && (useLinePLCI != 0)) {
if ((i->line_plci->isdnstate & CAPI_ISDN_STATE_DISCONNECT))
return -1;
if (capi_verify_resource_plci(i->line_plci) != 0)
return -1;
}
/* check for old echo-cancel configuration */
if ((i->ecSelector != FACILITYSELECTOR_ECHO_CANCEL) &&
(capi_controllers[i->controller]->broadband)) {
ecAvail = 1;
}
if ((i->ecSelector == FACILITYSELECTOR_ECHO_CANCEL) &&
(capi_controllers[i->controller]->echocancel)) {
ecAvail = 1;
}
if ((ecAvail == 0) ||
(capi_controllers[i->controller]->divaExtendedFeaturesAvailable == 0)) {
return -1;
}
if (capi_tcap_is_digital(i->transfercapability)) {
cc_verbose(3, 1, VERBOSE_PREFIX_2 "%s: No audio features in digital mode (PLCI=%#x)\n",
i->vname, i->PLCI);
return -1;
}
return 0;
}
/*
* diva audio features
*/
static void capi_diva_audio_features(struct capi_pvt *i, int useLinePLCI)
{
struct capi_pvt *effectiveIfc;
unsigned short divaAudioFlags, divaDigitalTxGain, divaDigitalRxGain;
const char* plciName;
if (capi_check_diva_tone_function_allowed(i, useLinePLCI) != 0)
return;
effectiveIfc = ((i->channeltype == CAPI_CHANNELTYPE_NULL) && (useLinePLCI != 0)) ? i->line_plci : i;
if (i->channeltype != CAPI_CHANNELTYPE_NULL) {
/* ISDN connection */
divaAudioFlags = (i->divaAudioFlags | i->divaDataStubAudioFlags);
divaDigitalTxGain = i->divaDigitalTxGain;
divaDigitalRxGain = i->divaDigitalRxGain;
plciName = "";
} else {
/* Resource PLCI */
if (useLinePLCI != 0) {
/* Command for line stub */
divaAudioFlags = i->divaAudioFlags;
divaDigitalTxGain = i->divaDigitalTxGain;
divaDigitalRxGain = i->divaDigitalRxGain;
plciName = "LINE-";
} else {
/* Command for data stub */
divaAudioFlags = i->divaDataStubAudioFlags;
divaDigitalTxGain = 0;
divaDigitalRxGain = 0;
plciName = "DATA-";
}
}
cc_verbose(3, 0, VERBOSE_PREFIX_2 "%s: Setting up audio features (%sPLCI=%#x, function=%04x, rx=%u, tx=%u)\n",
i->vname, plciName, i->PLCI, divaAudioFlags, divaDigitalRxGain, divaDigitalTxGain);
capi_sendf (effectiveIfc, 0, CAPI_MANUFACTURER_REQ, effectiveIfc->PLCI, get_capi_MessageNumber(),
"dw(b(bwww))",
_DI_MANU_ID,
_DI_DSP_CTRL,
0x1c,
0x0b,
divaAudioFlags,
divaDigitalTxGain,
divaDigitalRxGain);
}
static void capi_diva_clamping(struct capi_pvt *i, unsigned int duration)
{
if (capi_check_diva_tone_function_allowed(i, 0) != 0)
return;
if (duration != 0) {
cc_verbose(3, 0, VERBOSE_PREFIX_2 "%s: Setting DTMF clamping ON for %u mSec (PLCI=%#x)\n", i->vname, duration, i->PLCI);
capi_sendf (i, 0, CAPI_FACILITY_REQ, i->PLCI, get_capi_MessageNumber(), "w(www())", 1, 244, duration, duration);
} else {