/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2006, Digium, Inc.
*
* Mark Spencer <markster@digium.com>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*! \file
*
* \brief DAHDI for Pseudo TDM
*
* \author Mark Spencer <markster@digium.com>
*
* Connects to the DAHDI telephony library as well as
* libpri. Libpri is optional and needed only if you are
* going to use ISDN connections.
*
* You need to install libraries before you attempt to compile
* and install the DAHDI channel.
*
* \par See also
* \arg \ref Config_dahdi
*
* \ingroup channel_drivers
*
* \todo Deprecate the "musiconhold" configuration option post 1.4
*/
/*** MODULEINFO
<depend>res_smdi</depend>
<depend>dahdi</depend>
<depend>tonezone</depend>
<use>pri</use>
<use>ss7</use>
<use>openr2</use>
***/
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#if defined(__NetBSD__) || defined(__FreeBSD__)
#include <pthread.h>
#include <signal.h>
#else
#include <sys/signal.h>
#endif
#include <sys/ioctl.h>
#include <math.h>
#include <ctype.h>
#include <dahdi/user.h>
#include <dahdi/tonezone.h>
#ifdef HAVE_PRI
#include <libpri.h>
#endif
#ifdef HAVE_SS7
#include <libss7.h>
#endif
#ifdef HAVE_OPENR2
#include <openr2.h>
#endif
#include "asterisk/lock.h"
#include "asterisk/channel.h"
#include "asterisk/config.h"
#include "asterisk/module.h"
#include "asterisk/pbx.h"
#include "asterisk/file.h"
#include "asterisk/ulaw.h"
#include "asterisk/alaw.h"
#include "asterisk/callerid.h"
#include "asterisk/adsi.h"
#include "asterisk/cli.h"
#include "asterisk/cdr.h"
#include "asterisk/features.h"
#include "asterisk/musiconhold.h"
#include "asterisk/say.h"
#include "asterisk/tdd.h"
#include "asterisk/app.h"
#include "asterisk/dsp.h"
#include "asterisk/astdb.h"
#include "asterisk/manager.h"
#include "asterisk/causes.h"
#include "asterisk/term.h"
#include "asterisk/utils.h"
#include "asterisk/transcap.h"
#include "asterisk/stringfields.h"
#include "asterisk/abstract_jb.h"
#include "asterisk/smdi.h"
#include "asterisk/astobj.h"
#include "asterisk/event.h"
#include "asterisk/devicestate.h"
#include "asterisk/paths.h"
/*** DOCUMENTATION
<application name="DAHDISendKeypadFacility" language="en_US">
<synopsis>
Send digits out of band over a PRI.
</synopsis>
<syntax>
<parameter name="digits" required="true" />
</syntax>
<description>
<para>This application will send the given string of digits in a Keypad
Facility IE over the current channel.</para>
</description>
</application>
<application name="DAHDISendCallreroutingFacility" language="en_US">
<synopsis>
Send QSIG call rerouting facility over a PRI.
</synopsis>
<syntax argsep=",">
<parameter name="destination" required="true">
<para>Destination number.</para>
</parameter>
<parameter name="original">
<para>Original called number.</para>
</parameter>
<parameter name="reason">
<para>Diversion reason, if not specified defaults to <literal>unknown</literal></para>
</parameter>
</syntax>
<description>
<para>This application will send a Callrerouting Facility IE over the
current channel.</para>
</description>
</application>
<application name="DAHDIAcceptR2Call" language="en_US">
<synopsis>
Accept an R2 call if its not already accepted (you still need to answer it)
</synopsis>
<syntax>
<parameter name="charge" required="true">
<para>Yes or No.</para>
<para>Whether you want to accept the call with charge or without charge.</para>
</parameter>
</syntax>
<description>
<para>This application will Accept the R2 call either with charge or no charge.</para>
</description>
</application>
***/
#define SMDI_MD_WAIT_TIMEOUT 1500 /* 1.5 seconds */
static const char *lbostr[] = {
"0 db (CSU)/0-133 feet (DSX-1)",
"133-266 feet (DSX-1)",
"266-399 feet (DSX-1)",
"399-533 feet (DSX-1)",
"533-655 feet (DSX-1)",
"-7.5db (CSU)",
"-15db (CSU)",
"-22.5db (CSU)"
};
/*! Global jitterbuffer configuration - by default, jb is disabled */
static struct ast_jb_conf default_jbconf =
{
.flags = 0,
.max_size = -1,
.resync_threshold = -1,
.impl = "",
.target_extra = -1,
};
static struct ast_jb_conf global_jbconf;
/* define this to send PRI user-user information elements */
#undef SUPPORT_USERUSER
/*!
* \note Define ZHONE_HACK to cause us to go off hook and then back on hook when
* the user hangs up to reset the state machine so ring works properly.
* This is used to be able to support kewlstart by putting the zhone in
* groundstart mode since their forward disconnect supervision is entirely
* broken even though their documentation says it isn't and their support
* is entirely unwilling to provide any assistance with their channel banks
* even though their web site says they support their products for life.
*/
/* #define ZHONE_HACK */
/*! \note
* Define if you want to check the hook state for an FXO (FXS signalled) interface
* before dialing on it. Certain FXO interfaces always think they're out of
* service with this method however.
*/
/* #define DAHDI_CHECK_HOOKSTATE */
/*! \brief Typically, how many rings before we should send Caller*ID */
#define DEFAULT_CIDRINGS 1
#define CHANNEL_PSEUDO -12
#define AST_LAW(p) (((p)->law == DAHDI_LAW_ALAW) ? AST_FORMAT_ALAW : AST_FORMAT_ULAW)
/*! \brief Signaling types that need to use MF detection should be placed in this macro */
#define NEED_MFDETECT(p) (((p)->sig == SIG_FEATDMF) || ((p)->sig == SIG_FEATDMF_TA) || ((p)->sig == SIG_E911) || ((p)->sig == SIG_FGC_CAMA) || ((p)->sig == SIG_FGC_CAMAMF) || ((p)->sig == SIG_FEATB))
static const char tdesc[] = "DAHDI Telephony Driver"
#if defined(HAVE_PRI) || defined(HAVE_SS7) || defined(HAVE_OPENR2)
" w/"
#endif
#ifdef HAVE_PRI
"PRI"
#endif
#ifdef HAVE_SS7
#ifdef HAVE_PRI
" & SS7"
#else
"SS7"
#endif
#endif
#ifdef HAVE_OPENR2
#if defined(HAVE_PRI) || defined(HAVE_SS7)
" & MFC/R2"
#else
"MFC/R2"
#endif
#endif
;
static const char config[] = "chan_dahdi.conf";
#define SIG_EM DAHDI_SIG_EM
#define SIG_EMWINK (0x0100000 | DAHDI_SIG_EM)
#define SIG_FEATD (0x0200000 | DAHDI_SIG_EM)
#define SIG_FEATDMF (0x0400000 | DAHDI_SIG_EM)
#define SIG_FEATB (0x0800000 | DAHDI_SIG_EM)
#define SIG_E911 (0x1000000 | DAHDI_SIG_EM)
#define SIG_FEATDMF_TA (0x2000000 | DAHDI_SIG_EM)
#define SIG_FGC_CAMA (0x4000000 | DAHDI_SIG_EM)
#define SIG_FGC_CAMAMF (0x8000000 | DAHDI_SIG_EM)
#define SIG_FXSLS DAHDI_SIG_FXSLS
#define SIG_FXSGS DAHDI_SIG_FXSGS
#define SIG_FXSKS DAHDI_SIG_FXSKS
#define SIG_FXOLS DAHDI_SIG_FXOLS
#define SIG_FXOGS DAHDI_SIG_FXOGS
#define SIG_FXOKS DAHDI_SIG_FXOKS
#define SIG_PRI DAHDI_SIG_CLEAR
#define SIG_BRI (0x2000000 | DAHDI_SIG_CLEAR)
#define SIG_BRI_PTMP (0X4000000 | DAHDI_SIG_CLEAR)
#define SIG_SS7 (0x1000000 | DAHDI_SIG_CLEAR)
#define SIG_MFCR2 DAHDI_SIG_CAS
#define SIG_SF DAHDI_SIG_SF
#define SIG_SFWINK (0x0100000 | DAHDI_SIG_SF)
#define SIG_SF_FEATD (0x0200000 | DAHDI_SIG_SF)
#define SIG_SF_FEATDMF (0x0400000 | DAHDI_SIG_SF)
#define SIG_SF_FEATB (0x0800000 | DAHDI_SIG_SF)
#define SIG_EM_E1 DAHDI_SIG_EM_E1
#define SIG_GR303FXOKS (0x0100000 | DAHDI_SIG_FXOKS)
#define SIG_GR303FXSKS (0x0100000 | DAHDI_SIG_FXSKS)
#ifdef LOTS_OF_SPANS
#define NUM_SPANS DAHDI_MAX_SPANS
#else
#define NUM_SPANS 32
#endif
#define NUM_DCHANS 4 /*!< No more than 4 d-channels */
#define MAX_CHANNELS 672 /*!< No more than a DS3 per trunk group */
#define CHAN_PSEUDO -2
#define DCHAN_PROVISIONED (1 << 0)
#define DCHAN_NOTINALARM (1 << 1)
#define DCHAN_UP (1 << 2)
#define DCHAN_AVAILABLE (DCHAN_PROVISIONED | DCHAN_NOTINALARM | DCHAN_UP)
/* Overlap dialing option types */
#define DAHDI_OVERLAPDIAL_NONE 0
#define DAHDI_OVERLAPDIAL_OUTGOING 1
#define DAHDI_OVERLAPDIAL_INCOMING 2
#define DAHDI_OVERLAPDIAL_BOTH (DAHDI_OVERLAPDIAL_INCOMING|DAHDI_OVERLAPDIAL_OUTGOING)
#define CALLPROGRESS_PROGRESS 1
#define CALLPROGRESS_FAX_OUTGOING 2
#define CALLPROGRESS_FAX_INCOMING 4
#define CALLPROGRESS_FAX (CALLPROGRESS_FAX_INCOMING | CALLPROGRESS_FAX_OUTGOING)
static char defaultcic[64] = "";
static char defaultozz[64] = "";
static char parkinglot[AST_MAX_EXTENSION] = ""; /*!< Default parking lot for this channel */
/*! Run this script when the MWI state changes on an FXO line, if mwimonitor is enabled */
static char mwimonitornotify[PATH_MAX] = "";
#ifndef HAVE_DAHDI_LINEREVERSE_VMWI
static int mwisend_rpas = 0;
#endif
static char progzone[10] = "";
static int usedistinctiveringdetection = 0;
static int distinctiveringaftercid = 0;
static int numbufs = 4;
static int mwilevel = 512;
#ifdef HAVE_PRI
static struct ast_channel inuse;
#ifdef PRI_GETSET_TIMERS
static int pritimers[PRI_MAX_TIMERS];
#endif
static int pridebugfd = -1;
static char pridebugfilename[1024] = "";
#endif
/*! \brief Wait up to 16 seconds for first digit (FXO logic) */
static int firstdigittimeout = 16000;
/*! \brief How long to wait for following digits (FXO logic) */
static int gendigittimeout = 8000;
/*! \brief How long to wait for an extra digit, if there is an ambiguous match */
static int matchdigittimeout = 3000;
/*! \brief Protect the interface list (of dahdi_pvt's) */
AST_MUTEX_DEFINE_STATIC(iflock);
/* QSIG channel mapping option types */
#define DAHDI_CHAN_MAPPING_PHYSICAL 0
#define DAHDI_CHAN_MAPPING_LOGICAL 1
static int ifcount = 0;
#ifdef HAVE_PRI
AST_MUTEX_DEFINE_STATIC(pridebugfdlock);
#endif
/*! \brief Protect the monitoring thread, so only one process can kill or start it, and not
when it's doing something critical. */
AST_MUTEX_DEFINE_STATIC(monlock);
/*! \brief This is the thread for the monitor which checks for input on the channels
which are not currently in use. */
static pthread_t monitor_thread = AST_PTHREADT_NULL;
static ast_cond_t ss_thread_complete;
AST_MUTEX_DEFINE_STATIC(ss_thread_lock);
AST_MUTEX_DEFINE_STATIC(restart_lock);
static int ss_thread_count = 0;
static int num_restart_pending = 0;
static int restart_monitor(void);
static enum ast_bridge_result dahdi_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms);
static int dahdi_sendtext(struct ast_channel *c, const char *text);
static void mwi_event_cb(const struct ast_event *event, void *userdata)
{
/* This module does not handle MWI in an event-based manner. However, it
* subscribes to MWI for each mailbox that is configured so that the core
* knows that we care about it. Then, chan_dahdi will get the MWI from the
* event cache instead of checking the mailbox directly. */
}
/*! \brief Avoid the silly dahdi_getevent which ignores a bunch of events */
static inline int dahdi_get_event(int fd)
{
int j;
if (ioctl(fd, DAHDI_GETEVENT, &j) == -1)
return -1;
return j;
}
/*! \brief Avoid the silly dahdi_waitevent which ignores a bunch of events */
static inline int dahdi_wait_event(int fd)
{
int i, j = 0;
i = DAHDI_IOMUX_SIGEVENT;
if (ioctl(fd, DAHDI_IOMUX, &i) == -1)
return -1;
if (ioctl(fd, DAHDI_GETEVENT, &j) == -1)
return -1;
return j;
}
/*! Chunk size to read -- we use 20ms chunks to make things happy. */
#define READ_SIZE 160
#define MASK_AVAIL (1 << 0) /*!< Channel available for PRI use */
#define MASK_INUSE (1 << 1) /*!< Channel currently in use */
#define CALLWAITING_SILENT_SAMPLES ( (300 * 8) / READ_SIZE) /*!< 300 ms */
#define CALLWAITING_REPEAT_SAMPLES ( (10000 * 8) / READ_SIZE) /*!< 10,000 ms */
#define CIDCW_EXPIRE_SAMPLES ( (500 * 8) / READ_SIZE) /*!< 500 ms */
#define MIN_MS_SINCE_FLASH ( (2000) ) /*!< 2000 ms */
#define DEFAULT_RINGT ( (8000 * 8) / READ_SIZE) /*!< 8,000 ms */
struct dahdi_pvt;
/*!
* \brief Configured ring timeout base.
* \note Value computed from "ringtimeout" read in from chan_dahdi.conf if it exists.
*/
static int ringt_base = DEFAULT_RINGT;
#ifdef HAVE_SS7
#define LINKSTATE_INALARM (1 << 0)
#define LINKSTATE_STARTING (1 << 1)
#define LINKSTATE_UP (1 << 2)
#define LINKSTATE_DOWN (1 << 3)
#define SS7_NAI_DYNAMIC -1
#define LINKSET_FLAG_EXPLICITACM (1 << 0)
struct dahdi_ss7 {
pthread_t master; /*!< Thread of master */
ast_mutex_t lock;
int fds[NUM_DCHANS];
int numsigchans;
int linkstate[NUM_DCHANS];
int numchans;
int type;
enum {
LINKSET_STATE_DOWN = 0,
LINKSET_STATE_UP
} state;
char called_nai; /*!< Called Nature of Address Indicator */
char calling_nai; /*!< Calling Nature of Address Indicator */
char internationalprefix[10]; /*!< country access code ('00' for european dialplans) */
char nationalprefix[10]; /*!< area access code ('0' for european dialplans) */
char subscriberprefix[20]; /*!< area access code + area code ('0'+area code for european dialplans) */
char unknownprefix[20]; /*!< for unknown dialplans */
struct ss7 *ss7;
struct dahdi_pvt *pvts[MAX_CHANNELS]; /*!< Member channel pvt structs */
int flags; /*!< Linkset flags */
};
static struct dahdi_ss7 linksets[NUM_SPANS];
static int cur_ss7type = -1;
static int cur_linkset = -1;
static int cur_pointcode = -1;
static int cur_cicbeginswith = -1;
static int cur_adjpointcode = -1;
static int cur_networkindicator = -1;
static int cur_defaultdpc = -1;
#endif /* HAVE_SS7 */
#ifdef HAVE_OPENR2
struct dahdi_mfcr2 {
pthread_t r2master; /*!< Thread of master */
openr2_context_t *protocol_context; /*!< OpenR2 context handle */
struct dahdi_pvt *pvts[MAX_CHANNELS]; /*!< Member channel pvt structs */
int numchans; /*!< Number of channels in this R2 block */
int monitored_count; /*!< Number of channels being monitored */
};
struct dahdi_mfcr2_conf {
openr2_variant_t variant;
int mfback_timeout;
int metering_pulse_timeout;
int max_ani;
int max_dnis;
signed int get_ani_first:2;
#if defined(OR2_LIB_INTERFACE) && OR2_LIB_INTERFACE > 1
signed int skip_category_request:2;
#endif
unsigned int call_files:1;
unsigned int allow_collect_calls:1;
unsigned int charge_calls:1;
unsigned int accept_on_offer:1;
unsigned int forced_release:1;
unsigned int double_answer:1;
signed int immediate_accept:2;
char logdir[OR2_MAX_PATH];
char r2proto_file[OR2_MAX_PATH];
openr2_log_level_t loglevel;
openr2_calling_party_category_t category;
};
/* malloc'd array of malloc'd r2links */
static struct dahdi_mfcr2 **r2links;
/* how many r2links have been malloc'd */
static int r2links_count = 0;
#endif /* HAVE_OPENR2 */
#ifdef HAVE_PRI
#define PVT_TO_CHANNEL(p) (((p)->prioffset) | ((p)->logicalspan << 8) | (p->pri->mastertrunkgroup ? 0x10000 : 0))
#define PRI_CHANNEL(p) ((p) & 0xff)
#define PRI_SPAN(p) (((p) >> 8) & 0xff)
#define PRI_EXPLICIT(p) (((p) >> 16) & 0x01)
struct dahdi_pri {
pthread_t master; /*!< Thread of master */
ast_mutex_t lock; /*!< Mutex */
char idleext[AST_MAX_EXTENSION]; /*!< Where to idle extra calls */
char idlecontext[AST_MAX_CONTEXT]; /*!< What context to use for idle */
char idledial[AST_MAX_EXTENSION]; /*!< What to dial before dumping */
int minunused; /*!< Min # of channels to keep empty */
int minidle; /*!< Min # of "idling" calls to keep active */
int nodetype; /*!< Node type */
int switchtype; /*!< Type of switch to emulate */
int nsf; /*!< Network-Specific Facilities */
int dialplan; /*!< Dialing plan */
int localdialplan; /*!< Local dialing plan */
char internationalprefix[10]; /*!< country access code ('00' for european dialplans) */
char nationalprefix[10]; /*!< area access code ('0' for european dialplans) */
char localprefix[20]; /*!< area access code + area code ('0'+area code for european dialplans) */
char privateprefix[20]; /*!< for private dialplans */
char unknownprefix[20]; /*!< for unknown dialplans */
int dchannels[NUM_DCHANS]; /*!< What channel are the dchannels on */
int trunkgroup; /*!< What our trunkgroup is */
int mastertrunkgroup; /*!< What trunk group is our master */
int prilogicalspan; /*!< Logical span number within trunk group */
int numchans; /*!< Num of channels we represent */
int overlapdial; /*!< In overlap dialing mode */
int qsigchannelmapping; /*!< QSIG channel mapping type */
int discardremoteholdretrieval; /*!< shall remote hold or remote retrieval notifications be discarded? */
int facilityenable; /*!< Enable facility IEs */
struct pri *dchans[NUM_DCHANS]; /*!< Actual d-channels */
int dchanavail[NUM_DCHANS]; /*!< Whether each channel is available */
struct pri *pri; /*!< Currently active D-channel */
/*! \brief TRUE if to dump PRI event info (Tested but never set) */
int debug;
int fds[NUM_DCHANS]; /*!< FD's for d-channels */
/*! \brief Value set but not used */
int offset;
/*! \brief Span number put into user output messages */
int span;
/*! \brief TRUE if span is being reset/restarted */
int resetting;
/*! \brief Current position during a reset (-1 if not started) */
int resetpos;
#ifdef HAVE_PRI_INBANDDISCONNECT
unsigned int inbanddisconnect:1; /*!< Should we support inband audio after receiving DISCONNECT? */
#endif
time_t lastreset; /*!< time when unused channels were last reset */
long resetinterval; /*!< Interval (in seconds) for resetting unused channels */
/*! \brief ISDN signalling type (SIG_PRI, SIG_BRI, SIG_BRI_PTMP, etc...) */
int sig;
struct dahdi_pvt *pvts[MAX_CHANNELS]; /*!< Member channel pvt structs */
struct dahdi_pvt *crvs; /*!< Member CRV structs */
struct dahdi_pvt *crvend; /*!< Pointer to end of CRV structs */
};
static struct dahdi_pri pris[NUM_SPANS];
#if 0
#define DEFAULT_PRI_DEBUG (PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q921_DUMP | PRI_DEBUG_Q921_RAW | PRI_DEBUG_Q921_STATE)
#else
#define DEFAULT_PRI_DEBUG 0
#endif
static inline void pri_rel(struct dahdi_pri *pri)
{
ast_mutex_unlock(&pri->lock);
}
#else
/*! Shut up the compiler */
struct dahdi_pri;
#endif
#define SUB_REAL 0 /*!< Active call */
#define SUB_CALLWAIT 1 /*!< Call-Waiting call on hold */
#define SUB_THREEWAY 2 /*!< Three-way call */
/* Polarity states */
#define POLARITY_IDLE 0
#define POLARITY_REV 1
struct distRingData {
int ring[3];
int range;
};
struct ringContextData {
char contextData[AST_MAX_CONTEXT];
};
struct dahdi_distRings {
struct distRingData ringnum[3];
struct ringContextData ringContext[3];
};
static char *subnames[] = {
"Real",
"Callwait",
"Threeway"
};
struct dahdi_subchannel {
int dfd;
struct ast_channel *owner;
int chan;
short buffer[AST_FRIENDLY_OFFSET/2 + READ_SIZE];
struct ast_frame f; /*!< One frame for each channel. How did this ever work before? */
unsigned int needringing:1;
unsigned int needbusy:1;
unsigned int needcongestion:1;
unsigned int needcallerid:1;
unsigned int needanswer:1;
unsigned int needflash:1;
unsigned int needhold:1;
unsigned int needunhold:1;
unsigned int linear:1;
unsigned int inthreeway:1;
struct dahdi_confinfo curconf;
};
#define CONF_USER_REAL (1 << 0)
#define CONF_USER_THIRDCALL (1 << 1)
#define MAX_SLAVES 4
/* States for sending MWI message
* First three states are required for send Ring Pulse Alert Signal
*/
typedef enum {
MWI_SEND_NULL = 0,
MWI_SEND_SA,
MWI_SEND_SA_WAIT,
MWI_SEND_PAUSE,
MWI_SEND_SPILL,
MWI_SEND_CLEANUP,
MWI_SEND_DONE,
} mwisend_states;
struct mwisend_info {
struct timeval pause;
mwisend_states mwisend_current;
};
static struct dahdi_pvt {
ast_mutex_t lock; /*!< Channel private lock. */
struct ast_channel *owner; /*!< Our current active owner (if applicable) */
/*!< Up to three channels can be associated with this call */
struct dahdi_subchannel sub_unused; /*!< Just a safety precaution */
struct dahdi_subchannel subs[3]; /*!< Sub-channels */
struct dahdi_confinfo saveconf; /*!< Saved conference info */
struct dahdi_pvt *slaves[MAX_SLAVES]; /*!< Slave to us (follows our conferencing) */
struct dahdi_pvt *master; /*!< Master to us (we follow their conferencing) */
int inconference; /*!< If our real should be in the conference */
int bufsize; /*!< Size of the buffers */
int buf_no; /*!< Number of buffers */
int buf_policy; /*!< Buffer policy */
int faxbuf_no; /*!< Number of Fax buffers */
int faxbuf_policy; /*!< Fax buffer policy */
int sig; /*!< Signalling style */
/*!
* \brief Nonzero if the signaling type is sent over a radio.
* \note Set to a couple of nonzero values but it is only tested like a boolean.
*/
int radio;
int outsigmod; /*!< Outbound Signalling style (modifier) */
int oprmode; /*!< "Operator Services" mode */
struct dahdi_pvt *oprpeer; /*!< "Operator Services" peer tech_pvt ptr */
/*! \brief Amount of gain to increase during caller id */
float cid_rxgain;
/*! \brief Rx gain set by chan_dahdi.conf */
float rxgain;
/*! \brief Tx gain set by chan_dahdi.conf */
float txgain;
int tonezone; /*!< tone zone for this chan, or -1 for default */
struct dahdi_pvt *next; /*!< Next channel in list */
struct dahdi_pvt *prev; /*!< Prev channel in list */
/* flags */
/*!
* \brief TRUE if ADSI (Analog Display Services Interface) available
* \note Set from the "adsi" value read in from chan_dahdi.conf
*/
unsigned int adsi:1;
/*!
* \brief TRUE if we can use a polarity reversal to mark when an outgoing
* call is answered by the remote party.
* \note Set from the "answeronpolarityswitch" value read in from chan_dahdi.conf
*/
unsigned int answeronpolarityswitch:1;
/*!
* \brief TRUE if busy detection is enabled.
* (Listens for the beep-beep busy pattern.)
* \note Set from the "busydetect" value read in from chan_dahdi.conf
*/
unsigned int busydetect:1;
/*!
* \brief TRUE if call return is enabled.
* (*69, if your dialplan doesn't catch this first)
* \note Set from the "callreturn" value read in from chan_dahdi.conf
*/
unsigned int callreturn:1;
/*!
* \brief TRUE if busy extensions will hear the call-waiting tone
* and can use hook-flash to switch between callers.
* \note Can be disabled by dialing *70.
* \note Initialized with the "callwaiting" value read in from chan_dahdi.conf
*/
unsigned int callwaiting:1;
/*!
* \brief TRUE if send caller ID for Call Waiting
* \note Set from the "callwaitingcallerid" value read in from chan_dahdi.conf
*/
unsigned int callwaitingcallerid:1;
/*!
* \brief TRUE if support for call forwarding enabled.
* Dial *72 to enable call forwarding.
* Dial *73 to disable call forwarding.
* \note Set from the "cancallforward" value read in from chan_dahdi.conf
*/
unsigned int cancallforward:1;
/*!
* \brief TRUE if support for call parking is enabled.
* \note Set from the "canpark" value read in from chan_dahdi.conf
*/
unsigned int canpark:1;
/*! \brief TRUE if to wait for a DTMF digit to confirm answer */
unsigned int confirmanswer:1;
/*!
* \brief TRUE if the channel is to be destroyed on hangup.
* (Used by pseudo channels.)
*/
unsigned int destroy:1;
unsigned int didtdd:1; /*!< flag to say its done it once */
/*! \brief TRUE if analog type line dialed no digits in Dial() */
unsigned int dialednone:1;
/*! \brief TRUE if in the process of dialing digits or sending something. */
unsigned int dialing:1;
/*! \brief TRUE if the transfer capability of the call is digital. */
unsigned int digital:1;
/*! \brief TRUE if Do-Not-Disturb is enabled. */
unsigned int dnd:1;
/*! \brief XXX BOOLEAN Purpose??? */
unsigned int echobreak:1;
/*!
* \brief TRUE if echo cancellation enabled when bridged.
* \note Initialized with the "echocancelwhenbridged" value read in from chan_dahdi.conf
* \note Disabled if the echo canceller is not setup.
*/
unsigned int echocanbridged:1;
/*! \brief TRUE if echo cancellation is turned on. */
unsigned int echocanon:1;
/*! \brief TRUE if a fax tone has already been handled. */
unsigned int faxhandled:1;
/*! \brief TRUE if dynamic faxbuffers are configured for use, default is OFF */
unsigned int usefaxbuffers:1;
/*! \brief TRUE while dynamic faxbuffers are in use */
unsigned int bufferoverrideinuse:1;
/*! \brief TRUE if over a radio and dahdi_read() has been called. */
unsigned int firstradio:1;
/*!
* \brief TRUE if the call will be considered "hung up" on a polarity reversal.
* \note Set from the "hanguponpolarityswitch" value read in from chan_dahdi.conf
*/
unsigned int hanguponpolarityswitch:1;
/*! \brief TRUE if DTMF detection needs to be done by hardware. */
unsigned int hardwaredtmf:1;
/*!
* \brief TRUE if the outgoing caller ID is blocked/hidden.
* \note Caller ID can be disabled by dialing *67.
* \note Caller ID can be enabled by dialing *82.
* \note Initialized with the "hidecallerid" value read in from chan_dahdi.conf
*/
unsigned int hidecallerid:1;
/*!
* \brief TRUE if hide just the name not the number for legacy PBX use.
* \note Only applies to PRI channels.
* \note Set from the "hidecalleridname" value read in from chan_dahdi.conf
*/
unsigned int hidecalleridname:1;
/*! \brief TRUE if DTMF detection is disabled. */
unsigned int ignoredtmf:1;
/*!
* \brief TRUE if the channel should be answered immediately
* without attempting to gather any digits.
* \note Set from the "immediate" value read in from chan_dahdi.conf
*/
unsigned int immediate:1;
/*! \brief TRUE if in an alarm condition. */
unsigned int inalarm:1;
/*! \brief TRUE if TDD in MATE mode */
unsigned int mate:1;
/*! \brief TRUE if we originated the call leg. */
unsigned int outgoing:1;
/* unsigned int overlapdial:1; unused and potentially confusing */
/*!
* \brief TRUE if busy extensions will hear the call-waiting tone
* and can use hook-flash to switch between callers.
* \note Set from the "callwaiting" value read in from chan_dahdi.conf
*/
unsigned int permcallwaiting:1;
/*!
* \brief TRUE if the outgoing caller ID is blocked/restricted/hidden.
* \note Set from the "hidecallerid" value read in from chan_dahdi.conf
*/
unsigned int permhidecallerid:1;
/*!
* \brief TRUE if PRI congestion/busy indications are sent out-of-band.
* \note Set from the "priindication" value read in from chan_dahdi.conf
*/
unsigned int priindication_oob:1;
/*!
* \brief TRUE if PRI B channels are always exclusively selected.
* \note Set from the "priexclusive" value read in from chan_dahdi.conf
*/
unsigned int priexclusive:1;
/*!
* \brief TRUE if we will pulse dial.
* \note Set from the "pulsedial" value read in from chan_dahdi.conf
*/
unsigned int pulse:1;
/*! \brief TRUE if a pulsed digit was detected. (Pulse dial phone detected) */
unsigned int pulsedial:1;
unsigned int restartpending:1; /*!< flag to ensure counted only once for restart */
/*!
* \brief TRUE if caller ID is restricted.
* \note Set but not used. Should be deleted. Redundant with permhidecallerid.
* \note Set from the "restrictcid" value read in from chan_dahdi.conf
*/
unsigned int restrictcid:1;
/*!
* \brief TRUE if three way calling is enabled
* \note Set from the "threewaycalling" value read in from chan_dahdi.conf
*/
unsigned int threewaycalling:1;
/*!
* \brief TRUE if call transfer is enabled
* \note For FXS ports (either direct analog or over T1/E1):
* Support flash-hook call transfer
* \note For digital ports using ISDN PRI protocols:
* Support switch-side transfer (called 2BCT, RLT or other names)
* \note Set from the "transfer" value read in from chan_dahdi.conf
*/
unsigned int transfer:1;
/*!
* \brief TRUE if caller ID is used on this channel.
* \note PRI and SS7 spans will save caller ID from the networking peer.
* \note FXS ports will generate the caller ID spill.
* \note FXO ports will listen for the caller ID spill.
* \note Set from the "usecallerid" value read in from chan_dahdi.conf
*/
unsigned int use_callerid:1;
/*!
* \brief TRUE if we will use the calling presentation setting
* from the Asterisk channel for outgoing calls.
* \note Only applies to PRI and SS7 channels.
* \note Set from the "usecallingpres" value read in from chan_dahdi.conf
*/
unsigned int use_callingpres:1;
/*!
* \brief TRUE if distinctive rings are to be detected.
* \note For FXO lines
* \note Set indirectly from the "usedistinctiveringdetection" value read in from chan_dahdi.conf
*/
unsigned int usedistinctiveringdetection:1;
/*!
* \brief TRUE if we should use the callerid from incoming call on dahdi transfer.
* \note Set from the "useincomingcalleridondahditransfer" value read in from chan_dahdi.conf
*/
unsigned int dahditrcallerid:1;
/*!
* \brief TRUE if allowed to flash-transfer to busy channels.
* \note Set from the "transfertobusy" value read in from chan_dahdi.conf
*/
unsigned int transfertobusy:1;
/*!
* \brief TRUE if the FXO port monitors for neon type MWI indications from the other end.
* \note Set if the "mwimonitor" value read in contains "neon" from chan_dahdi.conf
*/
unsigned int mwimonitor_neon:1;
/*!
* \brief TRUE if the FXO port monitors for fsk type MWI indications from the other end.
* \note Set if the "mwimonitor" value read in contains "fsk" from chan_dahdi.conf
*/
unsigned int mwimonitor_fsk:1;
/*!
* \brief TRUE if the FXO port monitors for rpas precursor to fsk MWI indications from the other end.
* \note RPAS - Ring Pulse Alert Signal
* \note Set if the "mwimonitor" value read in contains "rpas" from chan_dahdi.conf
*/
unsigned int mwimonitor_rpas:1;
/*! \brief TRUE if an MWI monitor thread is currently active */
unsigned int mwimonitoractive:1;
/*! \brief TRUE if a MWI message sending thread is active */
unsigned int mwisendactive:1;
/*!
* \brief TRUE if channel is out of reset and ready
* \note Set but not used.
*/
unsigned int inservice:1;
/*!
* \brief TRUE if the channel is locally blocked.
* \note Applies to SS7 channels.
*/
unsigned int locallyblocked:1;
/*!
* \brief TRUE if the channel is remotely blocked.
* \note Applies to SS7 channels.
*/
unsigned int remotelyblocked:1;
#if defined(HAVE_PRI) || defined(HAVE_SS7)
/*!
* \brief XXX BOOLEAN Purpose???
* \note Applies to SS7 channels.
*/
unsigned int rlt:1;
/*! \brief TRUE if channel is alerting/ringing */
unsigned int alerting:1;
/*! \brief TRUE if the call has already gone/hungup */
unsigned int alreadyhungup:1;
/*!
* \brief TRUE if this is an idle call
* \note Applies to PRI channels.
*/
unsigned int isidlecall:1;
/*!
* \brief TRUE if call is in a proceeding state.
* The call has started working its way through the network.
*/
unsigned int proceeding:1;
/*! \brief TRUE if the call has seen progress through the network. */
unsigned int progress:1;
/*!
* \brief TRUE if this channel is being reset/restarted
* \note Applies to PRI channels.
*/
unsigned int resetting:1;
/*!
* \brief TRUE if this channel has received a SETUP_ACKNOWLEDGE
* \note Applies to PRI channels.
*/
unsigned int setup_ack:1;
#endif
/*!
* \brief TRUE if SMDI (Simplified Message Desk Interface) is enabled
* \note Set from the "usesmdi" value read in from chan_dahdi.conf
*/
unsigned int use_smdi:1;
struct mwisend_info mwisend_data;
/*! \brief The serial port to listen for SMDI data on */
struct ast_smdi_interface *smdi_iface;
/*! \brief Distinctive Ring data */
struct dahdi_distRings drings;
/*!
* \brief The configured context for incoming calls.
* \note The "context" string read in from chan_dahdi.conf
*/
char context[AST_MAX_CONTEXT];
/*!
* \brief Saved context string.
*/
char defcontext[AST_MAX_CONTEXT];
/*! \brief Extension to use in the dialplan. */
char exten[AST_MAX_EXTENSION];
/*!
* \brief Language configured for calls.
* \note The "language" string read in from chan_dahdi.conf
*/
char language[MAX_LANGUAGE];
/*!
* \brief The configured music-on-hold class to use for calls.
* \note The "musicclass" or "mohinterpret" or "musiconhold" string read in from chan_dahdi.conf
*/
char mohinterpret[MAX_MUSICCLASS];
/*!
* \brief Suggested music-on-hold class for peer channel to use for calls.
* \note The "mohsuggest" string read in from chan_dahdi.conf
*/
char mohsuggest[MAX_MUSICCLASS];
char parkinglot[AST_MAX_EXTENSION]; /*!< Parking lot for this channel */
#if defined(PRI_ANI) || defined(HAVE_SS7)
/*! \brief Automatic Number Identification number (Alternate PRI caller ID number) */
char cid_ani[AST_MAX_EXTENSION];
#endif
/*! \brief Automatic Number Identification code from PRI */
int cid_ani2;
/*! \brief Caller ID number from an incoming call. */
char cid_num[AST_MAX_EXTENSION];
/*! \brief Caller ID Q.931 TON/NPI field values. Set by PRI. Zero otherwise. */
int cid_ton;
/*! \brief Caller ID name from an incoming call. */
char cid_name[AST_MAX_EXTENSION];
/*! \brief Last Caller ID number from an incoming call. */
char lastcid_num[AST_MAX_EXTENSION];
/*! \brief Last Caller ID name from an incoming call. */
char lastcid_name[AST_MAX_EXTENSION];
char *origcid_num; /*!< malloced original callerid */
char *origcid_name; /*!< malloced original callerid */
/*! \brief Call waiting number. */
char callwait_num[AST_MAX_EXTENSION];
/*! \brief Call waiting name. */
char callwait_name[AST_MAX_EXTENSION];
/*! \brief Redirecting Directory Number Information Service (RDNIS) number */
char rdnis[AST_MAX_EXTENSION];
/*! \brief Dialed Number Identifier */
char dnid[AST_MAX_EXTENSION];
/*!
* \brief Bitmapped groups this belongs to.
* \note The "group" bitmapped group string read in from chan_dahdi.conf
*/
ast_group_t group;
/*! \brief Active PCM encoding format: DAHDI_LAW_ALAW or DAHDI_LAW_MULAW */
int law;
int confno; /*!< Our conference */
int confusers; /*!< Who is using our conference */
int propconfno; /*!< Propagated conference number */
/*!
* \brief Bitmapped call groups this belongs to.
* \note The "callgroup" bitmapped group string read in from chan_dahdi.conf
*/
ast_group_t callgroup;
/*!
* \brief Bitmapped pickup groups this belongs to.
* \note The "pickupgroup" bitmapped group string read in from chan_dahdi.conf
*/
ast_group_t pickupgroup;
/*!
* \brief Channel variable list with associated values to set when a channel is created.
* \note The "setvar" strings read in from chan_dahdi.conf
*/
struct ast_variable *vars;
int channel; /*!< Channel Number or CRV */
int span; /*!< Span number */
time_t guardtime; /*!< Must wait this much time before using for new call */
int cid_signalling; /*!< CID signalling type bell202 or v23 */
int cid_start; /*!< CID start indicator, polarity or ring */
int callingpres; /*!< The value of calling presentation that we're going to use when placing a PRI call */
int callwaitingrepeat; /*!< How many samples to wait before repeating call waiting */
int cidcwexpire; /*!< When to expire our muting for CID/CW */
/*! \brief Analog caller ID waveform sample buffer */
unsigned char *cidspill;
/*! \brief Position in the cidspill buffer to send out next. */
int cidpos;
/*! \brief Length of the cidspill buffer containing samples. */
int cidlen;
/*! \brief Ring timeout timer?? */
int ringt;
/*!
* \brief Ring timeout base.
* \note Value computed indirectly from "ringtimeout" read in from chan_dahdi.conf
*/
int ringt_base;
/*!
* \brief Number of most significant digits/characters to strip from the dialed number.
* \note Feature is deprecated. Use dialplan logic.
* \note The characters are stripped before the PRI TON/NPI prefix
* characters are processed.
*/
int stripmsd;
/*! \brief BOOLEAN. XXX Meaning what?? */
int callwaitcas;
/*! \brief Number of call waiting rings. */
int callwaitrings;
/*! \brief Echo cancel parameters. */
struct {
struct dahdi_echocanparams head;
struct dahdi_echocanparam params[DAHDI_MAX_ECHOCANPARAMS];
} echocancel;
/*!
* \brief Echo training time. 0 = disabled
* \note Set from the "echotraining" value read in from chan_dahdi.conf
*/
int echotraining;
/*! \brief Filled with 'w'. XXX Purpose?? */
char echorest[20];
/*!
* \brief Number of times to see "busy" tone before hanging up.
* \note Set from the "busycount" value read in from chan_dahdi.conf
*/
int busycount;
/*!
* \brief Length of "busy" tone on time.
* \note Set from the "busypattern" value read in from chan_dahdi.conf
*/
int busy_tonelength;
/*!
* \brief Length of "busy" tone off time.
* \note Set from the "busypattern" value read in from chan_dahdi.conf
*/
int busy_quietlength;
/*!
* \brief Bitmapped call progress detection flags. CALLPROGRESS_xxx values.
* \note Bits set from the "callprogress" and "faxdetect" values read in from chan_dahdi.conf
*/
int callprogress;
/*!
* \brief Number of milliseconds to wait for dialtone.
* \note Set from the "waitfordialtone" value read in from chan_dahdi.conf
*/
int waitfordialtone;
struct timeval waitingfordt; /*!< Time we started waiting for dialtone */
struct timeval flashtime; /*!< Last flash-hook time */
/*! \brief Opaque DSP configuration structure. */
struct ast_dsp *dsp;
//int cref; /*!< Call reference number (Not used) */
/*! \brief DAHDI dial operation command struct for ioctl() call. */
struct dahdi_dialoperation dop;
int whichwink; /*!< SIG_FEATDMF_TA Which wink are we on? */
/*! \brief Second part of SIG_FEATDMF_TA wink operation. */
char finaldial[64];
char accountcode[AST_MAX_ACCOUNT_CODE]; /*!< Account code */
int amaflags; /*!< AMA Flags */
struct tdd_state *tdd; /*!< TDD flag */
/*! \brief Accumulated call forwarding number. */
char call_forward[AST_MAX_EXTENSION];
/*!
* \brief Voice mailbox location.
* \note Set from the "mailbox" string read in from chan_dahdi.conf
*/
char mailbox[AST_MAX_EXTENSION];
/*! \brief Opaque event subscription parameters for message waiting indication support. */
struct ast_event_sub *mwi_event_sub;
/*! \brief Delayed dialing for E911. Overlap digits for ISDN. */
char dialdest[256];
/*! \brief Time the interface went on-hook. */
int onhooktime;
/*! \brief TRUE if the FXS port is off-hook */
int fxsoffhookstate;
/*! \brief -1 = unknown, 0 = no messages, 1 = new messages available */
int msgstate;
#ifdef HAVE_DAHDI_LINEREVERSE_VMWI
struct dahdi_vmwi_info mwisend_setting; /*!< Which VMWI methods to use */
unsigned int mwisend_fsk: 1; /*! Variable for enabling FSK MWI handling in chan_dahdi */
unsigned int mwisend_rpas:1; /*! Variable for enabling Ring Pulse Alert before MWI FSK Spill */
#endif
int distinctivering; /*!< Which distinctivering to use */
int cidrings; /*!< Which ring to deliver CID on */
int dtmfrelax; /*!< whether to run in relaxed DTMF mode */
/*! \brief Holding place for event injected from outside normal operation. */
int fake_event;
/*!
* \brief Minimal time period (ms) between the answer polarity
* switch and hangup polarity switch.
*/
int polarityonanswerdelay;
/*! \brief Start delay time if polarityonanswerdelay is nonzero. */
struct timeval polaritydelaytv;
/*!
* \brief Send caller ID after this many rings.
* \note Set from the "sendcalleridafter" value read in from chan_dahdi.conf
*/
int sendcalleridafter;
#ifdef HAVE_PRI
/*! \brief DAHDI PRI control parameters */
struct dahdi_pri *pri;
/*! \brief XXX Purpose??? */
struct dahdi_pvt *bearer;
/*! \brief XXX Purpose??? */
struct dahdi_pvt *realcall;
/*! \brief Opaque libpri call control structure */
q931_call *call;
/*! \brief Channel number in span. */
int prioffset;
/*! \brief Logical span number within trunk group */
int logicalspan;
#endif
/*! \brief Current line interface polarity. POLARITY_IDLE, POLARITY_REV */
int polarity;
/*! \brief DSP feature flags: DSP_FEATURE_xxx */
int dsp_features;
#ifdef HAVE_SS7
/*! \brief SS7 control parameters */
struct dahdi_ss7 *ss7;
/*! \brief Opaque libss7 call control structure */
struct isup_call *ss7call;
char charge_number[50];
char gen_add_number[50];
char gen_dig_number[50];
char orig_called_num[50];
char redirecting_num[50];
char generic_name[50];
unsigned char gen_add_num_plan;
unsigned char gen_add_nai;
unsigned char gen_add_pres_ind;
unsigned char gen_add_type;
unsigned char gen_dig_type;
unsigned char gen_dig_scheme;
char jip_number[50];
unsigned char lspi_type;
unsigned char lspi_scheme;
unsigned char lspi_context;
char lspi_ident[50];
unsigned int call_ref_ident;
unsigned int call_ref_pc;
unsigned char calling_party_cat;
int transcap;
int cic; /*!< CIC associated with channel */
unsigned int dpc; /*!< CIC's DPC */
unsigned int loopedback:1;
#endif
#ifdef HAVE_OPENR2
struct dahdi_mfcr2 *mfcr2;
openr2_chan_t *r2chan;
openr2_calling_party_category_t mfcr2_recvd_category;
openr2_calling_party_category_t mfcr2_category;
int mfcr2_dnis_index;
int mfcr2_ani_index;
int mfcr2call:1;
int mfcr2_answer_pending:1;
int mfcr2_charge_calls:1;
int mfcr2_allow_collect_calls:1;
int mfcr2_forced_release:1;
int mfcr2_dnis_matched:1;
int mfcr2_call_accepted:1;
int mfcr2_accept_on_offer:1;
#endif
/*! \brief DTMF digit in progress. 0 when no digit in progress. */
char begindigit;
/*! \brief TRUE if confrence is muted. */
int muting;
} *iflist = NULL, *ifend = NULL;
/*! \brief Channel configuration from chan_dahdi.conf .
* This struct is used for parsing the [channels] section of chan_dahdi.conf.
* Generally there is a field here for every possible configuration item.
*
* The state of fields is saved along the parsing and whenever a 'channel'
* statement is reached, the current dahdi_chan_conf is used to configure the
* channel (struct dahdi_pvt)
*
* \see dahdi_chan_init for the default values.
*/
struct dahdi_chan_conf {
struct dahdi_pvt chan;
#ifdef HAVE_PRI
struct dahdi_pri pri;
#endif
#ifdef HAVE_SS7
struct dahdi_ss7 ss7;
#endif
#ifdef HAVE_OPENR2
struct dahdi_mfcr2_conf mfcr2;
#endif
struct dahdi_params timing;
int is_sig_auto; /*!< Use channel signalling from DAHDI? */
/*!
* \brief The serial port to listen for SMDI data on
* \note Set from the "smdiport" string read in from chan_dahdi.conf
*/
char smdi_port[SMDI_MAX_FILENAME_LEN];
};
/*! returns a new dahdi_chan_conf with default values (by-value) */
static struct dahdi_chan_conf dahdi_chan_conf_default(void)
{
/* recall that if a field is not included here it is initialized
* to 0 or equivalent
*/
struct dahdi_chan_conf conf = {
#ifdef HAVE_PRI
.pri = {
.nsf = PRI_NSF_NONE,
.switchtype = PRI_SWITCH_NI2,
.dialplan = PRI_UNKNOWN + 1,
.localdialplan = PRI_NATIONAL_ISDN + 1,
.nodetype = PRI_CPE,
.qsigchannelmapping = DAHDI_CHAN_MAPPING_PHYSICAL,
.minunused = 2,
.idleext = "",
.idledial = "",
.internationalprefix = "",
.nationalprefix = "",
.localprefix = "",
.privateprefix = "",
.unknownprefix = "",
.resetinterval = -1,
},
#endif
#ifdef HAVE_SS7
.ss7 = {
.called_nai = SS7_NAI_NATIONAL,
.calling_nai = SS7_NAI_NATIONAL,
.internationalprefix = "",
.nationalprefix = "",
.subscriberprefix = "",
.unknownprefix = ""
},
#endif
#ifdef HAVE_OPENR2
.mfcr2 = {
.variant = OR2_VAR_ITU,
.mfback_timeout = -1,
.metering_pulse_timeout = -1,
.max_ani = 10,
.max_dnis = 4,
.get_ani_first = -1,
#if defined(OR2_LIB_INTERFACE) && OR2_LIB_INTERFACE > 1
.skip_category_request = -1,
#endif
.call_files = 0,
.allow_collect_calls = 0,
.charge_calls = 1,
.accept_on_offer = 1,
.forced_release = 0,
.double_answer = 0,
.immediate_accept = -1,
.logdir = "",
.r2proto_file = "",
.loglevel = OR2_LOG_ERROR | OR2_LOG_WARNING,
.category = OR2_CALLING_PARTY_CATEGORY_NATIONAL_SUBSCRIBER
},
#endif
.chan = {
.context = "default",
.cid_num = "",
.cid_name = "",
.mohinterpret = "default",
.mohsuggest = "",
.parkinglot = "",
.transfertobusy = 1,
.cid_signalling = CID_SIG_BELL,
.cid_start = CID_START_RING,
.dahditrcallerid = 0,
.use_callerid = 1,
.sig = -1,
.outsigmod = -1,
.cid_rxgain = +5.0,
.tonezone = -1,
.echocancel.head.tap_length = 1,
.busycount = 3,
.accountcode = "",
.mailbox = "",
#ifdef HAVE_DAHDI_LINEREVERSE_VMWI
.mwisend_fsk = 1,
#endif
.polarityonanswerdelay = 600,
.sendcalleridafter = DEFAULT_CIDRINGS,
.buf_policy = DAHDI_POLICY_IMMEDIATE,
.buf_no = numbufs,
.usefaxbuffers = 0,
.faxbuf_policy = DAHDI_POLICY_IMMEDIATE,
.faxbuf_no = numbufs,
},
.timing = {
.prewinktime = -1,
.preflashtime = -1,
.winktime = -1,
.flashtime = -1,
.starttime = -1,
.rxwinktime = -1,
.rxflashtime = -1,
.debouncetime = -1
},
.is_sig_auto = 1,
.smdi_port = "/dev/ttyS0",
};
return conf;
}
static struct ast_channel *dahdi_request(const char *type, int format, void *data, int *cause);
static int dahdi_digit_begin(struct ast_channel *ast, char digit);
static int dahdi_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
static int dahdi_sendtext(struct ast_channel *c, const char *text);
static int dahdi_call(struct ast_channel *ast, char *rdest, int timeout);
static int dahdi_hangup(struct ast_channel *ast);
static int dahdi_answer(struct ast_channel *ast);
static struct ast_frame *dahdi_read(struct ast_channel *ast);
static int dahdi_write(struct ast_channel *ast, struct ast_frame *frame);
static struct ast_frame *dahdi_exception(struct ast_channel *ast);
static int dahdi_indicate(struct ast_channel *chan, int condition, const void *data, size_t datalen);
static int dahdi_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
static int dahdi_setoption(struct ast_channel *chan, int option, void *data, int datalen);
static int dahdi_func_read(struct ast_channel *chan, const char *function, char *data, char *buf, size_t len);
static int dahdi_func_write(struct ast_channel *chan, const char *function, char *data, const char *value);
static struct dahdi_pvt *handle_init_event(struct dahdi_pvt *i, int event);
static const struct ast_channel_tech dahdi_tech = {
.type = "DAHDI",
.description = tdesc,
.capabilities = AST_FORMAT_SLINEAR | AST_FORMAT_ULAW | AST_FORMAT_ALAW,
.requester = dahdi_request,
.send_digit_begin = dahdi_digit_begin,
.send_digit_end = dahdi_digit_end,
.send_text = dahdi_sendtext,
.call = dahdi_call,
.hangup = dahdi_hangup,
.answer = dahdi_answer,
.read = dahdi_read,
.write = dahdi_write,
.bridge = dahdi_bridge,
.exception = dahdi_exception,
.indicate = dahdi_indicate,
.fixup = dahdi_fixup,
.setoption = dahdi_setoption,
.func_channel_read = dahdi_func_read,
.func_channel_write = dahdi_func_write,
};
#ifdef HAVE_PRI
#define GET_CHANNEL(p) ((p)->bearer ? (p)->bearer->channel : p->channel)
#else
#define GET_CHANNEL(p) ((p)->channel)
#endif
struct dahdi_pvt *round_robin[32];
#if defined(HAVE_PRI)
static inline int pri_grab(struct dahdi_pvt *pvt, struct dahdi_pri *pri)
{
int res;
/* Grab the lock first */
do {
res = ast_mutex_trylock(&pri->lock);
if (res) {
DEADLOCK_AVOIDANCE(&pvt->lock);
}
} while (res);
/* Then break the poll */
if (pri->master != AST_PTHREADT_NULL)
pthread_kill(pri->master, SIGURG);
return 0;
}
#endif /* defined(HAVE_PRI) */
#if defined(HAVE_SS7)
static inline void ss7_rel(struct dahdi_ss7 *ss7)
{
ast_mutex_unlock(&ss7->lock);
}
#endif /* defined(HAVE_SS7) */
#if defined(HAVE_SS7)
static inline int ss7_grab(struct dahdi_pvt *pvt, struct dahdi_ss7 *pri)
{
int res;
/* Grab the lock first */
do {
res = ast_mutex_trylock(&pri->lock);
if (res) {
DEADLOCK_AVOIDANCE(&pvt->lock);
}
} while (res);
/* Then break the poll */
if (pri->master != AST_PTHREADT_NULL)
pthread_kill(pri->master, SIGURG);
return 0;
}
#endif /* defined(HAVE_SS7) */
#define NUM_CADENCE_MAX 25
static int num_cadence = 4;
static int user_has_defined_cadences = 0;
static struct dahdi_ring_cadence cadences[NUM_CADENCE_MAX] = {
{ { 125, 125, 2000, 4000 } }, /*!< Quick chirp followed by normal ring */
{ { 250, 250, 500, 1000, 250, 250, 500, 4000 } }, /*!< British style ring */
{ { 125, 125, 125, 125, 125, 4000 } }, /*!< Three short bursts */
{ { 1000, 500, 2500, 5000 } }, /*!< Long ring */
};
/*! \brief cidrings says in which pause to transmit the cid information, where the first pause
* is 1, the second pause is 2 and so on.
*/
static int cidrings[NUM_CADENCE_MAX] = {
2, /*!< Right after first long ring */
4, /*!< Right after long part */
3, /*!< After third chirp */
2, /*!< Second spell */
};
/* ETSI EN300 659-1 specifies the ring pulse between 200 and 300 mS */
static struct dahdi_ring_cadence AS_RP_cadence = {{250, 10000}};
#define ISTRUNK(p) ((p->sig == SIG_FXSLS) || (p->sig == SIG_FXSKS) || \
(p->sig == SIG_FXSGS) || (p->sig == SIG_PRI))
#define CANBUSYDETECT(p) (ISTRUNK(p) || (p->sig & (SIG_EM | SIG_EM_E1 | SIG_SF)) /* || (p->sig & __DAHDI_SIG_FXO) */)
#define CANPROGRESSDETECT(p) (ISTRUNK(p) || (p->sig & (SIG_EM | SIG_EM_E1 | SIG_SF)) /* || (p->sig & __DAHDI_SIG_FXO) */)
static int dahdi_get_index(struct ast_channel *ast, struct dahdi_pvt *p, int nullok)
{
int res;
if (p->subs[SUB_REAL].owner == ast)
res = 0;
else if (p->subs[SUB_CALLWAIT].owner == ast)
res = 1;
else if (p->subs[SUB_THREEWAY].owner == ast)
res = 2;
else {
res = -1;
if (!nullok)
ast_log(LOG_WARNING, "Unable to get index, and nullok is not asserted\n");
}
return res;
}
static void wakeup_sub(struct dahdi_pvt *p, int a, struct dahdi_pri *pri)
{
#ifdef HAVE_PRI
if (pri)
ast_mutex_unlock(&pri->lock);
#endif
for (;;) {
if (p->subs[a].owner) {
if (ast_channel_trylock(p->subs[a].owner)) {
DEADLOCK_AVOIDANCE(&p->lock);
} else {
ast_queue_frame(p->subs[a].owner, &ast_null_frame);
ast_channel_unlock(p->subs[a].owner);
break;
}
} else
break;
}
#ifdef HAVE_PRI
if (pri)
ast_mutex_lock(&pri->lock);
#endif
}
static void dahdi_queue_frame(struct dahdi_pvt *p, struct ast_frame *f, void *data)
{
#ifdef HAVE_PRI
struct dahdi_pri *pri = (struct dahdi_pri*) data;
#endif
#ifdef HAVE_SS7
struct dahdi_ss7 *ss7 = (struct dahdi_ss7*) data;
#endif
/* We must unlock the PRI to avoid the possibility of a deadlock */
#if defined(HAVE_PRI) || defined(HAVE_SS7)
if (data) {
switch (p->sig) {
#ifdef HAVE_PRI
case SIG_BRI:
case SIG_BRI_PTMP:
case SIG_PRI:
ast_mutex_unlock(&pri->lock);
break;
#endif
#ifdef HAVE_SS7
case SIG_SS7:
ast_mutex_unlock(&ss7->lock);
break;
#endif
default:
break;
}
}
#endif
for (;;) {
if (p->owner) {
if (ast_channel_trylock(p->owner)) {
DEADLOCK_AVOIDANCE(&p->lock);
} else {
ast_queue_frame(p->owner, f);
ast_channel_unlock(p->owner);
break;
}
} else
break;
}
#if defined(HAVE_PRI) || defined(HAVE_SS7)
if (data) {
switch (p->sig) {
#ifdef HAVE_PRI
case SIG_BRI:
case SIG_BRI_PTMP:
case SIG_PRI:
ast_mutex_lock(&pri->lock);
break;
#endif
#ifdef HAVE_SS7
case SIG_SS7:
ast_mutex_lock(&ss7->lock);
break;
#endif
default:
break;
}
}
#endif
}
static struct ast_channel *dahdi_new(struct dahdi_pvt *, int, int, int, int, int);
#ifdef HAVE_OPENR2
static int dahdi_r2_answer(struct dahdi_pvt *p)
{
int res = 0;
/* openr2 1.1.0 and older does not even define OR2_LIB_INTERFACE
* and does not has support for openr2_chan_answer_call_with_mode
* */
#if defined(OR2_LIB_INTERFACE) && OR2_LIB_INTERFACE > 1
const char *double_answer = pbx_builtin_getvar_helper(p->owner, "MFCR2_DOUBLE_ANSWER");
int wants_double_answer = ast_true(double_answer) ? 1 : 0;
if (!double_answer) {
/* this still can result in double answer if the channel context
* was configured that way */
res = openr2_chan_answer_call(p->r2chan);
} else if (wants_double_answer) {
res = openr2_chan_answer_call_with_mode(p->r2chan, OR2_ANSWER_DOUBLE);
} else {
res = openr2_chan_answer_call_with_mode(p->r2chan, OR2_ANSWER_SIMPLE);
}
#else
res = openr2_chan_answer_call(p->r2chan);
#endif
return res;
}
/* should be called with the ast_channel locked */
static openr2_calling_party_category_t dahdi_r2_get_channel_category(struct ast_channel *c)
{
openr2_calling_party_category_t cat;
const char *catstr = pbx_builtin_getvar_helper(c, "MFCR2_CATEGORY");
struct dahdi_pvt *p = c->tech_pvt;
if (ast_strlen_zero(catstr)) {
ast_debug(1, "No MFC/R2 category specified for chan %s, using default %s\n",
c->name, openr2_proto_get_category_string(p->mfcr2_category));
return p->mfcr2_category;
}
if ((cat = openr2_proto_get_category(catstr)) == OR2_CALLING_PARTY_CATEGORY_UNKNOWN) {
ast_log(LOG_WARNING, "Invalid category specified '%s' for chan %s, using default %s\n",
catstr, c->name, openr2_proto_get_category_string(p->mfcr2_category));
return p->mfcr2_category;
}
ast_debug(1, "Using category %s\n", catstr);
return cat;
}
static void dahdi_r2_on_call_init(openr2_chan_t *r2chan)
{
struct dahdi_pvt *p = openr2_chan_get_client_data(r2chan);
ast_mutex_lock(&p->lock);
if (p->mfcr2call) {
ast_mutex_unlock(&p->lock);
/* TODO: This can happen when some other thread just finished dahdi_request requesting this very same
interface but has not yet seized the line (dahdi_call), and the far end wins and seize the line,
can we avoid this somehow?, at this point when dahdi_call send the seize, it is likely that since
the other end will see our seize as a forced release and drop the call, we will see an invalid
pattern that will be seen and treated as protocol error. */
ast_log(LOG_ERROR, "Collision of calls on chan %d detected!.\n", openr2_chan_get_number(r2chan));
return;
}
p->mfcr2call = 1;
/* better safe than sorry ... */
p->cid_name[0] = '\0';
p->cid_num[0] = '\0';
p->rdnis[0] = '\0';
p->exten[0] = '\0';
p->mfcr2_ani_index = '\0';
p->mfcr2_dnis_index = '\0';
p->mfcr2_dnis_matched = 0;
p->mfcr2_answer_pending = 0;
p->mfcr2_call_accepted = 0;
ast_mutex_unlock(&p->lock);
ast_verbose("New MFC/R2 call detected on chan %d.\n", openr2_chan_get_number(r2chan));
}
static int get_alarms(struct dahdi_pvt *p);
static void handle_alarms(struct dahdi_pvt *p, int alms);
static void dahdi_r2_on_hardware_alarm(openr2_chan_t *r2chan, int alarm)
{
int res;
struct dahdi_pvt *p = openr2_chan_get_client_data(r2chan);
ast_mutex_lock(&p->lock);
p->inalarm = alarm ? 1 : 0;
if (p->inalarm) {
res = get_alarms(p);
handle_alarms(p, res);
} else {
ast_log(LOG_NOTICE, "Alarm cleared on channel %d\n", p->channel);
manager_event(EVENT_FLAG_SYSTEM, "AlarmClear", "Channel: %d\r\n", p->channel);
}
ast_mutex_unlock(&p->lock);
}
static void dahdi_r2_on_os_error(openr2_chan_t *r2chan, int errorcode)
{
ast_log(LOG_ERROR, "OS error on chan %d: %s\n", openr2_chan_get_number(r2chan), strerror(errorcode));
}
static void dahdi_r2_on_protocol_error(openr2_chan_t *r2chan, openr2_protocol_error_t reason)
{
struct dahdi_pvt *p = openr2_chan_get_client_data(r2chan);
ast_log(LOG_ERROR, "MFC/R2 protocol error on chan %d: %s\n", openr2_chan_get_number(r2chan), openr2_proto_get_error(reason));
if (p->owner) {
p->owner->hangupcause = AST_CAUSE_PROTOCOL_ERROR;
p->owner->_softhangup |= AST_SOFTHANGUP_DEV;
}
ast_mutex_lock(&p->lock);
p->mfcr2call = 0;
ast_mutex_unlock(&p->lock);
}
static void dahdi_r2_disconnect_call(struct dahdi_pvt *p, openr2_call_disconnect_cause_t cause)
{
if (openr2_chan_disconnect_call(p->r2chan, cause)) {
ast_log(LOG_NOTICE, "Bad! failed to disconnect call on channel %d with reason %s, hope for the best!\n",
p->channel, openr2_proto_get_disconnect_string(cause));
/* force the chan to idle and release the call flag now since we will not see a clean on_call_end */
openr2_chan_set_idle(p->r2chan);
ast_mutex_lock(&p->lock);
p->mfcr2call = 0;
ast_mutex_unlock(&p->lock);
}
}
static void dahdi_r2_on_call_offered(openr2_chan_t *r2chan, const char *ani, const char *dnis, openr2_calling_party_category_t category)
{
struct dahdi_pvt *p;
struct ast_channel *c;
ast_verbose("MFC/R2 call offered on chan %d. ANI = %s, DNIS = %s, Category = %s\n",
openr2_chan_get_number(r2chan), ani ? ani : "(restricted)", dnis,
openr2_proto_get_category_string(category));
p = openr2_chan_get_client_data(r2chan);
/* if collect calls are not allowed and this is a collect call, reject it! */
if (!p->mfcr2_allow_collect_calls && category == OR2_CALLING_PARTY_CATEGORY_COLLECT_CALL) {
ast_log(LOG_NOTICE, "Rejecting MFC/R2 collect call\n");
dahdi_r2_disconnect_call(p, OR2_CAUSE_COLLECT_CALL_REJECTED);
return;
}
ast_mutex_lock(&p->lock);
p->mfcr2_recvd_category = category;
/* if we're not supposed to use CID, clear whatever we have */
if (!p->use_callerid) {
ast_log(LOG_DEBUG, "No CID allowed in configuration, CID is being cleared!\n");
p->cid_num[0] = 0;
p->cid_name[0] = 0;
}
/* if we're supposed to answer immediately, clear DNIS and set 's' exten */
if (p->immediate || !openr2_context_get_max_dnis(openr2_chan_get_context(r2chan))) {
ast_log(LOG_DEBUG, "Setting exten => s because of immediate or 0 DNIS configured\n");
p->exten[0] = 's';
p->exten[1] = 0;
}
ast_mutex_unlock(&p->lock);
if (!ast_exists_extension(NULL, p->context, p->exten, 1, p->cid_num)) {
ast_log(LOG_NOTICE, "MFC/R2 call on channel %d requested non-existent extension '%s' in context '%s'. Rejecting call.\n",
p->channel, p->exten, p->context);
dahdi_r2_disconnect_call(p, OR2_CAUSE_UNALLOCATED_NUMBER);
return;
}
if (!p->mfcr2_accept_on_offer) {
/* The user wants us to start the PBX thread right away without accepting the call first */
c = dahdi_new(p, AST_STATE_RING, 1, SUB_REAL, DAHDI_LAW_ALAW, 0);
if (c) {
/* Done here, don't disable reading now since we still need to generate MF tones to accept
the call or reject it and detect the tone off condition of the other end, all of this
will be done in the PBX thread now */
return;
}
ast_log(LOG_WARNING, "Unable to create PBX channel in DAHDI channel %d\n", p->channel);
dahdi_r2_disconnect_call(p, OR2_CAUSE_OUT_OF_ORDER);
} else if (p->mfcr2_charge_calls) {
ast_log(LOG_DEBUG, "Accepting MFC/R2 call with charge on chan %d\n", p->channel);
openr2_chan_accept_call(r2chan, OR2_CALL_WITH_CHARGE);
} else {
ast_log(LOG_DEBUG, "Accepting MFC/R2 call with no charge on chan %d\n", p->channel);
openr2_chan_accept_call(r2chan, OR2_CALL_NO_CHARGE);
}
}
static void dahdi_r2_on_call_end(openr2_chan_t *r2chan)
{
struct dahdi_pvt *p = openr2_chan_get_client_data(r2chan);
ast_verbose("MFC/R2 call end on channel %d\n", p->channel);
ast_mutex_lock(&p->lock);
p->mfcr2call = 0;
ast_mutex_unlock(&p->lock);
}
static void dahdi_enable_ec(struct dahdi_pvt *p);
static void dahdi_r2_on_call_accepted(openr2_chan_t *r2chan, openr2_call_mode_t mode)
{
struct dahdi_pvt *p = NULL;
struct ast_channel *c = NULL;
p = openr2_chan_get_client_data(r2chan);
dahdi_enable_ec(p);
p->mfcr2_call_accepted = 1;
/* if it's an incoming call ... */
if (OR2_DIR_BACKWARD == openr2_chan_get_direction(r2chan)) {
ast_verbose("MFC/R2 call has been accepted on backward channel %d\n", openr2_chan_get_number(r2chan));
/* If accept on offer is not set, it means at this point the PBX thread is already
launched (was launched in the 'on call offered' handler) and therefore this callback
is being executed already in the PBX thread rather than the monitor thread, don't launch
any other thread, just disable the openr2 reading and answer the call if needed */
if (!p->mfcr2_accept_on_offer) {
openr2_chan_disable_read(r2chan);
if (p->mfcr2_answer_pending) {
ast_log(LOG_DEBUG, "Answering MFC/R2 call after accepting it on chan %d\n", openr2_chan_get_number(r2chan));
dahdi_r2_answer(p);
}
return;
}
c = dahdi_new(p, AST_STATE_RING, 1, SUB_REAL, DAHDI_LAW_ALAW, 0);
if (c) {
/* chan_dahdi will take care of reading from now on in the PBX thread, tell the
library to forget about it */
openr2_chan_disable_read(r2chan);
return;
}
ast_log(LOG_WARNING, "Unable to create PBX channel in DAHDI channel %d\n", p->channel);
/* failed to create the channel, bail out and report it as an out of order line */
dahdi_r2_disconnect_call(p, OR2_CAUSE_OUT_OF_ORDER);
return;
}
/* this is an outgoing call, no need to launch the PBX thread, most likely we're in one already */
ast_verbose("MFC/R2 call has been accepted on forward channel %d\n", p->channel);
p->subs[SUB_REAL].needringing = 1;
p->dialing = 0;
/* chan_dahdi will take care of reading from now on in the PBX thread, tell the library to forget about it */
openr2_chan_disable_read(r2chan);
}
static void dahdi_r2_on_call_answered(openr2_chan_t *r2chan)
{
struct dahdi_pvt *p = openr2_chan_get_client_data(r2chan);
ast_verbose("MFC/R2 call has been answered on channel %d\n", openr2_chan_get_number(r2chan));
p->subs[SUB_REAL].needanswer = 1;
}
static void dahdi_r2_on_call_read(openr2_chan_t *r2chan, const unsigned char *buf, int buflen)
{
/*ast_log(LOG_DEBUG, "Read data from dahdi channel %d\n", openr2_chan_get_number(r2chan));*/
}
static int dahdi_r2_cause_to_ast_cause(openr2_call_disconnect_cause_t cause)
{
switch (cause) {
case OR2_CAUSE_BUSY_NUMBER:
return AST_CAUSE_BUSY;
case OR2_CAUSE_NETWORK_CONGESTION:
return AST_CAUSE_CONGESTION;
case OR2_CAUSE_OUT_OF_ORDER:
return AST_CAUSE_DESTINATION_OUT_OF_ORDER;
case OR2_CAUSE_UNALLOCATED_NUMBER:
return AST_CAUSE_UNREGISTERED;
case OR2_CAUSE_NO_ANSWER:
return AST_CAUSE_NO_ANSWER;
case OR2_CAUSE_NORMAL_CLEARING:
return AST_CAUSE_NORMAL_CLEARING;
case OR2_CAUSE_UNSPECIFIED:
default:
return AST_CAUSE_NOTDEFINED;
}
}
static void dahdi_r2_on_call_disconnect(openr2_chan_t *r2chan, openr2_call_disconnect_cause_t cause)
{
struct dahdi_pvt *p = openr2_chan_get_client_data(r2chan);
ast_verbose("MFC/R2 call disconnected on channel %d\n", openr2_chan_get_number(r2chan));
ast_mutex_lock(&p->lock);
if (!p->owner) {
ast_mutex_unlock(&p->lock);
/* no owner, therefore we can't use dahdi_hangup to disconnect, do it right now */
dahdi_r2_disconnect_call(p, OR2_CAUSE_NORMAL_CLEARING);
return;
}
/* when we have an owner we don't call dahdi_r2_disconnect_call here, that will
be done in dahdi_hangup */
if (p->owner->_state == AST_STATE_UP) {
p->owner->_softhangup |= AST_SOFTHANGUP_DEV;
ast_mutex_unlock(&p->lock);
} else if (openr2_chan_get_direction(r2chan) == OR2_DIR_FORWARD) {
/* being the forward side we must report what happened to the call to whoever requested it */
switch (cause) {
case OR2_CAUSE_BUSY_NUMBER:
p->subs[SUB_REAL].needbusy = 1;
break;
case OR2_CAUSE_NETWORK_CONGESTION:
case OR2_CAUSE_OUT_OF_ORDER:
case OR2_CAUSE_UNALLOCATED_NUMBER:
case OR2_CAUSE_NO_ANSWER:
case OR2_CAUSE_UNSPECIFIED:
case OR2_CAUSE_NORMAL_CLEARING:
p->subs[SUB_REAL].needcongestion = 1;
break;
default:
p->owner->_softhangup |= AST_SOFTHANGUP_DEV;
}
ast_mutex_unlock(&p->lock);
} else {
ast_mutex_unlock(&p->lock);
/* being the backward side and not UP yet, we only need to request hangup */
/* TODO: what about doing this same thing when were AST_STATE_UP? */
ast_queue_hangup_with_cause(p->owner, dahdi_r2_cause_to_ast_cause(cause));
}
}
static void dahdi_r2_write_log(openr2_log_level_t level, char *logmessage)
{
switch (level) {
case OR2_LOG_NOTICE:
ast_verbose("%s", logmessage);
break;
case OR2_LOG_WARNING:
ast_log(LOG_WARNING, "%s", logmessage);
break;
case OR2_LOG_ERROR:
ast_log(LOG_ERROR, "%s", logmessage);
break;
case OR2_LOG_STACK_TRACE:
case OR2_LOG_MF_TRACE:
case OR2_LOG_CAS_TRACE:
case OR2_LOG_DEBUG:
case OR2_LOG_EX_DEBUG:
ast_log(LOG_DEBUG, "%s", logmessage);
break;
default:
ast_log(LOG_WARNING, "We should handle logging level %d here.\n", level);
ast_log(LOG_DEBUG, "%s", logmessage);
break;
}
}
static void dahdi_r2_on_line_blocked(openr2_chan_t *r2chan)
{
struct dahdi_pvt *p = openr2_chan_get_client_data(r2chan);
ast_mutex_lock(&p->lock);
p->remotelyblocked = 1;
ast_mutex_unlock(&p->lock);
ast_log(LOG_NOTICE, "Far end blocked on chan %d\n", openr2_chan_get_number(r2chan));
}
static void dahdi_r2_on_line_idle(openr2_chan_t *r2chan)
{
struct dahdi_pvt *p = openr2_chan_get_client_data(r2chan);
ast_mutex_lock(&p->lock);
p->remotelyblocked = 0;
ast_mutex_unlock(&p->lock);
ast_log(LOG_NOTICE, "Far end unblocked on chan %d\n", openr2_chan_get_number(r2chan));
}
static void dahdi_r2_on_context_log(openr2_context_t *r2context, openr2_log_level_t level, const char *fmt, va_list ap)
__attribute__((format (printf, 3, 0)));
static void dahdi_r2_on_context_log(openr2_context_t *r2context, openr2_log_level_t level, const char *fmt, va_list ap)
{
#define CONTEXT_TAG "Context - "
char logmsg[256];
char completemsg[sizeof(logmsg) + sizeof(CONTEXT_TAG) - 1];
vsnprintf(logmsg, sizeof(logmsg), fmt, ap);
snprintf(completemsg, sizeof(completemsg), CONTEXT_TAG "%s", logmsg);
dahdi_r2_write_log(level, completemsg);
#undef CONTEXT_TAG
}
static void dahdi_r2_on_chan_log(openr2_chan_t *r2chan, openr2_log_level_t level, const char *fmt, va_list ap)
__attribute__((format (printf, 3, 0)));
static void dahdi_r2_on_chan_log(openr2_chan_t *r2chan, openr2_log_level_t level, const char *fmt, va_list ap)
{
#define CHAN_TAG "Chan "
char logmsg[256];
char completemsg[sizeof(logmsg) + sizeof(CHAN_TAG) - 1];
vsnprintf(logmsg, sizeof(logmsg), fmt, ap);
snprintf(completemsg, sizeof(completemsg), CHAN_TAG "%d - %s", openr2_chan_get_number(r2chan), logmsg);
dahdi_r2_write_log(level, completemsg);
}
static int dahdi_r2_on_dnis_digit_received(openr2_chan_t *r2chan, char digit)
{
struct dahdi_pvt *p = openr2_chan_get_client_data(r2chan);
/* if 'immediate' is set, let's stop requesting DNIS */
if (p->immediate) {
return 0;
}
p->exten[p->mfcr2_dnis_index] = digit;
p->rdnis[p->mfcr2_dnis_index] = digit;
p->mfcr2_dnis_index++;
p->exten[p->mfcr2_dnis_index] = 0;
p->rdnis[p->mfcr2_dnis_index] = 0;
/* if the DNIS is a match and cannot match more, stop requesting DNIS */
if ((p->mfcr2_dnis_matched ||
(ast_exists_extension(NULL, p->context, p->exten, 1, p->cid_num) && (p->mfcr2_dnis_matched = 1))) &&
!ast_matchmore_extension(NULL, p->context, p->exten, 1, p->cid_num)) {
return 0;
}
/* otherwise keep going */
return 1;
}
static void dahdi_r2_on_ani_digit_received(openr2_chan_t *r2chan, char digit)
{
struct dahdi_pvt *p = openr2_chan_get_client_data(r2chan);
p->cid_num[p->mfcr2_ani_index] = digit;
p->cid_name[p->mfcr2_ani_index] = digit;
p->mfcr2_ani_index++;
p->cid_num[p->mfcr2_ani_index] = 0;
p->cid_name[p->mfcr2_ani_index] = 0;
}
static void dahdi_r2_on_billing_pulse_received(openr2_chan_t *r2chan)
{
ast_verbose("MFC/R2 billing pulse received on channel %d\n", openr2_chan_get_number(r2chan));
}
static openr2_event_interface_t dahdi_r2_event_iface = {
.on_call_init = dahdi_r2_on_call_init,
.on_call_offered = dahdi_r2_on_call_offered,
.on_call_accepted = dahdi_r2_on_call_accepted,
.on_call_answered = dahdi_r2_on_call_answered,
.on_call_disconnect = dahdi_r2_on_call_disconnect,
.on_call_end = dahdi_r2_on_call_end,
.on_call_read = dahdi_r2_on_call_read,
.on_hardware_alarm = dahdi_r2_on_hardware_alarm,
.on_os_error = dahdi_r2_on_os_error,
.on_protocol_error = dahdi_r2_on_protocol_error,
.on_line_blocked = dahdi_r2_on_line_blocked,
.on_line_idle = dahdi_r2_on_line_idle,
/* cast seems to be needed to get rid of the annoying warning regarding format attribute */
.on_context_log = (openr2_handle_context_logging_func)dahdi_r2_on_context_log,
.on_dnis_digit_received = dahdi_r2_on_dnis_digit_received,
.on_ani_digit_received = dahdi_r2_on_ani_digit_received,
/* so far we do nothing with billing pulses */
.on_billing_pulse_received = dahdi_r2_on_billing_pulse_received
};
static inline int16_t dahdi_r2_alaw_to_linear(uint8_t sample)
{
return AST_ALAW(sample);
}
static inline uint8_t dahdi_r2_linear_to_alaw(int sample)
{
return AST_LIN2A(sample);
}
static openr2_transcoder_interface_t dahdi_r2_transcode_iface = {
dahdi_r2_alaw_to_linear,
dahdi_r2_linear_to_alaw
};
#endif /* HAVE_OPENR2 */
static int restore_gains(struct dahdi_pvt *p);
static void swap_subs(struct dahdi_pvt *p, int a, int b)
{
int tchan;
int tinthreeway;
struct ast_channel *towner;
ast_debug(1, "Swapping %d and %d\n", a, b);
tchan = p->subs[a].chan;
towner = p->subs[a].owner;
tinthreeway = p->subs[a].inthreeway;
p->subs[a].chan = p->subs[b].chan;
p->subs[a].owner = p->subs[b].owner;
p->subs[a].inthreeway = p->subs[b].inthreeway;
p->subs[b].chan = tchan;
p->subs[b].owner = towner;
p->subs[b].inthreeway = tinthreeway;
if (p->subs[a].owner)
ast_channel_set_fd(p->subs[a].owner, 0, p->subs[a].dfd);
if (p->subs[b].owner)
ast_channel_set_fd(p->subs[b].owner, 0, p->subs[b].dfd);
wakeup_sub(p, a, NULL);
wakeup_sub(p, b, NULL);
}
static int dahdi_open(char *fn)
{
int fd;
int isnum;
int chan = 0;
int bs;
int x;
isnum = 1;
for (x = 0; x < strlen(fn); x++) {
if (!isdigit(fn[x])) {
isnum = 0;
break;
}
}
if (isnum) {
chan = atoi(fn);
if (chan < 1) {
ast_log(LOG_WARNING, "Invalid channel number '%s'\n", fn);
return -1;
}
fn = "/dev/dahdi/channel";
}
fd = open(fn, O_RDWR | O_NONBLOCK);
if (fd < 0) {
ast_log(LOG_WARNING, "Unable to open '%s': %s\n", fn, strerror(errno));
return -1;
}
if (chan) {
if (ioctl(fd, DAHDI_SPECIFY, &chan)) {
x = errno;
close(fd);
errno = x;
ast_log(LOG_WARNING, "Unable to specify channel %d: %s\n", chan, strerror(errno));
return -1;
}
}
bs = READ_SIZE;
if (ioctl(fd, DAHDI_SET_BLOCKSIZE, &bs) == -1) {
ast_log(LOG_WARNING, "Unable to set blocksize '%d': %s\n", bs, strerror(errno));
x = errno;
close(fd);
errno = x;
return -1;
}
return fd;
}
static void dahdi_close(int fd)
{
if (fd > 0)
close(fd);
}
static void dahdi_close_sub(struct dahdi_pvt *chan_pvt, int sub_num)
{
dahdi_close(chan_pvt->subs[sub_num].dfd);
chan_pvt->subs[sub_num].dfd = -1;
}
#if defined(HAVE_PRI)
static void dahdi_close_pri_fd(struct dahdi_pri *pri, int fd_num)
{
dahdi_close(pri->fds[fd_num]);
pri->fds[fd_num] = -1;
}
#endif /* defined(HAVE_PRI) */
#if defined(HAVE_SS7)
static void dahdi_close_ss7_fd(struct dahdi_ss7 *ss7, int fd_num)
{
dahdi_close(ss7->fds[fd_num]);
ss7->fds[fd_num] = -1;
}
#endif /* defined(HAVE_SS7) */
static int dahdi_setlinear(int dfd, int linear)
{
int res;
res = ioctl(dfd, DAHDI_SETLINEAR, &linear);
if (res)
return res;
return 0;
}
static int alloc_sub(struct dahdi_pvt *p, int x)
{
struct dahdi_bufferinfo bi;
int res;
if (p->subs[x].dfd >= 0) {
ast_log(LOG_WARNING, "%s subchannel of %d already in use\n", subnames[x], p->channel);
return -1;
}
p->subs[x].dfd = dahdi_open("/dev/dahdi/pseudo");
if (p->subs[x].dfd <= -1) {
ast_log(LOG_WARNING, "Unable to open pseudo channel: %s\n", strerror(errno));
return -1;
}
res = ioctl(p->subs[x].dfd, DAHDI_GET_BUFINFO, &bi);
if (!res) {
bi.txbufpolicy = p->buf_policy;
bi.rxbufpolicy = p->buf_policy;
bi.numbufs = p->buf_no;
res = ioctl(p->subs[x].dfd, DAHDI_SET_BUFINFO, &bi);
if (res < 0) {
ast_log(LOG_WARNING, "Unable to set buffer policy on channel %d: %s\n", x, strerror(errno));
}
} else
ast_log(LOG_WARNING, "Unable to check buffer policy on channel %d: %s\n", x, strerror(errno));
if (ioctl(p->subs[x].dfd, DAHDI_CHANNO, &p->subs[x].chan) == 1) {
ast_log(LOG_WARNING, "Unable to get channel number for pseudo channel on FD %d: %s\n", p->subs[x].dfd, strerror(errno));
dahdi_close_sub(p, x);
p->subs[x].dfd = -1;
return -1;
}
ast_debug(1, "Allocated %s subchannel on FD %d channel %d\n", subnames[x], p->subs[x].dfd, p->subs[x].chan);
return 0;
}
static int unalloc_sub(struct dahdi_pvt *p, int x)
{
if (!x) {
ast_log(LOG_WARNING, "Trying to unalloc the real channel %d?!?\n", p->channel);
return -1;
}
ast_debug(1, "Released sub %d of channel %d\n", x, p->channel);
dahdi_close_sub(p, x);
p->subs[x].linear = 0;
p->subs[x].chan = 0;
p->subs[x].owner = NULL;
p->subs[x].inthreeway = 0;
p->polarity = POLARITY_IDLE;
memset(&p->subs[x].curconf, 0, sizeof(p->subs[x].curconf));
return 0;
}
static int digit_to_dtmfindex(char digit)
{
if (isdigit(digit))
return DAHDI_TONE_DTMF_BASE + (digit - '0');
else if (digit >= 'A' && digit <= 'D')
return DAHDI_TONE_DTMF_A + (digit - 'A');
else if (digit >= 'a' && digit <= 'd')
return DAHDI_TONE_DTMF_A + (digit - 'a');
else if (digit == '*')
return DAHDI_TONE_DTMF_s;
else if (digit == '#')
return DAHDI_TONE_DTMF_p;
else
return -1;
}
static int dahdi_digit_begin(struct ast_channel *chan, char digit)
{
struct dahdi_pvt *pvt;
int idx;
int dtmf = -1;
pvt = chan->tech_pvt;
ast_mutex_lock(&pvt->lock);
idx = dahdi_get_index(chan, pvt, 0);
if ((idx != SUB_REAL) || !pvt->owner)
goto out;
#ifdef HAVE_PRI
if (((pvt->sig == SIG_PRI) || (pvt->sig == SIG_BRI) || (pvt->sig == SIG_BRI_PTMP))
&& (chan->_state == AST_STATE_DIALING) && !pvt->proceeding) {
if (pvt->setup_ack) {
if (!pri_grab(pvt, pvt->pri)) {
pri_information(pvt->pri->pri, pvt->call, digit);
pri_rel(pvt->pri);
} else
ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", pvt->span);
} else if (strlen(pvt->dialdest) < sizeof(pvt->dialdest) - 1) {
int res;
ast_debug(1, "Queueing digit '%c' since setup_ack not yet received\n", digit);
res = strlen(pvt->dialdest);
pvt->dialdest[res++] = digit;
pvt->dialdest[res] = '\0';
}
goto out;
}
#endif
if ((dtmf = digit_to_dtmfindex(digit)) == -1)
goto out;
if (pvt->pulse || ioctl(pvt->subs[SUB_REAL].dfd, DAHDI_SENDTONE, &dtmf)) {
int res;
struct dahdi_dialoperation zo = {
.op = DAHDI_DIAL_OP_APPEND,
};
zo.dialstr[0] = 'T';
zo.dialstr[1] = digit;
zo.dialstr[2] = '\0';
if ((res = ioctl(pvt->subs[SUB_REAL].dfd, DAHDI_DIAL, &zo)))
ast_log(LOG_WARNING, "Couldn't dial digit %c: %s\n", digit, strerror(errno));
else
pvt->dialing = 1;
} else {
ast_debug(1, "Started VLDTMF digit '%c'\n", digit);
pvt->dialing = 1;
pvt->begindigit = digit;
}
out:
ast_mutex_unlock(&pvt->lock);
return 0;
}
static int dahdi_digit_end(struct ast_channel *chan, char digit, unsigned int duration)
{
struct dahdi_pvt *pvt;
int res = 0;
int idx;
int x;
pvt = chan->tech_pvt;
ast_mutex_lock(&pvt->lock);
idx = dahdi_get_index(chan, pvt, 0);
if ((idx != SUB_REAL) || !pvt->owner || pvt->pulse)
goto out;
#ifdef HAVE_PRI
/* This means that the digit was already sent via PRI signalling */
if (((pvt->sig == SIG_PRI) || (pvt->sig == SIG_BRI) || (pvt->sig == SIG_BRI_PTMP))
&& !pvt->begindigit)
goto out;
#endif
if (pvt->begindigit) {
x = -1;
ast_debug(1, "Ending VLDTMF digit '%c'\n", digit);
res = ioctl(pvt->subs[SUB_REAL].dfd, DAHDI_SENDTONE, &x);
pvt->dialing = 0;
pvt->begindigit = 0;
}
out:
ast_mutex_unlock(&pvt->lock);
return res;
}
static char *events[] = {
"No event",
"On hook",
"Ring/Answered",
"Wink/Flash",
"Alarm",
"No more alarm",
"HDLC Abort",
"HDLC Overrun",
"HDLC Bad FCS",
"Dial Complete",
"Ringer On",
"Ringer Off",
"Hook Transition Complete",
"Bits Changed",
"Pulse Start",
"Timer Expired",
"Timer Ping",
"Polarity Reversal",
"Ring Begin",
};
static struct {
int alarm;
char *name;
} alarms[] = {
{ DAHDI_ALARM_RED, "Red Alarm" },
{ DAHDI_ALARM_YELLOW, "Yellow Alarm" },
{ DAHDI_ALARM_BLUE, "Blue Alarm" },
{ DAHDI_ALARM_RECOVER, "Recovering" },
{ DAHDI_ALARM_LOOPBACK, "Loopback" },
{ DAHDI_ALARM_NOTOPEN, "Not Open" },
{ DAHDI_ALARM_NONE, "None" },
};
static char *alarm2str(int alm)
{
int x;
for (x = 0; x < ARRAY_LEN(alarms); x++) {
if (alarms[x].alarm & alm)
return alarms[x].name;
}
return alm ? "Unknown Alarm" : "No Alarm";
}
static char *event2str(int event)
{
static char buf[256];
if ((event < (ARRAY_LEN(events))) && (event > -1))
return events[event];
sprintf(buf, "Event %d", event); /* safe */
return buf;
}
#ifdef HAVE_PRI
static char *dialplan2str(int dialplan)
{
if (dialplan == -1 || dialplan == -2) {
return("Dynamically set dialplan in ISDN");
}
return (pri_plan2str(dialplan));
}
#endif
static char *dahdi_sig2str(int sig)
{
static char buf[256];
switch (sig) {
case SIG_EM:
return "E & M Immediate";
case SIG_EMWINK:
return "E & M Wink";
case SIG_EM_E1:
return "E & M E1";
case SIG_FEATD:
return "Feature Group D (DTMF)";
case SIG_FEATDMF:
return "Feature Group D (MF)";
case SIG_FEATDMF_TA:
return "Feature Groud D (MF) Tandem Access";
case SIG_FEATB:
return "Feature Group B (MF)";
case SIG_E911:
return "E911 (MF)";
case SIG_FGC_CAMA:
return "FGC/CAMA (Dialpulse)";
case SIG_FGC_CAMAMF:
return "FGC/CAMA (MF)";
case SIG_FXSLS:
return "FXS Loopstart";
case SIG_FXSGS:
return "FXS Groundstart";
case SIG_FXSKS:
return "FXS Kewlstart";
case SIG_FXOLS:
return "FXO Loopstart";
case SIG_FXOGS:
return "FXO Groundstart";
case SIG_FXOKS:
return "FXO Kewlstart";
case SIG_PRI:
return "ISDN PRI";
case SIG_BRI:
return "ISDN BRI Point to Point";
case SIG_BRI_PTMP:
return "ISDN BRI Point to MultiPoint";
case SIG_SS7:
return "SS7";
case SIG_MFCR2:
return "MFC/R2";
case SIG_SF:
return "SF (Tone) Immediate";
case SIG_SFWINK:
return "SF (Tone) Wink";
case SIG_SF_FEATD:
return "SF (Tone) with Feature Group D (DTMF)";
case SIG_SF_FEATDMF:
return "SF (Tone) with Feature Group D (MF)";
case SIG_SF_FEATB:
return "SF (Tone) with Feature Group B (MF)";
case SIG_GR303FXOKS:
return "GR-303 with FXOKS";
case SIG_GR303FXSKS:
return "GR-303 with FXSKS";
case 0:
return "Pseudo";
default:
snprintf(buf, sizeof(buf), "Unknown signalling %d", sig);
return buf;
}
}
#define sig2str dahdi_sig2str
static int conf_add(struct dahdi_pvt *p, struct dahdi_subchannel *c, int idx, int slavechannel)
{
/* If the conference already exists, and we're already in it
don't bother doing anything */
struct dahdi_confinfo zi;
memset(&zi, 0, sizeof(zi));
zi.chan = 0;
if (slavechannel > 0) {
/* If we have only one slave, do a digital mon */
zi.confmode = DAHDI_CONF_DIGITALMON;
zi.confno = slavechannel;
} else {
if (!idx) {
/* Real-side and pseudo-side both participate in conference */
zi.confmode = DAHDI_CONF_REALANDPSEUDO | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER |
DAHDI_CONF_PSEUDO_TALKER | DAHDI_CONF_PSEUDO_LISTENER;
} else
zi.confmode = DAHDI_CONF_CONF | DAHDI_CONF_TALKER | DAHDI_CONF_LISTENER;
zi.confno = p->confno;
}
if ((zi.confno == c->curconf.confno) && (zi.confmode == c->curconf.confmode))
return 0;
if (c->dfd < 0)
return 0;
if (ioctl(c->dfd, DAHDI_SETCONF, &zi)) {
ast_log(LOG_WARNING, "Failed to add %d to conference %d/%d: %s\n", c->dfd, zi.confmode, zi.confno, strerror(errno));
return -1;
}
if (slavechannel < 1) {
p->confno = zi.confno;
}
memcpy(&c->curconf, &zi, sizeof(c->curconf));
ast_debug(1, "Added %d to conference %d/%d\n", c->dfd, c->curconf.confmode, c->curconf.confno);
return 0;
}
static int isourconf(struct dahdi_pvt *p, struct dahdi_subchannel *c)
{
/* If they're listening to our channel, they're ours */
if ((p->channel == c->curconf.confno) && (c->curconf.confmode == DAHDI_CONF_DIGITALMON))
return 1;
/* If they're a talker on our (allocated) conference, they're ours */
if ((p->confno > 0) && (p->confno == c->curconf.confno) && (c->curconf.confmode & DAHDI_CONF_TALKER))
return 1;
return 0;
}
static int conf_del(struct dahdi_pvt *p, struct dahdi_subchannel *c, int idx)
{
struct dahdi_confinfo zi;
if (/* Can't delete if there's no dfd */
(c->dfd < 0) ||
/* Don't delete from the conference if it's not our conference */
!isourconf(p, c)
/* Don't delete if we don't think it's conferenced at all (implied) */
) return 0;
memset(&zi, 0, sizeof(zi));
if (ioctl(c->dfd, DAHDI_SETCONF, &zi)) {
ast_log(LOG_WARNING, "Failed to drop %d from conference %d/%d: %s\n", c->dfd, c->curconf.confmode, c->curconf.confno, strerror(errno));
return -1;
}
ast_debug(1, "Removed %d from conference %d/%d\n", c->dfd, c->curconf.confmode, c->curconf.confno);
memcpy(&c->curconf, &zi, sizeof(c->curconf));
return 0;
}
static int isslavenative(struct dahdi_pvt *p, struct dahdi_pvt **out)
{
int x;
int useslavenative;
struct dahdi_pvt *slave = NULL;
/* Start out optimistic */
useslavenative = 1;
/* Update conference state in a stateless fashion */
for (x = 0; x < 3; x++) {
/* Any three-way calling makes slave native mode *definitely* out
of the question */
if ((p->subs[x].dfd > -1) && p->subs[x].inthreeway)
useslavenative = 0;
}
/* If we don't have any 3-way calls, check to see if we have
precisely one slave */
if (useslavenative) {
for (x = 0; x < MAX_SLAVES; x++) {
if (p->slaves[x]) {
if (slave) {
/* Whoops already have a slave! No
slave native and stop right away */
slave = NULL;
useslavenative = 0;
break;
} else {
/* We have one slave so far */
slave = p->slaves[x];
}
}
}
}
/* If no slave, slave native definitely out */
if (!slave)
useslavenative = 0;
else if (slave->law != p->law) {
useslavenative = 0;
slave = NULL;
}
if (out)
*out = slave;
return useslavenative;
}
static int reset_conf(struct dahdi_pvt *p)
{
p->confno = -1;
memset(&p->subs[SUB_REAL].curconf, 0, sizeof(p->subs[SUB_REAL].curconf));
if (p->subs[SUB_REAL].dfd > -1) {
struct dahdi_confinfo zi;
memset(&zi, 0, sizeof(zi));
if (ioctl(p->subs[SUB_REAL].dfd, DAHDI_SETCONF, &zi))
ast_log(LOG_WARNING, "Failed to reset conferencing on channel %d: %s\n", p->channel, strerror(errno));
}
return 0;
}
static int update_conf(struct dahdi_pvt *p)
{
int needconf = 0;
int x;
int useslavenative;
struct dahdi_pvt *slave = NULL;
useslavenative = isslavenative(p, &slave);
/* Start with the obvious, general stuff */
for (x = 0; x < 3; x++) {
/* Look for three way calls */
if ((p->subs[x].dfd > -1) && p->subs[x].inthreeway) {
conf_add(p, &p->subs[x], x, 0);
needconf++;
} else {
conf_del(p, &p->subs[x], x);
}
}
/* If we have a slave, add him to our conference now. or DAX
if this is slave native */
for (x = 0; x < MAX_SLAVES; x++) {
if (p->slaves[x]) {
if (useslavenative)
conf_add(p, &p->slaves[x]->subs[SUB_REAL], SUB_REAL, GET_CHANNEL(p));
else {
conf_add(p, &p->slaves[x]->subs[SUB_REAL], SUB_REAL, 0);
needconf++;
}
}
}
/* If we're supposed to be in there, do so now */
if (p->inconference && !p->subs[SUB_REAL].inthreeway) {
if (useslavenative)
conf_add(p, &p->subs[SUB_REAL], SUB_REAL, GET_CHANNEL(slave));
else {
conf_add(p, &p->subs[SUB_REAL], SUB_REAL, 0);
needconf++;
}
}
/* If we have a master, add ourselves to his conference */
if (p->master) {
if (isslavenative(p->master, NULL)) {
conf_add(p->master, &p->subs[SUB_REAL], SUB_REAL, GET_CHANNEL(p->master));
} else {
conf_add(p->master, &p->subs[SUB_REAL], SUB_REAL, 0);
}
}
if (!needconf) {
/* Nobody is left (or should be left) in our conference.
Kill it. */
p->confno = -1;
}
ast_debug(1, "Updated conferencing on %d, with %d conference users\n", p->channel, needconf);
return 0;
}
static void dahdi_enable_ec(struct dahdi_pvt *p)
{
int x;
int res;
if (!p)
return;
if (p->echocanon) {
ast_debug(1, "Echo cancellation already on\n");
return;
}
if (p->digital) {
ast_debug(1, "Echo cancellation isn't required on digital connection\n");
return;
}
if (p->echocancel.head.tap_length) {
if ((p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP) || (p->sig == SIG_PRI) || (p->sig == SIG_SS7)) {
x = 1;
res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_AUDIOMODE, &x);
if (res)
ast_log(LOG_WARNING, "Unable to enable audio mode on channel %d (%s)\n", p->channel, strerror(errno));
}
res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_ECHOCANCEL_PARAMS, &p->echocancel);
if (res) {
ast_log(LOG_WARNING, "Unable to enable echo cancellation on channel %d (%s)\n", p->channel, strerror(errno));
} else {
p->echocanon = 1;
ast_debug(1, "Enabled echo cancellation on channel %d\n", p->channel);
}
} else
ast_debug(1, "No echo cancellation requested\n");
}
static void dahdi_train_ec(struct dahdi_pvt *p)
{
int x;
int res;
if (p && p->echocanon && p->echotraining) {
x = p->echotraining;
res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_ECHOTRAIN, &x);
if (res)
ast_log(LOG_WARNING, "Unable to request echo training on channel %d: %s\n", p->channel, strerror(errno));
else
ast_debug(1, "Engaged echo training on channel %d\n", p->channel);
} else {
ast_debug(1, "No echo training requested\n");
}
}
static void dahdi_disable_ec(struct dahdi_pvt *p)
{
int res;
if (p->echocanon) {
struct dahdi_echocanparams ecp = { .tap_length = 0 };
res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_ECHOCANCEL_PARAMS, &ecp);
if (res)
ast_log(LOG_WARNING, "Unable to disable echo cancellation on channel %d: %s\n", p->channel, strerror(errno));
else
ast_debug(1, "Disabled echo cancellation on channel %d\n", p->channel);
}
p->echocanon = 0;
}
static void fill_txgain(struct dahdi_gains *g, float gain, int law)
{
int j;
int k;
float linear_gain = pow(10.0, gain / 20.0);
switch (law) {
case DAHDI_LAW_ALAW:
for (j = 0; j < ARRAY_LEN(g->txgain); j++) {
if (gain) {
k = (int) (((float) AST_ALAW(j)) * linear_gain);
if (k > 32767) k = 32767;
if (k < -32767) k = -32767;
g->txgain[j] = AST_LIN2A(k);
} else {
g->txgain[j] = j;
}
}
break;
case DAHDI_LAW_MULAW:
for (j = 0; j < ARRAY_LEN(g->txgain); j++) {
if (gain) {
k = (int) (((float) AST_MULAW(j)) * linear_gain);
if (k > 32767) k = 32767;
if (k < -32767) k = -32767;
g->txgain[j] = AST_LIN2MU(k);
} else {
g->txgain[j] = j;
}
}
break;
}
}
static void fill_rxgain(struct dahdi_gains *g, float gain, int law)
{
int j;
int k;
float linear_gain = pow(10.0, gain / 20.0);
switch (law) {
case DAHDI_LAW_ALAW:
for (j = 0; j < ARRAY_LEN(g->rxgain); j++) {
if (gain) {
k = (int) (((float) AST_ALAW(j)) * linear_gain);
if (k > 32767) k = 32767;
if (k < -32767) k = -32767;
g->rxgain[j] = AST_LIN2A(k);
} else {
g->rxgain[j] = j;
}
}
break;
case DAHDI_LAW_MULAW:
for (j = 0; j < ARRAY_LEN(g->rxgain); j++) {
if (gain) {
k = (int) (((float) AST_MULAW(j)) * linear_gain);
if (k > 32767) k = 32767;
if (k < -32767) k = -32767;
g->rxgain[j] = AST_LIN2MU(k);
} else {
g->rxgain[j] = j;
}
}
break;
}
}
static int set_actual_txgain(int fd, int chan, float gain, int law)
{
struct dahdi_gains g;
int res;
memset(&g, 0, sizeof(g));
g.chan = chan;
res = ioctl(fd, DAHDI_GETGAINS, &g);
if (res) {
ast_debug(1, "Failed to read gains: %s\n", strerror(errno));
return res;
}
fill_txgain(&g, gain, law);
return ioctl(fd, DAHDI_SETGAINS, &g);
}
static int set_actual_rxgain(int fd, int chan, float gain, int law)
{
struct dahdi_gains g;
int res;
memset(&g, 0, sizeof(g));
g.chan = chan;
res = ioctl(fd, DAHDI_GETGAINS, &g);
if (res) {
ast_debug(1, "Failed to read gains: %s\n", strerror(errno));
return res;
}
fill_rxgain(&g, gain, law);
return ioctl(fd, DAHDI_SETGAINS, &g);
}
static int set_actual_gain(int fd, int chan, float rxgain, float txgain, int law)
{
return set_actual_txgain(fd, chan, txgain, law) | set_actual_rxgain(fd, chan, rxgain, law);
}
static int bump_gains(struct dahdi_pvt *p)
{
int res;
/* Bump receive gain by value stored in cid_rxgain */
res = set_actual_gain(p->subs[SUB_REAL].dfd, 0, p->rxgain + p->cid_rxgain, p->txgain, p->law);
if (res) {
ast_log(LOG_WARNING, "Unable to bump gain: %s\n", strerror(errno));
return -1;
}
return 0;
}
static int restore_gains(struct dahdi_pvt *p)
{
int res;
res = set_actual_gain(p->subs[SUB_REAL].dfd, 0, p->rxgain, p->txgain, p->law);
if (res) {
ast_log(LOG_WARNING, "Unable to restore gains: %s\n", strerror(errno));
return -1;
}
return 0;
}
static inline int dahdi_set_hook(int fd, int hs)
{
int x, res;
x = hs;
res = ioctl(fd, DAHDI_HOOK, &x);
if (res < 0) {
if (errno == EINPROGRESS)
return 0;
ast_log(LOG_WARNING, "DAHDI hook failed returned %d (trying %d): %s\n", res, hs, strerror(errno));
/* will expectedly fail if phone is off hook during operation, such as during a restart */
}
return res;
}
static inline int dahdi_confmute(struct dahdi_pvt *p, int muted)
{
int x, y, res;
x = muted;
if ((p->sig == SIG_PRI) || (p->sig == SIG_SS7) || (p->sig == SIG_BRI) || (p->sig == SIG_BRI_PTMP)) {
y = 1;
res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_AUDIOMODE, &y);
if (res)
ast_log(LOG_WARNING, "Unable to set audio mode on %d: %s\n", p->channel, strerror(errno));
}
res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_CONFMUTE, &x);
if (res < 0)
ast_log(LOG_WARNING, "DAHDI confmute(%d) failed on channel %d: %s\n", muted, p->channel, strerror(errno));
return res;
}
static int save_conference(struct dahdi_pvt *p)
{
struct dahdi_confinfo c;
int res;
if (p->saveconf.confmode) {
ast_log(LOG_WARNING, "Can't save conference -- already in use\n");
return -1;
}
p->saveconf.chan = 0;
res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_GETCONF, &p->saveconf);
if (res) {
ast_log(LOG_WARNING, "Unable to get conference info: %s\n", strerror(errno));
p->saveconf.confmode = 0;
return -1;
}
memset(&c, 0, sizeof(c));
c.confmode = DAHDI_CONF_NORMAL;
res = ioctl(p->subs[SUB_REAL].dfd, DAHDI_SETCONF, &c);
if (res