chan-capi/chan_capi.c

9737 lines
268 KiB
C

/*
* 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(&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 {
cc_verbose(3, 0, VERBOSE_PREFIX_2 "%s: Setting DTMF clamping OFF (PLCI=%#x)\n", i->vname, i->PLCI);
capi_sendf (i, 0, CAPI_FACILITY_REQ, i->PLCI, get_capi_MessageNumber(), "w(www())", 1, 245, 0, 0);
}
}
static void capi_diva_tone_processing_function(struct capi_pvt *i, unsigned char function)
{
if (capi_check_diva_tone_function_allowed(i, 0) != 0)
return;
cc_verbose(3, 0, VERBOSE_PREFIX_2 "%s: Apply tone processing function %u (PLCI=%#x)\n",
i->vname, function, i->PLCI);
capi_sendf(i, 0, CAPI_FACILITY_REQ, i->PLCI, get_capi_MessageNumber(),
"w(www())", 1, function, 0, 0);
}
static void capi_diva_send_tone_function(struct capi_pvt *i, unsigned char tone)
{
if (capi_check_diva_tone_function_allowed(i, 0) != 0)
return;
capi_sendf (i, 0, CAPI_FACILITY_REQ, i->PLCI, get_capi_MessageNumber(), "w(www(b)())",
FACILITYSELECTOR_DTMF, 252, /* send tone */ 0, 0, tone);
}
static void capi_diva_pitch_control_command(
struct capi_pvt *i,
int enable,
unsigned short rxpitch,
unsigned short txpitch)
{
if (capi_check_diva_tone_function_allowed(i, 0) != 0)
return;
capi_sendf (i, 0, CAPI_MANUFACTURER_REQ, i->PLCI, get_capi_MessageNumber(),
"dw(b(bwww))",
_DI_MANU_ID,
_DI_DSP_CTRL,
0x1c,
0x0a,
enable == 0 ? 0x0000 : 0x0001,
enable == 0 ? 0 : rxpitch,
enable == 0 ? 0 : txpitch);
}
/*
* turn on/off DTMF detection
*/
static int capi_detect_dtmf(struct capi_pvt *i, int flag)
{
MESSAGE_EXCHANGE_ERROR error;
if ((i->isdnstate & CAPI_ISDN_STATE_DISCONNECT))
return 0;
if ((i->channeltype == CAPI_CHANNELTYPE_NULL) &&
(((i->line_plci == NULL) && (null_plci_dtmf_support == 0)) ||
(i->resource_plci_type == CAPI_RESOURCE_PLCI_LINE))) {
return 0;
}
if (capi_tcap_is_digital(i->transfercapability)) {
cc_verbose(3, 1, VERBOSE_PREFIX_2 "%s: No dtmf-detect in digital mode (PLCI=%#x)\n",
i->vname, i->PLCI);
return 0;
}
if (((flag == 1) && (i->isdnstate & CAPI_ISDN_STATE_DTMF)) ||
((flag == 0) && (!(i->isdnstate & CAPI_ISDN_STATE_DTMF)))) {
cc_verbose(3, 1, VERBOSE_PREFIX_4 "%s: dtmf (PLCI=%#x, flag=%d) unchanged\n",
i->vname, i->PLCI, flag);
/* nothing to do */
return 0;
}
/* does the controller support dtmf? and do we want to use it? */
if ((capi_controllers[i->controller]->dtmf != 1) || (i->doDTMF != 0))
return 0;
cc_verbose(3, 0, VERBOSE_PREFIX_2 "%s: Setting up DTMF detector (PLCI=%#x, flag=%d)\n",
i->vname, i->PLCI, flag);
error = capi_sendf(i, 0, CAPI_FACILITY_REQ, i->PLCI, get_capi_MessageNumber(),
"w(www()())",
((i->channeltype != CAPI_CHANNELTYPE_NULL) || (i->line_plci != 0)) ? FACILITYSELECTOR_DTMF : PRIV_SELECTOR_DTMF_ONDATA,
(flag == 1) ? 1:2, /* start/stop DTMF listen */
CAPI_DTMF_DURATION,
CAPI_DTMF_DURATION
);
if (error != 0) {
return error;
}
if (flag == 1) {
i->isdnstate |= CAPI_ISDN_STATE_DTMF;
} else {
i->isdnstate &= ~CAPI_ISDN_STATE_DTMF;
}
return 0;
}
/*
* queue a frame to PBX
*/
static int local_queue_frame(struct capi_pvt *i, struct ast_frame *f)
{
unsigned char *wbuf;
int wbuflen;
if (!(i->isdnstate & CAPI_ISDN_STATE_PBX)) {
/* if there is no PBX running yet,
we don't need any frames sent */
return -1;
}
if ((i->state == CAPI_STATE_DISCONNECTING) ||
(i->isdnstate & CAPI_ISDN_STATE_HANGUP)) {
cc_verbose(3, 1, VERBOSE_PREFIX_4 "%s: no queue_frame in state disconnecting for %d/%d\n",
i->vname, f->frametype, FRAME_SUBCLASS_INTEGER(f->subclass));
return 0;
}
if ((capidebug) && (f->frametype != AST_FRAME_VOICE)) {
ast_frame_dump(i->vname, f, VERBOSE_PREFIX_3 "chan_capi queue frame:");
}
if ((f->frametype == AST_FRAME_CONTROL) &&
(FRAME_SUBCLASS_INTEGER(f->subclass) == AST_CONTROL_HANGUP)) {
i->isdnstate |= CAPI_ISDN_STATE_HANGUP;
}
if (i->writerfd == -1) {
if (i->resource_plci_type == 0) {
cc_log(LOG_ERROR, "No writerfd in local_queue_frame for %s\n",
i->vname);
return -1;
} else {
return (0);
}
}
if (f->frametype != AST_FRAME_VOICE)
f->datalen = 0;
wbuflen = sizeof(struct ast_frame) + f->datalen;
wbuf = alloca(wbuflen);
memcpy(wbuf, f, sizeof(struct ast_frame));
if (f->datalen) {
memcpy(wbuf + sizeof(struct ast_frame), f->FRAME_DATA_PTR, f->datalen);
}
if (write(i->writerfd, wbuf, wbuflen) != wbuflen) {
cc_log(LOG_ERROR, "Could not write to pipe for %s fd:%d errno:%d\n",
i->vname, i->writerfd, errno);
}
return 0;
}
/*
* set a new name for this channel
*/
static void update_channel_name(struct capi_pvt *i)
{
char name[AST_CHANNEL_NAME];
snprintf(name, sizeof(name) - 1, CC_MESSAGE_BIGNAME "/%s/%s-%x",
i->vname, i->dnid, capi_counter++);
if (i->owner) {
ast_change_name(i->owner, name);
}
cc_verbose(3, 0, VERBOSE_PREFIX_3 "%s: Updated channel name: %s\n",
i->vname, name);
}
/*
* send digits via INFO_REQ
*/
static int capi_send_info_digits(struct capi_pvt *i, char *digits, int len)
{
MESSAGE_EXCHANGE_ERROR error;
char buf[64];
int a;
memset(buf, 0, sizeof(buf));
if (len > (sizeof(buf) - 2))
len = sizeof(buf) - 2;
buf[0] = len + 1;
buf[1] = 0x80;
for (a = 0; a < len; a++) {
buf[a + 2] = digits[a];
}
error = capi_sendf(NULL, 0, CAPI_INFO_REQ, i->PLCI, get_capi_MessageNumber(),
"s()",
buf
);
if (error != 0) {
return error;
}
cc_verbose(3, 1, VERBOSE_PREFIX_4 "%s: sent CALLEDPARTYNUMBER INFO digits = '%s' (PLCI=%#x)\n",
i->vname, buf + 2, i->PLCI);
return 0;
}
#ifdef CC_AST_HAS_VERSION_1_4
/*
* begin send DMTF
*/
static int pbx_capi_send_digit_begin(struct ast_channel *c, char digit)
{
struct capi_pvt *i = CC_CHANNEL_PVT(c);
if ((i->state == CAPI_STATE_CONNECTED) && (i->isdnstate & CAPI_ISDN_STATE_B3_UP)) {
/* we have a real connection, so send real DTMF */
if ((capi_controllers[i->controller]->dtmf == 0) || (i->doDTMF > 0)) {
/* let * fake it */
return -1;
}
}
return 0;
}
#endif
/*
* send DTMF digit
*/
static int capi_send_dtmf_digits(struct capi_pvt *i, char digit)
{
int ret;
if (!(i->isdnstate & CAPI_ISDN_STATE_B3_UP)) {
cc_verbose(2, 1, VERBOSE_PREFIX_3 "%s: send DTMF: B-channel not connected.\n",
i->vname);
return -1;
}
cc_verbose(3, 1, VERBOSE_PREFIX_3 "%s: send DTMF '%c'.\n",
i->vname, digit);
if ((capi_controllers[i->controller]->dtmf == 0) || (i->doDTMF > 0)) {
/* let * fake it */
return -1;
}
ret = capi_sendf(i, 0, CAPI_FACILITY_REQ, i->NCCI, get_capi_MessageNumber(),
"w(www(b)())",
FACILITYSELECTOR_DTMF,
3, /* send DTMF digit */
CAPI_DTMF_DURATION, /* XXX: duration comes from asterisk in 1.4 */
CAPI_DTMF_DURATION,
digit
);
if (ret == 0) {
cc_verbose(3, 0, VERBOSE_PREFIX_4 "%s: sent dtmf '%c'\n",
i->vname, digit);
}
return ret;
}
/*
* send a digit
*/
#if defined(CC_AST_HAS_VERSION_1_4) && defined(CC_AST_HAS_SEND_DIGIT_END_DURATION)
static int pbx_capi_send_digit(struct ast_channel *c, char digit, unsigned int duration)
#else
static int pbx_capi_send_digit(struct ast_channel *c, char digit)
#endif
{
struct capi_pvt *i = CC_CHANNEL_PVT(c);
char did[2];
int ret = 0;
if (i == NULL) {
cc_log(LOG_ERROR, "No interface!\n");
return -1;
}
#ifdef CC_AST_HAS_VERSION_11_0
const enum ast_channel_state cur_state = ast_channel_state(c);
#else /* !defined(CC_AST_HAS_VERSION_11_0) */
const enum ast_channel_state cur_state = c->_state;
#endif /* defined(CC_AST_HAS_VERSION_11_0) */
cc_verbose(3, 1, VERBOSE_PREFIX_3 "%s: send_digit '%c' in state %d(%d)\n",
i->vname, digit, i->state, cur_state);
cc_mutex_lock(&i->lock);
if ((cur_state == AST_STATE_DIALING) &&
(i->state != CAPI_STATE_DISCONNECTING)) {
if (!(i->isdnstate & CAPI_ISDN_STATE_ISDNPROGRESS)) {
did[0] = digit;
did[1] = 0;
strncat(i->dnid, did, sizeof(i->dnid) - 1);
update_channel_name(i);
if ((i->isdnstate & CAPI_ISDN_STATE_SETUP_ACK) &&
(i->doOverlap == 0)) {
ret = capi_send_info_digits(i, &digit, 1);
} else {
/* if no SETUP-ACK yet, add it to the overlap list */
strncat(i->overlapdigits, &digit, 1);
i->doOverlap = 1;
}
cc_mutex_unlock(&i->lock);
return ret;
} else {
/* if PROGRESS arrived, we sent as DTMF */
ret = capi_send_dtmf_digits(i, digit);
cc_mutex_unlock(&i->lock);
return ret;
}
}
if (i->state == CAPI_STATE_CONNECTED) {
/* we have a real connection, so send real DTMF */
ret = capi_send_dtmf_digits(i, digit);
}
cc_mutex_unlock(&i->lock);
return ret;
}
/*
* send ALERT to ISDN line
*/
static int pbx_capi_alert(struct ast_channel *c)
{
struct capi_pvt *i = CC_CHANNEL_PVT(c);
unsigned char *facilityarray = NULL;
if ((i->state != CAPI_STATE_INCALL) &&
(i->state != CAPI_STATE_DID)) {
cc_verbose(2, 1, VERBOSE_PREFIX_3 "%s: attempting ALERT in state %d\n",
i->vname, i->state);
return -1;
}
facilityarray = alloca(CAPI_MAX_FACILITYDATAARRAY_SIZE);
facilityarray[0] = 0;
cc_qsig_add_call_alert_data(facilityarray, i, c);
pbx_capi_add_diva_protocol_independent_extension (i, facilityarray, c, "CALLEDNAME");
if (capi_sendf(NULL, 0, CAPI_ALERT_REQ, i->PLCI, get_capi_MessageNumber(),
"(()()()s())",
facilityarray
) != 0) {
return -1;
}
i->state = CAPI_STATE_ALERTING;
#ifdef CC_AST_HAS_VERSION_13_0
ast_channel_lock(c);
#endif
ast_setstate(c, AST_STATE_RING);
#ifdef CC_AST_HAS_VERSION_13_0
ast_channel_unlock(c);
#endif
return 0;
}
/*!
\brief Send CALL PROCEEDING (if supported by hardware)
\note Sending of Proceeding is not defined by CAPI spec.
Diva hardware uses ALERT with sending complete set.
Other hardware can send alert only
*/
static int pbx_capi_signal_proceeding(struct ast_channel *c, char *param)
{
static const unsigned char sending_complete[] = { 2, 1, 0 };
struct capi_pvt *i = CC_CHANNEL_PVT(c);
if ((i->state != CAPI_STATE_INCALL) &&
(i->state != CAPI_STATE_DID)) {
cc_verbose(2, 1, VERBOSE_PREFIX_3 "%s: attempting PROCEEDING in state %d\n",
i->vname, i->state);
}
if ((i->ntmode == 0)
/*! \todo || (capi_controllers[i->controlle]->mnufacturer != ManufacturerDiva) */) {
return (pbx_capi_alert(c));
}
if (((i->isdnstate2 & CAPI_ISDN_STATE2_PROCEEDING) != 0) ||
((i->isdnstate2 & CAPI_ISDN_STATE2_PROCEEDING_PENDING) != 0)) {
return 0;
}
if (capi_sendf(NULL, 0, CAPI_ALERT_REQ, i->PLCI, get_capi_MessageNumber(),
"(()()()()s)", sending_complete) != 0) {
return -1;
}
i->isdnstate2 |= CAPI_ISDN_STATE2_PROCEEDING_PENDING;
return 0;
}
/*
* cleanup the interface
*/
static void interface_cleanup(struct capi_pvt *i)
{
if (!i)
return;
cc_verbose(2, 1, VERBOSE_PREFIX_2 "%s: Interface cleanup PLCI=%#x\n",
i->vname, i->PLCI);
#ifdef DIVA_STREAMING
capi_DivaStreamingRemove(i);
#endif
pbx_capi_voicecommand_cleanup(i);
if (i->readerfd != -1) {
close(i->readerfd);
i->readerfd = -1;
}
if (i->writerfd != -1) {
close(i->writerfd);
i->writerfd = -1;
}
i->isdnstate = 0;
i->isdnstate2 = 0;
i->cause = 0;
i->fsetting = 0;
i->whentohangup = 0;
i->whentoqueuehangup = 0;
i->whentoretrieve = 0;
i->FaxState &= ~CAPI_FAX_STATE_MASK;
i->PLCI = 0;
i->MessageNumber = 0;
i->NCCI = 0;
i->onholdPLCI = 0;
i->doEC = i->doEC_global;
i->ccbsnrhandle = 0;
memset(i->cid, 0, sizeof(i->cid));
memset(i->dnid, 0, sizeof(i->dnid));
i->cid_ton = 0;
i->rtpcodec = 0;
if (i->rtp) {
#ifdef CC_AST_HAS_RTP_ENGINE_H
ast_rtp_instance_destroy(i->rtp);
#else
ast_rtp_destroy(i->rtp);
#endif
i->rtp = NULL;
}
interface_cleanup_qsig(i);
i->peer = NULL;
i->owner = NULL;
#ifdef CC_AST_HAS_VERSION_1_4
ast_module_unref(myself);
#endif
i->used = NULL;
i->reserved = 0;
if (i->channeltype == CAPI_CHANNELTYPE_NULL) {
capi_interface_task(i, CAPI_INTERFACE_TASK_NULLIFREMOVE);
}
return;
}
/*
* disconnect b3 and wait for confirmation
*/
static void cc_disconnect_b3(struct capi_pvt *i, int wait)
{
struct timespec abstime;
if (!(i->isdnstate & (CAPI_ISDN_STATE_B3_UP | CAPI_ISDN_STATE_B3_PEND)))
return;
if (wait) {
cc_mutex_lock(&i->lock);
capi_sendf(i, 1, CAPI_DISCONNECT_B3_REQ, i->NCCI, get_capi_MessageNumber(), "()");
} else {
capi_sendf(NULL, 0, CAPI_DISCONNECT_B3_REQ, i->NCCI, get_capi_MessageNumber(), "()");
return;
}
/* wait for the B3 layer to go down */
if ((i->isdnstate & (CAPI_ISDN_STATE_B3_UP | CAPI_ISDN_STATE_B3_PEND))) {
i->waitevent = CAPI_WAITEVENT_B3_DOWN;
abstime.tv_sec = time(NULL) + 2;
abstime.tv_nsec = 0;
cc_verbose(4, 1, "%s: wait for b3 down.\n",
i->vname);
if (ast_cond_timedwait(&i->event_trigger, &i->lock, &abstime) != 0) {
cc_log(LOG_WARNING, "%s: timed out waiting for b3 down.\n",
i->vname);
} else {
cc_verbose(4, 1, "%s: cond signal received for b3 down.\n",
i->vname);
}
}
cc_mutex_unlock(&i->lock);
if ((i->isdnstate & CAPI_ISDN_STATE_B3_UP)) {
cc_log(LOG_ERROR, "capi disconnect b3: didn't disconnect NCCI=0x%08x\n",
i->NCCI);
}
return;
}
/*
* send CONNECT_B3_REQ
*/
void cc_start_b3(struct capi_pvt *i)
{
if (!(i->isdnstate & (CAPI_ISDN_STATE_B3_UP | CAPI_ISDN_STATE_B3_PEND))) {
i->isdnstate |= CAPI_ISDN_STATE_B3_PEND;
capi_sendf(NULL, 0, CAPI_CONNECT_B3_REQ, i->PLCI, get_capi_MessageNumber(),
"s", capi_rtp_ncpi(i));
cc_verbose(4, 1, VERBOSE_PREFIX_3 "%s: sent CONNECT_B3_REQ PLCI=%#x\n",
i->vname, i->PLCI);
}
}
/*
* signal 'progress' to PBX
*/
static void send_progress(struct capi_pvt *i)
{
struct ast_frame fr = { AST_FRAME_CONTROL, };
if (i->doB3 != CAPI_B3_DONT &&
!(i->fsetting & CAPI_FSETTING_EARLYB3_ONLY_WHEN_TONES_AVAIL)) {
/* we do early B3 Connect */
cc_start_b3(i);
}
if (!(i->isdnstate & CAPI_ISDN_STATE_PROGRESS)) {
i->isdnstate |= CAPI_ISDN_STATE_PROGRESS;
FRAME_SUBCLASS_INTEGER(fr.subclass) = AST_CONTROL_PROGRESS;
local_queue_frame(i, &fr);
}
return;
}
/*
* send disconnect_req
*/
static void capi_send_disconnect(unsigned int PLCI)
{
if (PLCI == 0) {
return;
}
capi_sendf(NULL, 0, CAPI_DISCONNECT_REQ, PLCI, get_capi_MessageNumber(), "()");
}
/*
* hangup a line (CAPI messages)
* (this must be called with i->lock held)
*/
void capi_activehangup(struct capi_pvt *i, int state)
{
struct ast_channel *c = i->owner;
const char *cause;
if (c) {
#ifdef CC_AST_HAS_VERSION_11_0
int hangupcause = ast_channel_hangupcause(c);
#else /* !defined(CC_AST_HAS_VERSION_11_0) */
int hangupcause = c->hangupcause;
#endif /* defined(CC_AST_HAS_VERSION_11_0) */
i->cause = hangupcause;
if ((cause = pbx_builtin_getvar_helper(c, "PRI_CAUSE"))) {
i->cause = atoi(cause);
}
if ((i->isdnstate & CAPI_ISDN_STATE_ECT)) {
cc_verbose(3, 1, VERBOSE_PREFIX_3 "%s: activehangup ECT call\n",
i->vname);
}
}
cc_verbose(2, 1, VERBOSE_PREFIX_3 "%s: activehangingup (cause=%d) for PLCI=%#x\n",
i->vname, i->cause, i->PLCI);
if ((state == CAPI_STATE_ALERTING) ||
(state == CAPI_STATE_DID) || (state == CAPI_STATE_INCALL)) {
capi_sendf(NULL, 0, CAPI_CONNECT_RESP, i->PLCI, i->MessageNumber,
"w()()()()()",
(i->cause) ? (0x3480 | (i->cause & 0x7f)) : 2);
return;
}
if ((i->fsetting & CAPI_FSETTING_STAYONLINE)) {
/* user has requested to leave channel online for further actions
like CCBS */
cc_verbose(2, 1, VERBOSE_PREFIX_4 "%s: disconnect deferred, stay-online mode PLCI=%#x\n",
i->vname, i->PLCI);
i->whentohangup = time(NULL) + 18; /* timeout 18 seconds */
return;
}
/* active disconnect */
if ((i->isdnstate & CAPI_ISDN_STATE_B3_UP)) {
cc_disconnect_b3(i, 0);
return;
}
if (i->channeltype == CAPI_CHANNELTYPE_NULL) {
if (i->PLCI == 0) {
interface_cleanup(i);
return;
}
}
if ((state == CAPI_STATE_CONNECTED) || (state == CAPI_STATE_CONNECTPENDING) ||
(state == CAPI_STATE_ANSWERING) || (state == CAPI_STATE_ONHOLD)) {
if (i->PLCI == 0) {
/* CONNECT_CONF not received yet? */
capi_wait_conf(i, CAPI_CONNECT_CONF);
}
capi_send_disconnect(i->PLCI);
}
return;
}
/*
* PBX tells us to hangup a line
*/
static int pbx_capi_hangup(struct ast_channel *c)
{
struct capi_pvt *i = CC_CHANNEL_PVT(c);
int cleanup = 0;
int state;
/*
* hmm....ok...this is called to free the capi interface (passive disconnect)
* or to bring down the channel (active disconnect)
*/
if (i == NULL) {
cc_log(LOG_ERROR, "channel has no interface!\n");
return -1;
}
cc_mutex_lock(&i->lock);
state = i->state;
cc_verbose(3, 0, VERBOSE_PREFIX_2 "%s: " CC_MESSAGE_BIGNAME
" Hangingup for PLCI=%#x in state %d\n", i->vname, i->PLCI, state);
/* are we down, yet? */
if (state != CAPI_STATE_DISCONNECTED) {
/* no */
i->state = CAPI_STATE_DISCONNECTING;
} else {
cleanup = 1;
}
if ((i->doDTMF > 0) && (i->vad != NULL)) {
ast_dsp_free(i->vad);
i->vad = NULL;
}
if (cleanup) {
/* disconnect already done, so cleanup */
interface_cleanup(i);
} else {
/* not disconnected yet, we must actively do it */
capi_activehangup(i, state);
}
i->owner = NULL;
#ifdef CC_AST_HAS_VERSION_1_4
ast_module_unref(myself);
#endif
#ifdef CC_AST_HAS_VERSION_11_0
ast_channel_tech_pvt_set(c, NULL);
#else /* !defined(CC_AST_HAS_VERSION_11_0) */
CC_CHANNEL_PVT(c) = NULL;
#endif /* defined(CC_AST_HAS_VERSION_11_0) */
cc_mutex_unlock(&i->lock);
#ifdef CC_AST_HAS_VERSION_13_0
ast_channel_lock(c);
#endif
ast_setstate(c, AST_STATE_DOWN);
#ifdef CC_AST_HAS_VERSION_13_0
ast_channel_unlock(c);
#endif
#ifdef CC_AST_HAS_VERSION_1_4
ast_atomic_fetchadd_int(&usecnt, -1);
#else
cc_mutex_lock(&usecnt_lock);
usecnt--;
cc_mutex_unlock(&usecnt_lock);
#endif
ast_update_use_count();
return 0;
}
static void pbx_capi_call_build_calling_party_number(
struct ast_channel *c,
char* calling,
int max_calling,
int use_defaultcid,
const char* ocid)
{
struct capi_pvt *i = CC_CHANNEL_PVT(c);
const char *ton;
char callerid[AST_MAX_EXTENSION];
int CLIR;
int callernplan;
#ifdef CC_AST_HAS_VERSION_11_0
if (ast_channel_connected(c)->id.number.valid) {
CLIR = ast_channel_connected(c)->id.number.presentation;
callernplan = ast_channel_connected(c)->id.number.plan & 0x7f;
} else {
CLIR = 0;
callernplan = 0;
}
if (ast_channel_connected(c)->id.number.valid && !ast_strlen_zero(ast_channel_connected(c)->id.number.str)) {
ast_copy_string(callerid, ast_channel_connected(c)->id.number.str, sizeof(callerid));
} else {
memset(callerid, 0, sizeof(callerid));
}
#elif defined(CC_AST_HAS_VERSION_1_8)
if (c->connected.id.number.valid) {
CLIR = c->connected.id.number.presentation;
callernplan = c->connected.id.number.plan & 0x7f;
} else {
CLIR = 0;
callernplan = 0;
}
if (c->connected.id.number.valid && !ast_strlen_zero(c->connected.id.number.str)) {
ast_copy_string(callerid, c->connected.id.number.str, sizeof(callerid));
} else {
memset(callerid, 0, sizeof(callerid));
}
#else /* !(defined(CC_AST_HAS_VERSION_11_0) || defined(CC_AST_HAS_VERSION_1_8)) */
CLIR = c->cid.cid_pres;
callernplan = c->cid.cid_ton & 0x7f;
if (c->cid.cid_num) {
cc_copy_string(callerid, c->cid.cid_num, sizeof(callerid));
} else {
memset(callerid, 0, sizeof(callerid));
}
#endif /* defined(CC_AST_HAS_VERSION_11_0) || defined(CC_AST_HAS_VERSION_1_8) */
if (use_defaultcid) {
cc_copy_string(callerid, i->defaultcid, sizeof(callerid));
} else if (ocid) {
cc_copy_string(callerid, ocid, sizeof(callerid));
}
cc_copy_string(i->cid, callerid, sizeof(i->cid));
if ((ton = pbx_builtin_getvar_helper(c, "CALLERTON"))) {
callernplan = atoi(ton) & 0x7f;
}
i->cid_ton = callernplan;
calling[0] = strlen(callerid) + 2;
calling[1] = callernplan;
calling[2] = 0x80 | (CLIR & 0x63);
strncpy(&calling[3], callerid, max_calling - 4);
#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(1, 1, VERBOSE_PREFIX_2 "%s: Call %s %s%s (pres=0x%02x, ton=0x%02x)\n",
i->vname, cur_name, i->doB3 ? "with B3 ":" ",
i->doOverlap ? "overlap":"", CLIR, callernplan);
}
/*
* PBX tells us to make a call
*/
#ifdef CC_AST_HAS_VERSION_11_0
static int pbx_capi_call(struct ast_channel *c, const char *idest, int timeout)
#else /* !defined(CC_AST_HAS_VERSION_11_0) */
static int pbx_capi_call(struct ast_channel *c, void *idest, int timeout)
#endif /* defined(CC_AST_HAS_VERSION_11_0) */
{
struct capi_pvt *i = CC_CHANNEL_PVT(c);
char *dest, *interface, *param, *ocid;
char buffer[AST_MAX_EXTENSION];
char called[AST_MAX_EXTENSION], calling[AST_MAX_EXTENSION];
int use_defaultcid = 0;
_cword cip;
const char *p;
char *osa = NULL;
char *dsa = NULL;
char callingsubaddress[AST_MAX_EXTENSION];
char calledsubaddress[AST_MAX_EXTENSION];
int doqsig;
char *sending_complete;
unsigned char *facilityarray = NULL, *bc_s = NULL, *llc_s = 0, *hlc_s = 0;
int no_sending_complete = 0;
MESSAGE_EXCHANGE_ERROR error;
cc_copy_string(buffer, idest, sizeof(buffer));
capi_parse_dialstring(buffer, &interface, &dest, &param, &ocid);
/* init param settings */
i->doB3 = CAPI_B3_DONT;
i->doOverlap = 0;
memset(i->overlapdigits, 0, sizeof(i->overlapdigits));
doqsig = i->qsigfeat || i->divaqsig;
/* parse the parameters */
while ((param) && (*param)) {
switch (*param) {
case 'b': /* always B3 */
if (i->doB3 != CAPI_B3_DONT)
cc_log(LOG_WARNING, "B3 already set in '%s'\n", idest);
i->doB3 = CAPI_B3_ALWAYS;
break;
case 'B': /* only do B3 on successfull calls */
if (i->doB3 != CAPI_B3_DONT)
cc_log(LOG_WARNING, "B3 already set in '%s'\n", idest);
i->doB3 = CAPI_B3_ON_SUCCESS;
break;
case 't': /* enable B3 only on in-band tones available indication */
if ((i->fsetting & CAPI_FSETTING_EARLYB3_ONLY_WHEN_TONES_AVAIL))
cc_log(LOG_WARNING,
"B3 on in-band tones avail only already set in '%s'\n",
idest);
i->fsetting |= CAPI_FSETTING_EARLYB3_ONLY_WHEN_TONES_AVAIL;
break;
case 'o': /* overlap sending of digits */
if (i->doOverlap)
cc_log(LOG_WARNING, "Overlap already set in '%s'\n", idest);
i->doOverlap = 1;
break;
case 'c': /* Do not send sending complete */
if (no_sending_complete != 0)
cc_log(LOG_WARNING, "No sending complete already set in '%s'\n", idest);
no_sending_complete = 1;
break;
case 'd': /* use default cid */
if (use_defaultcid)
cc_log(LOG_WARNING, "Default CID already set in '%s'\n", idest);
use_defaultcid = 1;
break;
case 's': /* stay online */
if ((i->fsetting & CAPI_FSETTING_STAYONLINE))
cc_log(LOG_WARNING, "'stay-online' already set in '%s'\n", idest);
i->fsetting |= CAPI_FSETTING_STAYONLINE;
break;
case 'G': /* early bridge */
if ((i->fsetting & CAPI_FSETTING_EARLY_BRIDGE))
cc_log(LOG_WARNING, "'early-bridge' already set in '%s'\n", idest);
i->fsetting |= CAPI_FSETTING_EARLY_BRIDGE;
break;
case 'q': /* disable QSIG */
cc_verbose(4, 0, VERBOSE_PREFIX_4 "%s: QSIG extensions for this call disabled\n",
i->vname);
doqsig = 0;
break;
default:
cc_log(LOG_WARNING, "Unknown parameter '%c' in '%s', ignoring.\n",
*param, idest);
}
param++;
}
if (((!dest) || (!dest[0])) && (i->doB3 != CAPI_B3_ALWAYS)) {
cc_log(LOG_ERROR, "No destination or dialtone requested in '%s'\n", idest);
return -1;
}
if (((!dest) || (!dest[0])) &&
i->fsetting & CAPI_FSETTING_EARLYB3_ONLY_WHEN_TONES_AVAIL)
cc_log(LOG_WARNING,
"dialtone request with B3 on in-band tones avail setting might not work on all exchanges in '%s'\n",
idest);
if (i->doB3 == CAPI_B3_DONT &&
i->fsetting & CAPI_FSETTING_EARLYB3_ONLY_WHEN_TONES_AVAIL)
cc_log(LOG_WARNING,
"B3 on in-band tones avail setting ignored when early B3 is disabled in '%s'\n",
idest);
i->peer = cc_get_peer_link_id(pbx_builtin_getvar_helper(c, "CAPIPEERLINKID"));
i->outgoing = 1;
#ifdef CC_AST_HAS_VERSION_11_0
unsigned short cur_xfercap = ast_channel_transfercapability(c);
#else /* !defined(CC_AST_HAS_VERSION_11_0) */
unsigned short cur_xfercap = c->transfercapability;
#endif /* defined(CC_AST_HAS_VERSION_11_0) */
i->transfercapability = cur_xfercap;
i->isdnstate |= CAPI_ISDN_STATE_PBX;
i->state = CAPI_STATE_CONNECTPENDING;
ast_setstate(c, AST_STATE_DIALING);
i->MessageNumber = get_capi_MessageNumber();
/* if this is a CCBS/CCNR callback call */
if (i->ccbsnrhandle) {
_cword rbref;
cip = (_cword)tcap2cip(i->transfercapability);
i->doOverlap = 0;
rbref = capi_ccbsnr_take_ref(i->ccbsnrhandle);
if ((rbref == 0xdead) ||
((capi_sendf(NULL, 0, CAPI_FACILITY_REQ, i->controller, i->MessageNumber,
"w(w(www(wwwsss())()()()()))",
FACILITYSELECTOR_SUPPLEMENTARY,
0x0012, /* CCBS call */
rbref, /* reference */
cip, /* CIP */
0, /* reserved */
/* B protocol */
b_protocol_table[i->bproto].b1protocol,
b_protocol_table[i->bproto].b2protocol,
b_protocol_table[i->bproto].b3protocol,
diva_get_b1_conf(i),
b_protocol_table[i->bproto].b2configuration,
b_protocol_table[i->bproto].b3configuration
/* */ /* BC */
/* */ /* LLC */
/* */ /* HLC */
/* */ /* Additional Info */
)))) {
i->state = CAPI_STATE_DISCONNECTED;
ast_setstate(c, AST_STATE_RESERVED);
return 1;
}
return 0;
}
if ((p = pbx_builtin_getvar_helper(c, "CALLINGSUBADDRESS"))) {
callingsubaddress[0] = strlen(p) + 1;
callingsubaddress[1] = 0x80;
strncpy(&callingsubaddress[2], p, sizeof(callingsubaddress) - 3);
osa = callingsubaddress;
}
if ((p = pbx_builtin_getvar_helper(c, "CALLEDSUBADDRESS"))) {
calledsubaddress[0] = strlen(p) + 1;
calledsubaddress[1] = 0x80;
strncpy(&calledsubaddress[2], p, sizeof(calledsubaddress) - 3);
dsa = calledsubaddress;
}
if ((p = pbx_builtin_getvar_helper(c, "CAPI_CIP"))) {
cip = (_cword)atoi(p);
i->transfercapability = cip2tcap(cip);
} else {
cip = tcap2cip(i->transfercapability);
}
#if defined(CC_FORMAT_G722) || defined(CC_FORMAT_SIREN7) || defined(CC_FORMAT_SIREN14) || defined(CC_FORMAT_SLINEAR16)
if (capi_tcap_is_digital(i->transfercapability) == 0 && i->bproto == CC_BPROTO_VOCODER) {
static unsigned char llc_s_template[] = { 0x04, 0x00, 0xc0, 0x90, 0xa5 };
static unsigned char hlc_s_template[] = { 0x02, 0x91, 0x81 };
switch(i->codec) {
#if defined(CC_FORMAT_G722)
case CC_FORMAT_G722:
llc_s = llc_s_template;
hlc_s = hlc_s_template;
break;
#endif
#if defined(CC_FORMAT_SIREN7)
case CC_FORMAT_SIREN7:
llc_s = llc_s_template;
hlc_s = hlc_s_template;
break;
#endif
#if defined(CC_FORMAT_SIREN14)
case CC_FORMAT_SIREN14:
llc_s = llc_s_template;
hlc_s = hlc_s_template;
break;
#endif
#if defined(CC_FORMAT_SLINEAR16)
case CC_FORMAT_SLINEAR16:
llc_s = llc_s_template;
hlc_s = hlc_s_template;
break;
#endif
}
}
#endif
if (capi_tcap_is_digital(i->transfercapability)) {
i->bproto = CC_BPROTO_TRANSPARENT;
cc_verbose(4, 0, VERBOSE_PREFIX_2 "%s: is digital call, set proto to TRANSPARENT\n",
i->vname);
}
if ((i->doOverlap) || (!strlen(dest))) {
called[0] = 1;
sending_complete = "\x00";
if (strlen(dest)) {
cc_copy_string(i->overlapdigits, dest, sizeof(i->overlapdigits));
} else {
i->doOverlap = 0;
}
} else {
called[0] = strlen(dest) + 1;
sending_complete = (no_sending_complete == 0) ? "\x02\x01\x00" : "\x00";
}
if ((p = pbx_builtin_getvar_helper(c, "CALLEDTON"))) {
unsigned char ton = (unsigned char)atoi(p);
called[1] = ton | 0x80;
} else {
called[1] = 0x80;
}
strncpy(&called[2], dest, sizeof(called) - 3);
pbx_capi_call_build_calling_party_number(c, calling, sizeof(calling), use_defaultcid, ocid);
if (doqsig != 0) {
facilityarray = alloca(CAPI_MAX_FACILITYDATAARRAY_SIZE);
facilityarray[0] = 0;
if (i->qsigfeat != 0)
cc_qsig_add_call_setup_data(facilityarray, i, c);
pbx_capi_add_diva_protocol_independent_extension (i, facilityarray, NULL, "CALLED/CONNECTED NAME");
}
#ifdef DIVA_STREAMING
i->diva_stream_entry = 0;
if (pbx_capi_streaming_supported (i) != 0) {
capi_DivaStreamingOn(i, 1, i->MessageNumber);
}
#endif
error = capi_sendf(NULL, 0, CAPI_CONNECT_REQ, i->controller, i->MessageNumber,
"wssss(wwwsss())sss((w)()()ss)",
cip, /* CIP value */
called, /* called party number */
calling, /* calling party number */
dsa, /* called party subaddress */
osa, /* calling party subaddress */
/* B protocol */
b_protocol_table[i->bproto].b1protocol,
b_protocol_table[i->bproto].b2protocol,
b_protocol_table[i->bproto].b3protocol,
diva_get_b1_conf(i),
b_protocol_table[i->bproto].b2configuration,
b_protocol_table[i->bproto].b3configuration,
bc_s, /* BC */
llc_s, /* LLC */
hlc_s, /* HLC */
/* Additional Info */
0x0000, /* B channel info */
/* Keypad facility */
/* User-User data */
facilityarray, /* Facility data array */
sending_complete /* Sending complete */
);
if (error) {
i->state = CAPI_STATE_DISCONNECTED;
ast_setstate(c, AST_STATE_RESERVED);
return error;
}
/* now we shall return .... the rest has to be done by handle_msg */
return 0;
}
_cstruct diva_get_b1_conf (struct capi_pvt *i) {
_cstruct b1conf = b_protocol_table[i->bproto].b1configuration;
if (i->bproto == CC_BPROTO_VOCODER) {
switch(i->codec) {
case CC_FORMAT_ALAW:
b1conf = (_cstruct)"\x06\x08\x04\x03\x00\xa0\x00";
break;
case CC_FORMAT_ULAW:
b1conf = (_cstruct)"\x06\x00\x04\x03\x00\xa0\x00";
break;
case CC_FORMAT_GSM:
b1conf = (_cstruct)"\x06\x03\x04\x0f\x00\xa0\x00";
break;
case CC_FORMAT_G723_1:
b1conf = (_cstruct)"\x06\x04\x04\x01\x00\xa0\x00";
break;
case CC_FORMAT_G726:
b1conf = (_cstruct)"\x06\x02\x04\x0f\x00\xa0\x00";
break;
case CC_FORMAT_ILBC: /* 30 mSec 240 samples */
b1conf = (_cstruct)"\x06\x1b\x04\x03\x00\xf0\x00";
break;
case CC_FORMAT_G729A:
b1conf = (_cstruct)"\x06\x12\x04\x0f\x00\xa0\x00";
break;
#ifdef CC_FORMAT_G722
case CC_FORMAT_G722:
b1conf = (_cstruct)"\x06\x09\x04\x03\x00\xa0\x00";
break;
#endif
#ifdef CC_FORMAT_SIREN7
case CC_FORMAT_SIREN7:
b1conf = (_cstruct)"\x06\x24\x04\x0f\x02\xa0\x00"; /* 32 kBit/s */
break;
#endif
#ifdef CC_FORMAT_SIREN14
case CC_FORMAT_SIREN14:
b1conf = (_cstruct)"\x06\x24\x04\x0f\x07\xa0\x00"; /* 48 kBit/s */
break;
#endif
#if defined(CC_FORMAT_SLINEAR)
case CC_FORMAT_SLINEAR:
b1conf = (_cstruct)"\x06\x01\x04\x0f\x01\xa0\x00";
break;
#endif
#if defined(CC_FORMAT_SLINEAR16)
case CC_FORMAT_SLINEAR16:
b1conf = (_cstruct)"\x06\x01\x04\x0f\x05\xa0\x00";
break;
#endif
default:
cc_log(LOG_ERROR, "%s: format %s(%d) invalid.\n",
i->vname, cc_getformatname(i->codec), i->codec);
break;
}
}
return (b1conf);
}
/*
* answer a capi call
*/
static int capi_send_answer(struct ast_channel *c, _cstruct b3conf)
{
struct capi_pvt *i = CC_CHANNEL_PVT(c);
char buf[CAPI_MAX_STRING];
const char *dnid;
const char *connectednumber;
unsigned char *facilityarray = NULL;
_cstruct b1conf;
unsigned char* llc_s = NULL;
if (i->state == CAPI_STATE_DISCONNECTED) {
cc_verbose(3, 0, VERBOSE_PREFIX_2 "%s: Not answering disconnected call.\n",
i->vname);
return -1;
}
if ((i->isdnmode == CAPI_ISDNMODE_DID) &&
((strlen(i->incomingmsn) < strlen(i->dnid)) &&
(strcmp(i->incomingmsn, "*")))) {
dnid = i->dnid + strlen(i->incomingmsn);
} else {
dnid = i->dnid;
}
if ((connectednumber = pbx_builtin_getvar_helper(c, "CONNECTEDNUMBER"))) {
dnid = connectednumber;
}
if (strlen(dnid)) {
const char *p = pbx_builtin_getvar_helper(c, "CALLEDTON");
buf[0] = strlen(dnid) + 2;
buf[1] = (p != 0) ? (((unsigned char)atoi(p)) & ~0x80) : 0x01;
buf[2] = 0x80;
strncpy(&buf[3], dnid, sizeof(buf) - 4);
} else {
buf[0] = 0x00;
}
if (!b3conf) {
b3conf = b_protocol_table[i->bproto].b3configuration;
}
b1conf = diva_get_b1_conf(i);
cc_verbose(3, 0, VERBOSE_PREFIX_2 "%s: Answering for %s\n",
i->vname, dnid);
facilityarray = alloca(CAPI_MAX_FACILITYDATAARRAY_SIZE);
cc_qsig_add_call_answer_data(facilityarray, i, c);
pbx_capi_add_diva_protocol_independent_extension(i, facilityarray, c, "CONNECTEDNAME");
if (i->ntmode) {
/* in NT-mode we send the current local time to device */
capi_facility_add_datetime(facilityarray);
}
#if defined(CC_FORMAT_G722) || defined(CC_FORMAT_SIREN7) || defined(CC_FORMAT_SIREN14) || defined(CC_FORMAT_SLINEAR16)
if (i->bproto == CC_BPROTO_VOCODER) {
static unsigned char llc_s_template[] = { 0x03, 0x91, 0x90, 0xa5 };
switch(i->codec) {
#if defined(CC_FORMAT_G722)
case CC_FORMAT_G722:
llc_s = llc_s_template;
break;
#endif
#if defined(CC_FORMAT_SIREN7)
case CC_FORMAT_SIREN7:
llc_s = llc_s_template;
break;
#endif
#if defined(CC_FORMAT_SIREN14)
case CC_FORMAT_SIREN14:
llc_s = llc_s_template;
break;
#endif
#if defined(CC_FORMAT_SLINEAR16)
case CC_FORMAT_SLINEAR16:
llc_s = llc_s_template;
break;
#endif
}
}
#endif
if (capi_sendf(NULL, 0, CAPI_CONNECT_RESP, i->PLCI, i->MessageNumber,
"w(wwwssss)s()s(()()()s())",
0, /* accept call */
/* B protocol */
b_protocol_table[i->bproto].b1protocol,
b_protocol_table[i->bproto].b2protocol,
b_protocol_table[i->bproto].b3protocol,
b1conf,
b_protocol_table[i->bproto].b2configuration,
b3conf,
capi_set_global_configuration(i),
buf, /* connected number */
/* connected subaddress */
llc_s,/* LLC */
/* Additional info */
facilityarray
) != 0) {
return -1;
}
i->state = CAPI_STATE_ANSWERING;
i->doB3 = CAPI_B3_DONT;
i->outgoing = 0;
return 0;
}
/*
* PBX tells us to answer a call
*/
static int pbx_capi_answer(struct ast_channel *c)
{
struct capi_pvt *i = CC_CHANNEL_PVT(c);
int ret;
i->bproto = ((i->bproto == CC_BPROTO_VOCODER) && (i->codec != 0)) ? i->bproto : CC_BPROTO_TRANSPARENT;
if (i->rtp) {
if (!capi_tcap_is_digital(i->transfercapability))
i->bproto = CC_BPROTO_RTP;
}
ret = capi_send_answer(c, NULL);
return ret;
}
/*
* read for a channel
*/
static struct ast_frame *pbx_capi_read(struct ast_channel *c)
{
struct capi_pvt *i = CC_CHANNEL_PVT(c);
struct ast_frame *f;
f = capi_read_pipeframe(i);
if (f != NULL) {
if (f->frametype == AST_FRAME_VOICE) {
if ((f->datalen > 0) && (i->doDTMF > 0) && (i->vad != NULL)) {
f = ast_dsp_process(c, i->vad, f);
}
#ifdef CC_AST_HAS_VERSION_1_4
} else if (f->frametype == AST_FRAME_DTMF) {
/* Work around problem with recognition of fast sequences of events,
* see main/channel.c for details
* ... or better yet, get rid of this ugly hack alltogether.
*/
#ifdef CC_AST_HAS_VERSION_11_0
if (!(ast_test_flag(ast_channel_flags(c), AST_FLAG_END_DTMF_ONLY) ||
ast_test_flag(ast_channel_flags(c), AST_FLAG_EMULATE_DTMF) ||
ast_test_flag(ast_channel_flags(c), AST_FLAG_IN_DTMF))) {
ast_set_flag(ast_channel_flags(c), AST_FLAG_IN_DTMF);
struct timeval back_to_the_past = ast_tvsub(ast_tvnow(), ast_tv(0, 250*1000 /* to msec */));
ast_channel_dtmf_tv_set(c, &back_to_the_past);
#else /* !defined(CC_AST_HAS_VERSION_11_0 */
if (!(ast_test_flag(c, AST_FLAG_END_DTMF_ONLY) ||
ast_test_flag(c, AST_FLAG_EMULATE_DTMF) ||
ast_test_flag(c, AST_FLAG_IN_DTMF))) {
ast_set_flag(c, AST_FLAG_IN_DTMF);
c->dtmf_tv = ast_tvsub(ast_tvnow(),ast_tv(0,250*1000));
#endif /* defined(CC_AST_HAS_VERSION_11_0) */
if (!f->len)
f->len = 100;
}
#endif /* defined(CC_AST_HAS_VERSION_1_4) */
}
}
return f;
}
/*
* PBX tells us to write for a channel
*/
static int pbx_capi_write(struct ast_channel *c, struct ast_frame *f)
{
struct capi_pvt *i = CC_CHANNEL_PVT(c);
int ret = 0;
ret = capi_write_frame(i, f);
return ret;
}
/*
* new channel (masq)
*/
static int pbx_capi_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
{
struct capi_pvt *i = CC_CHANNEL_PVT(newchan);
#ifdef CC_AST_HAS_VERSION_11_0
const char *old_name = ast_channel_name(oldchan);
const char *new_name = ast_channel_name(newchan);
#else /* !defined(CC_AST_HAS_VERSION_11_0) */
const char *old_name = oldchan->name;
const char *new_name = newchan->name;
#endif /* defined(CC_AST_HAS_VERSION_11_0) */
cc_verbose(3, 1, VERBOSE_PREFIX_2 "%s: %s fixup now %s\n",
i->vname, old_name, new_name);
cc_mutex_lock(&i->lock);
i->owner = newchan;
cc_mutex_unlock(&i->lock);
return 0;
}
/*
* activate (another B protocol)
*/
static void cc_select_b(struct capi_pvt *i, _cstruct b3conf)
{
if (!b3conf) {
b3conf = b_protocol_table[i->bproto].b3configuration;
}
capi_sendf(NULL, 0, CAPI_SELECT_B_PROTOCOL_REQ, i->PLCI, get_capi_MessageNumber(),
"(wwwssss)",
b_protocol_table[i->bproto].b1protocol,
b_protocol_table[i->bproto].b2protocol,
b_protocol_table[i->bproto].b3protocol,
diva_get_b1_conf(i),
b_protocol_table[i->bproto].b2configuration,
b3conf,
capi_set_global_configuration(i)
);
}
/*
* do line initerconnect
*/
static int line_interconnect(struct capi_pvt *i0, struct capi_pvt *i1, int start)
{
if ((i0->isdnstate & CAPI_ISDN_STATE_DISCONNECT) ||
(i1->isdnstate & CAPI_ISDN_STATE_DISCONNECT))
return -1;
if (start) {
/* connect */
capi_sendf(i1, 0, CAPI_FACILITY_REQ, i0->PLCI, get_capi_MessageNumber(),
"w(w(d((dd))))",
FACILITYSELECTOR_LINE_INTERCONNECT,
0x0001,
/* struct LI Request Parameter */
0x00000000, /* Data Path */
/* struct */
/* struct LI Request Connect Participant */
i1->PLCI,
0x00000003 /* Data Path Participant */
);
i0->isdnstate |= CAPI_ISDN_STATE_LI;
i1->isdnstate |= CAPI_ISDN_STATE_LI;
} else {
/* disconnect */
capi_sendf(i1, 0, CAPI_FACILITY_REQ, i0->PLCI, get_capi_MessageNumber(),
"w(w(d))",
FACILITYSELECTOR_LINE_INTERCONNECT,
0x0002,
i1->PLCI
);
i0->isdnstate &= ~CAPI_ISDN_STATE_LI;
i1->isdnstate &= ~CAPI_ISDN_STATE_LI;
}
return 0;
}
/*
* try call transfer instead of bridge
*/
#ifndef CC_AST_HAS_VERSION_13_0
static int pbx_capi_bridge_transfer(
struct ast_channel *c0,
struct ast_channel *c1,
struct capi_pvt *i0,
struct capi_pvt *i1)
{
int ret = 0;
ast_group_t tgroup0 = i0->transfergroup;
ast_group_t tgroup1 = i1->transfergroup;
struct timespec abstime;
const char* var;
struct capi_pvt *heldcall;
struct capi_pvt *consultationcall;
/* variable may override config */
if ((var = pbx_builtin_getvar_helper(c0, "TRANSFERGROUP")) != NULL) {
tgroup0 = ast_get_group((char *)var);
}
if ((var = pbx_builtin_getvar_helper(c1, "TRANSFERGROUP")) != NULL) {
tgroup1 = ast_get_group((char *)var);
}
if ((!((1 << i0->controller) & tgroup1)) ||
(!((1 << i1->controller) & tgroup0))) {
/* transfer between those controllers is not allowed */
cc_verbose(4, 1, "%s: transfergroup mismatch %d(0x%x),%d(0x%x)\n",
i0->vname, i0->controller, tgroup1, i1->controller, tgroup0);
return 0;
}
if ((i0->qsigfeat) && (i1->qsigfeat)) {
/* QSIG */
ret = pbx_capi_qsig_bridge(i0, i1);
if (ret == 2) {
/* don't do bridge - call transfer is active */
#ifdef CC_AST_HAS_VERSION_11_0
const char *c0_name = ast_channel_name(c0);
const char *c1_name = ast_channel_name(c1);
#else /* !defined(CC_AST_HAS_VERSION_11_0) */
const char *c0_name = c0->name;
const char *c1_name = c1->name;
#endif /* defined(CC_AST_HAS_VERSION_11_0) */
cc_verbose(3, 1, VERBOSE_PREFIX_2 "%s:%s cancelled bridge (path replacement was sent) for %s and %s\n",
i0->vname, i1->vname, c0_name, c1_name);
}
} else {
/* standard ECT */
if (i0->isdnstate & CAPI_ISDN_STATE_HOLD) {
if (i1->isdnstate & CAPI_ISDN_STATE_HOLD) {
cc_verbose(3, 1, VERBOSE_PREFIX_2 "%s:%s both channels on hold, retrieving second one.\n",
i0->vname, i1->vname);
pbx_capi_retrieve(c1, NULL);
capi_wait_for_b3_up(i1);
}
heldcall = i0;
consultationcall = i1;
} else if (i1->isdnstate & CAPI_ISDN_STATE_HOLD) {
heldcall = i1;
consultationcall = i0;
} else {
/* no one on hold */
/* put first call on hold */
cc_mutex_lock(&i0->lock);
pbx_capi_hold(c0, NULL);
if ((i0->onholdPLCI != 0) && (i0->state != CAPI_STATE_ONHOLD)) {
i0->waitevent = CAPI_WAITEVENT_HOLD_IND;
abstime.tv_sec = time(NULL) + 2;
abstime.tv_nsec = 0;
if (ast_cond_timedwait(&i0->event_trigger, &i0->lock, &abstime) != 0) {
cc_log(LOG_WARNING, "%s: timed out waiting for HOLD.\n", i0->vname);
} else {
cc_verbose(4, 1, "%s: cond signal received for HOLD.\n", i0->vname);
}
}
cc_mutex_unlock(&i0->lock);
if (i0->state != CAPI_STATE_ONHOLD) {
cc_verbose(4, 1, "%s: HOLD impossible, transfer aborted.\n", i0->vname);
return 0;
}
heldcall = i0;
consultationcall = i1;
}
heldcall->whentoretrieve = 0;
/* start the ECT */
cc_disconnect_b3(consultationcall, 1);
cc_mutex_lock(&consultationcall->lock);
/* ECT */
capi_sendf(consultationcall, 1, CAPI_FACILITY_REQ, consultationcall->PLCI,
get_capi_MessageNumber(),
"w(w(d))",
FACILITYSELECTOR_SUPPLEMENTARY,
0x0006, /* ECT */
heldcall->PLCI
);
heldcall->isdnstate &= ~CAPI_ISDN_STATE_HOLD;
heldcall->isdnstate |= CAPI_ISDN_STATE_ECT;
consultationcall->isdnstate |= CAPI_ISDN_STATE_ECT;
cc_verbose(2, 1, VERBOSE_PREFIX_4 "%s: sent ECT for PLCI=%#x to PLCI=%#x\n",
consultationcall->vname, heldcall->PLCI, consultationcall->PLCI);
consultationcall->waitevent = CAPI_WAITEVENT_ECT_IND;
abstime.tv_sec = time(NULL) + 2;
abstime.tv_nsec = 0;
if (ast_cond_timedwait(&consultationcall->event_trigger,
&consultationcall->lock, &abstime) != 0) {
cc_log(LOG_WARNING, "%s: timed out waiting for ECT.\n", consultationcall->vname);
} else {
cc_verbose(4, 1, "%s: cond signal received for ECT.\n", consultationcall->vname);
}
cc_mutex_unlock(&consultationcall->lock);
if (consultationcall->isdnstate & CAPI_ISDN_STATE_ECT) {
/* ECT was activated */
ret = 1;
} else {
cc_log(LOG_WARNING, "%s: ECT was not activated, trying to resume normal operation.\n",
consultationcall->vname);
cc_start_b3(consultationcall);
capi_wait_for_b3_up(consultationcall);
pbx_capi_retrieve(heldcall->owner, NULL);
ret = 0;
}
}
return ret;
}
#endif
/*
* activate / deactivate b-channel bridge
*/
static int capi_bridge(int start, struct capi_pvt *i0, struct capi_pvt *i1, int flags)
{
int ret = 0;
if (start) {
if ((i0->isdnstate & CAPI_ISDN_STATE_LI) ||
(i1->isdnstate & CAPI_ISDN_STATE_LI)) {
/* already in bridge */
cc_verbose(4, 1, VERBOSE_PREFIX_3 "%s/%s: already in bridge.\n",
i0->vname, i1->vname);
return 0;
}
if (!(flags & AST_BRIDGE_DTMF_CHANNEL_0))
capi_detect_dtmf(i0, 0);
if (!(flags & AST_BRIDGE_DTMF_CHANNEL_1))
capi_detect_dtmf(i1, 0);
if ((capi_controllers[i0->controller]->ecOnTransit & EC_ECHOCANCEL_TRANSIT_A) == 0) {
capi_echo_canceller(i0, EC_FUNCTION_DISABLE);
}
if ((capi_controllers[i1->controller]->ecOnTransit & EC_ECHOCANCEL_TRANSIT_B) == 0) {
capi_echo_canceller(i1, EC_FUNCTION_DISABLE);
}
cc_verbose(4, 1, VERBOSE_PREFIX_3 "%s/%s: activating bridge.\n",
i0->vname, i1->vname);
ret = line_interconnect(i0, i1, 1);
} else {
cc_verbose(4, 1, VERBOSE_PREFIX_3 "%s/%s: deactivating bridge.\n",
i0->vname, i1->vname);
line_interconnect(i0, i1, 0);
capi_detect_dtmf(i0, 1);
capi_detect_dtmf(i1, 1);
capi_echo_canceller(i0, EC_FUNCTION_ENABLE);
capi_echo_canceller(i1, EC_FUNCTION_ENABLE);
}
return ret;
}
/*
* native bridging / line interconnect
*/
#ifndef CC_AST_HAS_VERSION_13_0
static CC_BRIDGE_RETURN pbx_capi_bridge(
struct ast_channel *c0,
struct ast_channel *c1,
int flags, struct ast_frame **fo,
struct ast_channel **rc,
int timeoutms)
{
struct capi_pvt *i0 = CC_CHANNEL_PVT(c0);
struct capi_pvt *i1 = CC_CHANNEL_PVT(c1);
CC_BRIDGE_RETURN ret = AST_BRIDGE_COMPLETE;
#ifdef CC_AST_HAS_VERSION_11_0
const char *c0_name = ast_channel_name(c0);
const char *c1_name = ast_channel_name(c1);
#else /* !defined(CC_AST_HAS_VERSION_11_0) */
const char *c0_name = c0->name;
const char *c1_name = c1->name;
#endif /* defined(CC_AST_HAS_VERSION_11_0) */
cc_verbose(3, 1, VERBOSE_PREFIX_2 "%s:%s Requested native bridge for %s and %s\n",
i0->vname, i1->vname, c0_name, c1_name);
if ((i0->isdnstate & CAPI_ISDN_STATE_ECT) ||
(i1->isdnstate & CAPI_ISDN_STATE_ECT)) {
/* If an ECT is already in progress, forget about the bridge */
return AST_BRIDGE_FAILED;
}
switch(pbx_capi_bridge_transfer(c0, c1, i0, i1)) {
case 1: /* transfer was sucessful */
return ret;
case 2: /* not successful, abort */
return AST_BRIDGE_FAILED_NOWARN;
default: /* go on with line-interconnect */
break;
}
/* do bridge aka line-interconnect here */
if ((!i0->bridge) || (!i1->bridge))
return AST_BRIDGE_FAILED_NOWARN;
if ((!capi_controllers[i0->controller]->lineinterconnect) ||
(!capi_controllers[i1->controller]->lineinterconnect)) {
return AST_BRIDGE_FAILED_NOWARN;
}
capi_wait_for_b3_up(i0);
capi_wait_for_b3_up(i1);
if (capi_bridge(1, i0, i1, flags)) {
return AST_BRIDGE_FAILED;
}
for (;;) {
struct ast_channel *c0_priority[2] = {c0, c1};
struct ast_channel *c1_priority[2] = {c1, c0};
int priority = 0;
struct ast_frame *f;
struct ast_channel *who;
who = ast_waitfor_n(priority ? c0_priority : c1_priority, 2, &timeoutms);
if (!who) {
if (!timeoutms) {
ret = AST_BRIDGE_RETRY;
break;
}
continue;
}
f = ast_read(who);
if (!f || (f->frametype == AST_FRAME_CONTROL)
|| (f->frametype == AST_FRAME_DTMF)) {
*fo = f;
*rc = who;
ret = AST_BRIDGE_COMPLETE;
break;
}
if (who == c0) {
ast_write(c1, f);
} else {
ast_write(c0, f);
}
ast_frfree(f);
/* Swap who gets priority */
priority = !priority;
}
capi_bridge(0, i0, i1, 0);
return ret;
}
#endif
/*
* a new channel is needed
*/
#ifdef CC_AST_HAS_VERSION_13_0
static struct ast_channel *capi_new(struct capi_pvt *i, int state, const struct ast_channel *requestor)
#else
static struct ast_channel *capi_new(struct capi_pvt *i, int state, const char *linkedid)
#endif
{
struct ast_channel *tmp;
int fmt;
#ifdef CC_AST_HAS_EXT_CHAN_ALLOC
tmp = ast_channel_alloc(0, state, i->cid, emptyid,
#ifdef CC_AST_HAS_EXT2_CHAN_ALLOC
i->accountcode, i->dnid, i->context,
#ifdef CC_AST_HAS_VERSION_13_0
NULL, requestor,
#endif
#ifdef CC_AST_HAS_LINKEDID_CHAN_ALLOC
linkedid,
#endif
i->amaflags,
#endif
CC_MESSAGE_BIGNAME "/%s/%s-%x", i->vname, i->dnid, capi_counter++);
#else
tmp = ast_channel_alloc(0);
#endif
cc_mutex_lock(&iflock);
i->reserved = 0;
if (tmp == NULL) {
cc_log(LOG_ERROR, "Unable to allocate channel!\n");
return NULL;
}
#ifndef CC_AST_HAS_EXT_CHAN_ALLOC
#ifdef CC_AST_HAS_STRINGFIELD_IN_CHANNEL
ast_string_field_build(tmp, name, CC_MESSAGE_BIGNAME "/%s/%s-%x",
i->vname, i->dnid, capi_counter++);
#else
snprintf(tmp->name, sizeof(tmp->name) - 1, CC_MESSAGE_BIGNAME "/%s/%s-%x",
i->vname, i->dnid, capi_counter++);
#endif
#endif
#ifndef CC_AST_HAS_VERSION_1_4
tmp->type = channeltype;
#endif
if (!(capi_create_reader_writer_pipe(i))) {
ast_channel_release(tmp);
return NULL;
}
#ifdef CC_AST_HAS_VERSION_1_6
ast_channel_set_fd(tmp, 0, i->readerfd);
#else
tmp->fds[0] = i->readerfd;
#endif
if (i->smoother != NULL) {
ast_smoother_reset(i->smoother, CAPI_MAX_B3_BLOCK_SIZE);
}
i->state = CAPI_STATE_DISCONNECTED;
i->calledPartyIsISDN = 1;
i->doB3 = CAPI_B3_DONT;
i->doES = i->ES;
i->outgoing = 0;
i->onholdPLCI = 0;
i->doholdtype = i->holdtype;
i->B3q = 0;
i->B3count = 0;
memset(i->txavg, 0, ECHO_TX_COUNT);
i->divaAudioFlags = 0;
i->divaDataStubAudioFlags = 0;
i->divaDigitalRxGain = 0;
i->divaDigitalRxGainDB = 0;
i->divaDigitalTxGain = 0;
i->divaDigitalTxGainDB = 0;
i->rxPitch = 8000;
i->txPitch = 8000;
i->special_tone_extension[0] = 0;
pbx_capi_voicecommand_cleanup(i);
if (i->doDTMF > 0) {
i->vad = ast_dsp_new();
#ifdef CC_AST_HAS_DSP_SET_DIGITMODE
ast_dsp_set_features(i->vad, DSP_FEATURE_DIGIT_DETECT);
if (i->doDTMF > 1) {
ast_dsp_set_digitmode(i->vad, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF);
}
#else
ast_dsp_set_features(i->vad, DSP_FEATURE_DTMF_DETECT);
if (i->doDTMF > 1) {
ast_dsp_digitmode(i->vad, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF);
}
#endif
}
#ifdef CC_AST_HAS_VERSION_11_0
ast_channel_tech_pvt_set(tmp, i);
ast_channel_callgroup_set(tmp, i->callgroup);
ast_channel_pickupgroup_set(tmp, i->pickupgroup);
#else /* !defined(CC_AST_HAS_VERSION_11_0) */
CC_CHANNEL_PVT(tmp) = i;
tmp->callgroup = i->callgroup;
tmp->pickupgroup = i->pickupgroup;
#endif /* defined(CC_AST_HAS_VERSION_11_0) */
#if 0
i->bproto = CC_BPROTO_TRANSPARENT;
tmp->nativeformats = capi_capability;
if ((i->rtpcodec = (capi_controllers[i->controller]->rtpcodec & i->capability))) {
tmp->nativeformats = i->rtpcodec;
i->bproto = CC_BPROTO_VOCODER;
}
fmt = ast_best_codec(tmp->nativeformats);
i->codec = fmt;
tmp->readformat = fmt;
tmp->writeformat = fmt;
tmp->rawreadformat = fmt;
tmp->rawwriteformat = fmt;
#else
if ((i->rtpcodec = (capi_controllers[i->controller]->rtpcodec & i->capability))) {
i->bproto = CC_BPROTO_VOCODER;
#ifdef CC_AST_HAS_VERSION_11_0
struct ast_format_cap *cur_nativefmts = ast_channel_nativeformats(tmp);
#else /* !defined(CC_AST_HAS_VERSION_11_0) */
struct ast_format_cap *cur_nativefmts = tmp->nativeformats;
#endif /* defined(CC_AST_HAS_VERSION_11_0) */
cc_add_formats(cur_nativefmts, i->rtpcodec);
} else {
i->bproto = CC_BPROTO_TRANSPARENT;
#ifdef CC_AST_HAS_VERSION_11_0
struct ast_format_cap *cur_nativefmts = ast_channel_nativeformats(tmp);
#else /* !defined(CC_AST_HAS_VERSION_11_0) */
struct ast_format_cap *cur_nativefmts = tmp->nativeformats;
#endif /* defined(CC_AST_HAS_VERSION_11_0) */
cc_add_formats(cur_nativefmts, capi_capability);
}
fmt = cc_set_best_codec(tmp);
i->codec = fmt;
#endif
#ifdef CC_AST_HAS_VERSION_11_0
ast_channel_tech_set(tmp, &capi_tech);
struct ast_format_cap *cur_nativefmts = ast_channel_nativeformats(tmp);
#else /* !defined(CC_AST_HAS_VERSION_11_0) */
tmp->tech = &capi_tech;
struct ast_format_cap *cur_nativefmts = tmp->nativeformats;
#endif /* defined(CC_AST_HAS_VERSION_11_0) */
#ifdef CC_AST_HAS_VERSION_13_0
struct ast_str *format_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
#endif
cc_verbose(3, 1, VERBOSE_PREFIX_2 "%s: setting format %s - %s%s\n",
i->vname, cc_getformatname(fmt),
#ifdef CC_AST_HAS_VERSION_13_0
ast_format_cap_get_names(cur_nativefmts, &format_buf),
#else
ast_getformatname_multiple(alloca(80), 80, cur_nativefmts),
#endif
(i->bproto == CC_BPROTO_VOCODER) ? "VOCODER" : ((i->rtp) ? " (RTP)" : ""));
if (!ast_strlen_zero(i->cid)) {
#ifdef CC_AST_HAS_VERSION_11_0
ast_free (ast_channel_connected(tmp)->id.number.str);
ast_channel_connected(tmp)->id.number.valid = 1;
ast_channel_connected(tmp)->id.number.str = ast_strdup(i->cid);
ast_channel_connected(tmp)->id.number.plan = i->cid_ton;
#elif defined(CC_AST_HAS_VERSION_1_8)
ast_free (tmp->connected.id.number.str);
tmp->connected.id.number.valid = 1;
tmp->connected.id.number.str = ast_strdup(i->cid);
tmp->connected.id.number.plan = i->cid_ton;
#else /* !(defined(CC_AST_HAS_VERSION_11_0) || defined(CC_AST_HAS_VERSION_1_8)) */
ast_free(tmp->cid.cid_num);
tmp->cid.cid_num = ast_strdup(i->cid);
#endif /* defined(CC_AST_HAS_VERSION_11_0) || defined(CC_AST_HAS_VERSION_1_8) */
}
if (!ast_strlen_zero(i->dnid)) {
#ifdef CC_AST_HAS_VERSION_11_0
ast_free (ast_channel_dialed(tmp)->number.str);
ast_channel_dialed(tmp)->number.str = ast_strdup (i->dnid);
#elif defined(CC_AST_HAS_VERSION_1_8)
ast_free (tmp->dialed.number.str);
tmp->dialed.number.str = ast_strdup (i->dnid);
#else /* !(defined(CC_AST_HAS_VERSION_11_0) || defined(CC_AST_HAS_VERSION_1_8)) */
ast_free(tmp->cid.cid_dnid);
tmp->cid.cid_dnid = ast_strdup(i->dnid);
#endif /* defined(CC_AST_HAS_VERSION_11_0) || defined(CC_AST_HAS_VERSION_1_8) */