|
|
|
/*
|
|
|
|
* 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
|
|
|
|
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
|
|
|
|
#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);
|
|
|
|
|
|
|
|
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(¤t_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 {
|
|