Skip to content
Snippets Groups Projects
chan_zap.c 158 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    /*
     * Asterisk -- A telephony toolkit for Linux.
     *
     * Tormenta T1 Card (via Zapata library) support 
     * 
     * Copyright (C) 1999, Mark Spencer
     *
     * Mark Spencer <markster@linux-support.net>
     *
     * This program is free software, distributed under the terms of
     * the GNU General Public License
     */
    
    #include <stdio.h>
    #include <string.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <asterisk/lock.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <asterisk/channel.h>
    #include <asterisk/channel_pvt.h>
    #include <asterisk/config.h>
    #include <asterisk/logger.h>
    #include <asterisk/module.h>
    #include <asterisk/pbx.h>
    #include <asterisk/options.h>
    #include <asterisk/file.h>
    #include <asterisk/ulaw.h>
    #include <asterisk/callerid.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <asterisk/adsi.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <asterisk/cli.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <asterisk/cdr.h>
    #include <asterisk/parking.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <asterisk/musiconhold.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <asterisk/tdd.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <sys/signal.h>
    #include <sys/select.h>
    #include <errno.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/ioctl.h>
    #include <linux/zaptel.h>
    #include <zap.h>
    #include <math.h>
    #include <tonezone.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include <dirent.h>
    
    Mark Spencer's avatar
    Mark Spencer committed
    #ifdef ZAPATA_PRI
    #include <libpri.h>
    #endif
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    #include "../asterisk.h"
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    /* 
       XXX 
       XXX   We definitely need to lock the private structure in zt_read and such 
       XXX  
     */
    
    #define RINGT 274
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    /*
     * Define ZHONE_HACK to cause us to go off hook and then back on hook when
     * the user hangs up to reset the state machine so ring works properly.
     * This is used to be able to support kewlstart by putting the zhone in
     * groundstart mode since their forward disconnect supervision is entirely
     * broken even though their documentation says it isn't and their support
     * is entirely unwilling to provide any assistance with their channel banks
     * even though their web site says they support their products for life.
     */
    
    #define ZHONE_HACK
    
    #define CHANNEL_PSEUDO -12
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    #ifdef ZAPATA_PRI
    static char *desc = "Zapata Telephony (PRI) Driver";
    static char *tdesc = "Zapata Telephony + PRI Interface Driver";
    #else
    static char *desc = "Zapata Telphony Driver";
    static char *tdesc = "Zapata Telephony Interface Driver";
    #endif
    static char *type = "Zap";
    static char *typecompat = "Tor";	/* Retain compatibility with chan_tor */
    static char *config = "zapata.conf";
    
    #define SIG_EM		ZT_SIG_EM
    #define SIG_EMWINK 	(0x10000 | ZT_SIG_EM)
    #define SIG_FEATD	(0x20000 | ZT_SIG_EM)
    
    Mark Spencer's avatar
    Mark Spencer committed
    #define	SIG_FEATDMF	(0x40000 | ZT_SIG_EM)
    #define	SIG_FEATB	(0x80000 | ZT_SIG_EM)
    
    Mark Spencer's avatar
    Mark Spencer committed
    #define SIG_FXSLS	ZT_SIG_FXSLS
    #define SIG_FXSGS	ZT_SIG_FXSGS
    #define SIG_FXSKS	ZT_SIG_FXSKS
    #define SIG_FXOLS	ZT_SIG_FXOLS
    #define SIG_FXOGS	ZT_SIG_FXOGS
    #define SIG_FXOKS	ZT_SIG_FXOKS
    #define SIG_PRI		ZT_SIG_CLEAR
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    #define NUM_SPANS 	32
    
    Mark Spencer's avatar
    Mark Spencer committed
    #define RESET_INTERVAL	3600	/* How often (in seconds) to reset unused channels */
    
    #define CHAN_PSEUDO	-2
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    static char context[AST_MAX_EXTENSION] = "default";
    static char callerid[256] = "";
    
    static char language[MAX_LANGUAGE] = "";
    
    Mark Spencer's avatar
    Mark Spencer committed
    static char musicclass[MAX_LANGUAGE] = "";
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    static int use_callerid = 1;
    
    static int cur_signalling = -1;
    
    static int cur_group = 0;
    static int cur_callergroup = 0;
    static int cur_pickupgroup = 0;
    
    static int immediate = 0;
    
    static int stripmsd = 0;
    
    static int callwaiting = 0;
    
    static int callwaitingcallerid = 0;
    
    static int hidecallerid = 0;
    
    static int threewaycalling = 0;
    
    static int transfer = 0;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int cancallforward = 0;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static float rxgain = 0.0;
    
    static float txgain = 0.0;
    
    static int echocancel;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int echocanbridged = 0;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static char accountcode[20] = "";
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static char mailbox[AST_MAX_EXTENSION];
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int amaflags = 0;
    
    static int adsi = 0;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    #ifdef ZAPATA_PRI
    static int minunused = 2;
    static int minidle = 0;
    static char idleext[AST_MAX_EXTENSION];
    static char idledial[AST_MAX_EXTENSION];
    #endif
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    /* Wait up to 16 seconds for first digit (FXO logic) */
    static int firstdigittimeout = 16000;
    
    /* How long to wait for following digits (FXO logic) */
    static int gendigittimeout = 8000;
    
    static int usecnt =0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    static pthread_mutex_t usecnt_lock = AST_MUTEX_INITIALIZER;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    /* Protect the interface list (of zt_pvt's) */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static pthread_mutex_t iflock = AST_MUTEX_INITIALIZER;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    /* Protect the monitoring thread, so only one process can kill or start it, and not
       when it's doing something critical. */
    
    Mark Spencer's avatar
    Mark Spencer committed
    static pthread_mutex_t monlock = AST_MUTEX_INITIALIZER;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    /* This is the thread for the monitor which checks for input on the channels
       which are not currently in use.  */
    static pthread_t monitor_thread = 0;
    
    static int restart_monitor(void);
    
    static int zt_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc);
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int zt_sendtext(struct ast_channel *c, char *text);
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static inline int zt_get_event(int fd)
    {
    	/* Avoid the silly zt_getevent which ignores a bunch of events */
    	int j;
    	if (ioctl(fd, ZT_GETEVENT, &j) == -1) return -1;
    	return j;
    }
    
    static inline int zt_wait_event(int fd)
    {
    	/* Avoid the silly zt_waitevent which ignores a bunch of events */
    	int i,j=0;
    	i = ZT_IOMUX_SIGEVENT;
    	if (ioctl(fd, ZT_IOMUX, &i) == -1) return -1;
    	if (ioctl(fd, ZT_GETEVENT, &j) == -1) return -1;
    	return j;
    }
    
    /* Chunk size to read -- we use the same size as the chunks that the zapata library uses.  */   
    #define READ_SIZE 204
    
    #define MASK_AVAIL		(1 << 0)		/* Channel available for PRI use */
    #define MASK_INUSE		(1 << 1)		/* Channel currently in use */
    
    #define CALLWAITING_SILENT_SAMPLES	( (300 * 8) / READ_SIZE) /* 300 ms */
    #define CALLWAITING_REPEAT_SAMPLES	( (10000 * 8) / READ_SIZE) /* 300 ms */
    
    struct zt_pvt;
    
    
    #ifdef ZAPATA_PRI
    struct zt_pri {
    	pthread_t master;			/* Thread of master */
    	pthread_mutex_t lock;		/* Mutex */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char idleext[AST_MAX_EXTENSION];		/* Where to idle extra calls */
    	char idlecontext[AST_MAX_EXTENSION];		/* What context to use for idle */
    	char idledial[AST_MAX_EXTENSION];		/* What to dial before dumping */
    	int minunused;				/* Min # of channels to keep empty */
    	int minidle;				/* Min # of "idling" calls to keep active */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int nodetype;				/* Node type */
    	int switchtype;				/* Type of switch to emulate */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int dchannel;			/* What channel the dchannel is on */
    	int channels;			/* Num of chans in span (31 or 24) */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct pri *pri;
    	int debug;
    	int fd;
    	int up;
    	int offset;
    	int span;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int chanmask[31];			/* Channel status */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int resetting;
    	int resetchannel;
    	time_t lastreset;
    	struct zt_pvt *pvt[31];	/* Member channel pvt structs */
    	struct zt_channel *chan[31];	/* Channels on each line */
    
    Mark Spencer's avatar
    Mark Spencer committed
    };
    
    
    static struct zt_pri pris[NUM_SPANS];
    
    static int pritype = PRI_CPE;
    
    #if 0
    
    Mark Spencer's avatar
    Mark Spencer committed
    #define DEFAULT_PRI_DEBUG (PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q921_DUMP | PRI_DEBUG_Q921_RAW | PRI_DEBUG_Q921_STATE)
    
    Mark Spencer's avatar
    Mark Spencer committed
    #else
    #define DEFAULT_PRI_DEBUG 0
    #endif
    
    static inline int pri_grab(struct zt_pri *pri)
    {
    	int res;
    	/* Grab the lock first */
        res = ast_pthread_mutex_lock(&pri->lock);
    	if (res)
    		return res;
    	/* Then break the select */
    	pthread_kill(pri->master, SIGURG);
    	return 0;
    }
    
    static inline void pri_rel(struct zt_pri *pri)
    {
    	ast_pthread_mutex_unlock(&pri->lock);
    }
    
    static int switchtype = PRI_SWITCH_NI2;
    
    #endif
    
    static struct zt_pvt {
    	ZAP *z;
    	pthread_mutex_t lock;
    	struct ast_channel *owner;	/* Our owner (if applicable) */
    	struct ast_channel *owners[3];	
    		/* Up to three channels can be associated with this call */
    		
    	int callwaitindex;			/* Call waiting index into owners */	
    	int thirdcallindex;			/* Three-way calling index into owners */
    	int normalindex;			/* "Normal" call index into owners */
    	
    	int sig;					/* Signalling style */
    	float rxgain;
    	float txgain;
    	struct zt_pvt *next;			/* Next channel in list */
    	char context[AST_MAX_EXTENSION];
    	char exten[AST_MAX_EXTENSION];
    	char language[MAX_LANGUAGE];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char musicclass[MAX_LANGUAGE];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char callerid[AST_MAX_EXTENSION];
    	char callwaitcid[AST_MAX_EXTENSION];
    	char dtmfq[AST_MAX_EXTENSION];
    	struct ast_frame f_unused;	/* Usually unused, but could in rare cases be needed */
    	struct ast_frame f[3];		/* One frame for each channel.  How did this ever work before? */
    	short buffer[3][AST_FRIENDLY_OFFSET/2 + READ_SIZE];
    	int group;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int law;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int callgroup;
    	int pickupgroup;
    	int immediate;				/* Answer before getting digits? */
    	int channel;				/* Channel Number */
    	int span;					/* Span number */
    	int dialing;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int dialednone;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int use_callerid;			/* Whether or not to use caller id on this channel */
    	int hidecallerid;
    	int permhidecallerid;		/* Whether to hide our outgoing caller ID or not */
    	int callwaitingrepeat;		/* How many samples to wait before repeating call waiting */
    	unsigned char *cidspill;
    	int cidpos;
    	int cidlen;
    	int ringt;
    	int stripmsd;
    	int needringing[3];
    	int needanswer[3];
    	int callwaiting;
    	int callwaitcas;
    	int callwaitrings;
    	int echocancel;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int echocanbridged;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int permcallwaiting;
    	int callwaitingcallerid;
    	int threewaycalling;
    	int transfer;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int dnd;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int cref;					/* Call reference number */
    	ZT_DIAL_OPERATION dop;
    	struct zt_confinfo conf;	/* Saved state of conference */
    	struct zt_confinfo conf2;	/* Saved state of alternate conference */
    	int confno;					/* Conference number */
    	ZAP *pseudo;				/* Pseudo channel FD */
    	int pseudochan;				/* Pseudo channel */
    	int destroy;
    	int ignoredtmf;				
    	int inalarm;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char accountcode[20];		/* Account code */
    	int amaflags;				/* AMA Flags */
    	char didtdd;			/* flag to say its done it once */
    	struct tdd_state *tdd;		/* TDD flag */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int reallinear;
    	int pseudolinear;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int adsi;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int cancallforward;
    	char call_forward[AST_MAX_EXTENSION];
    	char mailbox[AST_MAX_EXTENSION];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	
    	int confirmanswer;		/* Wait for '#' to confirm answer */
    	int distinctivering;	/* Which distinctivering to use */
    	int cidrings;			/* Which ring to deliver CID on */
    	
    	char mate;			/* flag to say its in MATE mode */
    
    Mark Spencer's avatar
    Mark Spencer committed
    #ifdef ZAPATA_PRI
    	struct zt_pri *pri;
    	q931_call *call;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int isidlecall;
    	int resetting;
    
    Mark Spencer's avatar
    Mark Spencer committed
    #endif	
    } *iflist = NULL;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static struct zt_ring_cadence cadences[] = {
    	{ { 125, 125, 2000, 4000 } },			/* Quick chirp followed by normal ring */
    	{ { 250, 250, 500, 1000, 250, 250, 500, 4000 } }, /* British style ring */
    	{ { 125, 125, 125, 125, 125, 4000 } },	/* Three short bursts */
    	{ { 1000, 500, 2500, 5000 } },	/* Long ring */
    };
    
    static int cidrings[] = {
    	2,										/* Right after first long ring */
    	4,										/* Right after long part */
    	3,										/* After third chirp */
    	2,										/* Second spell */
    };
    
    #define NUM_CADENCE (sizeof(cadences) / sizeof(cadences[0]))
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    #define INTHREEWAY(p) ((p->normalindex > -1) && (p->thirdcallindex > -1) && \
    		(p->owner == p->owners[p->normalindex]))
    
    #define ISTRUNK(p) ((p->sig == SIG_FXSLS) || (p->sig == SIG_FXSKS) || \
    			(p->sig == SIG_FXSGS))
    
    
    /* return non-zero if clear dtmf is appropriate */
    static int CLEARDTMF(struct ast_channel *chan) {
    struct zt_pvt *p = chan->pvt->pvt,*themp;
    struct ast_channel *them;
    	if (!p)
    		return 0;
    	  /* if not in a 3 way, we should be okay */
    	if (p->thirdcallindex == -1) return 1;
    	  /* get the other side of the call's channel pointer */
    	if (p->owners[p->normalindex] == chan)
    		them = p->owners[p->thirdcallindex];
    	else
    		them = p->owners[p->normalindex];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!them)
    		return 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!them->bridge) return 1;
    	  /* get their private structure, too */
    	themp = them->pvt->pvt;
    	  /* if does not use zt bridge code, return 0 */
    	if (them->pvt->bridge != zt_bridge) return 0;
    	if (them->bridge->pvt->bridge != zt_bridge) return 0;
    	return 1; /* okay, I guess we are okay to be clear */
    }
    
    static int alloc_pseudo(struct zt_pvt *p)
    {
    	int x;
    	ZAP *z;
    	int res;
    	ZT_BUFFERINFO bi;
    	if (p->pseudo || p->pseudochan){
    		ast_log(LOG_WARNING, "Already have a pseudo fd: %d, chan: %d\n",
    			zap_fd(p->pseudo), p->pseudochan);
    		return -1;
    	}
    	z = zap_open("/dev/zap/pseudo", 1);
    	if (!z) {
    		ast_log(LOG_WARNING, "Unable to open /dev/zap/pseudo: %s\n", strerror(errno));
    		return -1;
    	} else {
    		res = ioctl(zap_fd(z), ZT_GET_BUFINFO, &bi);
    		if (!res) {
    			bi.txbufpolicy = ZT_POLICY_IMMEDIATE;
    			bi.rxbufpolicy = ZT_POLICY_IMMEDIATE;
    			bi.numbufs = 4;
    			res = ioctl(zap_fd(z), ZT_SET_BUFINFO, &bi);
    			if (res < 0) {
    				ast_log(LOG_WARNING, "Unable to set buffer policy on channel %d\n", x);
    			}
    		} else 
    			ast_log(LOG_WARNING, "Unable to check buffer policy on channel %d\n", x);
    		p->pseudo = z;
    		if (ioctl(zap_fd(z), ZT_CHANNO, &x) == 1) {
    			ast_log(LOG_WARNING,"Unable to get channel number for pseudo channel on FD %d\n",zap_fd(z));
    			return -1;
    		}
    		p->pseudochan = x;
    		if (option_debug)
    			ast_log(LOG_DEBUG, "Allocated pseudo channel %d on FD %d\n", p->pseudochan, zap_fd(p->pseudo));
    		return 0;
    	}
    	/* Never reached */
    	return 0;
    }
    
    static int unalloc_pseudo(struct zt_pvt *p)
    {
    	if (p->pseudo)
    		zap_close(p->pseudo);
    	if (option_debug)
    		ast_log(LOG_DEBUG, "Released pseudo channel %d\n", p->pseudochan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	p->pseudolinear = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	p->pseudo = NULL;
    	p->pseudochan = 0;
    	return 0;
    }
    
    static int zt_digit(struct ast_channel *ast, char digit)
    {
    	ZT_DIAL_OPERATION zo;
    	struct zt_pvt *p;
    	int res;
    	zo.op = ZT_DIAL_OP_APPEND;
    	zo.dialstr[0] = 'T';
    	zo.dialstr[1] = digit;
    	zo.dialstr[2] = 0;
    	p = ast->pvt->pvt;
    	if ((res = ioctl(zap_fd(p->z), ZT_DIAL, &zo)))
    		ast_log(LOG_WARNING, "Couldn't dial digit %c\n", digit);
    	else
    		p->dialing = 1;
    	
    	return res;
    }
    
    static char *events[] = {
            "No event",
            "On hook",
            "Ring/Answered",
            "Wink/Flash",
            "Alarm",
            "No more alarm",
    		"HDLC Abort",
    		"HDLC Overrun",
    		"HDLC Bad FCS",
    		"Dial Complete",
    		"Ringer On",
    		"Ringer Off",
    		"Hook Transition Complete"
    };
     
    static char *event2str(int event)
    {
            static char buf[256];
            if ((event < 13) && (event > -1))
                    return events[event];
            sprintf(buf, "Event %d", event);
            return buf;
    }
    
    static char *sig2str(int sig)
    {
    	static char buf[256];
    	switch(sig) {
    	case SIG_EM:
    		return "E & M Immediate";
    	case SIG_EMWINK:
    		return "E & M Wink";
    	case SIG_FEATD:
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return "Feature Group D (DTMF)";
    	case SIG_FEATDMF:
    		return "Feature Group D (MF)";
    	case SIG_FEATB:
    		return "Feature Group B (MF)";
    
    Mark Spencer's avatar
    Mark Spencer committed
    	case SIG_FXSLS:
    		return "FXS Loopstart";
    	case SIG_FXSGS:
    		return "FXS Groundstart";
    	case SIG_FXSKS:
    		return "FXS Kewlstart";
    	case SIG_FXOLS:
    		return "FXO Loopstart";
    	case SIG_FXOGS:
    		return "FXO Groundstart";
    	case SIG_FXOKS:
    		return "FXO Kewlstart";
    	case SIG_PRI:
    		return "PRI Signalling";
    
    Mark Spencer's avatar
    Mark Spencer committed
    	case 0:
    		return "Pseudo Signalling";
    
    Mark Spencer's avatar
    Mark Spencer committed
    	default:
    
    Mark Spencer's avatar
    Mark Spencer committed
    		snprintf(buf, sizeof(buf), "Unknown signalling %d", sig);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return buf;
    	}
    }
    
    static int conf_set(struct zt_pvt *p, int req, int force)
    {
    
    	/* Set channel to given conference, -1 to allocate one */
    	ZT_CONFINFO ci;
    	ZT_CONFINFO cip;
    	int res;
    	if ((p->confno > -1) && (p->confno != req) && (!force)) {
    		ast_log(LOG_WARNING, "Channel %d already has conference %d allocated\n", p->channel, p->confno);
    		return -1;
    	}
    	ci.chan = 0;
    	ci.confno = 0;
    	/* Check current conference stuff */
    	res = ioctl(zap_fd(p->z), ZT_GETCONF, &ci);
    	if (res < 0) {
    		ast_log(LOG_WARNING, "Failed to get conference info on channel %d: %s\n",
    			p->channel, strerror(errno));
    		return -1;
    	}
    	if (!force && ci.confmode && (ci.confno != p->confno)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Channel %d is already in a conference (%d, %x) we didn't create (though we did make %d) (req = %d)\n", p->channel, ci.confno, ci.confmode, p->confno, req);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    	ci.chan = 0;
    	ci.confno = req;
    	ci.confmode = ZT_CONF_REALANDPSEUDO | ZT_CONF_TALKER | ZT_CONF_LISTENER | ZT_CONF_PSEUDO_LISTENER | ZT_CONF_PSEUDO_TALKER; 
    	res = ioctl(zap_fd(p->z), ZT_SETCONF, &ci);
    	if (res < 0) {
    		ast_log(LOG_WARNING, "Failed to set conference to %d on channel %d: %s\n",
    			req, p->channel, strerror(errno));
    		return -1;
    	}
    	if (INTHREEWAY(p)) {
    			/* We have a three way call active, be sure the third participant is included in
    			   our conference. */
    		cip.chan = 0;
    		cip.confno = ci.confno;
    		cip.confmode = ZT_CONF_CONF | ZT_CONF_TALKER | ZT_CONF_LISTENER;
    		
    		res = ioctl(zap_fd(p->pseudo), ZT_SETCONF, &cip);
    		if (res < 0) {
    			ast_log(LOG_WARNING, "Failed to set conference info on pseudo channel %d: %s\n",
    				p->pseudochan, strerror(errno));
    			return -1;
    		}
    		ast_log(LOG_DEBUG, "Conferenced in third way call\n");
    	} else {
    		if (p->pseudo || (p->pseudochan)) {
    			ast_log(LOG_DEBUG, "There's a pseudo something on %d (channel %d), but we're not conferencing it in at the moment?\n",
    				zap_fd(p->pseudo), p->pseudochan);
    			cip.chan = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			cip.confno = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			cip.confmode = ZT_CONF_NORMAL;
    			res = ioctl(zap_fd(p->pseudo), ZT_SETCONF, &cip);
    			if (res < 0) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_log(LOG_WARNING, "Failed to set conference info on pseudo channel %d (mode %08x, conf %d): %s\n",
    					p->pseudochan, cip.confno, cip.confmode, strerror(errno));
    
    Mark Spencer's avatar
    Mark Spencer committed
    				return -1;
    			}
    		}
    	}
    	p->confno = ci.confno;
    	return 0;
    }
    
    static int three_way(struct zt_pvt *p)
    {
    	ast_log(LOG_DEBUG, "Setting up three way call\n");
    	return conf_set(p, p->confno, 0);
    }
    
    static int conf_clear(struct zt_pvt *p)
    {
    	ZT_CONFINFO ci;
    	int res;
    	ci.confmode = ZT_CONF_NORMAL;
    	ci.chan = 0;
    	ci.confno = 0;
    	res = ioctl(zap_fd(p->z), ZT_SETCONF, &ci);
    	if (res < 0) {
    		ast_log(LOG_WARNING, "Failed to clear conference info on channel %d: %s\n",
    			p->channel, strerror(errno));
    		return -1;
    	}
    	p->confno = -1;
    	return 0;
    }
    
    static void zt_enable_ec(struct zt_pvt *p)
    {
    	int x;
    	int res;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (p && p->echocancel) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		x = p->echocancel;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		res = ioctl(zap_fd(p->z), ZT_ECHOCANCEL, &x);
    		if (res) 
    			ast_log(LOG_WARNING, "Unable to enable echo cancellation on channel %d\n", p->channel);
    		else
    			ast_log(LOG_DEBUG, "Enabled echo cancellation on channel %d\n", p->channel);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else
    		ast_log(LOG_DEBUG, "No echocancellation requested\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    }
    
    static void zt_disable_ec(struct zt_pvt *p)
    {
    	int x;
    	int res;
    	if (p->echocancel) {
    		x = 0;
    		res = ioctl(zap_fd(p->z), ZT_ECHOCANCEL, &x);
    		if (res) 
    			ast_log(LOG_WARNING, "Unable to disable echo cancellation on channel %d\n", p->channel);
    		else
    			ast_log(LOG_DEBUG, "disabled echo cancellation on channel %d\n", p->channel);
    	}
    }
    
    static int zt_get_index(struct ast_channel *ast, struct zt_pvt *p, int nullok)
    {
    	int res;
    	if (p->owners[0] == ast)
    		res = 0;
    	else if (p->owners[1] == ast)
    		res = 1;
    	else if (p->owners[2] == ast)
    		res = 2;
    	else {
    		res = -1;
    		if (!nullok)
    			ast_log(LOG_WARNING, "Unable to get index, and nullok is not asserted\n");
    	}
    	return res;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    int set_actual_gain(int fd, int chan, float rxgain, float txgain)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct	zt_gains g;
    	float ltxgain;
    	float lrxgain;
    	int j,k;
    	g.chan = chan;
    	  /* caluculate linear value of tx gain */
    	ltxgain = pow(10.0,txgain / 20.0);
    	  /* caluculate linear value of rx gain */
    	lrxgain = pow(10.0,rxgain / 20.0);
    	for (j=0;j<256;j++) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* XXX Fix for A-law XXX */
    		k = (int)(((float)AST_MULAW(j)) * lrxgain);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (k > 32767) k = 32767;
    		if (k < -32767) k = -32767;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		g.rxgain[j] = AST_LIN2MU(k);
    		k = (int)(((float)AST_MULAW(j)) * ltxgain);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (k > 32767) k = 32767;
    		if (k < -32767) k = -32767;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		g.txgain[j] = AST_LIN2MU(k);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    		
    	  /* set 'em */
    	return(ioctl(fd,ZT_SETGAINS,&g));
    }
    
    static inline int zt_set_hook(int fd, int hs)
    {
    	int x, res;
    	x = hs;
    	res = ioctl(fd, ZT_HOOK, &x);
    	if (res < 0) 
    		ast_log(LOG_WARNING, "zt hook failed: %s\n", strerror(errno));
    	return res;
    }
    
    static int save_conference(struct zt_pvt *p)
    {
    	struct zt_confinfo c;
    	int res;
    	if (p->conf.confmode) {
    		ast_log(LOG_WARNING, "Can't save conference -- already in use\n");
    		return -1;
    	}
    	p->conf.chan = 0;
    	res = ioctl(zap_fd(p->z), ZT_GETCONF, &p->conf);
    	if (res) {
    		ast_log(LOG_WARNING, "Unable to get conference info: %s\n", strerror(errno));
    		p->conf.confmode = 0;
    		return -1;
    	}
    	c.chan = 0;
    	c.confno = 0;
    	c.confmode = ZT_CONF_NORMAL;
    	res = ioctl(zap_fd(p->z), ZT_SETCONF, &c);
    	if (res) {
    		ast_log(LOG_WARNING, "Unable to set conference info: %s\n", strerror(errno));
    		return -1;
    	}
    	switch(p->conf.confmode) {
    	case ZT_CONF_NORMAL:
    		p->conf2.confmode = 0;
    		break;
    	case ZT_CONF_MONITOR:
    		/* Get the other size */
    		p->conf2.chan = p->conf.confno;
    		res = ioctl(zap_fd(p->z), ZT_GETCONF, &p->conf2);
    		if (res) {
    			ast_log(LOG_WARNING, "Unable to get secondaryconference info: %s\n", strerror(errno));
    			p->conf2.confmode = 0;
    			return -1;
    		}
    		c.chan = p->conf.confno;
    		c.confno = 0;
    		c.confmode = ZT_CONF_NORMAL;
    		res = ioctl(zap_fd(p->z), ZT_SETCONF, &c);
    		if (res) {
    			ast_log(LOG_WARNING, "Unable to set secondaryconference info: %s\n", strerror(errno));
    			p->conf2.confmode = 0;
    			return -1;
    		}
    		break;
    	case ZT_CONF_CONF | ZT_CONF_LISTENER | ZT_CONF_TALKER:
    		p->conf2.confmode = 0;
    		break;
    	default:
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Don't know how to save conference state for conf mode %08x\n", p->conf.confmode);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    	if (option_debug)
    		ast_log(LOG_DEBUG, "Disabled conferencing\n");
    	return 0;
    }
    
    static int restore_conference(struct zt_pvt *p)
    {
    	int res;
    	if (p->conf.confmode) {
    		res = ioctl(zap_fd(p->z), ZT_SETCONF, &p->conf);
    		p->conf.confmode = 0;
    		if (res) {
    			ast_log(LOG_WARNING, "Unable to restore conference info: %s\n", strerror(errno));
    			return -1;
    		}
    		if (p->conf2.confmode) {
    			res = ioctl(zap_fd(p->z), ZT_SETCONF, &p->conf2);
    			p->conf2.confmode = 0;
    			if (res) {
    				ast_log(LOG_WARNING, "Unable to restore conference info: %s\n", strerror(errno));
    				return -1;
    			}
    		}
    	}
    	if (option_debug)
    		ast_log(LOG_DEBUG, "Restored conferencing\n");
    	return 0;
    }
    
    static int send_callerid(struct zt_pvt *p);
    
    int send_cwcidspill(struct zt_pvt *p)
    {
    	p->callwaitcas = 0;
    	p->cidspill = malloc(MAX_CALLERID_SIZE);
    	if (p->cidspill) {
    		memset(p->cidspill, 0x7f, MAX_CALLERID_SIZE);
    		p->cidlen = ast_callerid_callwaiting_generate(p->cidspill, p->callwaitcid);
    		/* Make sure we account for the end */
    		p->cidlen += READ_SIZE * 4;
    		p->cidpos = 0;
    		send_callerid(p);
    		if (option_verbose > 2)
    			ast_verbose(VERBOSE_PREFIX_3 "CPE supports Call Waiting Caller*ID.  Sending '%s'\n", p->callwaitcid);
    	} else return -1;
    	return 0;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int has_voicemail(struct zt_pvt *p)
    {
    	DIR *dir;
    	struct dirent *de;
    	char fn[256];
    
    	/* If no mailbox, return immediately */
    	if (!strlen(p->mailbox))
    		return 0;
    	snprintf(fn, sizeof(fn), "%s/vm/%s/INBOX", AST_SPOOL_DIR, p->mailbox);
    	dir = opendir(fn);
    	if (!dir)
    		return 0;
    	while ((de = readdir(dir))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!strncasecmp(de->d_name, "msg", 3))
    
    Mark Spencer's avatar
    Mark Spencer committed
    			break;
    	}
    	closedir(dir);
    	if (de)
    		return 1;
    	return 0;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int send_callerid(struct zt_pvt *p)
    {
    	/* Assumes spill in p->cidspill, p->cidlen in length and we're p->cidpos into it */
    	int res;
    	while(p->cidpos < p->cidlen) {
    		res = write(zap_fd(p->z), p->cidspill + p->cidpos, p->cidlen - p->cidpos);
    		if (res < 0) {
    			if (errno == EAGAIN)
    				return 0;
    			else {
    				ast_log(LOG_WARNING, "write failed: %s\n", strerror(errno));
    				return -1;
    			}
    		}
    		if (!res)
    			return 0;
    		p->cidpos += res;
    	}
    	free(p->cidspill);
    	p->cidspill = 0;
    	if (p->callwaitcas) {
    		zap_clrdtmfn(p->z);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Check for a the ack on the CAS (up to 500 ms) */
    		res = zap_getdtmf(p->z, 1, NULL, 0, 500, 500, ZAP_HOOKEXIT | ZAP_TIMEOUTOK);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (res > 0) {
    			char tmp[2];
    
    Mark Spencer's avatar
    Mark Spencer committed
    			strncpy(tmp, zap_dtmfbuf(p->z), sizeof(tmp)-1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			zap_clrdtmfn(p->z);
    			if ((tmp[0] == 'A') || (tmp[0] == 'D')) {
    				send_cwcidspill(p);
    			}
    		} else {
    			if (option_verbose > 2)
    				ast_verbose(VERBOSE_PREFIX_3 "CPE does not support Call Waiting Caller*ID.\n");
    			restore_conference(p);
    		}
    	} else
    		restore_conference(p);
    	return 0;
    }
    
    static int zt_callwait(struct ast_channel *ast)
    {
    	struct zt_pvt *p = ast->pvt->pvt;
    	p->callwaitingrepeat = CALLWAITING_REPEAT_SAMPLES;
    	if (p->cidspill) {
    		ast_log(LOG_WARNING, "Spill already exists?!?\n");
    		free(p->cidspill);
    	}
    	p->cidspill = malloc(2400 /* SAS */ + 680 /* CAS */ + READ_SIZE * 4);
    	if (p->cidspill) {
    		save_conference(p);
    		/* Silence */
    		memset(p->cidspill, 0x7f, 2400 + 600 + READ_SIZE * 4);
    		if (!p->callwaitrings && p->callwaitingcallerid) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_gen_cas(p->cidspill, 1, 2400 + 680);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			p->callwaitcas = 1;
    			p->cidlen = 2400 + 680 + READ_SIZE * 4;
    		} else {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_gen_cas(p->cidspill, 1, 2400);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			p->callwaitcas = 0;
    			p->cidlen = 2400 + READ_SIZE * 4;
    		}
    		p->cidpos = 0;
    		send_callerid(p);
    	} else {
    		ast_log(LOG_WARNING, "Unable to create SAS/CAS spill\n");
    		return -1;
    	}
    	return 0;
    }
    
    static int zt_call(struct ast_channel *ast, char *dest, int timeout)
    {
    	struct zt_pvt *p = ast->pvt->pvt;
    	int x, res, index;
    	char *c, *n, *l;
    	char callerid[256];
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "zt_call called on %s, neither down nor reserved\n", ast->name);
    		return -1;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	p->dialednone = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	switch(p->sig) {
    	case SIG_FXOLS:
    	case SIG_FXOGS:
    	case SIG_FXOKS:
    		if (p->owner == ast) {
    			/* Normal ring, on hook */
    
    Mark Spencer's avatar
    Mark Spencer committed
    			
    			/* Don't send audio while on hook, until the call is answered */
    			p->dialing = 1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (p->use_callerid) {
    				/* Generate the Caller-ID spill if desired */
    				if (p->cidspill) {
    					ast_log(LOG_WARNING, "cidspill already exists??\n");
    					free(p->cidspill);
    				}
    				p->cidspill = malloc(MAX_CALLERID_SIZE);
    				p->callwaitcas = 0;
    				if (p->cidspill) {
    					p->cidlen = ast_callerid_generate(p->cidspill, ast->callerid);
    					p->cidpos = 0;
    					send_callerid(p);
    				} else
    					ast_log(LOG_WARNING, "Unable to generate CallerID spill\n");
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* Select proper cadence */
    			if ((p->distinctivering > 0) && (p->distinctivering <= NUM_CADENCE)) {
    				if (ioctl(zap_fd(p->z), ZT_SETCADENCE, &cadences[p->distinctivering-1]))
    					ast_log(LOG_WARNING, "Unable to set distinctive ring cadence %d on '%s'\n", p->distinctivering, ast->name);
    				p->cidrings = cidrings[p->distinctivering - 1];
    			} else {
    				if (ioctl(zap_fd(p->z), ZT_SETCADENCE, NULL))
    					ast_log(LOG_WARNING, "Unable to reset default ring on '%s'\n", ast->name);
    				p->cidrings = 1;
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			x = ZT_RING;
    			if (ioctl(zap_fd(p->z), ZT_HOOK, &x) && (errno != EINPROGRESS)) {
    				ast_log(LOG_WARNING, "Unable to ring phone: %s\n", strerror(errno));
    				return -1;
    			}
    			p->dialing = 1;
    		} else {
    			/* Call waiting call */
    			p->callwaitrings = 0;
    			if (ast->callerid)
    
    Mark Spencer's avatar
    Mark Spencer committed
    				strncpy(p->callwaitcid, ast->callerid, sizeof(p->callwaitcid)-1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			else
    				strcpy(p->callwaitcid, "");
    			/* Call waiting tone instead */
    			if (zt_callwait(ast))
    				return -1;
    				
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_setstate(ast, AST_STATE_RINGING);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		index = zt_get_index(ast, p, 0);
    		if (index > -1) {
    			p->needringing[index] = 1;
    		}
    		break;
    	case SIG_FXSLS:
    	case SIG_FXSGS:
    	case SIG_FXSKS:
    	case SIG_EMWINK:
    	case SIG_EM:
    	case SIG_FEATD:
    
    Mark Spencer's avatar
    Mark Spencer committed
    	case SIG_FEATDMF:
    	case SIG_FEATB:
    
    Mark Spencer's avatar
    Mark Spencer committed
    		c = strchr(dest, '/');
    		if (c)
    			c++;
    		else
    			c = dest;
    		if (strlen(c) < p->stripmsd) {
    			ast_log(LOG_WARNING, "Number '%s' is shorter than stripmsd (%d)\n", c, p->stripmsd);
    			return -1;
    		}
    		x = ZT_START;
    		/* Start the trunk */
    		res = ioctl(zap_fd(p->z), ZT_HOOK, &x);
    		if (res < 0) {
    			if (errno != EINPROGRESS) {
    				ast_log(LOG_WARNING, "Unable to start channel: %s\n", strerror(errno));
    				return -1;
    			}
    		}
    		ast_log(LOG_DEBUG, "Dialing '%s'\n", c);