diff --git a/bridges/bridge_softmix.c b/bridges/bridge_softmix.c index cf891c6687..b2d3218154 100644 --- a/bridges/bridge_softmix.c +++ b/bridges/bridge_softmix.c @@ -623,6 +623,9 @@ static int softmix_bridge_write(struct ast_bridge *bridge, struct ast_bridge_cha } switch (frame->frametype) { + case AST_FRAME_NULL: + /* "Accept" the frame and discard it. */ + break; case AST_FRAME_DTMF_BEGIN: case AST_FRAME_DTMF_END: res = ast_bridge_queue_everyone_else(bridge, bridge_channel, frame); diff --git a/channels/Makefile b/channels/Makefile index 10d487cfbc..154d52e532 100644 --- a/channels/Makefile +++ b/channels/Makefile @@ -1,6 +1,6 @@ # # Asterisk -- An open source telephony toolkit. -# +# # Makefile for channel drivers # # Copyright (C) 1999-2006, Digium, Inc. @@ -72,10 +72,19 @@ dist-clean:: $(if $(filter chan_iax2,$(EMBEDDED_MODS)),modules.link,chan_iax2.so): $(subst .c,.o,$(wildcard iax2/*.c)) $(subst .c,.o,$(wildcard iax2/*.c)): _ASTCFLAGS+=$(call MOD_ASTCFLAGS,chan_iax2) + $(if $(filter chan_sip,$(EMBEDDED_MODS)),modules.link,chan_sip.so): $(subst .c,.o,$(wildcard sip/*.c)) $(subst .c,.o,$(wildcard sip/*.c)): _ASTCFLAGS+=$(call MOD_ASTCFLAGS,chan_sip) -$(if $(filter chan_dahdi,$(EMBEDDED_MODS)),modules.link,chan_dahdi.so): sig_analog.o sig_pri.o sig_ss7.o -sig_analog.o sig_pri.o sig_ss7.o: _ASTCFLAGS+=$(call MOD_ASTCFLAGS,chan_dahdi) + +# Additional objects to combine with chan_dahdi.so +CHAN_DAHDI_OBJS= \ + $(subst .c,.o,$(wildcard dahdi/*.c)) \ + sig_analog.o \ + sig_pri.o \ + sig_ss7.o \ + +$(if $(filter chan_dahdi,$(EMBEDDED_MODS)),modules.link,chan_dahdi.so): $(CHAN_DAHDI_OBJS) +$(CHAN_DAHDI_OBJS): _ASTCFLAGS+=$(call MOD_ASTCFLAGS,chan_dahdi) ifneq ($(filter chan_h323,$(EMBEDDED_MODS)),) modules.link: h323/libchanh323.a diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c index ac8695369b..0806ca570e 100644 --- a/channels/chan_dahdi.c +++ b/channels/chan_dahdi.c @@ -62,13 +62,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #else #include #endif -#include -#include #include -#include -#include -#include #include "sig_analog.h" /* Analog signaling is currently still present in chan_dahdi for use with * radio. Sig_analog does not currently handle any radio operations. If @@ -90,11 +85,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #endif #endif /* defined(HAVE_SS7) */ -#ifdef HAVE_OPENR2 +#if defined(HAVE_OPENR2) /* put this here until sig_mfcr2 comes along */ #define SIG_MFCR2_MAX_CHANNELS 672 /*!< No more than a DS3 per trunk group */ -#include -#endif +#endif /* defined(HAVE_OPENR2) */ #include "asterisk/lock.h" #include "asterisk/channel.h" @@ -131,6 +125,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/features_config.h" #include "asterisk/bridging.h" #include "asterisk/stasis_channels.h" +#include "chan_dahdi.h" +#include "dahdi/bridge_native_dahdi.h" /*** DOCUMENTATION @@ -422,7 +418,7 @@ static struct ast_jb_conf global_jbconf; /*! \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" +static const char tdesc[] = "DAHDI Telephony" #if defined(HAVE_PRI) || defined(HAVE_SS7) || defined(HAVE_OPENR2) " w/" #if defined(HAVE_PRI) @@ -445,33 +441,6 @@ static const char tdesc[] = "DAHDI Telephony Driver" 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 - #ifdef LOTS_OF_SPANS #define NUM_SPANS DAHDI_MAX_SPANS #else @@ -578,8 +547,6 @@ 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(void *userdata, struct stasis_subscription *sub, struct stasis_topic *topic, struct stasis_message *msg) @@ -625,8 +592,6 @@ static inline int dahdi_wait_event(int fd) #define DEFAULT_RINGT ((8000 * 8) / READ_SIZE) /*!< 8,000 ms */ #define DEFAULT_DIALTONE_DETECT_TIMEOUT ((10000 * 8) / READ_SIZE) /*!< 10,000 ms */ -struct dahdi_pvt; - /*! * \brief Configured ring timeout base. * \note Value computed from "ringtimeout" read in from chan_dahdi.conf if it exists. @@ -722,644 +687,16 @@ static const char dahdi_pri_cc_type[] = "DAHDI/PRI"; 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 const char * const subnames[] = { +const char * const 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 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; -}; - -/*! Specify the lists dahdi_pvt can be put in. */ -enum DAHDI_IFLIST { - DAHDI_IFLIST_NONE, /*!< The dahdi_pvt is not in any list. */ - DAHDI_IFLIST_MAIN, /*!< The dahdi_pvt is in the main interface list */ -#if defined(HAVE_PRI) - DAHDI_IFLIST_NO_B_CHAN, /*!< The dahdi_pvt is in a no B channel interface list */ -#endif /* defined(HAVE_PRI) */ -}; - -struct dahdi_pvt { - ast_mutex_t lock; /*!< Channel private lock. */ - struct callerid_state *cs; - 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; - - float txdrc; /*!< Dynamic Range Compression factor. a number between 1 and 6ish */ - float rxdrc; - - int tonezone; /*!< tone zone for this chan, or -1 for default */ - enum DAHDI_IFLIST which_iflist; /*!< Which interface list is this structure listed? */ - 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. - * \note This is used as a receive squelch for ISDN until connected. - */ - 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, present only for non sig_analog */ - 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; - /*! TRUE if dynamic faxbuffers are configured for use, default is OFF */ - unsigned int usefaxbuffers:1; - /*! TRUE while buffer configuration override is 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 and MFCR2 channels. - */ - unsigned int locallyblocked:1; - /*! - * \brief TRUE if the channel is remotely blocked. - * \note Applies to SS7 and MFCR2 channels. - */ - unsigned int remotelyblocked:1; - /*! - * \brief TRUE if the channel alarms will be managed also as Span ones - * \note Applies to all channels - */ - unsigned int manages_span_alarms:1; - -#if defined(HAVE_PRI) - struct sig_pri_span *pri; - int logicalspan; -#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 SMDI interface to get SMDI messages from. */ - 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 A description for the channel configuration - * \note The "description" string read in from chan_dahdi.conf - */ - char description[32]; - /*! - * \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(HAVE_PRI) || defined(HAVE_SS7) - /*! \brief Automatic Number Identification number (Alternate PRI caller ID number) */ - char cid_ani[AST_MAX_EXTENSION]; -#endif /* defined(HAVE_PRI) || defined(HAVE_SS7) */ - /*! \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 tag from incoming call - * \note the "cid_tag" string read in from chan_dahdi.conf - */ - char cid_tag[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 Caller ID subaddress from an incoming call. */ - char cid_subaddr[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 Default call PCM encoding format: DAHDI_LAW_ALAW or DAHDI_LAW_MULAW. */ - int law_default; - /*! \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 Named call groups this belongs to. - * \note The "namedcallgroup" string read in from chan_dahdi.conf - */ - struct ast_namedgroups *named_callgroups; - /*! - * \brief Named pickup groups this belongs to. - * \note The "namedpickupgroup" string read in from chan_dahdi.conf - */ - struct ast_namedgroups *named_pickupgroups; - /*! - * \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 */ - 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 or DTMF without warning event */ - int dtmfcid_holdoff_state; /*!< State indicator that allows for line to settle before checking for dtmf energy */ - struct timeval dtmfcid_delay; /*!< Time value used for allow line to settle */ - 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 stop waiting for CID/CW CAS response (In samples) */ - int cid_suppress_expire; /*!< How many samples to suppress after a CID spill. */ - /*! \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 TRUE if Call Waiting (CW) CPE Alert Signal (CAS) is being sent. - * \note - * After CAS is sent, the call waiting caller id will be sent if the phone - * gives a positive reply. - */ - 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 Busy cadence pattern description. - * \note Set from the "busypattern" value read from chan_dahdi.conf - */ - struct ast_dsp_busy_pattern busy_cadence; - /*! - * \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; - /*! - * \brief Number of frames to watch for dialtone in incoming calls - * \note Set from the "dialtone_detect" value read in from chan_dahdi.conf - */ - int dialtone_detect; - int dialtone_scanning_time_elapsed; /*!< Amount of audio scanned for dialtone, in frames */ - 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; - /*! \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 stasis_subscription *mwi_event_sub; - /*! \brief Delayed dialing for E911. Overlap digits for ISDN. */ - char dialdest[256]; -#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 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 on FXS after this many rings. Set to 1 for US. - * \note Set from the "sendcalleridafter" value read in from chan_dahdi.conf - */ - int sendcalleridafter; - /*! \brief Current line interface polarity. POLARITY_IDLE, POLARITY_REV */ - int polarity; - /*! \brief DSP feature flags: DSP_FEATURE_xxx */ - int dsp_features; -#if defined(HAVE_SS7) - /*! \brief SS7 control parameters */ - struct sig_ss7_linkset *ss7; -#endif /* defined(HAVE_SS7) */ -#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; - int mfcr2_progress_sent:1; -#endif - /*! \brief DTMF digit in progress. 0 when no digit in progress. */ - char begindigit; - /*! \brief TRUE if confrence is muted. */ - int muting; - void *sig_pvt; - struct ast_cc_config_params *cc_params; - /* DAHDI channel names may differ greatly from the - * string that was provided to an app such as Dial. We - * need to save the original string passed to dahdi_request - * for call completion purposes. This way, we can replicate - * the original dialed string later. - */ - char dialstring[AST_CHANNEL_NAME]; -}; - #define DATA_EXPORT_DAHDI_PVT(MEMBER) \ MEMBER(dahdi_pvt, cid_rxgain, AST_DATA_DOUBLE) \ MEMBER(dahdi_pvt, rxgain, AST_DATA_DOUBLE) \ @@ -1634,8 +971,6 @@ static int dahdi_func_write(struct ast_channel *chan, const char *function, char static int dahdi_devicestate(const char *data); static int dahdi_cc_callback(struct ast_channel *inbound, const char *dest, ast_cc_callback_fn callback); -/* BUGBUG The DAHDI channel driver needs its own native bridge technology. */ -/* BUGBUG The transfer=yes option is broken for ISDN to push tromboned calls to the peer. */ static struct ast_channel_tech dahdi_tech = { .type = "DAHDI", .description = tdesc, @@ -1648,7 +983,6 @@ static struct ast_channel_tech dahdi_tech = { .answer = dahdi_answer, .read = dahdi_read, .write = dahdi_write, - .bridge = dahdi_bridge, .exception = dahdi_exception, .indicate = dahdi_indicate, .fixup = dahdi_fixup, @@ -1662,77 +996,6 @@ static struct ast_channel_tech dahdi_tech = { #define GET_CHANNEL(p) ((p)->channel) -#define SIG_PRI_LIB_HANDLE_CASES \ - SIG_PRI: \ - case SIG_BRI: \ - case SIG_BRI_PTMP - -/*! - * \internal - * \brief Determine if sig_pri handles the signaling. - * \since 1.8 - * - * \param signaling Signaling to determine if is for sig_pri. - * - * \return TRUE if the signaling is for sig_pri. - */ -static inline int dahdi_sig_pri_lib_handles(int signaling) -{ - int handles; - - switch (signaling) { - case SIG_PRI_LIB_HANDLE_CASES: - handles = 1; - break; - default: - handles = 0; - break; - } - - return handles; -} - -static int analog_lib_handles(int signalling, int radio, int oprmode) -{ - switch (signalling) { - case SIG_FXOLS: - case SIG_FXOGS: - case SIG_FXOKS: - case SIG_FXSLS: - case SIG_FXSGS: - case SIG_FXSKS: - case SIG_EMWINK: - case SIG_EM: - case SIG_EM_E1: - case SIG_FEATD: - case SIG_FEATDMF: - case SIG_E911: - case SIG_FGC_CAMA: - case SIG_FGC_CAMAMF: - case SIG_FEATB: - case SIG_SFWINK: - case SIG_SF: - case SIG_SF_FEATD: - case SIG_SF_FEATDMF: - case SIG_FEATDMF_TA: - case SIG_SF_FEATB: - break; - default: - /* The rest of the function should cover the remainder of signalling types */ - return 0; - } - - if (radio) { - return 0; - } - - if (oprmode) { - return 0; - } - - return 1; -} - static enum analog_sigtype dahdisig_to_analogsig(int sig) { switch (sig) { @@ -3161,9 +2424,6 @@ static int my_is_off_hook(void *pvt) return par.rxisoffhook; } -static void dahdi_enable_ec(struct dahdi_pvt *p); -static void dahdi_disable_ec(struct dahdi_pvt *p); - static int my_set_echocanceller(void *pvt, int enable) { struct dahdi_pvt *p = pvt; @@ -3996,8 +3256,7 @@ struct analog_callback analog_callbacks = /*! Round robin search locations. */ static struct dahdi_pvt *round_robin[32]; -#define dahdi_get_index(ast, p, nullok) _dahdi_get_index(ast, p, nullok, __PRETTY_FUNCTION__, __LINE__) -static int _dahdi_get_index(struct ast_channel *ast, struct dahdi_pvt *p, int nullok, const char *fname, unsigned long line) +int _dahdi_get_index(struct ast_channel *ast, struct dahdi_pvt *p, int nullok, const char *fname, unsigned long line) { int res; if (p->subs[SUB_REAL].owner == ast) @@ -4312,7 +3571,6 @@ static void dahdi_r2_on_call_end(openr2_chan_t *r2chan) 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; @@ -5106,7 +4364,7 @@ static int reset_conf(struct dahdi_pvt *p) return 0; } -static int update_conf(struct dahdi_pvt *p) +void update_conf(struct dahdi_pvt *p) { int needconf = 0; int x; @@ -5159,10 +4417,9 @@ static int update_conf(struct dahdi_pvt *p) 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) +void dahdi_enable_ec(struct dahdi_pvt *p) { int res; if (!p) @@ -5234,7 +4491,7 @@ static void dahdi_train_ec(struct dahdi_pvt *p) } } -static void dahdi_disable_ec(struct dahdi_pvt *p) +void dahdi_disable_ec(struct dahdi_pvt *p) { int res; @@ -6954,7 +6211,7 @@ static int dahdi_answer(struct ast_channel *ast) return res; } -static void disable_dtmf_detect(struct dahdi_pvt *p) +void disable_dtmf_detect(struct dahdi_pvt *p) { int val = 0; @@ -6968,7 +6225,7 @@ static void disable_dtmf_detect(struct dahdi_pvt *p) } } -static void enable_dtmf_detect(struct dahdi_pvt *p) +void enable_dtmf_detect(struct dahdi_pvt *p) { int val = DAHDI_TONEDETECT_ON | DAHDI_TONEDETECT_MUTE; @@ -7458,7 +6715,7 @@ static int dahdi_func_write(struct ast_channel *chan, const char *function, char return res; } -static void dahdi_unlink(struct dahdi_pvt *slave, struct dahdi_pvt *master, int needlock) +void dahdi_unlink(struct dahdi_pvt *slave, struct dahdi_pvt *master, int needlock) { /* Unlink a specific slave or all slaves/masters from a given master */ int x; @@ -7514,7 +6771,8 @@ static void dahdi_unlink(struct dahdi_pvt *slave, struct dahdi_pvt *master, int } } -static void dahdi_link(struct dahdi_pvt *slave, struct dahdi_pvt *master) { +void dahdi_link(struct dahdi_pvt *slave, struct dahdi_pvt *master) +{ int x; if (!slave || !master) { ast_log(LOG_WARNING, "Tried to link to/from NULL??\n"); @@ -7537,373 +6795,6 @@ static void dahdi_link(struct dahdi_pvt *slave, struct dahdi_pvt *master) { ast_debug(1, "Making %d slave to master %d at %d\n", slave->channel, master->channel, x); } -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) -{ - struct ast_channel *who; - struct dahdi_pvt *p0, *p1, *op0, *op1; - struct dahdi_pvt *master = NULL, *slave = NULL; - struct ast_frame *f; - int inconf = 0; - int nothingok = 1; - int ofd0, ofd1; - int oi0, oi1, i0 = -1, i1 = -1, t0, t1; - int os0 = -1, os1 = -1; - int priority = 0; - struct ast_channel *oc0, *oc1; - enum ast_bridge_result res; - struct timeval start = ast_tvnow(); -#ifdef PRI_2BCT - int triedtopribridge = 0; - q931_call *q931c0; - q931_call *q931c1; -#endif - - /* For now, don't attempt to native bridge if either channel needs DTMF detection. - There is code below to handle it properly until DTMF is actually seen, - but due to currently unresolved issues it's ignored... - */ - - if (flags & (AST_BRIDGE_DTMF_CHANNEL_0 | AST_BRIDGE_DTMF_CHANNEL_1)) - return AST_BRIDGE_FAILED_NOWARN; - - ast_channel_lock_both(c0, c1); - - p0 = ast_channel_tech_pvt(c0); - p1 = ast_channel_tech_pvt(c1); - /* cant do pseudo-channels here */ - if (!p0 || (!p0->sig) || !p1 || (!p1->sig)) { - ast_channel_unlock(c0); - ast_channel_unlock(c1); - return AST_BRIDGE_FAILED_NOWARN; - } - - oi0 = dahdi_get_index(c0, p0, 0); - oi1 = dahdi_get_index(c1, p1, 0); - if ((oi0 < 0) || (oi1 < 0)) { - ast_channel_unlock(c0); - ast_channel_unlock(c1); - return AST_BRIDGE_FAILED; - } - - op0 = p0 = ast_channel_tech_pvt(c0); - op1 = p1 = ast_channel_tech_pvt(c1); - ofd0 = ast_channel_fd(c0, 0); - ofd1 = ast_channel_fd(c1, 0); - oc0 = p0->owner; - oc1 = p1->owner; - - if (ast_mutex_trylock(&p0->lock)) { - /* Don't block, due to potential for deadlock */ - ast_channel_unlock(c0); - ast_channel_unlock(c1); - ast_log(LOG_NOTICE, "Avoiding deadlock...\n"); - return AST_BRIDGE_RETRY; - } - if (ast_mutex_trylock(&p1->lock)) { - /* Don't block, due to potential for deadlock */ - ast_mutex_unlock(&p0->lock); - ast_channel_unlock(c0); - ast_channel_unlock(c1); - ast_log(LOG_NOTICE, "Avoiding deadlock...\n"); - return AST_BRIDGE_RETRY; - } - - if ((p0->callwaiting && p0->callwaitingcallerid) - || (p1->callwaiting && p1->callwaitingcallerid)) { - /* - * Call Waiting Caller ID requires DTMF detection to know if it - * can send the CID spill. - * - * For now, don't attempt to native bridge if either channel - * needs DTMF detection. There is code below to handle it - * properly until DTMF is actually seen, but due to currently - * unresolved issues it's ignored... - */ - ast_mutex_unlock(&p0->lock); - ast_mutex_unlock(&p1->lock); - ast_channel_unlock(c0); - ast_channel_unlock(c1); - return AST_BRIDGE_FAILED_NOWARN; - } - -#if defined(HAVE_PRI) - if ((dahdi_sig_pri_lib_handles(p0->sig) - && ((struct sig_pri_chan *) p0->sig_pvt)->no_b_channel) - || (dahdi_sig_pri_lib_handles(p1->sig) - && ((struct sig_pri_chan *) p1->sig_pvt)->no_b_channel)) { - /* - * PRI nobch channels (hold and call waiting) are equivalent to - * pseudo channels and cannot be done here. - */ - ast_mutex_unlock(&p0->lock); - ast_mutex_unlock(&p1->lock); - ast_channel_unlock(c0); - ast_channel_unlock(c1); - return AST_BRIDGE_FAILED_NOWARN; - } -#endif /* defined(HAVE_PRI) */ - - if ((oi0 == SUB_REAL) && (oi1 == SUB_REAL)) { - if (p0->owner && p1->owner) { - /* If we don't have a call-wait in a 3-way, and we aren't in a 3-way, we can be master */ - if (!p0->subs[SUB_CALLWAIT].inthreeway && !p1->subs[SUB_REAL].inthreeway) { - master = p0; - slave = p1; - inconf = 1; - } else if (!p1->subs[SUB_CALLWAIT].inthreeway && !p0->subs[SUB_REAL].inthreeway) { - master = p1; - slave = p0; - inconf = 1; - } else { - ast_log(LOG_WARNING, "Huh? Both calls are callwaits or 3-ways? That's clever...?\n"); - ast_log(LOG_WARNING, "p0: chan %d/%d/CW%d/3W%d, p1: chan %d/%d/CW%d/3W%d\n", - p0->channel, - oi0, (p0->subs[SUB_CALLWAIT].dfd > -1) ? 1 : 0, - p0->subs[SUB_REAL].inthreeway, p0->channel, - oi0, (p1->subs[SUB_CALLWAIT].dfd > -1) ? 1 : 0, - p1->subs[SUB_REAL].inthreeway); - } - nothingok = 0; - } - } else if ((oi0 == SUB_REAL) && (oi1 == SUB_THREEWAY)) { - if (p1->subs[SUB_THREEWAY].inthreeway) { - master = p1; - slave = p0; - nothingok = 0; - } - } else if ((oi0 == SUB_THREEWAY) && (oi1 == SUB_REAL)) { - if (p0->subs[SUB_THREEWAY].inthreeway) { - master = p0; - slave = p1; - nothingok = 0; - } - } else if ((oi0 == SUB_REAL) && (oi1 == SUB_CALLWAIT)) { - /* We have a real and a call wait. If we're in a three way call, put us in it, otherwise, - don't put us in anything */ - if (p1->subs[SUB_CALLWAIT].inthreeway) { - master = p1; - slave = p0; - nothingok = 0; - } - } else if ((oi0 == SUB_CALLWAIT) && (oi1 == SUB_REAL)) { - /* Same as previous */ - if (p0->subs[SUB_CALLWAIT].inthreeway) { - master = p0; - slave = p1; - nothingok = 0; - } - } - ast_debug(1, "master: %d, slave: %d, nothingok: %d\n", - master ? master->channel : 0, slave ? slave->channel : 0, nothingok); - if (master && slave) { - /* Stop any tones, or play ringtone as appropriate. If they're bridged - in an active threeway call with a channel that is ringing, we should - indicate ringing. */ - if ((oi1 == SUB_THREEWAY) && - p1->subs[SUB_THREEWAY].inthreeway && - p1->subs[SUB_REAL].owner && - p1->subs[SUB_REAL].inthreeway && - (ast_channel_state(p1->subs[SUB_REAL].owner) == AST_STATE_RINGING)) { - ast_debug(1, - "Playing ringback on %d/%d(%s) since %d/%d(%s) is in a ringing three-way\n", - p0->channel, oi0, ast_channel_name(c0), p1->channel, oi1, ast_channel_name(c1)); - tone_zone_play_tone(p0->subs[oi0].dfd, DAHDI_TONE_RINGTONE); - os1 = ast_channel_state(p1->subs[SUB_REAL].owner); - } else { - ast_debug(1, "Stopping tones on %d/%d(%s) talking to %d/%d(%s)\n", - p0->channel, oi0, ast_channel_name(c0), p1->channel, oi1, ast_channel_name(c1)); - tone_zone_play_tone(p0->subs[oi0].dfd, -1); - } - if ((oi0 == SUB_THREEWAY) && - p0->subs[SUB_THREEWAY].inthreeway && - p0->subs[SUB_REAL].owner && - p0->subs[SUB_REAL].inthreeway && - (ast_channel_state(p0->subs[SUB_REAL].owner) == AST_STATE_RINGING)) { - ast_debug(1, - "Playing ringback on %d/%d(%s) since %d/%d(%s) is in a ringing three-way\n", - p1->channel, oi1, ast_channel_name(c1), p0->channel, oi0, ast_channel_name(c0)); - tone_zone_play_tone(p1->subs[oi1].dfd, DAHDI_TONE_RINGTONE); - os0 = ast_channel_state(p0->subs[SUB_REAL].owner); - } else { - ast_debug(1, "Stopping tones on %d/%d(%s) talking to %d/%d(%s)\n", - p1->channel, oi1, ast_channel_name(c1), p0->channel, oi0, ast_channel_name(c0)); - tone_zone_play_tone(p1->subs[oi1].dfd, -1); - } - if ((oi0 == SUB_REAL) && (oi1 == SUB_REAL)) { - if (!p0->echocanbridged || !p1->echocanbridged) { - /* Disable echo cancellation if appropriate */ - dahdi_disable_ec(p0); - dahdi_disable_ec(p1); - } - } - dahdi_link(slave, master); - master->inconference = inconf; - } else if (!nothingok) - ast_log(LOG_WARNING, "Can't link %d/%s with %d/%s\n", p0->channel, subnames[oi0], p1->channel, subnames[oi1]); - - update_conf(p0); - update_conf(p1); - t0 = p0->subs[SUB_REAL].inthreeway; - t1 = p1->subs[SUB_REAL].inthreeway; - - ast_mutex_unlock(&p0->lock); - ast_mutex_unlock(&p1->lock); - - ast_channel_unlock(c0); - ast_channel_unlock(c1); - - /* Native bridge failed */ - if ((!master || !slave) && !nothingok) { - dahdi_enable_ec(p0); - dahdi_enable_ec(p1); - return AST_BRIDGE_FAILED; - } - - ast_verb(3, "Native bridging %s and %s\n", ast_channel_name(c0), ast_channel_name(c1)); - - if (!(flags & AST_BRIDGE_DTMF_CHANNEL_0) && (oi0 == SUB_REAL)) - disable_dtmf_detect(op0); - - if (!(flags & AST_BRIDGE_DTMF_CHANNEL_1) && (oi1 == SUB_REAL)) - disable_dtmf_detect(op1); - - for (;;) { - struct ast_channel *c0_priority[2] = {c0, c1}; - struct ast_channel *c1_priority[2] = {c1, c0}; - int ms; - - /* Here's our main loop... Start by locking things, looking for private parts, - and then balking if anything is wrong */ - - ast_channel_lock_both(c0, c1); - - p0 = ast_channel_tech_pvt(c0); - p1 = ast_channel_tech_pvt(c1); - - if (op0 == p0) - i0 = dahdi_get_index(c0, p0, 1); - if (op1 == p1) - i1 = dahdi_get_index(c1, p1, 1); - - ast_channel_unlock(c0); - ast_channel_unlock(c1); - ms = ast_remaining_ms(start, timeoutms); - if (!ms || - (op0 != p0) || - (op1 != p1) || - (ofd0 != ast_channel_fd(c0, 0)) || - (ofd1 != ast_channel_fd(c1, 0)) || - (p0->subs[SUB_REAL].owner && (os0 > -1) && (os0 != ast_channel_state(p0->subs[SUB_REAL].owner))) || - (p1->subs[SUB_REAL].owner && (os1 > -1) && (os1 != ast_channel_state(p1->subs[SUB_REAL].owner))) || - (oc0 != p0->owner) || - (oc1 != p1->owner) || - (t0 != p0->subs[SUB_REAL].inthreeway) || - (t1 != p1->subs[SUB_REAL].inthreeway) || - (oi0 != i0) || - (oi1 != i1)) { - ast_debug(1, "Something changed out on %d/%d to %d/%d, returning -3 to restart\n", - op0->channel, oi0, op1->channel, oi1); - res = AST_BRIDGE_RETRY; - goto return_from_bridge; - } - -#ifdef PRI_2BCT - if (!triedtopribridge) { - triedtopribridge = 1; - if (p0->pri && p0->pri == p1->pri && p0->pri->transfer) { - ast_mutex_lock(&p0->pri->lock); - switch (p0->sig) { - case SIG_PRI_LIB_HANDLE_CASES: - q931c0 = ((struct sig_pri_chan *) (p0->sig_pvt))->call; - break; - default: - q931c0 = NULL; - break; - } - switch (p1->sig) { - case SIG_PRI_LIB_HANDLE_CASES: - q931c1 = ((struct sig_pri_chan *) (p1->sig_pvt))->call; - break; - default: - q931c1 = NULL; - break; - } - if (q931c0 && q931c1) { - pri_channel_bridge(q931c0, q931c1); - } - ast_mutex_unlock(&p0->pri->lock); - } - } -#endif - - who = ast_waitfor_n(priority ? c0_priority : c1_priority, 2, &ms); - if (!who) { - ast_debug(1, "Ooh, empty read...\n"); - continue; - } - f = ast_read(who); - switch (f ? f->frametype : AST_FRAME_CONTROL) { - case AST_FRAME_CONTROL: - if (f && f->subclass.integer == AST_CONTROL_PVT_CAUSE_CODE) { - ast_channel_hangupcause_hash_set((who == c0) ? c1 : c0, f->data.ptr, f->datalen); - break; - } - *fo = f; - *rc = who; - res = AST_BRIDGE_COMPLETE; - goto return_from_bridge; - case AST_FRAME_DTMF_END: - if ((who == c0) && p0->pulsedial) { - ast_write(c1, f); - } else if ((who == c1) && p1->pulsedial) { - ast_write(c0, f); - } else { - *fo = f; - *rc = who; - res = AST_BRIDGE_COMPLETE; - goto return_from_bridge; - } - break; - case AST_FRAME_TEXT: - if (who == c0) { - ast_write(c1, f); - } else { - ast_write(c0, f); - } - break; - case AST_FRAME_VOICE: - /* Native bridge handles voice frames in hardware. */ - case AST_FRAME_NULL: - break; - default: - ast_debug(1, "Chan '%s' is discarding frame of frametype:%d\n", - ast_channel_name(who), f->frametype); - break; - } - ast_frfree(f); - - /* Swap who gets priority */ - priority = !priority; - } - -return_from_bridge: - if (op0 == p0) - dahdi_enable_ec(p0); - - if (op1 == p1) - dahdi_enable_ec(p1); - - if (!(flags & AST_BRIDGE_DTMF_CHANNEL_0) && (oi0 == SUB_REAL)) - enable_dtmf_detect(op0); - - if (!(flags & AST_BRIDGE_DTMF_CHANNEL_1) && (oi1 == SUB_REAL)) - enable_dtmf_detect(op1); - - dahdi_unlink(slave, master, 1); - - return res; -} - static int dahdi_fixup(struct ast_channel *oldchan, struct ast_channel *newchan) { struct dahdi_pvt *p = ast_channel_tech_pvt(newchan); @@ -17407,6 +16298,8 @@ static int __unload_module(void) #endif /* defined(HAVE_SS7) */ ast_cond_destroy(&ss_thread_complete); + dahdi_native_unload(); + dahdi_tech.capabilities = ast_format_cap_destroy(dahdi_tech.capabilities); STASIS_MESSAGE_TYPE_CLEANUP(dahdichannel_type); return 0; @@ -19420,6 +18313,10 @@ static int load_module(void) ast_format_cap_add(dahdi_tech.capabilities, ast_format_set(&tmpfmt, AST_FORMAT_ULAW, 0)); ast_format_cap_add(dahdi_tech.capabilities, ast_format_set(&tmpfmt, AST_FORMAT_ALAW, 0)); + if (dahdi_native_load(ast_module_info->self, &dahdi_tech)) { + return AST_MODULE_LOAD_FAILURE; + } + #ifdef HAVE_PRI memset(pris, 0, sizeof(pris)); for (y = 0; y < NUM_SPANS; y++) { @@ -19640,5 +18537,5 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, tdesc, .unload = unload_module, .reload = reload, .load_pri = AST_MODPRI_CHANNEL_DRIVER, - .nonoptreq = "res_smdi", + .nonoptreq = "res_smdi", ); diff --git a/channels/chan_dahdi.h b/channels/chan_dahdi.h new file mode 100644 index 0000000000..69c20db2cd --- /dev/null +++ b/channels/chan_dahdi.h @@ -0,0 +1,808 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013 Digium, Inc. + * + * Richard Mudgett + * + * 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 internal API definitions. + * + * \author Richard Mudgett + * + * See Also: + * \arg \ref AstCREDITS + */ + +#ifndef _ASTERISK_CHAN_DAHDI_H +#define _ASTERISK_CHAN_DAHDI_H + +#if defined(HAVE_OPENR2) +#include +#endif /* defined(HAVE_OPENR2) */ + +#include +#include + +#include "asterisk/channel.h" +#include "asterisk/dsp.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +/* ------------------------------------------------------------------- */ + +#if defined(HAVE_PRI) +struct sig_pri_span; +#endif /* defined(HAVE_PRI) */ +#if defined(HAVE_SS7) +struct sig_ss7_linkset; +#endif /* defined(HAVE_SS7) */ + +#define SUB_REAL 0 /*!< Active call */ +#define SUB_CALLWAIT 1 /*!< Call-Waiting call on hold */ +#define SUB_THREEWAY 2 /*!< Three-way call */ + + +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]; +}; + + +extern const char * const subnames[]; + +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 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 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; +}; + +/*! Specify the lists dahdi_pvt can be put in. */ +enum DAHDI_IFLIST { + DAHDI_IFLIST_NONE, /*!< The dahdi_pvt is not in any list. */ + DAHDI_IFLIST_MAIN, /*!< The dahdi_pvt is in the main interface list */ +#if defined(HAVE_PRI) + DAHDI_IFLIST_NO_B_CHAN, /*!< The dahdi_pvt is in a no B channel interface list */ +#endif /* defined(HAVE_PRI) */ +}; + +struct dahdi_pvt { + ast_mutex_t lock; /*!< Channel private lock. */ + struct callerid_state *cs; + 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; + + float txdrc; /*!< Dynamic Range Compression factor. a number between 1 and 6ish */ + float rxdrc; + + int tonezone; /*!< tone zone for this chan, or -1 for default */ + enum DAHDI_IFLIST which_iflist; /*!< Which interface list is this structure listed? */ + 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. + * \note This is used as a receive squelch for ISDN until connected. + */ + 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, present only for non sig_analog */ + 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; + /*! TRUE if dynamic faxbuffers are configured for use, default is OFF */ + unsigned int usefaxbuffers:1; + /*! TRUE while buffer configuration override is 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; + /*! + * \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 and MFCR2 channels. + */ + unsigned int locallyblocked:1; + /*! + * \brief TRUE if the channel is remotely blocked. + * \note Applies to SS7 and MFCR2 channels. + */ + unsigned int remotelyblocked:1; + /*! + * \brief TRUE if the channel alarms will be managed also as Span ones + * \note Applies to all channels + */ + unsigned int manages_span_alarms:1; + +#if defined(HAVE_PRI) + struct sig_pri_span *pri; + int logicalspan; +#endif /* defined(HAVE_PRI) */ + /*! + * \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 SMDI interface to get SMDI messages from. */ + 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 A description for the channel configuration + * \note The "description" string read in from chan_dahdi.conf + */ + char description[32]; + /*! + * \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(HAVE_PRI) || defined(HAVE_SS7) + /*! \brief Automatic Number Identification number (Alternate PRI caller ID number) */ + char cid_ani[AST_MAX_EXTENSION]; +#endif /* defined(HAVE_PRI) || defined(HAVE_SS7) */ + /*! \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 tag from incoming call + * \note the "cid_tag" string read in from chan_dahdi.conf + */ + char cid_tag[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 Caller ID subaddress from an incoming call. */ + char cid_subaddr[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 Default call PCM encoding format: DAHDI_LAW_ALAW or DAHDI_LAW_MULAW. */ + int law_default; + /*! \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 Named call groups this belongs to. + * \note The "namedcallgroup" string read in from chan_dahdi.conf + */ + struct ast_namedgroups *named_callgroups; + /*! + * \brief Named pickup groups this belongs to. + * \note The "namedpickupgroup" string read in from chan_dahdi.conf + */ + struct ast_namedgroups *named_pickupgroups; + /*! + * \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 */ + 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 or DTMF without warning event */ + int dtmfcid_holdoff_state; /*!< State indicator that allows for line to settle before checking for dtmf energy */ + struct timeval dtmfcid_delay; /*!< Time value used for allow line to settle */ + 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 stop waiting for CID/CW CAS response (In samples) */ + int cid_suppress_expire; /*!< How many samples to suppress after a CID spill. */ + /*! \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 TRUE if Call Waiting (CW) CPE Alert Signal (CAS) is being sent. + * \note + * After CAS is sent, the call waiting caller id will be sent if the phone + * gives a positive reply. + */ + 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 Busy cadence pattern description. + * \note Set from the "busypattern" value read from chan_dahdi.conf + */ + struct ast_dsp_busy_pattern busy_cadence; + /*! + * \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; + /*! + * \brief Number of frames to watch for dialtone in incoming calls + * \note Set from the "dialtone_detect" value read in from chan_dahdi.conf + */ + int dialtone_detect; + int dialtone_scanning_time_elapsed; /*!< Amount of audio scanned for dialtone, in frames */ + 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; + /*! \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 stasis_subscription *mwi_event_sub; + /*! \brief Delayed dialing for E911. Overlap digits for ISDN. */ + char dialdest[256]; +#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 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 on FXS after this many rings. Set to 1 for US. + * \note Set from the "sendcalleridafter" value read in from chan_dahdi.conf + */ + int sendcalleridafter; + /*! \brief Current line interface polarity. POLARITY_IDLE, POLARITY_REV */ + int polarity; + /*! \brief DSP feature flags: DSP_FEATURE_xxx */ + int dsp_features; +#if defined(HAVE_SS7) + /*! \brief SS7 control parameters */ + struct sig_ss7_linkset *ss7; +#endif /* defined(HAVE_SS7) */ +#if defined(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; + int mfcr2_progress_sent:1; +#endif /* defined(HAVE_OPENR2) */ + /*! \brief DTMF digit in progress. 0 when no digit in progress. */ + char begindigit; + /*! \brief TRUE if confrence is muted. */ + int muting; + void *sig_pvt; + struct ast_cc_config_params *cc_params; + /* DAHDI channel names may differ greatly from the + * string that was provided to an app such as Dial. We + * need to save the original string passed to dahdi_request + * for call completion purposes. This way, we can replicate + * the original dialed string later. + */ + char dialstring[AST_CHANNEL_NAME]; +}; + + +/* Analog signaling */ +#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_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 + +/* PRI signaling */ +#define SIG_PRI DAHDI_SIG_CLEAR +#define SIG_BRI (0x2000000 | DAHDI_SIG_CLEAR) +#define SIG_BRI_PTMP (0X4000000 | DAHDI_SIG_CLEAR) + +/* SS7 signaling */ +#define SIG_SS7 (0x1000000 | DAHDI_SIG_CLEAR) + +/* MFC/R2 signaling */ +#define SIG_MFCR2 DAHDI_SIG_CAS + + +#define SIG_PRI_LIB_HANDLE_CASES \ + SIG_PRI: \ + case SIG_BRI: \ + case SIG_BRI_PTMP + +/*! + * \internal + * \brief Determine if sig_pri handles the signaling. + * \since 1.8 + * + * \param signaling Signaling to determine if is for sig_pri. + * + * \return TRUE if the signaling is for sig_pri. + */ +static inline int dahdi_sig_pri_lib_handles(int signaling) +{ + int handles; + + switch (signaling) { + case SIG_PRI_LIB_HANDLE_CASES: + handles = 1; + break; + default: + handles = 0; + break; + } + + return handles; +} + +static inline int analog_lib_handles(int signalling, int radio, int oprmode) +{ + switch (signalling) { + case SIG_FXOLS: + case SIG_FXOGS: + case SIG_FXOKS: + case SIG_FXSLS: + case SIG_FXSGS: + case SIG_FXSKS: + case SIG_EMWINK: + case SIG_EM: + case SIG_EM_E1: + case SIG_FEATD: + case SIG_FEATDMF: + case SIG_E911: + case SIG_FGC_CAMA: + case SIG_FGC_CAMAMF: + case SIG_FEATB: + case SIG_SFWINK: + case SIG_SF: + case SIG_SF_FEATD: + case SIG_SF_FEATDMF: + case SIG_FEATDMF_TA: + case SIG_SF_FEATB: + break; + default: + /* The rest of the function should cover the remainder of signalling types */ + return 0; + } + + if (radio) { + return 0; + } + + if (oprmode) { + return 0; + } + + return 1; +} + +#define dahdi_get_index(ast, p, nullok) _dahdi_get_index(ast, p, nullok, __PRETTY_FUNCTION__, __LINE__) +int _dahdi_get_index(struct ast_channel *ast, struct dahdi_pvt *p, int nullok, const char *fname, unsigned long line); + +void disable_dtmf_detect(struct dahdi_pvt *p); +void enable_dtmf_detect(struct dahdi_pvt *p); + +void dahdi_enable_ec(struct dahdi_pvt *p); +void dahdi_disable_ec(struct dahdi_pvt *p); + +void update_conf(struct dahdi_pvt *p); +void dahdi_link(struct dahdi_pvt *slave, struct dahdi_pvt *master); +void dahdi_unlink(struct dahdi_pvt *slave, struct dahdi_pvt *master, int needlock); + +/* ------------------------------------------------------------------- */ + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + +#endif /* _ASTERISK_CHAN_DAHDI_H */ diff --git a/channels/dahdi/bridge_native_dahdi.c b/channels/dahdi/bridge_native_dahdi.c new file mode 100644 index 0000000000..611994f5ec --- /dev/null +++ b/channels/dahdi/bridge_native_dahdi.c @@ -0,0 +1,928 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013 Digium, Inc. + * + * Richard Mudgett + * + * 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 Native DAHDI bridging support. + * + * \author Richard Mudgett + * + * See Also: + * \arg \ref AstCREDITS + */ + +/*** MODULEINFO + core + ***/ + +#include "asterisk.h" + +ASTERISK_FILE_VERSION(__FILE__, "$Revision$") + +#include "../sig_analog.h" +#if defined(HAVE_PRI) +#include "../sig_pri.h" +#endif /* defined(HAVE_PRI) */ +#include "../chan_dahdi.h" + +#include "asterisk/astobj.h" +#include "bridge_native_dahdi.h" +#include "asterisk/bridging.h" +#include "asterisk/bridging_technology.h" +#include "asterisk/frame.h" + +/* ------------------------------------------------------------------- */ + +static const struct ast_channel_tech *dahdi_tech; + +struct native_pvt_chan { + /*! Original private. */ + struct dahdi_pvt *pvt; + /*! Original private owner. */ + struct ast_channel *owner; + /*! Original owner index. */ + int index; + /*! Original file descriptor 0. */ + int fd0; + /*! Original channel state. */ + int state; + /*! Original inthreeway. */ + unsigned int inthreeway:1; +}; + +struct native_pvt_bridge { + /*! Master channel in the native bridge. */ + struct dahdi_pvt *master; + /*! Slave channel in the native bridge. */ + struct dahdi_pvt *slave; + /*! TRUE if the bridge can start when ready. */ + unsigned int saw_start:1; + /*! TRUE if the channels are connected in a conference. */ + unsigned int connected:1; +#if defined(HAVE_PRI) && defined(PRI_2BCT) + /*! + * \brief TRUE if tried to eliminate possible PRI tromboned call. + * + * \note A tromboned call uses two B channels of the same ISDN + * span. One leg comes into Asterisk, the other leg goes out of + * Asterisk, and Asterisk is natively bridging the two legs. + */ + unsigned int tried_trombone_removal:1; +#endif /* defined(HAVE_PRI) && defined(PRI_2BCT) */ +}; + +/*! + * \internal + * \brief Create a bridge technology instance for a bridge. + * \since 12.0.0 + * + * \retval 0 on success + * \retval -1 on failure + * + * \note On entry, bridge may or may not already be locked. + * However, it can be accessed as if it were locked. + */ +static int native_bridge_create(struct ast_bridge *bridge) +{ + struct native_pvt_bridge *tech_pvt; + + ast_assert(!bridge->tech_pvt); + + tech_pvt = ast_calloc(1, sizeof(*tech_pvt)); + if (!tech_pvt) { + return -1; + } + + bridge->tech_pvt = tech_pvt; + return 0; +} + +/*! + * \internal + * \brief Destroy a bridging technology instance for a bridge. + * \since 12.0.0 + * + * \note On entry, bridge must NOT be locked. + */ +static void native_bridge_destroy(struct ast_bridge *bridge) +{ + struct native_pvt_bridge *tech_pvt; + + tech_pvt = bridge->tech_pvt; + bridge->tech_pvt = NULL; + ast_free(tech_pvt); +} + +/*! + * \internal + * \brief Stop native bridging activity. + * \since 12.0.0 + * + * \param bridge What to operate upon. + * + * \return Nothing + * + * \note On entry, bridge is already locked. + */ +static void native_stop(struct ast_bridge *bridge) +{ + struct native_pvt_bridge *bridge_tech_pvt; + struct ast_bridge_channel *cur; + + ast_assert(bridge->tech_pvt != NULL); + + AST_LIST_TRAVERSE(&bridge->channels, cur, entry) { + struct native_pvt_chan *chan_tech_pvt; + + chan_tech_pvt = cur->tech_pvt; + if (!chan_tech_pvt) { + continue; + } + + ast_mutex_lock(&chan_tech_pvt->pvt->lock); + if (chan_tech_pvt->pvt == ast_channel_tech_pvt(cur->chan)) { + dahdi_enable_ec(chan_tech_pvt->pvt); + } + if (chan_tech_pvt->index == SUB_REAL) { + enable_dtmf_detect(chan_tech_pvt->pvt); + } + ast_mutex_unlock(&chan_tech_pvt->pvt->lock); + } + + bridge_tech_pvt = bridge->tech_pvt; + dahdi_unlink(bridge_tech_pvt->slave, bridge_tech_pvt->master, 1); + + ast_debug(2, "Stop native bridging %s and %s\n", + ast_channel_name(AST_LIST_FIRST(&bridge->channels)->chan), + ast_channel_name(AST_LIST_LAST(&bridge->channels)->chan)); +} + +/*! + * \internal + * \brief Request to stop native bridging activity. + * \since 12.0.0 + * + * \param bridge What to operate upon. + * + * \return Nothing + * + * \note On entry, bridge is already locked. + */ +static void native_request_stop(struct ast_bridge *bridge) +{ + struct native_pvt_bridge *tech_pvt; + + ast_assert(bridge->tech_pvt != NULL); + + tech_pvt = bridge->tech_pvt; + if (!tech_pvt->connected) { + return; + } + tech_pvt->connected = 0; + + /* Now to actually stop the bridge. */ + native_stop(bridge); +} + +/*! + * \internal + * \brief Start native bridging activity. + * \since 12.0.0 + * + * \param bridge What to operate upon. + * + * \retval 0 on success. + * \retval -1 on error. Could not start the bridge. + * + * \note On entry, bridge may or may not already be locked. + * However, it can be accessed as if it were locked. + */ +static int native_start(struct ast_bridge *bridge) +{ + struct native_pvt_bridge *tech_pvt; + struct ast_bridge_channel *bc0; + struct ast_bridge_channel *bc1; + struct native_pvt_chan *npc0; + struct native_pvt_chan *npc1; + struct ast_channel *c0; + struct ast_channel *c1; + struct dahdi_pvt *p0; + struct dahdi_pvt *p1; + struct dahdi_pvt *master; + struct dahdi_pvt *slave; + int inconf; + int nothing_ok; + + ast_assert(bridge->tech_pvt != NULL); + + bc0 = AST_LIST_FIRST(&bridge->channels); + bc1 = AST_LIST_LAST(&bridge->channels); + c0 = bc0->chan; + c1 = bc1->chan; + + /* Lock channels and privates */ + for (;;) { + ast_channel_lock(c0); + if (!ast_channel_trylock(c1)) { + p0 = ast_channel_tech_pvt(c0); + if (!ast_mutex_trylock(&p0->lock)) { + p1 = ast_channel_tech_pvt(c1); + if (!ast_mutex_trylock(&p1->lock)) { + /* Got all locks */ + break; + } + ast_mutex_unlock(&p0->lock); + } + ast_channel_unlock(c1); + } + ast_channel_unlock(c0); + sched_yield(); + } + + npc0 = bc0->tech_pvt; + ast_assert(npc0 != NULL); + npc0->pvt = p0; + npc0->owner = p0->owner; + npc0->index = dahdi_get_index(c0, p0, 0); + npc0->fd0 = ast_channel_fd(c0, 0); + npc0->state = -1; + npc0->inthreeway = p0->subs[SUB_REAL].inthreeway; + + npc1 = bc1->tech_pvt; + ast_assert(npc1 != NULL); + npc1->pvt = p1; + npc1->owner = p1->owner; + npc1->index = dahdi_get_index(c1, p1, 0); + npc1->fd0 = ast_channel_fd(c1, 0); + npc1->state = -1; + npc1->inthreeway = p1->subs[SUB_REAL].inthreeway; + + /* + * Check things that can change on the privates while in native + * bridging and cause native to not activate. + */ + if (npc0->index < 0 || npc1->index < 0 +#if defined(HAVE_PRI) + /* + * PRI nobch channels (hold and call waiting) are equivalent to + * pseudo channels and cannot be nativly bridged. + */ + || (dahdi_sig_pri_lib_handles(p0->sig) + && ((struct sig_pri_chan *) p0->sig_pvt)->no_b_channel) + || (dahdi_sig_pri_lib_handles(p1->sig) + && ((struct sig_pri_chan *) p1->sig_pvt)->no_b_channel) +#endif /* defined(HAVE_PRI) */ + ) { + ast_mutex_unlock(&p0->lock); + ast_mutex_unlock(&p1->lock); + ast_channel_unlock(c0); + ast_channel_unlock(c1); + return -1; + } + + inconf = 0; + nothing_ok = 1; + master = NULL; + slave = NULL; + if (npc0->index == SUB_REAL && npc1->index == SUB_REAL) { + if (p0->owner && p1->owner) { + /* + * If we don't have a call-wait in a 3-way, and we aren't in a + * 3-way, we can be master. + */ + if (!p0->subs[SUB_CALLWAIT].inthreeway && !p1->subs[SUB_REAL].inthreeway) { + master = p0; + slave = p1; + inconf = 1; + } else if (!p1->subs[SUB_CALLWAIT].inthreeway && !p0->subs[SUB_REAL].inthreeway) { + master = p1; + slave = p0; + inconf = 1; + } else { + ast_log(LOG_WARNING, "Huh? Both calls are callwaits or 3-ways? That's clever...?\n"); + ast_log(LOG_WARNING, "p0: chan %d/%d/CW%d/3W%d, p1: chan %d/%d/CW%d/3W%d\n", + p0->channel, + npc0->index, (p0->subs[SUB_CALLWAIT].dfd > -1) ? 1 : 0, + p0->subs[SUB_REAL].inthreeway, + p0->channel, + npc0->index, (p1->subs[SUB_CALLWAIT].dfd > -1) ? 1 : 0, + p1->subs[SUB_REAL].inthreeway); + } + nothing_ok = 0; + } + } else if (npc0->index == SUB_REAL && npc1->index == SUB_THREEWAY) { + if (p1->subs[SUB_THREEWAY].inthreeway) { + master = p1; + slave = p0; + nothing_ok = 0; + } + } else if (npc0->index == SUB_THREEWAY && npc1->index == SUB_REAL) { + if (p0->subs[SUB_THREEWAY].inthreeway) { + master = p0; + slave = p1; + nothing_ok = 0; + } + } else if (npc0->index == SUB_REAL && npc1->index == SUB_CALLWAIT) { + /* + * We have a real and a call wait. If we're in a three way + * call, put us in it, otherwise, don't put us in anything. + */ + if (p1->subs[SUB_CALLWAIT].inthreeway) { + master = p1; + slave = p0; + nothing_ok = 0; + } + } else if (npc0->index == SUB_CALLWAIT && npc1->index == SUB_REAL) { + /* Same as previous */ + if (p0->subs[SUB_CALLWAIT].inthreeway) { + master = p0; + slave = p1; + nothing_ok = 0; + } + } + ast_debug(3, "master: %d, slave: %d, nothing_ok: %d\n", + master ? master->channel : 0, + slave ? slave->channel : 0, + nothing_ok); + if (master && slave) { + /* + * Stop any tones, or play ringtone as appropriate. If they are + * bridged in an active threeway call with a channel that is + * ringing, we should indicate ringing. + */ + if (npc1->index == SUB_THREEWAY + && p1->subs[SUB_THREEWAY].inthreeway + && p1->subs[SUB_REAL].owner + && p1->subs[SUB_REAL].inthreeway + && ast_channel_state(p1->subs[SUB_REAL].owner) == AST_STATE_RINGING) { + ast_debug(2, + "Playing ringback on %d/%d(%s) since %d/%d(%s) is in a ringing three-way\n", + p0->channel, npc0->index, ast_channel_name(c0), + p1->channel, npc1->index, ast_channel_name(c1)); + tone_zone_play_tone(p0->subs[npc0->index].dfd, DAHDI_TONE_RINGTONE); + npc1->state = ast_channel_state(p1->subs[SUB_REAL].owner); + } else { + ast_debug(2, "Stopping tones on %d/%d(%s) talking to %d/%d(%s)\n", + p0->channel, npc0->index, ast_channel_name(c0), + p1->channel, npc1->index, ast_channel_name(c1)); + tone_zone_play_tone(p0->subs[npc0->index].dfd, -1); + } + + if (npc0->index == SUB_THREEWAY + && p0->subs[SUB_THREEWAY].inthreeway + && p0->subs[SUB_REAL].owner + && p0->subs[SUB_REAL].inthreeway + && ast_channel_state(p0->subs[SUB_REAL].owner) == AST_STATE_RINGING) { + ast_debug(2, + "Playing ringback on %d/%d(%s) since %d/%d(%s) is in a ringing three-way\n", + p1->channel, npc1->index, ast_channel_name(c1), + p0->channel, npc0->index, ast_channel_name(c0)); + tone_zone_play_tone(p1->subs[npc1->index].dfd, DAHDI_TONE_RINGTONE); + npc0->state = ast_channel_state(p0->subs[SUB_REAL].owner); + } else { + ast_debug(2, "Stopping tones on %d/%d(%s) talking to %d/%d(%s)\n", + p1->channel, npc1->index, ast_channel_name(c1), + p0->channel, npc0->index, ast_channel_name(c0)); + tone_zone_play_tone(p1->subs[npc1->index].dfd, -1); + } + + if (npc0->index == SUB_REAL && npc1->index == SUB_REAL) { + if (!p0->echocanbridged || !p1->echocanbridged) { + /* Disable echo cancellation if appropriate */ + dahdi_disable_ec(p0); + dahdi_disable_ec(p1); + } + } + dahdi_link(slave, master); + master->inconference = inconf; + } else if (!nothing_ok) { + ast_log(LOG_WARNING, "Can't link %d/%s with %d/%s\n", + p0->channel, subnames[npc0->index], + p1->channel, subnames[npc1->index]); + } + update_conf(p0); + update_conf(p1); + + ast_channel_unlock(c0); + ast_channel_unlock(c1); + + /* Native bridge failed */ + if ((!master || !slave) && !nothing_ok) { + ast_mutex_unlock(&p0->lock); + ast_mutex_unlock(&p1->lock); + return -1; + } + + if (npc0->index == SUB_REAL) { + disable_dtmf_detect(p0); + } + if (npc1->index == SUB_REAL) { + disable_dtmf_detect(p1); + } + + ast_mutex_unlock(&p0->lock); + ast_mutex_unlock(&p1->lock); + + tech_pvt = bridge->tech_pvt; + tech_pvt->master = master; + tech_pvt->slave = slave; + + ast_debug(2, "Start native bridging %s and %s\n", + ast_channel_name(c0), ast_channel_name(c1)); + +#if defined(HAVE_PRI) && defined(PRI_2BCT) + if (!tech_pvt->tried_trombone_removal) { + tech_pvt->tried_trombone_removal = 1; + + if (p0->pri && p0->pri == p1->pri && p0->pri->transfer) { + q931_call *q931_c0; + q931_call *q931_c1; + + /* Try to eliminate the tromboned call. */ + ast_mutex_lock(&p0->pri->lock); + ast_assert(dahdi_sig_pri_lib_handles(p0->sig)); + ast_assert(dahdi_sig_pri_lib_handles(p1->sig)); + q931_c0 = ((struct sig_pri_chan *) (p0->sig_pvt))->call; + q931_c1 = ((struct sig_pri_chan *) (p1->sig_pvt))->call; + if (q931_c0 && q931_c1) { + pri_channel_bridge(q931_c0, q931_c1); + ast_debug(2, "Attempt to eliminate tromboned call with %s and %s\n", + ast_channel_name(c0), ast_channel_name(c1)); + } + ast_mutex_unlock(&p0->pri->lock); + } + } +#endif /* defined(HAVE_PRI) && defined(PRI_2BCT) */ + return 0; +} + +/*! + * \internal + * \brief Request to start native bridging activity. + * \since 12.0.0 + * + * \param bridge What to operate upon. + * + * \return Nothing + * + * \note On entry, bridge may or may not already be locked. + * However, it can be accessed as if it were locked. + */ +static void native_request_start(struct ast_bridge *bridge) +{ + struct native_pvt_bridge *tech_pvt; + struct ast_bridge_channel *cur; + + ast_assert(bridge->tech_pvt != NULL); + + tech_pvt = bridge->tech_pvt; + + if (bridge->num_channels != 2 || !tech_pvt->saw_start || tech_pvt->connected) { + return; + } + AST_LIST_TRAVERSE(&bridge->channels, cur, entry) { + if (cur->suspended || !cur->tech_pvt) { + return; + } + } + + /* Actually try starting the native bridge. */ + if (native_start(bridge)) { + return; + } + tech_pvt->connected = 1; +} + +/*! + * \internal + * \brief Request a bridge technology instance start operations. + * \since 12.0.0 + * + * \retval 0 on success + * \retval -1 on failure + * + * \note On entry, bridge may or may not already be locked. + * However, it can be accessed as if it were locked. + */ +static int native_bridge_start(struct ast_bridge *bridge) +{ + struct native_pvt_bridge *tech_pvt; + + ast_assert(bridge->tech_pvt != NULL); + + tech_pvt = bridge->tech_pvt; + tech_pvt->saw_start = 1; + + native_request_start(bridge); + return 0; +} + +/*! + * \internal + * \brief Request a bridge technology instance stop in preparation for being destroyed. + * \since 12.0.0 + * + * \note On entry, bridge is already locked. + */ +static void native_bridge_stop(struct ast_bridge *bridge) +{ + struct native_pvt_bridge *tech_pvt; + + tech_pvt = bridge->tech_pvt; + if (!tech_pvt) { + return; + } + + tech_pvt->saw_start = 0; + native_request_stop(bridge); +} + +/*! + * \internal + * \brief Add a channel to a bridging technology instance for a bridge. + * \since 12.0.0 + * + * \retval 0 on success + * \retval -1 on failure + * + * \note On entry, bridge is already locked. + */ +static int native_bridge_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel) +{ + struct native_pvt_chan *tech_pvt; + struct ast_channel *c0; + struct ast_channel *c1; + + ast_assert(!bridge_channel->tech_pvt); + + tech_pvt = ast_calloc(1, sizeof(*tech_pvt)); + if (!tech_pvt) { + return -1; + } + + bridge_channel->tech_pvt = tech_pvt; + native_request_start(bridge); + + c0 = AST_LIST_FIRST(&bridge->channels)->chan; + c1 = AST_LIST_LAST(&bridge->channels)->chan; + if (c0 != c1) { + /* + * Make the channels compatible in case the native bridge did + * not start for some reason and we need to fallback to 1-1 + * bridging. + */ + ast_channel_make_compatible(c0, c1); + } + + return 0; +} + +/*! + * \internal + * \brief Remove a channel from a bridging technology instance for a bridge. + * \since 12.0.0 + * + * \note On entry, bridge is already locked. + */ +static void native_bridge_leave(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel) +{ + struct native_pvt_chan *tech_pvt; + + native_request_stop(bridge); + + tech_pvt = bridge_channel->tech_pvt; + bridge_channel->tech_pvt = NULL; + ast_free(tech_pvt); +} + +/*! + * \internal + * \brief Suspend a channel on a bridging technology instance for a bridge. + * \since 12.0.0 + * + * \note On entry, bridge is already locked. + */ +static void native_bridge_suspend(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel) +{ + native_request_stop(bridge); +} + +/*! + * \internal + * \brief Unsuspend a channel on a bridging technology instance for a bridge. + * \since 12.0.0 + * + * \note On entry, bridge is already locked. + */ +static void native_bridge_unsuspend(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel) +{ + native_request_start(bridge); +} + +/*! + * \internal + * \brief Check if channel is compatible. + * \since 12.0.0 + * + * \param bridge_channel Is this channel compatible. + * + * \retval TRUE if channel is compatible with native DAHDI bridge. + */ +static int native_bridge_is_capable(struct ast_bridge_channel *bridge_channel) +{ + struct ast_channel *chan = bridge_channel->chan; + struct dahdi_pvt *pvt; + int is_capable; + + if (ao2_container_count(bridge_channel->features->dtmf_hooks)) { + ast_debug(2, "Channel '%s' has DTMF hooks.\n", ast_channel_name(chan)); + return 0; + } + + ast_channel_lock(chan); + + if (dahdi_tech != ast_channel_tech(chan)) { + ast_debug(2, "Channel '%s' is not %s.\n", + ast_channel_name(chan), dahdi_tech->type); + ast_channel_unlock(chan); + return 0; + } + if (ast_channel_has_audio_frame_or_monitor(chan)) { + ast_debug(2, "Channel '%s' has an active monitor, audiohook, or framehook.\n", + ast_channel_name(chan)); + ast_channel_unlock(chan); + return 0; + } + pvt = ast_channel_tech_pvt(chan); + if (!pvt || !pvt->sig) { + /* No private; or signaling is for a pseudo channel. */ + ast_channel_unlock(chan); + return 0; + } + + is_capable = 1; + ast_mutex_lock(&pvt->lock); + + if (pvt->callwaiting && pvt->callwaitingcallerid) { + /* + * Call Waiting Caller ID requires DTMF detection to know if it + * can send the CID spill. + */ + ast_debug(2, "Channel '%s' has call waiting caller ID enabled.\n", + ast_channel_name(chan)); + is_capable = 0; + } + + ast_mutex_unlock(&pvt->lock); + ast_channel_unlock(chan); + + return is_capable; +} + +/*! + * \internal + * \brief Check if a bridge is compatible with the bridging technology. + * \since 12.0.0 + * + * \retval 0 if not compatible + * \retval non-zero if compatible + * + * \note On entry, bridge may or may not already be locked. + * However, it can be accessed as if it were locked. + */ +static int native_bridge_compatible(struct ast_bridge *bridge) +{ + struct ast_bridge_channel *cur; + + /* We require two channels before even considering native bridging. */ + if (bridge->num_channels != 2) { + ast_debug(1, "Bridge %s: Cannot use native DAHDI. Must have two channels.\n", + bridge->uniqueid); + return 0; + } + + AST_LIST_TRAVERSE(&bridge->channels, cur, entry) { + if (!native_bridge_is_capable(cur)) { + ast_debug(1, "Bridge %s: Cannot use native DAHDI. Channel '%s' not compatible.\n", + bridge->uniqueid, ast_channel_name(cur->chan)); + return 0; + } + } + + return -1; +} + +/*! + * \internal + * \brief Check if something changed on the channel. + * \since 12.0.0 + * + * \param bridge_channel What to operate upon. + * + * \retval 0 Nothing changed. + * \retval -1 Something changed. + * + * \note On entry, bridge_channel->bridge is already locked. + */ +static int native_chan_changed(struct ast_bridge_channel *bridge_channel) +{ + struct native_pvt_chan *tech_pvt; + struct ast_channel *chan; + struct dahdi_pvt *pvt; + int idx = -1; + + ast_assert(bridge_channel->tech_pvt != NULL); + + tech_pvt = bridge_channel->tech_pvt; + + chan = bridge_channel->chan; + ast_channel_lock(chan); + pvt = ast_channel_tech_pvt(chan); + if (tech_pvt->pvt == pvt) { + idx = dahdi_get_index(chan, pvt, 1); + } + ast_channel_unlock(chan); + + if (/* Did chan get masqueraded or PRI change associated B channel? */ + tech_pvt->pvt != pvt + /* Did the pvt active owner change? */ + || tech_pvt->owner != pvt->owner + /* Did the pvt three way call status change? */ + || tech_pvt->inthreeway != pvt->subs[SUB_REAL].inthreeway + /* Did the owner index change? */ + || tech_pvt->index != idx + /* + * Did chan file descriptor change? (This seems redundant with + * masquerade and active owner change checks.) + */ + || tech_pvt->fd0 != ast_channel_fd(chan, 0) + /* Did chan state change? i.e. Did it stop ringing? */ + || (pvt->subs[SUB_REAL].owner + && tech_pvt->state > -1 + && tech_pvt->state != ast_channel_state(pvt->subs[SUB_REAL].owner))) { + return -1; + } + + return 0; +} + +/*! + * \internal + * \brief Check if something changed on the bridge channels. + * \since 12.0.0 + * + * \param bridge What to operate upon. + * + * \retval 0 Nothing changed. + * \retval -1 Something changed. + * + * \note On entry, bridge is already locked. + */ +static int native_bridge_changed(struct ast_bridge *bridge) +{ + struct ast_bridge_channel *cur; + + AST_LIST_TRAVERSE(&bridge->channels, cur, entry) { + if (native_chan_changed(cur)) { + ast_debug(1, "Bridge %s: Something changed on channel '%s'.\n", + bridge->uniqueid, ast_channel_name(cur->chan)); + return -1; + } + } + return 0; +} + +/*! + * \internal + * \brief Write a frame into the bridging technology instance for a bridge. + * \since 12.0.0 + * + * \note The bridge must be tolerant of bridge_channel being NULL. + * + * \retval 0 Frame accepted into the bridge. + * \retval -1 Frame needs to be deferred. + * + * \note On entry, bridge is already locked. + */ +static int native_bridge_write(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame) +{ + struct native_pvt_bridge *tech_pvt; + + /* + * When we are not native bridged by DAHDI, we are like a normal + * 1-1 bridge. + */ + + ast_assert(bridge->tech_pvt != NULL); + + /* Recheck native bridging validity. */ + tech_pvt = bridge->tech_pvt; + switch (frame->frametype) { + case AST_FRAME_VOICE: + case AST_FRAME_VIDEO: + if (!tech_pvt->connected) { + /* Don't try to start native mode on media frames. */ + break; + } + if (native_bridge_changed(bridge)) { + native_request_stop(bridge); + native_request_start(bridge); + if (!tech_pvt->connected) { + break; + } + } + + /* + * Native bridge handles voice frames in hardware. However, it + * also passes the frames up to Asterisk anyway. Discard the + * media frames. + */ + return 0; + default: + if (!tech_pvt->connected) { + native_request_start(bridge); + break; + } + if (native_bridge_changed(bridge)) { + native_request_stop(bridge); + native_request_start(bridge); + } + break; + } + + return ast_bridge_queue_everyone_else(bridge, bridge_channel, frame); +} + +static struct ast_bridge_technology native_bridge = { + .name = "native_dahdi", + .capabilities = AST_BRIDGE_CAPABILITY_NATIVE, + .preference = AST_BRIDGE_PREFERENCE_BASE_NATIVE, + .create = native_bridge_create, + .start = native_bridge_start, + .stop = native_bridge_stop, + .destroy = native_bridge_destroy, + .join = native_bridge_join, + .leave = native_bridge_leave, + .suspend = native_bridge_suspend, + .unsuspend = native_bridge_unsuspend, + .compatible = native_bridge_compatible, + .write = native_bridge_write, +}; + +/*! + * \internal + * \brief Destroy the DAHDI native bridge support. + * \since 12.0.0 + * + * \return Nothing + */ +void dahdi_native_unload(void) +{ + ast_bridge_technology_unregister(&native_bridge); + ast_format_cap_destroy(native_bridge.format_capabilities); +} + +/*! + * \internal + * \brief Initialize the DAHDI native bridge support. + * \since 12.0.0 + * + * \retval 0 on success. + * \retval -1 on error. + */ +int dahdi_native_load(struct ast_module *mod, const struct ast_channel_tech *tech) +{ + struct ast_format format; + + dahdi_tech = tech; + + native_bridge.format_capabilities = ast_format_cap_alloc(); + if (!native_bridge.format_capabilities) { + return -1; + } + + /* + * This is used to make channels compatible with the bridge + * itself not with each other. + */ + ast_format_cap_add(native_bridge.format_capabilities, ast_format_set(&format, AST_FORMAT_SLINEAR, 0)); + ast_format_cap_add(native_bridge.format_capabilities, ast_format_set(&format, AST_FORMAT_ULAW, 0)); + ast_format_cap_add(native_bridge.format_capabilities, ast_format_set(&format, AST_FORMAT_ALAW, 0)); + + return __ast_bridge_technology_register(&native_bridge, mod); +} diff --git a/channels/dahdi/bridge_native_dahdi.h b/channels/dahdi/bridge_native_dahdi.h new file mode 100644 index 0000000000..91e8d16b75 --- /dev/null +++ b/channels/dahdi/bridge_native_dahdi.h @@ -0,0 +1,47 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2013 Digium, Inc. + * + * Richard Mudgett + * + * 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 Native DAHDI bridging support. + * + * \author Richard Mudgett + * + * See Also: + * \arg \ref AstCREDITS + */ + +#ifndef _ASTERISK_BRIDGE_NATIVE_DAHDI_H +#define _ASTERISK_BRIDGE_NATIVE_DAHDI_H + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +/* ------------------------------------------------------------------- */ + +void dahdi_native_unload(void); +int dahdi_native_load(struct ast_module *mod, const struct ast_channel_tech *tech); + +/* ------------------------------------------------------------------- */ + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + +#endif /* _ASTERISK_BRIDGE_NATIVE_DAHDI_H */ diff --git a/main/bridging.c b/main/bridging.c index 0419ab1a3c..132b7d4be9 100644 --- a/main/bridging.c +++ b/main/bridging.c @@ -375,6 +375,10 @@ int ast_bridge_channel_queue_frame(struct ast_bridge_channel *bridge_channel, st /* Drop non-deferable frames when suspended. */ return 0; } + if (fr->frametype == AST_FRAME_NULL) { + /* "Accept" the frame and discard it. */ + return 0; + } dup = ast_frdup(fr); if (!dup) { @@ -427,6 +431,11 @@ int ast_bridge_queue_everyone_else(struct ast_bridge *bridge, struct ast_bridge_ struct ast_bridge_channel *cur; int not_written = -1; + if (frame->frametype == AST_FRAME_NULL) { + /* "Accept" the frame and discard it. */ + return 0; + } + AST_LIST_TRAVERSE(&bridge->channels, cur, entry) { if (cur == bridge_channel) { continue; @@ -1243,10 +1252,6 @@ static void bridge_handle_trip(struct ast_bridge_channel *bridge_channel) return; } switch (frame->frametype) { - case AST_FRAME_NULL: - /* Just discard it. */ - ast_frfree(frame); - return; case AST_FRAME_CONTROL: switch (frame->subclass.integer) { case AST_CONTROL_HANGUP: