aboutsummaryrefslogtreecommitdiffstats
path: root/channels
diff options
context:
space:
mode:
authormarkster <markster@f38db490-d61c-443f-a65b-d21fe96a405b>2001-05-07 03:31:07 +0000
committermarkster <markster@f38db490-d61c-443f-a65b-d21fe96a405b>2001-05-07 03:31:07 +0000
commitd967546fb3c11b521bdef33e4c540c3c72f13bbf (patch)
tree2cabc36a1437b85070a91a00fd01f2324dd700de /channels
parentb92c0a231fa237381b2e9d2a67e303d621c74d1b (diff)
Version 0.1.8 from FTP
git-svn-id: http://svn.digium.com/svn/asterisk/trunk@317 f38db490-d61c-443f-a65b-d21fe96a405b
Diffstat (limited to 'channels')
-rwxr-xr-xchannels/chan_tor.c2223
1 files changed, 1975 insertions, 248 deletions
diff --git a/channels/chan_tor.c b/channels/chan_tor.c
index 45ed420a5..65a779177 100755
--- a/channels/chan_tor.c
+++ b/channels/chan_tor.c
@@ -24,6 +24,7 @@
#include <asterisk/file.h>
#include <asterisk/ulaw.h>
#include <asterisk/callerid.h>
+#include <asterisk/cli.h>
#include <sys/signal.h>
#include <sys/select.h>
#include <errno.h>
@@ -34,10 +35,24 @@
#include <zap.h>
#include <math.h>
#include <tonezone.h>
+#ifdef TORMENTA_PRI
+#include <libpri.h>
+#endif
+
+/*
+ XXX
+ XXX We definitely need to lock the private structure in tor_read and such
+ XXX
+ */
+#ifdef TORMENTA_PRI
+static char *desc = "Tormenta (Zapata) Channelized T1/PRI Driver";
+static char *tdesc = "Tormenta T1//PRI Driver";
+#else
static char *desc = "Tormenta (Zapata) Channelized T1 Driver";
-static char *type = "Tor";
static char *tdesc = "Tormenta T1 Driver";
+#endif
+static char *type = "Tor";
static char *config = "tormenta.conf";
#define SIG_EM 0x1
@@ -49,8 +64,9 @@ static char *config = "tormenta.conf";
#define SIG_FXOLS 0x5
#define SIG_FXOGS 0x6
#define SIG_FXOKS 0x7
+#define SIG_PRI 0x8
-#define tor_STATE_DOWN 0
+#define NUM_SPANS 2
static char context[AST_MAX_EXTENSION] = "default";
static char callerid[256] = "";
@@ -73,6 +89,22 @@ 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;
+
+static float rxgain = 0.0;
+
+static float txgain = 0.0;
+
+static int echocancel;
+
/* Wait up to 16 seconds for first digit (FXO logic) */
static int firstdigittimeout = 16000;
@@ -116,31 +148,184 @@ static inline int tor_wait_event(int fd)
/* 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 tor_pvt;
+
+
+#ifdef TORMENTA_PRI
+struct tor_pri {
+ pthread_t master; /* Thread of master */
+ pthread_mutex_t lock; /* Mutex */
+ int nodetype; /* Node type */
+ int switchtype; /* Type of switch to emulate */
+ struct pri *pri;
+ int debug;
+ int fd;
+ int up;
+ int offset;
+ int span;
+ int chanmask[24]; /* Channel status */
+ struct tor_pvt *pvt[24]; /* Member channel pvt structs */
+ struct tor_channel *chan[24]; /* Channels on each line */
+};
+
+static struct tor_pri pris[NUM_SPANS];
+
+static int pritype = PRI_CPE;
+
+#if 0
+#define DEFAULT_PRI_DEBUG (PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q931_STATE)
+#else
+#define DEFAULT_PRI_DEBUG 0
+/*
+#define DEFAULT_PRI_DEBUG (PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q921_DUMP | PRI_DEBUG_Q921_RAW)
+*/
+#endif
+
+static inline int pri_grab(struct tor_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 tor_pri *pri)
+{
+ ast_pthread_mutex_unlock(&pri->lock);
+}
+
+static int switchtype = PRI_SWITCH_NI2;
+
+#endif
+
static struct tor_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 tor_pvt *next; /* Next channel in list */
char context[AST_MAX_EXTENSION];
+ char exten[AST_MAX_EXTENSION];
char language[MAX_LANGUAGE];
char callerid[AST_MAX_EXTENSION];
+ char callwaitcid[AST_MAX_EXTENSION];
char dtmfq[AST_MAX_EXTENSION];
struct ast_frame f;
short buffer[AST_FRIENDLY_OFFSET/2 + READ_SIZE];
int group;
- int state; /* Perhaps useful state info */
int immediate; /* Answer before getting digits? */
int channel; /* Channel Number */
- int ringgothangup; /* Have we received exactly one hangup after a ring */
+ int span; /* Span number */
int dialing;
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 stripmsd;
+ int needringing[3];
+ int needanswer[3];
+ int callwaiting;
+ int callwaitcas;
+ int callwaitrings;
+ int echocancel;
+ int permcallwaiting;
+ int callwaitingcallerid;
+ int threewaycalling;
+ int transfer;
+ int cref; /* Call reference number */
DIAL_OPERATION dop;
+ struct tor_confinfo conf; /* Saved state of conference */
+ struct tor_confinfo conf2; /* Saved state of alternate conference */
+ int confno; /* Conference number */
+ ZAP *pseudo; /* Pseudo channel FD */
+ int pseudochan; /* Pseudo channel */
+#ifdef TORMENTA_PRI
+ struct tor_pri *pri;
+ q931_call *call;
+#endif
} *iflist = NULL;
+#define FIRST_PSEUDO 49
+
+#define INTHREEWAY(p) ((p->normalindex > -1) && (p->thirdcallindex > -1) && \
+ (p->owner == p->owners[p->normalindex]))
+
+static int alloc_pseudo(struct tor_pvt *p)
+{
+ int x;
+ ZAP *z;
+ int res;
+ BUFFER_INFO bi;
+ char fn[256];
+ 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;
+ }
+ for (x=FIRST_PSEUDO;;x++) {
+ snprintf(fn, sizeof(fn), "/dev/tor/%d", x);
+ z = zap_open(fn, 1);
+ if (!z) {
+ if (errno != EBUSY) {
+ ast_log(LOG_WARNING, "Unable to open %s: %s\n", fn, strerror(errno));
+ return -1;
+ }
+ } else {
+ res = ioctl(zap_fd(z), TOR_GET_BUFINFO, &bi);
+ if (!res) {
+ bi.txbufpolicy = POLICY_IMMEDIATE;
+ bi.rxbufpolicy = POLICY_IMMEDIATE;
+ bi.numbufs = 4;
+ res = ioctl(zap_fd(z), TOR_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;
+ 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 tor_pvt *p)
+{
+ if (p->pseudo)
+ zap_close(p->pseudo);
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Released pseudo channel %d\n", p->pseudochan);
+ p->pseudo = NULL;
+ p->pseudochan = 0;
+ return 0;
+}
+
static int tor_digit(struct ast_channel *ast, char digit)
{
DIAL_OPERATION zo;
@@ -206,12 +391,147 @@ static char *sig2str(int sig)
return "FXO Groundstart";
case SIG_FXOKS:
return "FXO Kewlstart";
+ case SIG_PRI:
+ return "PRI Signalling";
default:
snprintf(buf, sizeof(buf), "Unknown signalling %d\n", sig);
return buf;
}
}
+static int conf_set(struct tor_pvt *p, int req, int force)
+{
+ /* Set channel to given conference, -1 to allocate one */
+ TOR_CONFINFO ci;
+ TOR_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), TOR_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)) {
+ ast_log(LOG_WARNING, "Channel %d is already in a conference (%d, %d) we didn't create (req = %d)\n", p->channel, ci.confno, ci.confmode, req);
+ return -1;
+ }
+ ci.chan = 0;
+ ci.confno = req;
+ ci.confmode = TOR_CONF_REALANDPSEUDO | TOR_CONF_TALKER | TOR_CONF_LISTENER | TOR_CONF_PSEUDO_LISTENER | TOR_CONF_PSEUDO_TALKER;
+ res = ioctl(zap_fd(p->z), TOR_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 = TOR_CONF_CONF | TOR_CONF_TALKER | TOR_CONF_LISTENER;
+
+ res = ioctl(zap_fd(p->pseudo), TOR_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;
+ cip.confno = ci.confno;
+ cip.confmode = TOR_CONF_NORMAL;
+ res = ioctl(zap_fd(p->pseudo), TOR_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;
+ }
+ }
+ }
+ p->confno = ci.confno;
+ return 0;
+}
+
+static int three_way(struct tor_pvt *p)
+{
+ ast_log(LOG_DEBUG, "Setting up three way call\n");
+ return conf_set(p, p->confno, 0);
+}
+
+static int conf_clear(struct tor_pvt *p)
+{
+ TOR_CONFINFO ci;
+ int res;
+ ci.confmode = TOR_CONF_NORMAL;
+ ci.chan = 0;
+ ci.confno = 0;
+ res = ioctl(zap_fd(p->z), TOR_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 tor_enable_ec(struct tor_pvt *p)
+{
+ int x;
+ int res;
+ if (p->echocancel) {
+ x = 1;
+ res = ioctl(zap_fd(p->z), TOR_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);
+ }
+}
+
+static void tor_disable_ec(struct tor_pvt *p)
+{
+ int x;
+ int res;
+ if (p->echocancel) {
+ x = 0;
+ res = ioctl(zap_fd(p->z), TOR_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 tor_get_index(struct ast_channel *ast, struct tor_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;
+}
+
static int set_actual_gain(int fd, int chan, float rxgain, float txgain)
{
struct tor_gains g;
@@ -237,6 +557,7 @@ static int set_actual_gain(int fd, int chan, float rxgain, float txgain)
/* set 'em */
return(ioctl(fd,TOR_SETGAINS,&g));
}
+
static inline int tor_set_hook(int fd, int hs)
{
int x, res;
@@ -247,6 +568,106 @@ static inline int tor_set_hook(int fd, int hs)
return res;
}
+static int save_conference(struct tor_pvt *p)
+{
+ struct tor_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), TOR_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 = TOR_CONF_NORMAL;
+ res = ioctl(zap_fd(p->z), TOR_SETCONF, &c);
+ if (res) {
+ ast_log(LOG_WARNING, "Unable to set conference info: %s\n", strerror(errno));
+ return -1;
+ }
+ switch(p->conf.confmode) {
+ case TOR_CONF_NORMAL:
+ p->conf2.confmode = 0;
+ break;
+ case TOR_CONF_MONITOR:
+ /* Get the other size */
+ p->conf2.chan = p->conf.confno;
+ res = ioctl(zap_fd(p->z), TOR_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 = TOR_CONF_NORMAL;
+ res = ioctl(zap_fd(p->z), TOR_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 TOR_CONF_CONF | TOR_CONF_LISTENER | TOR_CONF_TALKER:
+ p->conf2.confmode = 0;
+ break;
+ default:
+ ast_log(LOG_WARNING, "Don't know how to save conference state for conf mode %d\n", p->conf.confmode);
+ return -1;
+ }
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Disabled conferencing\n");
+ return 0;
+}
+
+static int restore_conference(struct tor_pvt *p)
+{
+ int res;
+ if (p->conf.confmode) {
+ res = ioctl(zap_fd(p->z), TOR_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), TOR_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 tor_pvt *p);
+
+int send_cwcidspill(struct tor_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;
+}
static int send_callerid(struct tor_pvt *p)
{
@@ -268,13 +689,62 @@ static int send_callerid(struct tor_pvt *p)
}
free(p->cidspill);
p->cidspill = 0;
+ if (p->callwaitcas) {
+ zap_clrdtmfn(p->z);
+ /* Check for a the ack on the CAS */
+ res = zap_getdtmf(p->z, 1, NULL, 0, 250, 250, ZAP_HOOKEXIT | ZAP_TIMEOUTOK);
+ if (res > 0) {
+ char tmp[2];
+ strncpy(tmp, zap_dtmfbuf(p->z), sizeof(tmp));
+ 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 tor_callwait(struct ast_channel *ast)
+{
+ struct tor_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) {
+ ast_callerid_gen_cas(p->cidspill, 2400 + 680);
+ p->callwaitcas = 1;
+ p->cidlen = 2400 + 680 + READ_SIZE * 4;
+ } else {
+ ast_callerid_gen_cas(p->cidspill, 2400);
+ 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 tor_call(struct ast_channel *ast, char *dest, int timeout)
{
struct tor_pvt *p = ast->pvt->pvt;
- int x, res;
+ int x, res, index;
char *c, *n, *l;
char callerid[256];
if ((ast->state != AST_STATE_DOWN) && (ast->state != AST_STATE_RESERVED)) {
@@ -285,26 +755,46 @@ static int tor_call(struct ast_channel *ast, char *dest, int timeout)
case SIG_FXOLS:
case SIG_FXOGS:
case SIG_FXOKS:
- 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);
+ if (p->owner == ast) {
+ /* Normal ring, on hook */
+ 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");
}
- p->cidspill = malloc(MAX_CALLERID_SIZE);
- 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");
- }
- x = TOR_RING;
- if (ioctl(zap_fd(p->z), TOR_HOOK, &x) && (errno != EINPROGRESS)) {
- ast_log(LOG_WARNING, "Unable to ring phone: %s\n", strerror(errno));
- return -1;
+ x = TOR_RING;
+ if (ioctl(zap_fd(p->z), TOR_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)
+ strncpy(p->callwaitcid, ast->callerid, sizeof(p->callwaitcid));
+ else
+ strcpy(p->callwaitcid, "");
+ /* Call waiting tone instead */
+ if (tor_callwait(ast))
+ return -1;
+
}
ast->state = AST_STATE_RINGING;
+ index = tor_get_index(ast, p, 0);
+ if (index > -1) {
+ p->needringing[index] = 1;
+ }
break;
case SIG_FXSLS:
case SIG_FXSGS:
@@ -336,7 +826,6 @@ static int tor_call(struct ast_channel *ast, char *dest, int timeout)
if (ast->callerid) {
strncpy(callerid, ast->callerid, sizeof(callerid));
ast_callerid_parse(callerid, &n, &l);
- printf("Name: %s, number: %s\n", n, l);
if (l) {
ast_shrink_phone_number(l);
if (!ast_isphonenumber(l))
@@ -362,6 +851,36 @@ static int tor_call(struct ast_channel *ast, char *dest, int timeout)
p->dialing = 1;
ast->state = AST_STATE_DIALING;
break;
+#ifdef TORMENTA_PRI
+ case SIG_PRI:
+ c = strchr(dest, '/');
+ if (c)
+ c++;
+ else
+ c = dest;
+ if (ast->callerid) {
+ strncpy(callerid, ast->callerid, sizeof(callerid));
+ ast_callerid_parse(callerid, &n, &l);
+ if (l) {
+ ast_shrink_phone_number(l);
+ if (!ast_isphonenumber(l))
+ l = NULL;
+ }
+ } else
+ l = NULL;
+ if (strlen(c) < p->stripmsd) {
+ ast_log(LOG_WARNING, "Number '%s' is shorter than stripmsd (%d)\n", c, p->stripmsd);
+ return -1;
+ }
+ if (pri_call(p->pri->pri, p->call, PRI_TRANS_CAP_SPEECH,
+ ((p->channel - 1) % 24) + 1, p->pri->nodetype == PRI_NETWORK ? 0 : 1, 1, l, PRI_NATIONAL_ISDN,
+ l ? PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN : PRES_NUMBER_NOT_AVAILABLE,
+ c + p->stripmsd, PRI_NATIONAL_ISDN)) {
+ ast_log(LOG_WARNING, "Unable to setup call to %s\n", c + p->stripmsd);
+ return -1;
+ }
+ break;
+#endif
default:
ast_log(LOG_DEBUG, "not yet implemented\n");
return -1;
@@ -372,6 +891,7 @@ static int tor_call(struct ast_channel *ast, char *dest, int timeout)
static int tor_hangup(struct ast_channel *ast)
{
int res;
+ int index;
struct tor_pvt *p = ast->pvt->pvt;
TOR_PARAMS par;
if (option_debug)
@@ -380,47 +900,126 @@ static int tor_hangup(struct ast_channel *ast)
ast_log(LOG_WARNING, "Asked to hangup channel not connected\n");
return 0;
}
- res = tor_set_hook(zap_fd(p->z), TOR_ONHOOK);
- if (res < 0) {
- ast_log(LOG_WARNING, "Unable to hangup line %s\n", ast->name);
- return -1;
+ index = tor_get_index(ast, p, 1);
+
+ zap_digitmode(p->z,0);
+ ast->state = AST_STATE_DOWN;
+ ast_log(LOG_DEBUG, "Hangup: index = %d, normal = %d, callwait = %d, thirdcall = %d\n",
+ index, p->normalindex, p->callwaitindex, p->thirdcallindex);
+
+ if (index > -1) {
+ /* Real channel, do some fixup */
+ p->owners[index] = NULL;
+ p->needanswer[index] = 0;
+ p->needringing[index] = 0;
+ if (index == p->normalindex) {
+ p->normalindex = -1;
+ if ((p->callwaitindex > -1) && (p->thirdcallindex > -1))
+ ast_log(LOG_WARNING, "Normal call hung up with both three way call and a call waiting call in place?\n");
+ if (p->callwaitindex > -1) {
+ /* If we hung up the normal call, make the call wait call
+ be the normal call if there was one */
+ p->normalindex = p->callwaitindex;
+ p->callwaitindex = -1;
+ } else if (p->thirdcallindex > -1) {
+ /* This was part of a three way call */
+ p->normalindex = p->thirdcallindex;
+ p->owners[p->normalindex]->fds[0] = zap_fd(p->z);
+ p->thirdcallindex = -1;
+ unalloc_pseudo(p);
+ }
+ } else if (index == p->callwaitindex) {
+ /* If this was a call waiting call, mark the call wait
+ index as -1, so we know it's available again */
+ p->callwaitindex = -1;
+ } else if (index == p->thirdcallindex) {
+ /* If this was part of a three way call index, let us make
+ another three way call */
+ p->thirdcallindex = -1;
+ unalloc_pseudo(p);
+ } else {
+ /* This wasn't any sort of call, but how are we an index? */
+ ast_log(LOG_WARNING, "Index found but not any type of call?\n");
+ }
}
- switch(p->sig) {
- case SIG_FXOGS:
- case SIG_FXOLS:
- case SIG_FXOKS:
- res = ioctl(zap_fd(p->z), TOR_GET_PARAMS, &par);
- if (!res) {
- /* If they're off hook, try playing congestion */
- if (par.rxisoffhook)
- tone_zone_play_tone(zap_fd(p->z), TOR_TONE_CONGESTION);
+
+ if (!p->owners[0] && !p->owners[1] && !p->owners[2]) {
+ p->owner = NULL;
+ /* Perform low level hangup if no owner left */
+#ifdef TORMENTA_PRI
+ if (p->sig == SIG_PRI) {
+ if (p->call) {
+ if (!pri_grab(p->pri)) {
+ res = pri_disconnect(p->pri->pri, p->call, PRI_CAUSE_NORMAL_CLEARING);
+ p->call = NULL;
+ if (res < 0)
+ ast_log(LOG_WARNING, "pri_disconnect failed\n");
+ pri_rel(p->pri);
+ } else {
+ ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->span);
+ res = -1;
+ }
+ } else
+ res = 0;
+ } else
+#endif
+ res = tor_set_hook(zap_fd(p->z), TOR_ONHOOK);
+ if (res < 0) {
+ ast_log(LOG_WARNING, "Unable to hangup line %s\n", ast->name);
+ return -1;
}
- break;
- default:
+ switch(p->sig) {
+ case SIG_FXOGS:
+ case SIG_FXOLS:
+ case SIG_FXOKS:
+ res = ioctl(zap_fd(p->z), TOR_GET_PARAMS, &par);
+ if (!res) {
+#if 0
+ ast_log(LOG_DEBUG, "Hanging up channel %d, offhook = %d\n", p->channel, par.rxisoffhook);
+#endif
+ /* If they're off hook, try playing congestion */
+ if (par.rxisoffhook)
+ tone_zone_play_tone(zap_fd(p->z), TOR_TONE_CONGESTION);
+ else
+ tone_zone_play_tone(zap_fd(p->z), -1);
+ }
+ break;
+ default:
+ }
+ if (index > -1) {
+ p->needringing[index] = 0;
+ p->needanswer[index] = 0;
+ }
+ if (p->cidspill)
+ free(p->cidspill);
+ tor_disable_ec(p);
+ p->cidspill = NULL;
+ p->callwaitcas = 0;
+ p->callwaiting = p->permcallwaiting;
+ p->hidecallerid = p->permhidecallerid;
+ p->dialing = 0;
+ conf_clear(p);
+ unalloc_pseudo(p);
+ restart_monitor();
}
+ p->callwaitingrepeat = 0;
+ ast->pvt->pvt = NULL;
ast->state = AST_STATE_DOWN;
- p->owner = NULL;
- p->ringgothangup = 0;
- if (p->cidspill)
- free(p->cidspill);
- p->cidspill = NULL;
- pthread_mutex_lock(&usecnt_lock);
+ ast_pthread_mutex_lock(&usecnt_lock);
usecnt--;
if (usecnt < 0)
ast_log(LOG_WARNING, "Usecnt < 0???\n");
- pthread_mutex_unlock(&usecnt_lock);
+ ast_pthread_mutex_unlock(&usecnt_lock);
ast_update_use_count();
if (option_verbose > 2)
ast_verbose( VERBOSE_PREFIX_3 "Hungup '%s'\n", ast->name);
- ast->pvt->pvt = NULL;
- ast->state = AST_STATE_DOWN;
- restart_monitor();
return 0;
}
static int tor_answer(struct ast_channel *ast)
{
struct tor_pvt *p = ast->pvt->pvt;
+ int res=0;
ast->state = AST_STATE_UP;
switch(p->sig) {
case SIG_FXSLS:
@@ -434,41 +1033,62 @@ static int tor_answer(struct ast_channel *ast)
case SIG_FXOKS:
/* Pick up the line */
ast_log(LOG_DEBUG, "Took %s off hook\n", ast->name);
- return tor_set_hook(zap_fd(p->z), TOR_OFFHOOK);
+ res = tor_set_hook(zap_fd(p->z), TOR_OFFHOOK);
+ tone_zone_play_tone(zap_fd(p->z), -1);
+ if (INTHREEWAY(p))
+ tone_zone_play_tone(zap_fd(p->pseudo), -1);
+ p->dialing = 0;
break;
- /* Nothing */
+#ifdef TORMENTA_PRI
+ case SIG_PRI:
+ /* Send a pri acknowledge */
+ if (!pri_grab(p->pri)) {
+ res = pri_answer(p->pri->pri, p->call, 0, 1);
+ pri_rel(p->pri);
+ } else {
+ ast_log(LOG_WARNING, "Unable to grab PRI on span %d\n", p->span);
+ res= -1;
+ }
break;
+#endif
default:
ast_log(LOG_WARNING, "Don't know how to answer signalling %d (channel %d)\n", p->sig, p->channel);
return -1;
}
- return 0;
+ return res;
}
-static int bridge_cleanup(struct tor_pvt *p0, struct tor_pvt *p1)
+static inline int bridge_cleanup(struct tor_pvt *p0, struct tor_pvt *p1)
{
- struct tor_confinfo c;
int res;
- c.chan = 0;
- c.confno = 0;
- c.confmode = TOR_CONF_NORMAL;
- res = ioctl(zap_fd(p0->z), TOR_SETCONF, &c);
- if (res) {
- ast_log(LOG_WARNING, "ioctl(TOR_SETCONF) failed on channel %d: %s\n", p0->channel, strerror(errno));
+ res = conf_clear(p0);
+ res |= conf_clear(p1);
+ return res;
+}
+
+static int tor_setoption(struct ast_channel *chan, int option, void *data, int datalen)
+{
+char *cp;
+
+ struct tor_pvt *p = chan->pvt->pvt;
+
+ ast_log(LOG_DEBUG, "Set option %d, data %p, len %d\n", option, data, datalen);
+ if (option != AST_OPTION_TONE_VERIFY)
+ {
+ errno = ENOSYS;
return -1;
- }
- c.chan = 0;
- c.confno = 0;
- c.confmode = TOR_CONF_NORMAL;
- res = ioctl(zap_fd(p1->z), TOR_SETCONF, &c);
- if (res) {
- ast_log(LOG_WARNING, "ioctl(TOR_SETCONF) failed on channel %d: %s\n", p1->channel, strerror(errno));
+ }
+ cp = (char *)data;
+ if ((!cp) || (datalen < 1))
+ {
+ errno = EINVAL;
return -1;
- }
+ }
+ zap_digitmode(p->z,((*cp) ? ZAP_MUTECONF : 0)); /* set mute mode if desired */
+ errno = 0;
return 0;
}
-
static int tor_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc)
{
/* Do a quickie conference between the two channels and wait for something to happen */
@@ -476,40 +1096,96 @@ static int tor_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags,
struct tor_pvt *p1 = c1->pvt->pvt;
struct ast_channel *who, *cs[3];
struct ast_frame *f;
- struct tor_confinfo c;
- int res;
int to = -1;
- /* Put the first channel in a unique conference */
- c.chan = 0;
- c.confno = p1->channel;
- c.confmode = TOR_CONF_MONITOR;
- /* Stop any playing */
+ int confno = -1;
+
+ /* Stop any playing tones */
tone_zone_play_tone(zap_fd(p0->z), -1);
- res = ioctl(zap_fd(p0->z), TOR_SETCONF, &c);
- if (res) {
- ast_log(LOG_WARNING, "ioctl(TOR_SETCONF) failed on channel %s: %s\n", c0->name, strerror(errno));
- bridge_cleanup(p0, p1);
- return -1;
- }
- if (option_debug)
- ast_log(LOG_DEBUG, "Channel %d got put on conference %d\n", c.chan, c.confno);
- /* Put the other channel on the same conference */
- c.chan = 0;
- c.confno = p0->channel;
- res = ioctl(zap_fd(p1->z), TOR_SETCONF, &c);
- if (res) {
- ast_log(LOG_WARNING, "ioctl(TOR_SETCONF) failed on channel %s: %s\n", c0->name, strerror(errno));
- bridge_cleanup(p0, p1);
- return -1;
- }
- if (option_debug)
- ast_log(LOG_DEBUG, "Channel %d got put on conference %d\n", c.chan, c.confno);
+ tone_zone_play_tone(zap_fd(p1->z), -1);
+ cs[0] = c0;
+ cs[1] = c1;
for (;;) {
- cs[0] = c0;
- cs[1] = c1;
+ pthread_mutex_lock(&c0->lock);
+ pthread_mutex_lock(&c1->lock);
+ p0 = c0->pvt->pvt;
+ p1 = c1->pvt->pvt;
+
+ if (!p0 || !p1) {
+ pthread_mutex_unlock(&c0->lock);
+ pthread_mutex_unlock(&c1->lock);
+ return -1;
+ }
+
+ if (INTHREEWAY(p0) && (c0 == p0->owners[p0->thirdcallindex]))
+ tone_zone_play_tone(zap_fd(p0->pseudo), -1);
+ if (INTHREEWAY(p1) && (c1 == p1->owners[p1->thirdcallindex]))
+ tone_zone_play_tone(zap_fd(p1->pseudo), -1);
+ if (INTHREEWAY(p0) && (INTHREEWAY(p1))) {
+ ast_log(LOG_WARNING, "Too weird, can't bridge multiple three way calls\n");
+ pthread_mutex_unlock(&c0->lock);
+ pthread_mutex_unlock(&c1->lock);
+ return -1;
+ }
+ if ((p0->owner == c0) && (p1->owner == c1)) {
+ /* Okay, this call should actually be connected */
+ if ((p0->confno > -1) && (p1->confno > -1) && (p0->confno != p1->confno)) {
+ /* We have a conflict here. Try to resolve it. */
+ if ((INTHREEWAY(p0) && (c0 == p0->owners[p0->normalindex]))) {
+ ast_log(LOG_DEBUG, "Channel %s is in a three way call with us, moving to our conference %d\n",
+ c1->name, p0->confno);
+ conf_set(p1, p0->confno, 1);
+ } else if (INTHREEWAY(p1) && (c1 == p1->owners[p1->normalindex])) {
+ ast_log(LOG_DEBUG, "Channel %s is in a three way call with us, moving to our conference %d\n",
+ c0->name, p1->confno);
+ conf_set(p0, p1->confno, 1);
+ } else {
+ ast_log(LOG_WARNING, "Can't bridge since %s is on conf %d and %s is on conf %d\n",
+ c0->name, p0->confno, c1->name, p1->confno);
+ pthread_mutex_unlock(&c0->lock);
+ pthread_mutex_unlock(&c1->lock);
+ return -1;
+ }
+ }
+ if (p0->confno > -1)
+ confno = p0->confno;
+ else
+ confno = p1->confno;
+ if (confno < 0) {
+ conf_set(p0, -1, 0);
+ confno = p0->confno;
+ ast_log(LOG_DEBUG, "Creating new conference %d for %s\n", confno, c0->name);
+ }
+ if (p0->confno != confno) {
+ ast_log(LOG_DEBUG, "Placing %s in conference %d\n", c0->name, confno);
+ conf_set(p0, confno, 0);
+ }
+ if (p1->confno != confno) {
+ ast_log(LOG_DEBUG, "Placing %s in conference %d\n", c1->name, confno);
+ conf_set(p1, confno, 0);
+ }
+ } else if (INTHREEWAY(p0) && (c0 == p0->owners[p0->thirdcallindex])) {
+ /* p0 is in a three way call and we're the third leg. Join their
+ conference, already in progress if there is one */
+ if ((p0->confno > -1) && (p1->confno != p0->confno)) {
+ confno = p0->confno;
+ ast_log(LOG_DEBUG, "Placing %s in conference %d\n", c1->name, confno);
+ conf_set(p1, confno, 0);
+ }
+ } else if (INTHREEWAY(p1) && (c1 == p1->owners[p1->thirdcallindex])) {
+ /* p0 is in a three way call and we're the third leg. Join their
+ conference, already in progress if there is one */
+ if ((p1->confno > -1) && (p1->confno != p0->confno)) {
+ confno = p0->confno;
+ ast_log(LOG_DEBUG, "Placing %s in conference %d\n", c0->name, confno);
+ conf_set(p0, confno, 0);
+ }
+ }
+ pthread_mutex_unlock(&c0->lock);
+ pthread_mutex_unlock(&c1->lock);
+
who = ast_waitfor_n(cs, 2, &to);
if (!who) {
ast_log(LOG_WARNING, "Nobody there??\n");
@@ -555,14 +1231,105 @@ static int tor_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags,
cs[0] = cs[1];
cs[1] = cs[2];
}
-
+ return 0;
+}
+
+static int tor_indicate(struct ast_channel *chan, int condition);
+
+static int tor_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
+{
+ struct tor_pvt *p = newchan->pvt->pvt;
+ int x;
+ ast_log(LOG_DEBUG, "New owner for channel %d is %s\n", p->channel, newchan->name);
+ p->owner = newchan;
+ for (x=0;x<3;x++)
+ if (p->owners[x] == oldchan)
+ p->owners[x] = newchan;
+ if (newchan->state == AST_STATE_RINGING)
+ tor_indicate(newchan, AST_CONTROL_RINGING);
+ return 0;
+}
+
+static int tor_ring_phone(struct tor_pvt *p)
+{
+ int x;
+ int res;
+ /* Make sure our transmit state is on hook */
+ x = 0;
+ x = TOR_ONHOOK;
+ res = ioctl(zap_fd(p->z), TOR_HOOK, &x);
+ do {
+ x = TOR_RING;
+ res = ioctl(zap_fd(p->z), TOR_HOOK, &x);
+#if 0
+ printf("Res: %d, error: %s\n", res, strerror(errno));
+#endif
+ if (res) {
+ switch(errno) {
+ case EBUSY:
+ case EINTR:
+ /* Wait just in case */
+ usleep(10000);
+ continue;
+ case EINPROGRESS:
+ res = 0;
+ break;
+ default:
+ ast_log(LOG_WARNING, "Couldn't ring the phone: %s\n", strerror(errno));
+ res = 0;
+ }
+ }
+ } while (res);
+ return res;
+}
+
+static void *ss_thread(void *data);
+
+static struct ast_channel *tor_new(struct tor_pvt *, int, int, int, int);
+
+static int attempt_transfer(struct tor_pvt *p)
+{
+ /* In order to transfer, we need at least one of the channels to
+ actually be in a call bridge. We can't conference two applications
+ together (but then, why would we want to?) */
+ if (p->owners[p->normalindex]->bridge) {
+ if (ast_channel_masquerade(p->owners[p->thirdcallindex], p->owners[p->normalindex]->bridge)) {
+ ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
+ p->owners[p->normalindex]->bridge->name, p->owners[p->thirdcallindex]->name);
+ return -1;
+ }
+ /* Orphan the channel */
+ p->owners[p->thirdcallindex] = NULL;
+ p->thirdcallindex = -1;
+ } else if (p->owners[p->thirdcallindex]->bridge) {
+ if (ast_channel_masquerade(p->owners[p->normalindex], p->owners[p->thirdcallindex]->bridge)) {
+ ast_log(LOG_WARNING, "Unable to masquerade %s as %s\n",
+ p->owners[p->thirdcallindex]->bridge->name, p->owners[p->normalindex]->name);
+ return -1;
+ }
+ /* Orphan the normal channel */
+ p->owners[p->normalindex] = NULL;
+ p->normalindex = p->thirdcallindex;
+ p->thirdcallindex = -1;
+ } else {
+ ast_log(LOG_DEBUG, "Neither %s nor %s are in a bridge, nothing to transfer\n",
+ p->owners[p->normalindex]->name, p->owners[p->thirdcallindex]->name);
+ p->owners[p->thirdcallindex]->softhangup=1;
+ }
return 0;
}
struct ast_frame *tor_handle_event(struct ast_channel *ast)
{
int res;
+ int index;
struct tor_pvt *p = ast->pvt->pvt;
+ pthread_t threadid;
+ pthread_attr_t attr;
+ struct ast_channel *chan;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ index = tor_get_index(ast, p, 0);
p->f.frametype = AST_FRAME_NULL;
p->f.datalen = 0;
p->f.timelen = 0;
@@ -570,10 +1337,13 @@ struct ast_frame *tor_handle_event(struct ast_channel *ast)
p->f.offset = 0;
p->f.src = "tor_handle_event";
p->f.data = NULL;
+ if (index < 0)
+ return &p->f;
res = tor_get_event(zap_fd(p->z));
- ast_log(LOG_DEBUG, "Got event %s(%d) on channel %d\n", event2str(res), res, p->channel);
+ ast_log(LOG_DEBUG, "Got event %s(%d) on channel %d (index %d)\n", event2str(res), res, p->channel, index);
switch(res) {
case TOR_EVENT_DIALCOMPLETE:
+ tor_enable_ec(p);
p->dialing = 0;
if (ast->state == AST_STATE_DIALING) {
#if 0
@@ -586,7 +1356,72 @@ struct ast_frame *tor_handle_event(struct ast_channel *ast)
}
break;
case TOR_EVENT_ONHOOK:
- return NULL;
+ switch(p->sig) {
+ case SIG_FXOLS:
+ case SIG_FXOGS:
+ case SIG_FXOKS:
+ /* Check for some special conditions regarding call waiting */
+ if (index == p->normalindex) {
+ /* The normal line was hung up */
+ if (p->callwaitindex > -1) {
+ /* There's a call waiting call, so ring the phone */
+ p->owner = p->owners[p->callwaitindex];
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Channel %s still has (callwait) call, ringing phone\n", p->owner);
+ p->needanswer[index] = 0;
+ p->needringing[index] = 0;
+ p->callwaitingrepeat = 0;
+ tor_ring_phone(p);
+ } else if (p->thirdcallindex > -1) {
+ if (p->transfer) {
+ if (attempt_transfer(p))
+ p->owners[p->thirdcallindex]->softhangup = 1;
+ } else
+ p->owners[p->thirdcallindex]->softhangup=1;
+ }
+ } else if (index == p->callwaitindex) {
+ /* Check to see if there is a normal call */
+ if (p->normalindex > -1) {
+ /* There's a call waiting call, so ring the phone */
+ p->owner = p->owners[p->normalindex];
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Channel %s still has (normal) call, ringing phone\n", p->owner);
+ p->needanswer[index] = 0;
+ p->needringing[index] = 0;
+ p->callwaitingrepeat = 0;
+ tor_ring_phone(p);
+ }
+ } else if (index == p->thirdcallindex) {
+ if ((ast->state != AST_STATE_UP) && (ast->state != AST_STATE_RINGING) &&
+ (ast->state != AST_STATE_RING)) {
+ /* According to the LSSGR, we should kill everything now, and we
+ do, instead of ringing the phone */
+ if (p->normalindex > -1)
+ p->owners[p->normalindex]->softhangup=1;
+ if (p->callwaitindex > -1) {
+ ast_log(LOG_WARNING, "Somehow there was a call wait\n");
+ p->owners[p->callwaitindex]->softhangup = 1;
+ }
+
+ } else {
+ if (p->transfer) {
+ if (attempt_transfer(p))
+ p->owners[p->normalindex]->softhangup = 1;
+ else {
+ /* Don't actually hangup. We're going to get transferred */
+ tor_disable_ec(p);
+ break;
+ }
+ } else
+ p->owners[p->normalindex]->softhangup = 1;
+ }
+ }
+ /* Fall through */
+ default:
+ tor_disable_ec(p);
+ return NULL;
+ }
+ break;
case TOR_EVENT_RINGOFFHOOK:
switch(p->sig) {
case SIG_FXOLS:
@@ -594,6 +1429,7 @@ struct ast_frame *tor_handle_event(struct ast_channel *ast)
case SIG_FXOKS:
switch(ast->state) {
case AST_STATE_RINGING:
+ tor_enable_ec(p);
ast->state = AST_STATE_UP;
p->f.frametype = AST_FRAME_CONTROL;
p->f.subclass = AST_CONTROL_ANSWER;
@@ -605,6 +1441,7 @@ struct ast_frame *tor_handle_event(struct ast_channel *ast)
free(p->cidspill);
p->cidspill = NULL;
}
+ p->dialing = 0;
return &p->f;
case AST_STATE_DOWN:
ast->state = AST_STATE_RING;
@@ -613,6 +1450,9 @@ struct ast_frame *tor_handle_event(struct ast_channel *ast)
p->f.subclass = AST_CONTROL_OFFHOOK;
ast_log(LOG_DEBUG, "channel %d picked up\n", p->channel);
return &p->f;
+ case AST_STATE_UP:
+ /* Okay -- probably call waiting*/
+ break;
default:
ast_log(LOG_WARNING, "FXO phone off hook in weird state %d??\n", ast->state);
}
@@ -647,6 +1487,7 @@ struct ast_frame *tor_handle_event(struct ast_channel *ast)
ast_log(LOG_WARNING, "Didn't finish Caller-ID spill. Cancelling.\n");
free(p->cidspill);
p->cidspill = NULL;
+ p->callwaitcas = 0;
}
p->f.frametype = AST_FRAME_CONTROL;
p->f.subclass = AST_CONTROL_RINGING;
@@ -659,8 +1500,88 @@ struct ast_frame *tor_handle_event(struct ast_channel *ast)
case SIG_FXOLS:
case SIG_FXOGS:
case SIG_FXOKS:
- /* XXX For now, treat as a hang up */
- return NULL;
+ ast_log(LOG_DEBUG, "Winkflash, index: %d, normal: %d, callwait: %d, thirdcall: %d\n",
+ index, p->normalindex, p->callwaitindex, p->thirdcallindex);
+ if (index == p->normalindex) {
+ if (p->callwaitindex > -1) {
+ tone_zone_play_tone(zap_fd(p->z), -1);
+ p->owner = p->owners[p->callwaitindex];
+ if (p->owner->state == AST_STATE_RINGING) {
+ p->owner->state = AST_STATE_UP;
+ p->needanswer[p->callwaitindex] = 1;
+ }
+ p->callwaitingrepeat = 0;
+ conf_clear(p);
+ } else if (p->thirdcallindex == -1) {
+ if (p->threewaycalling) {
+ if ((ast->state == AST_STATE_RINGING) ||
+ (ast->state == AST_STATE_UP) ||
+ (ast->state == AST_STATE_RING)) {
+ if (!alloc_pseudo(p)) {
+ /* Start three way call */
+ res = tone_zone_play_tone(zap_fd(p->z), TOR_TONE_DIALRECALL);
+ if (res)
+ ast_log(LOG_WARNING, "Unable to start dial recall tone on channel %d\n", p->channel);
+ chan = tor_new(p, AST_STATE_RESERVED,0,0,1);
+ p->owner = chan;
+ if (pthread_create(&threadid, &attr, ss_thread, chan)) {
+ ast_log(LOG_WARNING, "Unable to start simple switch on channel %d\n", p->channel);
+ res = tone_zone_play_tone(zap_fd(p->z), TOR_TONE_CONGESTION);
+ ast_hangup(chan);
+ } else {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Started three way call on channel %d (index %d)\n", p->channel, p->thirdcallindex);
+ conf_clear(p);
+ }
+ } else
+ ast_log(LOG_WARNING, "Unable to allocate pseudo channel\n");
+ } else
+ ast_log(LOG_DEBUG, "Flash when call not up or ringing\n");
+ }
+ } else {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Got flash with three way call up, dropping last call %d\n",
+ p->thirdcallindex);
+ /* Drop the last call and stop the conference */
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Dropping three-way call on %s\n", p->owners[p->thirdcallindex]->name);
+ p->owners[p->thirdcallindex]->softhangup=1;
+ conf_clear(p);
+ }
+ } else if (index == p->callwaitindex) {
+ if (p->normalindex > -1) {
+ p->owner = p->owners[p->normalindex];
+ p->callwaitingrepeat = 0;
+ conf_clear(p);
+ } else
+ ast_log(LOG_WARNING, "Wink/Flash on call wait, with no normal channel to flash to on channel %d?\n", p->channel);
+ } else if (index == p->thirdcallindex) {
+ if (p->normalindex > -1) {
+ if ((ast->state != AST_STATE_RINGING) && (ast->state != AST_STATE_UP) && (ast->state != AST_STATE_RING)) {
+ tone_zone_play_tone(zap_fd(p->z), -1);
+ p->owner = p->owners[p->normalindex];
+ ast_log(LOG_DEBUG, "Dumping incomplete three way call in state %d\n", ast->state);
+ return NULL;
+ }
+ p->owner = p->owners[p->normalindex];
+ p->owners[p->thirdcallindex]->fds[0] = zap_fd(p->pseudo);
+ p->callwaitingrepeat = 0;
+ if (p->owners[p->thirdcallindex]->state == AST_STATE_RINGING) {
+ /* If we were ringing, stop the ringing on the main line and start it on
+ the pseudo */
+ tone_zone_play_tone(zap_fd(p->z), -1);
+ tone_zone_play_tone(zap_fd(p->pseudo), TOR_TONE_RINGTONE);
+ }
+ three_way(p);
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Established 3-way conference between %s and %s\n",
+ p->owners[p->normalindex]->name, p->owners[p->thirdcallindex]->name);
+ } else {
+ ast_log(LOG_WARNING, "Wink/Flash on threeway call, with no normal channel to flash to on channel %d?\n", p->channel);
+ return NULL;
+ }
+ }
+ break;
case SIG_EM:
case SIG_EMWINK:
case SIG_FEATD:
@@ -693,6 +1614,73 @@ struct ast_frame *tor_handle_event(struct ast_channel *ast)
struct ast_frame *tor_exception(struct ast_channel *ast)
{
+ struct tor_pvt *p = ast->pvt->pvt;
+ int res;
+ int usedindex=-1;
+ p->f.frametype = AST_FRAME_NULL;
+ p->f.datalen = 0;
+ p->f.timelen = 0;
+ p->f.mallocd = 0;
+ p->f.offset = 0;
+ p->f.subclass = 0;
+ p->f.src = "tor_exception";
+ p->f.data = NULL;
+ if ((p->owner != p->owners[0]) &&
+ (p->owner != p->owners[1]) &&
+ (p->owner != p->owners[2])) {
+ /* If nobody owns us, absorb the event appropriately, otherwise
+ we loop indefinitely. This occurs when, during call waiting, the
+ other end hangs up our channel so that it no longer exists, but we
+ have neither FLASH'd nor ONHOOK'd to signify our desire to
+ change to the other channel. */
+ res = tor_get_event(zap_fd(p->z));
+ if ((p->callwaitindex > -1) && (p->normalindex > -1))
+ ast_log(LOG_WARNING, "Absorbing exception on unowned channel, but there is both a normal and call waiting call still here?\n");
+ if (p->callwaitindex > -1) {
+ tone_zone_play_tone(zap_fd(p->z), -1);
+ p->owner = p->owners[p->callwaitindex];
+ usedindex = p->callwaitindex;
+ } else if (p->normalindex > -1) {
+ tone_zone_play_tone(zap_fd(p->z), -1);
+ p->owner = p->owners[p->normalindex];
+ usedindex = p->normalindex;
+ } else {
+ ast_log(LOG_WARNING, "No call wait call, no normal call, what do I do?\n");
+ return NULL;
+ }
+ switch(res) {
+ case TOR_EVENT_ONHOOK:
+ tor_disable_ec(p);
+ if (p->owner) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Channel %s still has call, ringing phone\n", p->owner->name);
+ tor_ring_phone(p);
+ p->callwaitingrepeat = 0;
+ } else
+ ast_log(LOG_WARNING, "Absorbed on hook, but nobody is left!?!?\n");
+ break;
+ case TOR_EVENT_WINKFLASH:
+ if (p->owner) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Channel %d flashed to other channel %s\n", p->channel, p->owner->name);
+ if ((usedindex == p->callwaitindex) && (p->owner->state == AST_STATE_RINGING)) {
+ /* Answer the call wait if necessary */
+ p->needanswer[usedindex] = 1;
+ p->owner->state = AST_STATE_UP;
+ }
+ p->callwaitingrepeat = 0;
+ } else
+ ast_log(LOG_WARNING, "Absorbed on hook, but nobody is left!?!?\n");
+ break;
+ default:
+ ast_log(LOG_WARNING, "Don't know how to absorb event %s\n", event2str(res));
+ }
+ return &p->f;
+ }
+ /* If it's not us, return NULL immediately */
+ if (ast != p->owner)
+ return &p->f;
+
return tor_handle_event(ast);
}
@@ -700,21 +1688,78 @@ struct ast_frame *tor_read(struct ast_channel *ast)
{
struct tor_pvt *p = ast->pvt->pvt;
int res,x;
+ int index;
unsigned char ireadbuf[READ_SIZE];
unsigned char *readbuf;
+ ZAP *z = NULL;
- p->f.frametype = AST_FRAME_DTMF;
+ pthread_mutex_lock(&p->lock);
+
+ p->f.frametype = AST_FRAME_NULL;
p->f.datalen = 0;
p->f.timelen = 0;
p->f.mallocd = 0;
p->f.offset = 0;
+ p->f.subclass = 0;
p->f.src = "tor_read";
p->f.data = NULL;
+ index = tor_get_index(ast, p, 0);
+
+ /* Hang up if we don't really exist */
+ if (index < 0) {
+ ast_log(LOG_WARNING, "We dont exist?\n");
+ pthread_mutex_unlock(&p->lock);
+ return NULL;
+ }
+
+ if (p->needringing[index]) {
+ /* Send ringing frame if requested */
+ p->needringing[index] = 0;
+ p->f.frametype = AST_FRAME_CONTROL;
+ p->f.subclass = AST_CONTROL_RINGING;
+ pthread_mutex_unlock(&p->lock);
+ return &p->f;
+ }
+
+ if (p->needanswer[index]) {
+ /* Send ringing frame if requested */
+ p->needanswer[index] = 0;
+ p->f.frametype = AST_FRAME_CONTROL;
+ p->f.subclass = AST_CONTROL_ANSWER;
+ pthread_mutex_unlock(&p->lock);
+ return &p->f;
+ }
+
+ if (ast != p->owner) {
+ /* If it's not us. If this isn't a three way call, return immediately */
+ if (!INTHREEWAY(p)) {
+ pthread_mutex_unlock(&p->lock);
+ return &p->f;
+ }
+ /* If it's not the third call, return immediately */
+ if (ast != p->owners[p->thirdcallindex]) {
+ pthread_mutex_unlock(&p->lock);
+ return &p->f;
+ }
+ if (!p->pseudo)
+ ast_log(LOG_ERROR, "No pseudo channel\n");
+ z = p->pseudo;
+ } else
+ z = p->z;
+
+ if (!z) {
+ ast_log(LOG_WARNING, "No zap structure?!?\n");
+ pthread_mutex_unlock(&p->lock);
+ return NULL;
+ }
+
/* Check first for any outstanding DTMF characters */
if (strlen(p->dtmfq)) {
p->f.subclass = p->dtmfq[0];
memmove(p->dtmfq, p->dtmfq + 1, sizeof(p->dtmfq) - 1);
+ p->f.frametype = AST_FRAME_DTMF;
+ pthread_mutex_unlock(&p->lock);
return &p->f;
}
@@ -726,34 +1771,66 @@ struct ast_frame *tor_read(struct ast_channel *ast)
readbuf = ((unsigned char *)p->buffer) + AST_FRIENDLY_OFFSET;
} else {
ast_log(LOG_WARNING, "Don't know how to read frames in format %d\n", ast->pvt->rawreadformat);
+ pthread_mutex_unlock(&p->lock);
return NULL;
}
CHECK_BLOCKING(ast);
- res = zap_recchunk(p->z, readbuf, READ_SIZE, ZAP_DTMFINT);
+ if ((z != p->z) && (z != p->pseudo)) {
+ pthread_mutex_unlock(&p->lock);
+ return NULL;
+ }
+ res = zap_recchunk(z, readbuf, READ_SIZE, ZAP_DTMFINT);
ast->blocking = 0;
/* Check for hangup */
if (res < 0) {
if (res == -1)
ast_log(LOG_WARNING, "tor_rec: %s\n", strerror(errno));
+ pthread_mutex_unlock(&p->lock);
return NULL;
}
if (res != READ_SIZE) {
if (option_debug)
ast_log(LOG_DEBUG, "Short read, must be DTMF or something...\n");
/* XXX UGLY!! Zapata's DTMF handling is a bit ugly XXX */
- if (zap_dtmfwaiting(p->z) && !strlen(zap_dtmfbuf(p->z))) {
- zap_getdtmf(p->z, 1, NULL, 0, 1, 1, 0);
+ if (zap_dtmfwaiting(z) && !strlen(zap_dtmfbuf(z))) {
+ zap_getdtmf(z, 1, NULL, 0, 1, 1, 0);
}
- if (strlen(zap_dtmfbuf(p->z))) {
- ast_log(LOG_DEBUG, "Got some dtmf ('%s')... on channel %s\n", zap_dtmfbuf(p->z), ast->name);
+ if (strlen(zap_dtmfbuf(z))) {
+ ast_log(LOG_DEBUG, "Got some dtmf ('%s')... on channel %s\n", zap_dtmfbuf(z), ast->name);
/* DTMF tone detected. Queue and erturn */
- strncpy(p->dtmfq + strlen(p->dtmfq), zap_dtmfbuf(p->z), sizeof(p->dtmfq) - strlen(p->dtmfq));
- zap_clrdtmfn(p->z);
+ if (p->callwaitcas) {
+ if (!strcmp(zap_dtmfbuf(z), "A") || !strcmp(zap_dtmfbuf(z), "D")) {
+ ast_log(LOG_DEBUG, "Got some DTMF, but it's for the CAS\n");
+ if (p->cidspill)
+ free(p->cidspill);
+ send_cwcidspill(p);
+ }
+ /* Return NULL */
+ pthread_mutex_unlock(&p->lock);
+ return &p->f;
+ } else {
+ strncpy(p->dtmfq + strlen(p->dtmfq), zap_dtmfbuf(z), sizeof(p->dtmfq) - strlen(p->dtmfq));
+ zap_clrdtmfn(z);
+ }
} else {
+ pthread_mutex_unlock(&p->lock);
return tor_handle_event(ast);
}
+ if (strlen(p->dtmfq)) {
+ p->f.subclass = p->dtmfq[0];
+ memmove(p->dtmfq, p->dtmfq + 1, sizeof(p->dtmfq) - 1);
+ p->f.frametype = AST_FRAME_DTMF;
+ }
+ pthread_mutex_unlock(&p->lock);
return &p->f;
}
+ if (p->callwaitingrepeat)
+ p->callwaitingrepeat--;
+ /* Repeat callwaiting */
+ if (p->callwaitingrepeat == 1) {
+ p->callwaitrings++;
+ tor_callwait(ast);
+ }
if (ast->pvt->rawreadformat == AST_FORMAT_SLINEAR) {
for (x=0;x<READ_SIZE;x++) {
p->buffer[x + AST_FRIENDLY_OFFSET/2] = ast_mulaw[readbuf[x]];
@@ -763,7 +1840,7 @@ struct ast_frame *tor_read(struct ast_channel *ast)
p->f.datalen = READ_SIZE;
/* Handle CallerID Transmission */
- if ((ast->rings == 1) && (p->cidspill))
+ if (p->cidspill &&((ast->state == AST_STATE_UP) || (ast->rings == 1)))
send_callerid(p);
p->f.frametype = AST_FRAME_VOICE;
@@ -775,19 +1852,35 @@ struct ast_frame *tor_read(struct ast_channel *ast)
#if 0
ast_log(LOG_DEBUG, "Read %d of voice on %s\n", p->f.datalen, ast->name);
#endif
+ if (p->dialing) {
+ /* Whoops, we're still dialing, don't send anything */
+ p->f.frametype = AST_FRAME_NULL;
+ p->f.subclass = 0;
+ p->f.timelen = 0;
+ p->f.mallocd = 0;
+ p->f.offset = 0;
+ p->f.data = NULL;
+ p->f.datalen= 0;
+ }
+ pthread_mutex_unlock(&p->lock);
return &p->f;
}
-static int my_tor_write(struct tor_pvt *p, unsigned char *buf, int len)
+static int my_tor_write(struct tor_pvt *p, unsigned char *buf, int len, int threeway)
{
int sent=0;
int size;
int res;
+ int fd;
+ if (threeway)
+ fd = zap_fd(p->pseudo);
+ else
+ fd = zap_fd(p->z);
while(len) {
size = len;
if (size > READ_SIZE)
size = READ_SIZE;
- res = write(zap_fd(p->z), buf, size);
+ res = write(fd, buf, size);
if (res != size) {
ast_log(LOG_DEBUG, "Write returned %d (%s) on channel %d\n", res, strerror(errno), p->channel);
return sent;
@@ -805,6 +1898,18 @@ static int tor_write(struct ast_channel *ast, struct ast_frame *frame)
int res;
unsigned char outbuf[4096];
short *inbuf;
+
+ if (ast != p->owner) {
+ /* If it's not us. If this isn't a three way call, return immediately */
+ if (!INTHREEWAY(p)) {
+ return 0;
+ }
+ /* If it's not the third call, return immediately */
+ if (ast != p->owners[p->thirdcallindex]) {
+ return 0;
+ }
+ }
+
/* Write a frame of (presumably voice) data */
if (frame->frametype != AST_FRAME_VOICE) {
ast_log(LOG_WARNING, "Don't know what to do with frame type '%d'\n", frame->frametype);
@@ -815,11 +1920,17 @@ static int tor_write(struct ast_channel *ast, struct ast_frame *frame)
return -1;
}
if (p->dialing) {
- ast_log(LOG_DEBUG, "Dropping frame since I'm still dialing...\n");
+#if 0
+ if (option_debug)
+#endif
+ ast_log(LOG_DEBUG, "Dropping frame since I'm still dialing...\n");
return 0;
}
if (p->cidspill) {
- ast_log(LOG_DEBUG, "Dropping frame since I've still got a callerid spill\n");
+#if 0
+ if (option_debug)
+#endif
+ ast_log(LOG_DEBUG, "Dropping frame since I've still got a callerid spill\n");
return 0;
}
/* Return if it's not valid data */
@@ -833,10 +1944,10 @@ static int tor_write(struct ast_channel *ast, struct ast_frame *frame)
inbuf = frame->data;
for (x=0;x<frame->datalen/2;x++)
outbuf[x] = ast_lin2mu[inbuf[x]+32768];
- res = my_tor_write(p, outbuf, frame->datalen/2);
+ res = my_tor_write(p, outbuf, frame->datalen/2, (ast != p->owner));
} else {
/* uLaw already */
- res = my_tor_write(p, (unsigned char *)frame->data, frame->datalen);
+ res = my_tor_write(p, (unsigned char *)frame->data, frame->datalen, (ast != p->owner));
}
if (res < 0) {
ast_log(LOG_WARNING, "write failed: %s\n", strerror(errno));
@@ -848,14 +1959,44 @@ static int tor_write(struct ast_channel *ast, struct ast_frame *frame)
return 0;
}
-static struct ast_channel *tor_new(struct tor_pvt *i, int state, int startpbx)
+static int tor_indicate(struct ast_channel *chan, int condition)
+{
+ struct tor_pvt *p = chan->pvt->pvt;
+ int res=-1;
+ switch(condition) {
+ case AST_CONTROL_BUSY:
+ res = tone_zone_play_tone(zap_fd(p->z), TOR_TONE_BUSY);
+ break;
+ case AST_CONTROL_RINGING:
+ res = tone_zone_play_tone(zap_fd(p->z), TOR_TONE_RINGTONE);
+ if (chan->state != AST_STATE_UP)
+ chan->state = AST_STATE_RINGING;
+ break;
+ case AST_CONTROL_CONGESTION:
+ res = tone_zone_play_tone(zap_fd(p->z), TOR_TONE_CONGESTION);
+ break;
+ default:
+ ast_log(LOG_WARNING, "Don't know how to set condition %d on channel %s\n", condition, chan->name);
+ }
+ return res;
+}
+
+static struct ast_channel *tor_new(struct tor_pvt *i, int state, int startpbx, int callwaiting, int thirdcall)
{
struct ast_channel *tmp;
+ int x;
+ for (x=0;x<3;x++)
+ if (!i->owners[x])
+ break;
+ if (x > 2) {
+ ast_log(LOG_WARNING, "No available owner slots\n");
+ return NULL;
+ }
tmp = ast_channel_alloc();
if (tmp) {
- snprintf(tmp->name, sizeof(tmp->name), "Tor/%d", i->channel);
+ snprintf(tmp->name, sizeof(tmp->name), "Tor/%d-%d", i->channel, x + 1);
tmp->type = type;
- tmp->fd = zap_fd(i->z);
+ tmp->fds[0] = zap_fd(i->z);
tmp->nativeformats = AST_FORMAT_SLINEAR | AST_FORMAT_ULAW;
/* Start out assuming ulaw since it's smaller :) */
tmp->pvt->rawreadformat = AST_FORMAT_ULAW;
@@ -875,15 +2016,38 @@ static struct ast_channel *tor_new(struct tor_pvt *i, int state, int startpbx)
tmp->pvt->write = tor_write;
tmp->pvt->bridge = tor_bridge;
tmp->pvt->exception = tor_exception;
+ tmp->pvt->indicate = tor_indicate;
+ tmp->pvt->fixup = tor_fixup;
+ tmp->pvt->setoption = tor_setoption;
if (strlen(i->language))
strncpy(tmp->language, i->language, sizeof(tmp->language));
- i->owner = tmp;
- pthread_mutex_lock(&usecnt_lock);
+ /* Keep track of who owns it */
+ i->owners[x] = tmp;
+ if (!i->owner)
+ i->owner = tmp;
+ if (callwaiting) {
+ if (i->callwaitindex > -1)
+ ast_log(LOG_WARNING, "channel %d already has a call wait call\n", i->channel);
+ i->callwaitindex = x;
+ } else if (thirdcall) {
+ if (i->thirdcallindex > -1)
+ ast_log(LOG_WARNING, "channel %d already has a third call\n", i->channel);
+ i->thirdcallindex = x;
+ } else {
+ if (i->normalindex > -1)
+ ast_log(LOG_WARNING, "channel %d already has a normal call\n", i->channel);
+ i->normalindex = x;
+ }
+ ast_pthread_mutex_lock(&usecnt_lock);
usecnt++;
- pthread_mutex_unlock(&usecnt_lock);
+ ast_pthread_mutex_unlock(&usecnt_lock);
ast_update_use_count();
strncpy(tmp->context, i->context, sizeof(tmp->context));
+ if (strlen(i->exten))
+ strncpy(tmp->exten, i->exten, sizeof(tmp->exten));
if (startpbx) {
+ if (strlen(i->callerid))
+ tmp->callerid = strdup(i->callerid);
if (ast_pbx_start(tmp)) {
ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
ast_hangup(tmp);
@@ -900,7 +2064,7 @@ static int ignore_pat(char *s)
{
int x;
for (x=0;x<dialpats;x++)
- if (!strcmp(s, keepdialpat[x]))
+ if (ast_extension_match(keepdialpat[x], s))
return 1;
return 0;
}
@@ -909,7 +2073,7 @@ static int bump_gains(struct tor_pvt *p)
{
int res;
/* Bump receive gain by 9.0db */
- res = set_actual_gain(zap_fd(p->z), 0, 9, 0);
+ res = set_actual_gain(zap_fd(p->z), 0, p->rxgain + 5.0, p->txgain);
if (res) {
ast_log(LOG_WARNING, "Unable to bump gain\n");
return -1;
@@ -921,7 +2085,7 @@ static int restore_gains(struct tor_pvt *p)
{
int res;
/* Bump receive gain by 9.0db */
- res = set_actual_gain(zap_fd(p->z), 0, 0, 0);
+ res = set_actual_gain(zap_fd(p->z), 0, p->rxgain, p->txgain);
if (res) {
ast_log(LOG_WARNING, "Unable to restore gain\n");
return -1;
@@ -941,6 +2105,7 @@ static void *ss_thread(void *data)
char *name, *number;
int flags;
int i;
+ int timeout;
char *s1, *s2;
int len = 0;
int res;
@@ -991,16 +2156,15 @@ static void *ss_thread(void *data)
} else
ast_log(LOG_WARNING, "Got a non-Feature Group D input on channel %d. Assuming E&M Wink instead\n", p->channel);
}
- res = tone_zone_play_tone(zap_fd(p->z), TOR_TONE_RINGTONE);
- if (res < 0)
- ast_log(LOG_WARNING, "Unable to start ringback tone on channel %d\n", p->channel);
+ tor_enable_ec(p);
if (ast_exists_extension(chan, chan->context, exten, 1)) {
strncpy(chan->exten, exten, sizeof(chan->exten));
zap_clrdtmf(p->z);
res = ast_pbx_run(chan);
- if (res)
+ if (res) {
ast_log(LOG_WARNING, "PBX exited non-zero\n");
- res = tone_zone_play_tone(zap_fd(p->z), TOR_TONE_CONGESTION);
+ res = tone_zone_play_tone(zap_fd(p->z), TOR_TONE_CONGESTION);
+ }
return NULL;
} else {
if (option_verbose > 2)
@@ -1023,74 +2187,96 @@ static void *ss_thread(void *data)
case SIG_FXOGS:
case SIG_FXOKS:
/* Read the first digit */
- res = zap_getdtmf(p->z, 1, NULL, 0, firstdigittimeout, firstdigittimeout, ZAP_HOOKEXIT | ZAP_TIMEOUTOK);
- if (res < 0) {
- if (option_debug)
- ast_log(LOG_DEBUG, "getdtmf returned %d (%s)...\n", res, strerror(errno));
- /* Got hung up on apparently. Stop any playing tones. We're done */
- res = tone_zone_play_tone(zap_fd(p->z), -1);
- ast_hangup(chan);
- return NULL;
- }
- if (res) {
- strncpy(exten + len, zap_dtmfbuf(p->z), sizeof(exten) - len);
- len++;
- if (ast_exists_extension(chan, chan->context, exten, 1)) {
- if (!ignore_pat(exten)) {
- res = tone_zone_play_tone(zap_fd(p->z), TOR_TONE_RINGTONE);
- if (res < 0)
- ast_log(LOG_WARNING, "Unable to start ringback tone on channel %d\n", p->channel);
- }
- /* Check for a single digit extension */
- strncpy(chan->exten, exten, sizeof(chan->exten));
- zap_clrdtmf(p->z);
- res = ast_pbx_run(chan);
- if (res)
- ast_log(LOG_WARNING, "PBX exited non-zero\n");
+ timeout = firstdigittimeout;
+ while(len < AST_MAX_EXTENSION-1) {
+ res = ast_waitfordigit(chan, timeout);
+ if (res < 0) {
+ ast_log(LOG_DEBUG, "waitfordigit returned < 0...\n");
+ res = tone_zone_play_tone(zap_fd(p->z), -1);
+ ast_hangup(chan);
+ return NULL;
+ } else if (res == 0) {
+ ast_log(LOG_DEBUG, "not enough digits...\n");
res = tone_zone_play_tone(zap_fd(p->z), TOR_TONE_CONGESTION);
+ tor_wait_event(zap_fd(p->z));
+ ast_hangup(chan);
return NULL;
+ } else {
+ exten[len++]=res;
+ exten[len] = '\0';
}
-
if (!ignore_pat(exten))
tone_zone_play_tone(zap_fd(p->z), -1);
- while(len < AST_MAX_EXTENSION-1) {
- zap_clrdtmf(p->z);
- res = zap_getdtmf(p->z, 1, NULL, 0, gendigittimeout, gendigittimeout, ZAP_HOOKEXIT | ZAP_TIMEOUTOK);
- if (res < 0) {
- ast_log(LOG_DEBUG, "getdtmf returned < 0...\n");
- res = tone_zone_play_tone(zap_fd(p->z), -1);
- ast_hangup(chan);
- return NULL;
- } else if (res == 0) {
- ast_log(LOG_DEBUG, "not enough digits...\n");
+ if (ast_exists_extension(chan, chan->context, exten, 1)) {
+ res = tone_zone_play_tone(zap_fd(p->z), -1);
+ strncpy(chan->exten, exten, sizeof(chan->exten));
+ if (strlen(p->callerid) && !p->hidecallerid)
+ chan->callerid = strdup(p->callerid);
+ chan->state = AST_STATE_RING;
+ tor_enable_ec(p);
+ res = ast_pbx_run(chan);
+ if (res) {
+ ast_log(LOG_WARNING, "PBX exited non-zero\n");
res = tone_zone_play_tone(zap_fd(p->z), TOR_TONE_CONGESTION);
- tor_wait_event(zap_fd(p->z));
- ast_hangup(chan);
- return NULL;
- } else {
- strncpy(exten + len, zap_dtmfbuf(p->z), sizeof(exten) - len);
- len++;
- if (!ignore_pat(exten))
- tone_zone_play_tone(zap_fd(p->z), -1);
- if (ast_exists_extension(chan, chan->context, exten, 1)) {
- res = tone_zone_play_tone(zap_fd(p->z), TOR_TONE_RINGTONE);
- if (res < 0)
- ast_log(LOG_WARNING, "Unable to start ringback tone on channel %d\n", p->channel);
- strncpy(chan->exten, exten, sizeof(chan->exten));
- if (strlen(p->callerid))
- chan->callerid = strdup(p->callerid);
- zap_clrdtmf(p->z);
- res = ast_pbx_run(chan);
- if (res)
- ast_log(LOG_WARNING, "PBX exited non-zero\n");
- res = tone_zone_play_tone(zap_fd(p->z), TOR_TONE_CONGESTION);
- return NULL;
- } else if (!ast_canmatch_extension(chan, chan->context, exten, 1)) {
- printf("Can't match %s is context %s\n", exten, chan->context);
- break;
- }
+ }
+ return NULL;
+ } else if (p->callwaiting && !strcmp(exten, "*70")) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Disabling call waiting on %s\n", chan->name);
+ /* Disable call waiting if enabled */
+ p->callwaiting = 0;
+ res = tone_zone_play_tone(zap_fd(p->z), TOR_TONE_DIALRECALL);
+ if (res) {
+ ast_log(LOG_WARNING, "Unable to do dial recall on channel %s: %s\n",
+ chan->name, strerror(errno));
+ }
+ len = 0;
+ memset(exten, 0, sizeof(exten));
+ timeout = firstdigittimeout;
+
+ } else if (!p->hidecallerid && !strcmp(exten, "*67")) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Disabling Caller*ID on %s\n", chan->name);
+ /* Disable Caller*ID if enabled */
+ p->hidecallerid = 1;
+ if (chan->callerid)
+ free(chan->callerid);
+ chan->callerid = NULL;
+ res = tone_zone_play_tone(zap_fd(p->z), TOR_TONE_DIALRECALL);
+ if (res) {
+ ast_log(LOG_WARNING, "Unable to do dial recall on channel %s: %s\n",
+ chan->name, strerror(errno));
}
+ len = 0;
+ memset(exten, 0, sizeof(exten));
+ timeout = firstdigittimeout;
+
+ } else if (p->hidecallerid && !strcmp(exten, "*82")) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Enabling Caller*ID on %s\n", chan->name);
+ /* Enable Caller*ID if enabled */
+ p->hidecallerid = 0;
+ if (chan->callerid)
+ free(chan->callerid);
+ chan->callerid = NULL;
+ res = tone_zone_play_tone(zap_fd(p->z), TOR_TONE_DIALRECALL);
+ if (res) {
+ ast_log(LOG_WARNING, "Unable to do dial recall on channel %s: %s\n",
+ chan->name, strerror(errno));
+ }
+ len = 0;
+ memset(exten, 0, sizeof(exten));
+ timeout = firstdigittimeout;
+
+ } else if (!ast_canmatch_extension(chan, chan->context, exten, 1) &&
+ ((exten[0] != '*') || (strlen(exten) > 2))) {
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Can't match %s from '%s' in context %s\n", exten, chan->callerid ? chan->callerid : "<Unknown Caller>", chan->context);
+ break;
}
+ timeout = gendigittimeout;
+ if (len && !ignore_pat(exten))
+ tone_zone_play_tone(zap_fd(p->z), -1);
}
break;
case SIG_FXSLS:
@@ -1162,6 +2348,7 @@ static void *ss_thread(void *data)
chan->callerid = strdup(cid);
chan->state = AST_STATE_RING;
chan->rings = 1;
+ tor_enable_ec(p);
res = ast_pbx_run(chan);
if (res) {
ast_hangup(chan);
@@ -1185,7 +2372,10 @@ static int handle_init_event(struct tor_pvt *i, int event)
{
int res;
pthread_t threadid;
+ pthread_attr_t attr;
struct ast_channel *chan;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
/* Handle an event on a given channel for the monitor thread. */
switch(event) {
case TOR_EVENT_RINGOFFHOOK:
@@ -1195,8 +2385,10 @@ static int handle_init_event(struct tor_pvt *i, int event)
case SIG_FXOGS:
case SIG_FXOKS:
if (i->immediate) {
+ tor_enable_ec(i);
/* The channel is immediately up. Start right away */
- chan = tor_new(i, AST_STATE_UP, 1);
+ res = tone_zone_play_tone(zap_fd(i->z), TOR_TONE_RINGTONE);
+ chan = tor_new(i, AST_STATE_RING, 1, 0, 0);
if (!chan) {
ast_log(LOG_WARNING, "Unable to start PBX on channel %d\n", i->channel);
res = tone_zone_play_tone(zap_fd(i->z), TOR_TONE_CONGESTION);
@@ -1208,14 +2400,17 @@ static int handle_init_event(struct tor_pvt *i, int event)
if (res < 0)
ast_log(LOG_WARNING, "Unable to play dialtone on channel %d\n", i->channel);
/* Check for callerid, digits, etc */
- chan = tor_new(i, AST_STATE_DOWN, 0);
- if (pthread_create(&threadid, NULL, ss_thread, chan)) {
+ chan = tor_new(i, AST_STATE_DOWN, 0, 0, 0);
+ if (pthread_create(&threadid, &attr, ss_thread, chan)) {
ast_log(LOG_WARNING, "Unable to start simple switch thread on channel %d\n", i->channel);
res = tone_zone_play_tone(zap_fd(i->z), TOR_TONE_CONGESTION);
if (res < 0)
ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", i->channel);
ast_hangup(chan);
}
+#if 0
+ printf("Created thread %ld detached in switch\n", threadid);
+#endif
}
break;
case SIG_EMWINK:
@@ -1225,14 +2420,17 @@ static int handle_init_event(struct tor_pvt *i, int event)
case SIG_FXSGS:
case SIG_FXSKS:
/* Check for callerid, digits, etc */
- chan = tor_new(i, AST_STATE_RING, 0);
- if (pthread_create(&threadid, NULL, ss_thread, chan)) {
+ chan = tor_new(i, AST_STATE_RING, 0, 0, 0);
+ if (pthread_create(&threadid, &attr, ss_thread, chan)) {
ast_log(LOG_WARNING, "Unable to start simple switch thread on channel %d\n", i->channel);
res = tone_zone_play_tone(zap_fd(i->z), TOR_TONE_CONGESTION);
if (res < 0)
ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", i->channel);
ast_hangup(chan);
}
+#if 0
+ printf("Created thread %ld detached in switch(2)\n", threadid);
+#endif
break;
default:
ast_log(LOG_WARNING, "Don't know how to handle ring/answer with signalling %s on channel %d\n", sig2str(i->sig), i->channel);
@@ -1255,6 +2453,7 @@ static int handle_init_event(struct tor_pvt *i, int event)
case SIG_FXSLS:
case SIG_FXSGS:
case SIG_FXSKS:
+ tor_disable_ec(i);
res = tone_zone_play_tone(zap_fd(i->z), -1);
tor_set_hook(zap_fd(i->z), TOR_ONHOOK);
break;
@@ -1270,7 +2469,6 @@ static int handle_init_event(struct tor_pvt *i, int event)
static void *do_monitor(void *data)
{
- fd_set rfds;
fd_set efds;
int n, res;
struct tor_pvt *i;
@@ -1285,16 +2483,9 @@ static void *do_monitor(void *data)
ast_log(LOG_DEBUG, "Monitor starting...\n");
#endif
for(;;) {
- /* Don't let anybody kill us right away. Nobody should lock the interface list
- and wait for the monitor list, but the other way around is okay. */
- if (pthread_mutex_lock(&monlock)) {
- ast_log(LOG_ERROR, "Unable to grab monitor lock\n");
- return NULL;
- }
/* Lock the interface list */
- if (pthread_mutex_lock(&iflock)) {
+ if (ast_pthread_mutex_lock(&iflock)) {
ast_log(LOG_ERROR, "Unable to grab interface lock\n");
- pthread_mutex_unlock(&monlock);
return NULL;
}
/* Build the stuff we're going to select on, that is the socket of every
@@ -1314,13 +2505,11 @@ static void *do_monitor(void *data)
i = i->next;
}
/* Okay, now that we know what to do, release the interface lock */
- pthread_mutex_unlock(&iflock);
+ ast_pthread_mutex_unlock(&iflock);
- /* And from now on, we're okay to be killed, so release the monitor lock as well */
- pthread_mutex_unlock(&monlock);
pthread_testcancel();
/* Wait indefinitely for something to happen */
- res = select(n + 1, &rfds, NULL, &efds, NULL);
+ res = select(n + 1, NULL, NULL, &efds, NULL);
pthread_testcancel();
/* Okay, select has finished. Let's see what happened. */
if (res < 0) {
@@ -1330,7 +2519,7 @@ static void *do_monitor(void *data)
}
/* Alright, lock the interface list again, and let's look and see what has
happened */
- if (pthread_mutex_lock(&iflock)) {
+ if (ast_pthread_mutex_lock(&iflock)) {
ast_log(LOG_WARNING, "Unable to lock the interface list\n");
continue;
}
@@ -1343,12 +2532,13 @@ static void *do_monitor(void *data)
continue;
}
res = tor_get_event(zap_fd(i->z));
- ast_log(LOG_DEBUG, "Monitor doohicky got event %s on channel %d\n", event2str(res), i->channel);
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Monitor doohicky got event %s on channel %d\n", event2str(res), i->channel);
handle_init_event(i, res);
}
i=i->next;
}
- pthread_mutex_unlock(&iflock);
+ ast_pthread_mutex_unlock(&iflock);
}
/* Never reached */
return NULL;
@@ -1357,34 +2547,36 @@ static void *do_monitor(void *data)
static int restart_monitor(void)
{
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
/* If we're supposed to be stopped -- stay stopped */
if (monitor_thread == -2)
return 0;
- if (pthread_mutex_lock(&monlock)) {
+ if (ast_pthread_mutex_lock(&monlock)) {
ast_log(LOG_WARNING, "Unable to lock monitor\n");
return -1;
}
if (monitor_thread == pthread_self()) {
- pthread_mutex_unlock(&monlock);
+ ast_pthread_mutex_unlock(&monlock);
ast_log(LOG_WARNING, "Cannot kill myself\n");
return -1;
}
if (monitor_thread) {
-#if 1
pthread_cancel(monitor_thread);
-#endif
pthread_kill(monitor_thread, SIGURG);
-#if 0
pthread_join(monitor_thread, NULL);
-#endif
}
/* Start a new monitor */
- if (pthread_create(&monitor_thread, NULL, do_monitor, NULL) < 0) {
- pthread_mutex_unlock(&monlock);
+ if (pthread_create(&monitor_thread, &attr, do_monitor, NULL) < 0) {
+ ast_pthread_mutex_unlock(&monlock);
ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
return -1;
}
- pthread_mutex_unlock(&monlock);
+#if 0
+ printf("Created thread %ld detached in restart monitor\n", monitor_thread);
+#endif
+ ast_pthread_mutex_unlock(&monlock);
return 0;
}
@@ -1397,6 +2589,7 @@ static struct tor_pvt *mkif(int channel, int signalling)
struct tor_bufferinfo bi;
#endif
int res;
+ int span;
TOR_PARAMS p;
tmp = malloc(sizeof(struct tor_pvt));
@@ -1419,8 +2612,50 @@ static struct tor_pvt *mkif(int channel, int signalling)
}
if (p.sigtype != (signalling & 0xf)) {
ast_log(LOG_ERROR, "Signalling requested is %s but line is in %s signalling\n", sig2str(signalling), sig2str(p.sigtype));
+ free(tmp);
return NULL;
}
+ span = (channel - 1)/24;
+ tmp->span = span + 1;
+#ifdef TORMENTA_PRI
+ if (signalling == SIG_PRI) {
+ int offset;
+ offset = 1;
+ if (ioctl(zap_fd(tmp->z), TOR_AUDIOMODE, &offset)) {
+ ast_log(LOG_ERROR, "Unable to set audio mode on clear channel %d of span %d: %s\n", channel, span, strerror(errno));
+ return NULL;
+ }
+ if (span >= NUM_SPANS) {
+ ast_log(LOG_ERROR, "Channel %d does not lie on a span I know of (%d)\n", channel, span);
+ free(tmp);
+ return NULL;
+ } else {
+ offset = (channel -1) % 24 + 1;
+ if (offset < 24) {
+ if (pris[span].nodetype && (pris[span].nodetype != pritype)) {
+ ast_log(LOG_ERROR, "Span %d is already a %s node\n", span + 1, pri_node2str(pris[span].nodetype));
+ free(tmp);
+ return NULL;
+ }
+ if (pris[span].switchtype && (pris[span].switchtype != switchtype)) {
+ ast_log(LOG_ERROR, "Span %d is already a %s switch\n", span + 1, pri_switch2str(pris[span].switchtype));
+ free(tmp);
+ return NULL;
+ }
+ pris[span].nodetype = pritype;
+ pris[span].switchtype = switchtype;
+ pris[span].chanmask[offset] |= MASK_AVAIL;
+ pris[span].pvt[offset] = tmp;
+ tmp->pri = &pris[span];
+ tmp->call = NULL;
+ } else {
+ ast_log(LOG_ERROR, "Channel 24 is reserved for D-channel.\n");
+ free(tmp);
+ return NULL;
+ }
+ }
+ }
+#endif
/* Adjust starttime on loopstart and kewlstart trunks to reasonable values */
if ((signalling == SIG_FXSKS) || (signalling == SIG_FXSLS)) {
p.starttime = 250;
@@ -1454,24 +2689,82 @@ static struct tor_pvt *mkif(int channel, int signalling)
ast_log(LOG_WARNING, "Unable to check buffer policy on channel %d\n", channel);
#endif
tmp->immediate = immediate;
- tmp->state = tor_STATE_DOWN;
tmp->sig = signalling;
- tmp->use_callerid = use_callerid;
+ if ((signalling == SIG_FXOKS) || (signalling == SIG_FXOLS) || (signalling == SIG_FXOGS))
+ tmp->permcallwaiting = callwaiting;
+ else
+ tmp->permcallwaiting = 0;
+ tmp->callwaitingcallerid = callwaitingcallerid;
+ tmp->threewaycalling = threewaycalling;
+ tmp->permhidecallerid = hidecallerid;
+ tmp->echocancel = echocancel;
+ tmp->callwaiting = tmp->permcallwaiting;
+ tmp->hidecallerid = tmp->permhidecallerid;
tmp->channel = channel;
tmp->stripmsd = stripmsd;
+ tmp->use_callerid = use_callerid;
+ tmp->callwaitindex = -1;
+ tmp->normalindex = -1;
+ tmp->thirdcallindex = -1;
+ tmp->normalindex = -1;
+ tmp->confno = -1;
+ tmp->pseudo = NULL;
+ tmp->pseudochan = 0;
+ tmp->transfer = transfer;
+ pthread_mutex_init(&tmp->lock, NULL);
strncpy(tmp->language, language, sizeof(tmp->language));
strncpy(tmp->context, context, sizeof(tmp->context));
strncpy(tmp->callerid, callerid, sizeof(tmp->callerid));
tmp->group = cur_group;
tmp->next = NULL;
- tmp->ringgothangup = 0;
- /* Hang it up to be sure it's good */
- tor_set_hook(zap_fd(tmp->z), TOR_ONHOOK);
-
+ tmp->rxgain = rxgain;
+ tmp->txgain = txgain;
+ set_actual_gain(zap_fd(tmp->z), 0, tmp->rxgain, tmp->txgain);
+ zap_digitmode(tmp->z, ZAP_DTMF /* | ZAP_MUTECONF */);
+ conf_clear(tmp);
+ if (signalling != SIG_PRI)
+ /* Hang it up to be sure it's good */
+ tor_set_hook(zap_fd(tmp->z), TOR_ONHOOK);
}
return tmp;
}
+static inline int available(struct tor_pvt *p, int channelmatch, int groupmatch)
+{
+ /* First, check group matching */
+ if ((p->group & groupmatch) != groupmatch)
+ return 0;
+ /* Check to see if we have a channel match */
+ if ((channelmatch > 0) && (p->channel != channelmatch))
+ return 0;
+
+ /* If no owner definitely available */
+ if (!p->owner)
+ return 1;
+
+ if (!p->callwaiting) {
+ /* If they don't have call waiting enabled, then for sure they're unavailable at this point */
+ return 0;
+ }
+
+ if (p->callwaitindex > -1) {
+ /* If there is already a call waiting call, then we can't take a second one */
+ return 0;
+ }
+
+ if ((p->owner->state != AST_STATE_UP) &&
+ (p->owner->state != AST_STATE_RINGING)) {
+ /* If the current call is not up, then don't allow the call */
+ return 0;
+ }
+ if ((p->thirdcallindex > -1) && (p->owner == p->owners[p->thirdcallindex])) {
+ /* Can't take a call wait when the three way calling hasn't been merged yet. */
+ return 0;
+ }
+ /* We're cool */
+ return 1;
+}
+
static struct ast_channel *tor_request(char *type, int format, void *data)
{
int oldformat;
@@ -1482,6 +2775,7 @@ static struct ast_channel *tor_request(char *type, int format, void *data)
char *dest=NULL;
int x;
char *s;
+ int callwait;
/* We do signed linear */
oldformat = format;
@@ -1515,24 +2809,32 @@ static struct ast_channel *tor_request(char *type, int format, void *data)
channelmatch = x;
}
/* Search for an unowned channel */
- if (pthread_mutex_lock(&iflock)) {
+ if (ast_pthread_mutex_lock(&iflock)) {
ast_log(LOG_ERROR, "Unable to lock interface list???\n");
return NULL;
}
p = iflist;
while(p && !tmp) {
- if (!p->owner && /* No current owner */
- ((channelmatch < 0) || (p->channel == channelmatch)) && /* Right channel */
- (((p->group & groupmatch) == groupmatch)) /* Right group */
- ) {
+ if (available(p, channelmatch, groupmatch)) {
if (option_debug)
ast_log(LOG_DEBUG, "Using channel %d\n", p->channel);
- tmp = tor_new(p, AST_STATE_RESERVED, 0);
+#ifdef TORMENTA_PRI
+ if (p->pri)
+ if (!(p->call = pri_new_call(p->pri->pri))) {
+ ast_log(LOG_WARNING, "Unable to create call on channel %d\n", p->channel);
+ break;
+ }
+#endif
+ callwait = (p->owner != NULL);
+ tmp = tor_new(p, AST_STATE_RESERVED, 0, p->owner ? 1 : 0, 0);
+ /* Note if the call is a call waiting call */
+ if (callwait)
+ tmp->cdrflags |= AST_CDR_CALLWAIT;
break;
}
p = p->next;
}
- pthread_mutex_unlock(&iflock);
+ ast_pthread_mutex_unlock(&iflock);
restart_monitor();
return tmp;
}
@@ -1572,6 +2874,358 @@ static int get_group(char *s)
return group;
}
+#ifdef TORMENTA_PRI
+
+static int pri_find_empty_chan(struct tor_pri *pri)
+{
+ int x;
+ for (x=23;x>0;x--) {
+ if (pri->pvt[x] && !pri->pvt[x]->owner)
+ return x;
+ }
+ return 0;
+}
+
+static int pri_fixup(struct tor_pri *pri, int channel, q931_call *c)
+{
+ int x;
+ for (x=1;x<24;x++) {
+ if (!pri->pvt[x]) continue;
+ if (pri->pvt[x]->call == c) {
+ /* Found our call */
+ if (channel != x) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Moving call from channel %d to channel %d\n",
+ x, channel);
+ if (pri->pvt[channel]->owner) {
+ ast_log(LOG_WARNING, "Can't fix up channel from %d to %d because %d is already in use\n",
+ x, channel, channel);
+ return 0;
+ }
+ /* Fix it all up now */
+ pri->pvt[channel]->owner = pri->pvt[x]->owner;
+ pri->pvt[channel]->owner->pvt->pvt = pri->pvt[channel];
+ pri->pvt[channel]->owner->fds[0] = zap_fd(pri->pvt[channel]->z);
+ pri->pvt[channel]->call = pri->pvt[x]->call;
+
+ /* Free up the old channel, now not in use */
+ pri->pvt[x]->owner = NULL;
+ pri->pvt[x]->call = NULL;
+ }
+ return channel;
+ }
+ }
+ return 0;
+}
+
+static void *pri_dchannel(void *vpri)
+{
+ struct tor_pri *pri = vpri;
+ pri_event *e;
+ fd_set efds;
+ fd_set rfds;
+ int res;
+ int chan;
+ int x;
+ struct ast_channel *c;
+ for(;;) {
+ FD_ZERO(&rfds);
+ FD_ZERO(&efds);
+ FD_SET(pri->fd, &rfds);
+ FD_SET(pri->fd, &efds);
+ res = select(pri->fd + 1, &rfds, NULL, &efds, pri_schedule_next(pri->pri));
+ pthread_mutex_lock(&pri->lock);
+ if (!res) {
+ /* Just a timeout, run the scheduler */
+ pri_schedule_run(pri->pri);
+ } else if (res > -1) {
+ e = pri_check_event(pri->pri);
+ if (e) {
+ if (pri->debug)
+ pri_dump_event(pri->pri, e);
+ switch(e->e) {
+ case PRI_EVENT_DCHAN_UP:
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "D-Channel on span %d up\n", pri->span);
+ pri->up = 1;
+ break;
+ case PRI_EVENT_DCHAN_DOWN:
+ if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "D-Channel on span %d down\n", pri->span);
+ pri->up = 0;
+ break;
+ case PRI_EVENT_RESTART:
+ chan = e->restart.channel;
+ if (chan > -1) {
+ if ((chan < 1) || (chan > 23) )
+ ast_log(LOG_WARNING, "Restart requested on odd channel number %d on span %d\n", chan, pri->span);
+ else if (!pri->pvt[chan])
+ ast_log(LOG_WARNING, "Restart requested on unconfigured channel %d on span %d\n", chan, pri->span);
+ else {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "B-channel %d restarted on span %d\n",
+ chan, pri->span);
+ /* Force soft hangup if appropriate */
+ if (pri->pvt[chan]->owner)
+ pri->pvt[chan]->owner->softhangup = 1;
+ }
+ } else {
+ if (option_verbose > 2)
+ ast_verbose("Restart on requested on entire span %d\n", pri->span);
+ for (x=1;x<24;x++)
+ if (pri->pvt[chan]->owner)
+ pri->pvt[chan]->owner->softhangup = 1;
+ }
+ break;
+ case PRI_EVENT_RING:
+ chan = e->ring.channel;
+ if ((chan < 1) || (chan > 23)) {
+ ast_log(LOG_WARNING, "Ring requested on odd channel number %d span %d\n", chan, pri->span);
+ chan = 0;
+ } else if (!pri->pvt[chan]) {
+ ast_log(LOG_WARNING, "Ring requested on unconfigured channel %d span %d\n", chan, pri->span);
+ chan = 0;
+ } else if (pri->pvt[chan]->owner) {
+ ast_log(LOG_WARNING, "Ring requested on channel %d already in use on span %d\n", chan, pri->span);
+ chan = 0;
+ }
+ if (!chan && (e->ring.flexible))
+ chan = pri_find_empty_chan(pri);
+ if (chan) {
+ /* Get caller ID */
+ if (pri->pvt[chan]->use_callerid)
+ strncpy(pri->pvt[chan]->callerid, e->ring.callingnum, sizeof(pri->pvt[chan]->callerid));
+ else
+ strcpy(pri->pvt[chan]->callerid, "");
+ /* Get called number */
+ if (strlen(e->ring.callednum)) {
+ strncpy(pri->pvt[chan]->exten, e->ring.callednum, sizeof(pri->pvt[chan]->exten));
+ } else
+ strcpy(pri->pvt[chan]->exten, "s");
+ /* Make sure extension exists */
+ if (ast_exists_extension(NULL, pri->pvt[chan]->context, pri->pvt[chan]->exten, 1)) {
+ /* Start PBX */
+ pri->pvt[chan]->call = e->ring.call;
+ c = tor_new(pri->pvt[chan], AST_STATE_RING, 1, 0, 0);
+ if (c) {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Accepting call from '%s' to '%s' on channel %d, span %d\n",
+ e->ring.callingnum, pri->pvt[chan]->exten, chan, pri->span);
+ pri_acknowledge(pri->pri, e->ring.call, chan, 0);
+ } else {
+ ast_log(LOG_WARNING, "Unable to start PBX on channel %d, span %d\n", chan, pri->span);
+ pri_release(pri->pri, e->ring.call, PRI_CAUSE_SWITCH_CONGESTION);
+ pri->pvt[chan]->call = NULL;
+ }
+ } else {
+ if (option_verbose > 2)
+ ast_verbose(VERBOSE_PREFIX_3 "Extension '%s' in context '%s' does not exist. Rejecting call on channel %d, span %d\n",
+ pri->pvt[chan]->exten, pri->pvt[chan]->context, chan, pri->span);
+ pri_release(pri->pri, e->ring.call, PRI_CAUSE_UNALLOCATED);
+ }
+ } else
+ pri_release(pri->pri, e->ring.call, PRI_CAUSE_REQUESTED_CHAN_UNAVAIL);
+ break;
+ case PRI_EVENT_RINGING:
+ chan = e->ringing.channel;
+ if ((chan < 1) || (chan > 23)) {
+ ast_log(LOG_WARNING, "Ringing requested on odd channel number %d span %d\n", chan, pri->span);
+ chan = 0;
+ } else if (!pri->pvt[chan]) {
+ ast_log(LOG_WARNING, "Ringing requested on unconfigured channel %d span %d\n", chan, pri->span);
+ chan = 0;
+ }
+ if (chan) {
+ chan = pri_fixup(pri, chan, e->ringing.call);
+ if (!chan) {
+ ast_log(LOG_WARNING, "Ringing requested on channel %d not in use on span %d\n", e->ringing.channel, pri->span);
+ chan = 0;
+ } else
+ pri->pvt[chan]->needringing[0] =1;
+ }
+ break;
+ case PRI_EVENT_ANSWER:
+ chan = e->answer.channel;
+ if ((chan < 1) || (chan > 23)) {
+ ast_log(LOG_WARNING, "Answer on odd channel number %d span %d\n", chan, pri->span);
+ chan = 0;
+ } else if (!pri->pvt[chan]) {
+ ast_log(LOG_WARNING, "Answer on unconfigured channel %d span %d\n", chan, pri->span);
+ chan = 0;
+ }
+ if (chan) {
+ chan = pri_fixup(pri, chan, e->ringing.call);
+ if (!chan) {
+ ast_log(LOG_WARNING, "Ring requested on channel %d not in use on span %d\n", chan, pri->span);
+ chan = 0;
+ } else
+ pri->pvt[chan]->needanswer[0] =1;
+ }
+ break;
+ case PRI_EVENT_HANGUP:
+ chan = e->hangup.channel;
+ if ((chan < 1) || (chan > 23)) {
+ ast_log(LOG_WARNING, "Hangup requested on odd channel number %d span %d\n", chan, pri->span);
+ chan = 0;
+ } else if (!pri->pvt[chan]) {
+ ast_log(LOG_WARNING, "Hanngup requested on unconfigured channel %d span %d\n", chan, pri->span);
+ chan = 0;
+ }
+ if (chan) {
+ chan = pri_fixup(pri, chan, e->hangup.call);
+ if (chan) {
+ if (pri->pvt[chan]->owner) {
+ if (option_verbose > 3)
+ ast_verbose(VERBOSE_PREFIX_3, "Channel %d, span %d got hangup\n", chan, pri->span);
+ pri->pvt[chan]->owner->softhangup = 1;
+ pri->pvt[chan]->call = NULL;
+ }
+ }
+ }
+ break;
+ case PRI_EVENT_CONFIG_ERR:
+ ast_log(LOG_WARNING, "PRI Error: %s\n", e->err.err);
+ break;
+ default:
+ ast_log(LOG_DEBUG, "Event: %d\n", e->e);
+ }
+ } else {
+ /* Check for an event */
+ x = 0;
+ res = ioctl(pri->fd, TOR_GETEVENT, &x);
+ if (option_debug)
+ ast_log(LOG_DEBUG, "Got event %s (%d) on D-channel for span %d\n", event2str(x), x, pri->span);
+ }
+ } else {
+ if (errno != EINTR)
+ ast_log(LOG_WARNING, "pri_event returned error %d (%s)\n", errno, strerror(errno));
+ }
+ pthread_mutex_unlock(&pri->lock);
+ }
+ /* Never reached */
+ return NULL;
+}
+
+static int start_pri(struct tor_pri *pri)
+{
+ char filename[80];
+ int res;
+ TOR_PARAMS p;
+ BUFFER_INFO bi;
+ snprintf(filename, sizeof(filename), "/dev/tor/%d", pri->offset + 24);
+ pri->fd = open(filename, O_RDWR, 0600);
+ if (pri->fd < 0) {
+ ast_log(LOG_ERROR, "Unable to open D-channel %s (%s)\n", filename, strerror(errno));
+ return -1;
+ }
+ res = ioctl(pri->fd, TOR_GET_PARAMS, &p);
+ if (res) {
+ close(pri->fd);
+ pri->fd = -1;
+ ast_log(LOG_ERROR, "Unable to get parameters for D-channel %s (%s)\n", filename, strerror(errno));
+ return -1;
+ }
+ if (p.sigtype != TOR_HDLCFCS) {
+ close(pri->fd);
+ pri->fd = -1;
+ ast_log(LOG_ERROR, "D-channel %s is not in HDLC/FCS mode. See /etc/tormenta.conf\n", filename);
+ return -1;
+ }
+ bi.txbufpolicy = POLICY_IMMEDIATE;
+ bi.rxbufpolicy = POLICY_IMMEDIATE;
+ bi.numbufs = 4;
+ bi.bufsize = 1024;
+ if (ioctl(pri->fd, TOR_SET_BUFINFO, &bi)) {
+ ast_log(LOG_ERROR, "Unable to set appropriate buffering on %s\n", filename);
+ close(pri->fd);
+ pri->fd = -1;
+ return -1;
+ }
+ pri->pri = pri_new(pri->fd, pri->nodetype, pri->switchtype);
+ if (!pri->pri) {
+ close(pri->fd);
+ pri->fd = -1;
+ ast_log(LOG_ERROR, "Unable to create PRI structure\n");
+ return -1;
+ }
+ pri_set_debug(pri->pri, DEFAULT_PRI_DEBUG);
+ if (pthread_create(&pri->master, NULL, pri_dchannel, pri)) {
+ close(pri->fd);
+ pri->fd = -1;
+ ast_log(LOG_ERROR, "Unable to spawn D-channel: %s\n", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static char *complete_span(char *line, char *word, int pos, int state)
+{
+ int span=1;
+ char tmp[50];
+ while(span <= NUM_SPANS) {
+ if (span > state)
+ break;
+ span++;
+ }
+ if (span <= NUM_SPANS) {
+ snprintf(tmp, sizeof(tmp), "%d", span);
+ return strdup(tmp);
+ } else
+ return NULL;
+}
+
+static int handle_pri_debug(int fd, int argc, char *argv[])
+{
+ int span;
+ span = atoi(argv[3]);
+ if ((span < 1) || (span > NUM_SPANS)) {
+ ast_cli(fd, "Invalid span %s. Should be a number %d to %d\n", argv[3], 1, NUM_SPANS);
+ return RESULT_SUCCESS;
+ }
+ if (!pris[span-1].pri) {
+ ast_cli(fd, "No PRI running on span %d\n", span);
+ return RESULT_SUCCESS;
+ }
+ pri_set_debug(pris[span-1].pri, PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q931_STATE);
+ ast_cli(fd, "Enabled debugging on span %d\n", span);
+ return RESULT_SUCCESS;
+}
+
+static int handle_pri_no_debug(int fd, int argc, char *argv[])
+{
+ int span;
+ span = atoi(argv[4]);
+ if ((span < 1) || (span > NUM_SPANS)) {
+ ast_cli(fd, "Invalid span %s. Should be a number %d to %d\n", argv[4], 1, NUM_SPANS);
+ return RESULT_SUCCESS;
+ }
+ if (!pris[span-1].pri) {
+ ast_cli(fd, "No PRI running on span %d\n", span);
+ return RESULT_SUCCESS;
+ }
+ pri_set_debug(pris[span-1].pri, 0);
+ ast_cli(fd, "Disabled debugging on span %d\n", span);
+ return RESULT_SUCCESS;
+}
+
+
+static char pri_debug_help[] =
+ "Usage: pri debug span <span>\n"
+ " Enables debugging on a given PRI span\n";
+
+static char pri_no_debug_help[] =
+ "Usage: pri no debug span <span>\n"
+ " Disables debugging on a given PRI span\n";
+
+static struct ast_cli_entry pri_debug = {
+ { "pri", "debug", "span", NULL }, handle_pri_debug, "Enables PRI debugging on a span", pri_debug_help, complete_span
+};
+
+static struct ast_cli_entry pri_no_debug = {
+ { "pri", "no", "debug", "span", NULL }, handle_pri_no_debug, "Enables PRI debugging on a span", pri_no_debug_help, complete_span };
+
+#endif
+
int load_module()
{
struct ast_config *cfg;
@@ -1579,6 +3233,17 @@ int load_module()
struct tor_pvt *tmp;
char *chan;
int start, finish,x;
+#ifdef TORMENTA_PRI
+ int y;
+#endif
+
+
+#ifdef TORMENTA_PRI
+ memset(pris, 0, sizeof(pris));
+ for (y=0;y<NUM_SPANS;y++)
+ pris[y].fd = -1;
+#endif
+
cfg = ast_load(config);
/* We *must* have a config file otherwise stop immediately */
@@ -1588,7 +3253,7 @@ int load_module()
}
- if (pthread_mutex_lock(&iflock)) {
+ if (ast_pthread_mutex_lock(&iflock)) {
/* It's a little silly to lock it, but we mind as well just to be sure */
ast_log(LOG_ERROR, "Unable to lock interface list???\n");
return -1;
@@ -1600,7 +3265,7 @@ int load_module()
if (cur_signalling < 0) {
ast_log(LOG_ERROR, "Signalling must be specified before any channels are.\n");
ast_destroy(cfg);
- pthread_mutex_unlock(&iflock);
+ ast_pthread_mutex_unlock(&iflock);
unload_module();
return -1;
}
@@ -1614,7 +3279,7 @@ int load_module()
} else {
ast_log(LOG_ERROR, "Syntax error parsing '%s' at '%s'\n", v->value, chan);
ast_destroy(cfg);
- pthread_mutex_unlock(&iflock);
+ ast_pthread_mutex_unlock(&iflock);
unload_module();
return -1;
}
@@ -1634,13 +3299,27 @@ int load_module()
} else {
ast_log(LOG_ERROR, "Unable to register channel '%s'\n", v->value);
ast_destroy(cfg);
- pthread_mutex_unlock(&iflock);
+ ast_pthread_mutex_unlock(&iflock);
unload_module();
return -1;
}
}
chan = strtok(NULL, ",");
}
+ } else if (!strcasecmp(v->name, "usecallerid")) {
+ use_callerid = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "threewaycalling")) {
+ threewaycalling = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "transfer")) {
+ transfer = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "echocancel")) {
+ echocancel = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "hidecallerid")) {
+ hidecallerid = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "callwaiting")) {
+ callwaiting = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "callwaitingcallerid")) {
+ callwaitingcallerid = ast_true(v->value);
} else if (!strcasecmp(v->name, "context")) {
strncpy(context, v->value, sizeof(context));
} else if (!strcasecmp(v->name, "language")) {
@@ -1651,6 +3330,14 @@ int load_module()
cur_group = get_group(v->value);
} else if (!strcasecmp(v->name, "immediate")) {
immediate = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "rxgain")) {
+ if (sscanf(v->value, "%f", &rxgain) != 1) {
+ ast_log(LOG_WARNING, "Invalid rxgain: %s\n", v->value);
+ }
+ } else if (!strcasecmp(v->name, "txgain")) {
+ if (sscanf(v->value, "%f", &txgain) != 1) {
+ ast_log(LOG_WARNING, "Invalid txgain: %s\n", v->value);
+ }
} else if (!strcasecmp(v->name, "callerid")) {
if (!strcasecmp(v->value, "asreceived"))
strcpy(callerid,"");
@@ -1681,18 +3368,40 @@ int load_module()
cur_signalling = SIG_FXOKS;
} else if (!strcasecmp(v->value, "featd")) {
cur_signalling = SIG_FEATD;
+#ifdef TORMENTA_PRI
+ } else if (!strcasecmp(v->value, "pri_net")) {
+ cur_signalling = SIG_PRI;
+ pritype = PRI_NETWORK;
+ } else if (!strcasecmp(v->value, "pri_cpe")) {
+ cur_signalling = SIG_PRI;
+ pritype = PRI_CPE;
+#endif
} else {
ast_log(LOG_ERROR, "Unknown signalling method '%s'\n", v->value);
+ }
+#ifdef TORMENTA_PRI
+ } else if (!strcasecmp(v->name, "switchtype")) {
+ if (!strcasecmp(v->value, "national"))
+ switchtype = PRI_SWITCH_NI2;
+ else if (!strcasecmp(v->value, "dms100"))
+ switchtype = PRI_SWITCH_DMS100;
+ else if (!strcasecmp(v->value, "4ess"))
+ switchtype = PRI_SWITCH_ATT4ESS;
+ else if (!strcasecmp(v->value, "5ess"))
+ switchtype = PRI_SWITCH_LUCENT5E;
+ else {
+ ast_log(LOG_ERROR, "Unknown switchtype '%s'\n", v->value);
ast_destroy(cfg);
- pthread_mutex_unlock(&iflock);
+ ast_pthread_mutex_unlock(&iflock);
unload_module();
return -1;
}
+#endif
} else
ast_log(LOG_DEBUG, "Ignoring %s\n", v->name);
v = v->next;
}
- pthread_mutex_unlock(&iflock);
+ ast_pthread_mutex_unlock(&iflock);
/* Make sure we can register our Tor channel type */
if (ast_channel_register(type, tdesc, AST_FORMAT_SLINEAR | AST_FORMAT_ULAW, tor_request)) {
ast_log(LOG_ERROR, "Unable to register channel class %s\n", type);
@@ -1701,6 +3410,24 @@ int load_module()
return -1;
}
ast_destroy(cfg);
+#ifdef TORMENTA_PRI
+ for (x=0;x<NUM_SPANS;x++) {
+ for (y=1;y<23;y++) {
+ if (pris[x].chanmask[y]) {
+ pris[x].offset = x * 24;
+ pris[x].span = x + 1;
+ if (start_pri(pris + x)) {
+ ast_log(LOG_ERROR, "Unable to start D-channel on span %d\n", x + 1);
+ return -1;
+ } else if (option_verbose > 1)
+ ast_verbose(VERBOSE_PREFIX_2 "Starting D-Channel on span %d\n", x + 1);
+ break;
+ }
+ }
+ }
+ ast_cli_register(&pri_debug);
+ ast_cli_register(&pri_no_debug);
+#endif
/* And start the monitor for the first time */
restart_monitor();
return 0;
@@ -1711,7 +3438,7 @@ int unload_module()
struct tor_pvt *p, *pl;
/* First, take us out of the channel loop */
ast_channel_unregister(type);
- if (!pthread_mutex_lock(&iflock)) {
+ if (!ast_pthread_mutex_lock(&iflock)) {
/* Hangup all interfaces if they have an owner */
p = iflist;
while(p) {
@@ -1720,25 +3447,25 @@ int unload_module()
p = p->next;
}
iflist = NULL;
- pthread_mutex_unlock(&iflock);
+ ast_pthread_mutex_unlock(&iflock);
} else {
ast_log(LOG_WARNING, "Unable to lock the monitor\n");
return -1;
}
- if (!pthread_mutex_lock(&monlock)) {
+ if (!ast_pthread_mutex_lock(&monlock)) {
if (monitor_thread) {
pthread_cancel(monitor_thread);
pthread_kill(monitor_thread, SIGURG);
pthread_join(monitor_thread, NULL);
}
monitor_thread = -2;
- pthread_mutex_unlock(&monlock);
+ ast_pthread_mutex_unlock(&monlock);
} else {
ast_log(LOG_WARNING, "Unable to lock the monitor\n");
return -1;
}
- if (!pthread_mutex_lock(&iflock)) {
+ if (!ast_pthread_mutex_lock(&iflock)) {
/* Destroy all the interfaces and free their memory */
p = iflist;
while(p) {
@@ -1754,7 +3481,7 @@ int unload_module()
free(pl);
}
iflist = NULL;
- pthread_mutex_unlock(&iflock);
+ ast_pthread_mutex_unlock(&iflock);
} else {
ast_log(LOG_WARNING, "Unable to lock the monitor\n");
return -1;
@@ -1765,9 +3492,9 @@ int unload_module()
int usecount()
{
int res;
- pthread_mutex_lock(&usecnt_lock);
+ ast_pthread_mutex_lock(&usecnt_lock);
res = usecnt;
- pthread_mutex_unlock(&usecnt_lock);
+ ast_pthread_mutex_unlock(&usecnt_lock);
return res;
}