Newer
Older
/*
* Asterisk -- A telephony toolkit for Linux.
*
* VoiceTronix Interface driver
*
* Copyright (C) 2003, Paul Bagyenda
*
* Paul Bagyenda <bagyenda@dsmagic.com>
*
* This program is free software, distributed under the terms of
* the GNU General Public License
*/
#include <stdio.h>
#include <pthread.h>
#include <string.h>
#include <asterisk/lock.h>
#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 <sys/socket.h>
#include <sys/time.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#define DEFAULT_GAIN 0
#define DEFAULT_ECHO_CANCEL 1
#define VPB_SAMPLES 240
#define VPB_MAX_BUF VPB_SAMPLES*4 + AST_FRIENDLY_OFFSET
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
static char *desc = "VoiceTronix V6PCI/V12PCI/V4PCI API Support";
static char *type = "vpb";
static char *tdesc = "Standard VoiceTronix API Driver";
static char *config = "vpb.conf";
/* Default context for dialtone mode */
static char context[AST_MAX_EXTENSION] = "default";
/* Default language */
static char language[MAX_LANGUAGE] = "";
static int usecnt =0;
static int gruntdetect_timeout = 3600000; /* Grunt detect timeout is 1hr. */
static const int prefformat = AST_FORMAT_SLINEAR;
static ast_mutex_t usecnt_lock = AST_MUTEX_INITIALIZER;
/* Protect the interface list (of vpb_pvt's) */
static ast_mutex_t iflock = AST_MUTEX_INITIALIZER;
/* Protect the monitoring thread, so only one process can kill or start it, and not
when it's doing something critical. */
static ast_mutex_t monlock = AST_MUTEX_INITIALIZER;
/* This is the thread for the monitor which checks for input on the channels
which are not currently in use. */
static int mthreadactive = -1; /* Flag for monitoring monitorthread.*/
static int restart_monitor(void);
/* The private structures of the VPB channels are
linked for selecting outgoing channels */
#define MODE_DIALTONE 1
#define MODE_IMMEDIATE 2
#define MODE_FXO 3
/* Pick a country or add your own! */
#define TONES_AU
#ifdef TONES_AU
static VPB_TONE Dialtone = {440, 440, 440, -10, -10, -10, 5000, 0 };
static VPB_TONE Busytone = {470, 0, 0, -10, -100, -100, 5000, 0 };
static VPB_TONE Ringbacktone = {400, 50, 440, -10, -10, -10, 1400, 800 };
#endif
/*
#define TONES_USA
#ifdef TONES_USA
static VPB_TONE Dialtone = {425, 0, 0, -16, -100, -100, 10000, 0};
static VPB_TONE Busytone = {425, 0, 0, -10, -100, -100, 500, 500};
static VPB_TONE Ringbacktone = {400, 425, 450, -20, -20, -20, 1000, 1000};
#endif
*/
/* grunt tone defn's */
static VPB_DETECT toned_grunt = { 3, VPB_GRUNT, 1, 2000, 3000, 0, 0, -40, 0, 0, 0, 40, { { VPB_DELAY, 1000, 0, 0 }, { VPB_RISING, 0, 40, 0 }, { 0, 100, 0, 0 } } };
static VPB_DETECT toned_ungrunt = { 2, VPB_GRUNT, 1, 2000, 1, 0, 0, -40, 0, 0, 30, 40, { { 0, 0, 0, 0 } } };
/* Use loop drop detection */
static int UseLoopDrop=1;
#define TIMER_PERIOD_RINGBACK 2000
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#define TIMER_PERIOD_BUSY 700
#define VPB_EVENTS_ALL (VPB_MRING|VPB_MDIGIT|VPB_MDTMF|VPB_MTONEDETECT|VPB_MTIMEREXP|VPB_MPLAY_UNDERFLOW \
|VPB_MRECORD_OVERFLOW|VPB_MSTATION_OFFHOOK|VPB_MSTATION_ONHOOK \
|VPB_MRING_OFF|VPB_MDROP|VPB_MSTATION_FLASH)
#define VPB_EVENTS_NODTMF (VPB_MRING|VPB_MDIGIT|VPB_MTONEDETECT|VPB_MTIMEREXP|VPB_MPLAY_UNDERFLOW \
|VPB_MRECORD_OVERFLOW|VPB_MSTATION_OFFHOOK|VPB_MSTATION_ONHOOK \
|VPB_MRING_OFF|VPB_MDROP|VPB_MSTATION_FLASH)
#define VPB_EVENTS_STAT (VPB_MRING|VPB_MDIGIT|VPB_MDTMF|VPB_MTONEDETECT|VPB_MTIMEREXP|VPB_MPLAY_UNDERFLOW \
|VPB_MRECORD_OVERFLOW|VPB_MSTATION_OFFHOOK|VPB_MSTATION_ONHOOK \
|VPB_MRING_OFF|VPB_MSTATION_FLASH)
// Dialing parameters for Australia
//#define DIAL_WITH_CALL_PROGRESS
VPB_TONE_MAP DialToneMap[] = { { VPB_BUSY_AUST, VPB_CALL_DISCONNECT, 0 },
{ VPB_DIAL, VPB_CALL_DIALTONE, 0 },
{ VPB_RINGBACK_308, VPB_CALL_RINGBACK, 0 },
{ VPB_BUSY_AUST, VPB_CALL_BUSY, 0 },
{ VPB_GRUNT, VPB_CALL_GRUNT, 0 },
{ 0, 0, 1 } };
#define VPB_DIALTONE_WAIT 2000 /* Wait up to 2s for a dialtone */
#define VPB_RINGWAIT 4000 /* Wait up to 4s for ring tone after dialing */
#define VPB_CONNECTED_WAIT 4000 /* If no ring tone detected for 4s then consider call connected */
#define TIMER_PERIOD_NOANSWER 120000 /* Let it ring for 120s before deciding theres noone there */
#define MAX_BRIDGES_V4PCI 2
#define MAX_BRIDGES_V12PCI 128
/* port states */
#define VPB_STATE_ONHOOK 0
#define VPB_STATE_OFFHOOK 1
#define VPB_STATE_DIALLING 2
#define VPB_STATE_JOINED 3
#define VPB_STATE_GETDTMF 4
#define VPB_STATE_PLAYDIAL 5
#define VPB_STATE_PLAYBUSY 6
#define VPB_STATE_PLAYRING 7
typedef struct {
int inuse;
struct ast_channel *c0, *c1, **rc;
struct ast_frame **fo;
int flags;
ast_mutex_t lock;
pthread_cond_t cond;
int endbridge;
} vpb_bridge_t;
static vpb_bridge_t * bridges;
static int max_bridges = MAX_BRIDGES_V4PCI;
static ast_mutex_t bridge_lock = AST_MUTEX_INITIALIZER;
vpb_model_unknown = 0,
vpb_model_v4pci,
vpb_model_v12pci
ast_mutex_t owner_lock; /* Protect blocks that expect ownership to remain the same */
struct ast_channel *owner; /* Channel who owns us, possibly NULL */
int golock; /* Got owner lock ? */
int mode; /* fxo/imediate/dialtone*/
int handle; /* Handle for vpb interface */
int state; /* used to keep port state (internal to driver) */
int group; /* Which group this port belongs to */
char dev[256]; /* Device name, eg vpb/1-1 */
vpb_model_t vpb_model; /* card model */
struct ast_frame f, fr; /* Asterisk frame interface */
char buf[VPB_MAX_BUF]; /* Static buffer for reading frames */
float txgain, rxgain; /* Hardware gain control */
float txswgain, rxswgain; /* Software gain control */
int wantdtmf; /* Waiting for DTMF. */
char context[AST_MAX_EXTENSION]; /* The context for this channel */
char ext[AST_MAX_EXTENSION]; /* DTMF buffer for the ext[ens] */
char language[MAX_LANGUAGE]; /* language being used */
char callerid[AST_MAX_EXTENSION]; /* CallerId used for directly connected phone */
int lastoutput; /* Holds the last Audio format output'ed */
int lastinput; /* Holds the last Audio format input'ed */
void *busy_timer; /* Void pointer for busy vpb_timer */
int busy_timer_id; /* unique timer ID for busy timer */
void *ringback_timer; /* Void pointer for ringback vpb_timer */
int ringback_timer_id; /* unique timer ID for ringback timer */
double lastgrunt; /* time stamp (secs since epoc) of last grunt event */
ast_mutex_t lock; /* This one just protects bridge ptr below */
vpb_bridge_t *bridge;
int stopreads; /* Stop reading...*/
pthread_t readthread; /* For monitoring read channel. One per owned channel. */
ast_mutex_t record_lock; /* This one prevents reentering a record_buf block */
ast_mutex_t play_lock; /* This one prevents reentering a play_buf block */
ast_mutex_t play_dtmf_lock;
char play_dtmf[16];
struct vpb_pvt *next; /* Next channel in list */
static struct ast_channel *vpb_new(struct vpb_pvt *i, int state, char *context);
static void *do_chanreads(void *pvt);
// Can't get vpb_bridge() working on v4pci without either a horrible
// high pitched feedback noise or bad hiss noise depending on gain settings
// Get asterisk to do the bridging
#define BAD_V4PCI_BRIDGE
// This one enables a half duplex bridge which may be required to prevent high pitched
// feedback when getting asterisk to do the bridging and when using certain gain settings.
//#define HALF_DUPLEX_BRIDGE
static int vpb_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc)
{
struct vpb_pvt *p0 = (struct vpb_pvt *)c0->pvt->pvt;
struct vpb_pvt *p1 = (struct vpb_pvt *)c1->pvt->pvt;
int i, res;
#ifdef BAD_V4PCI_BRIDGE
if(p0->vpb_model==vpb_model_v4pci)
return -2;
#endif
ast_mutex_lock(&p0->lock);
ast_mutex_lock(&p1->lock);
/* Bridge channels, check if we can. I believe we always can, so find a slot.*/
ast_mutex_lock(&bridge_lock); {
for (i = 0; i < max_bridges; i++)
if (!bridges[i].inuse)
break;
if (i < max_bridges) {
bridges[i].inuse = 1;
bridges[i].endbridge = 0;
bridges[i].flags = flags;
bridges[i].rc = rc;
bridges[i].fo = fo;
bridges[i].c0 = c0;
bridges[i].c1 = c1;
ast_mutex_init(&bridges[i].lock);
pthread_cond_init(&bridges[i].cond, NULL);
}
} ast_mutex_unlock(&bridge_lock);
if (i == max_bridges) {
ast_log(LOG_WARNING, "Failed to bridge %s and %s!\n", c0->name, c1->name);
ast_mutex_unlock(&p0->lock);
ast_mutex_unlock(&p1->lock);
/* Set bridge pointers. You don't want to take these locks while holding bridge lock.*/
ast_mutex_lock(&p0->lock); {
p0->bridge = &bridges[i];
} ast_mutex_unlock(&p0->lock);
ast_mutex_lock(&p1->lock); {
p1->bridge = &bridges[i];
} ast_mutex_unlock(&p1->lock);
if (option_verbose>1)
ast_verbose(VERBOSE_PREFIX_2 "Bridging call entered with [%s, %s]\n", c0->name, c1->name);
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
if (option_verbose>1)
ast_verbose(VERBOSE_PREFIX_2 "Starting half-duplex bridge [%s, %s]\n", c0->name, c1->name);
int dir = 0;
memset(p0->buf, 0, sizeof p0->buf);
memset(p1->buf, 0, sizeof p1->buf);
vpb_record_buf_start(p0->handle, VPB_ALAW);
vpb_record_buf_start(p1->handle, VPB_ALAW);
vpb_play_buf_start(p0->handle, VPB_ALAW);
vpb_play_buf_start(p1->handle, VPB_ALAW);
while( !bridges[i].endbridge ) {
struct vpb_pvt *from, *to;
if(++dir%2) {
from = p0;
to = p1;
} else {
from = p1;
to = p0;
}
vpb_record_buf_sync(from->handle, from->buf, VPB_SAMPLES);
vpb_play_buf_sync(to->handle, from->buf, VPB_SAMPLES);
}
vpb_record_buf_finish(p0->handle);
vpb_record_buf_finish(p1->handle);
vpb_play_buf_finish(p0->handle);
vpb_play_buf_finish(p1->handle);
if (option_verbose>1)
ast_verbose(VERBOSE_PREFIX_2 "Finished half-duplex bridge [%s, %s]\n", c0->name, c1->name);
res = VPB_OK;
#else
res = vpb_bridge(p0->handle, p1->handle, VPB_BRIDGE_ON, i+1 /* resource 1 & 2 only for V4PCI*/ );
if (res == VPB_OK) {
//pthread_cond_wait(&bridges[i].cond, &bridges[i].lock); /* Wait for condition signal. */
while( !bridges[i].endbridge ) {};
vpb_bridge(p0->handle, p1->handle, VPB_BRIDGE_OFF, i+1 /* resource 1 & 2 only for V4PCI*/ );
}
#endif
ast_mutex_lock(&bridge_lock); {
bridges[i].inuse = 0;
ast_mutex_destroy(&bridges[i].lock);
pthread_cond_destroy(&bridges[i].cond);
} ast_mutex_unlock(&bridge_lock);
if (option_verbose>1)
ast_verbose(VERBOSE_PREFIX_2 "Bridging call done with [%s, %s] => %d\n", c0->name, c1->name, res);
ast_mutex_unlock(&p0->lock);
ast_mutex_unlock(&p1->lock);
}
static double get_time_in_ms()
{
struct timeval tv;
gettimeofday(&tv, NULL);
return ((double)tv.tv_sec*1000)+((double)tv.tv_usec/1000);
}
// Caller ID can be located in different positions between the rings depending on your Telco
// Australian (Telstra) callerid starts 700ms after 1st ring and finishes 1.5s after first ring
// Use ANALYSE_CID to record rings and determine location of callerid
//#define ANALYSE_CID
#define RING_SKIP 600
#define CID_MSECS 1700
static void get_callerid(struct vpb_pvt *p)
{
short buf[CID_MSECS*8]; // 8kHz sampling rate
double cid_record_time;
int rc;
if(strcasecmp(p->callerid, "on")) {
p->owner->callerid = strdup("unknown");
return;
}
if( ast_mutex_trylock(&p->record_lock) == 0 ) {
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
char callerid[AST_MAX_EXTENSION] = "";
cid_record_time = get_time_in_ms();
if (option_verbose>3)
ast_verbose(VERBOSE_PREFIX_4 "CID record - start\n");
#ifdef ANALYSE_CID
VPB_RECORD record_parms = { "", 8 * 1000 }; // record a couple of rings
vpb_record_set(p->handle,&record_parms);
ast_verbose(VERBOSE_PREFIX_4 "Recording debug CID sample to /tmp/cid.raw\n");
vpb_record_file_sync(p->handle,"/tmp/cid.raw",VPB_LINEAR);
rc = 1; // don't try to decode_cid
#else
// Skip any trailing ringtone
vpb_sleep(RING_SKIP);
if (option_verbose>3)
ast_verbose(VERBOSE_PREFIX_4 "CID record - skipped %fms trailing ring\n",
get_time_in_ms() - cid_record_time);
cid_record_time = get_time_in_ms();
// Record bit between the rings which contains the callerid
vpb_record_buf_start(p->handle, VPB_LINEAR);
rc = vpb_record_buf_sync(p->handle, (char*)buf, sizeof(buf));
vpb_record_buf_finish(p->handle);
#endif
if (option_verbose>3)
ast_verbose(VERBOSE_PREFIX_4 "CID record - recorded %fms between rings\n",
get_time_in_ms() - cid_record_time);
ast_mutex_unlock(&p->record_lock);
if( rc != VPB_OK ) {
ast_log(LOG_ERROR, "Failed to record caller id sample on %s\n", p->dev );
return;
}
// This decodes FSK 1200baud type callerid
if ((rc=vpb_cid_decode(callerid, buf, CID_MSECS*8)) == VPB_OK ) {
if(!*callerid)
strcpy(callerid,"undisclosed"); // blocked CID (eg caller used 1831)
} else {
ast_log(LOG_ERROR, "Failed to decode caller id on %s - %s\n", p->dev, vpb_strerror(rc) );
strcpy(callerid,"unknown");
}
p->owner->callerid = strdup(callerid);
} else
ast_log(LOG_ERROR, "Failed to set record mode for caller id on %s\n", p->dev );
}
// Terminate any tones we are presently playing
static void stoptone( int handle)
{
while(vpb_playtone_state(handle)!=VPB_OK){
vpb_tone_terminate(handle);
ret = vpb_get_event_ch_async(handle,&je);
if ((ret == VPB_OK)&&(je.type != VPB_DIALEND)){
if (option_verbose > 3){
ast_verbose(VERBOSE_PREFIX_4 "Stop tone collected a wrong event!!\n");
}
vpb_put_event(&je);
}
static int playtone( int handle, VPB_TONE *tone)
{
if (option_verbose > 3)
ast_verbose(VERBOSE_PREFIX_4 "[%02d]: Playing tone\n", handle);
ret = vpb_playtone_async(handle, tone);
return ret;
static inline int monitor_handle_owned(struct vpb_pvt *p, VPB_EVENT *e)
{
struct ast_frame f = {AST_FRAME_CONTROL}; /* default is control, Clear rest. */
int endbridge = 0;
if (option_verbose > 3)
ast_verbose(VERBOSE_PREFIX_4 "%s: handle_owned: got event: [%d=>%d]\n",
p->dev, e->type, e->data);
f.src = type;
switch (e->type) {
case VPB_RING:
if (p->mode == MODE_FXO) {
f.subclass = AST_CONTROL_RING;
} else
f.frametype = -1; /* ignore ring on station port. */
break;
case VPB_RING_OFF:
f.frametype = -1;
break;
case VPB_TIMEREXP:
if (e->data == p->busy_timer_id) {
playtone(p->handle,&Busytone);
p->state = VPB_STATE_PLAYBUSY;
vpb_timer_stop(p->busy_timer);
vpb_timer_start(p->busy_timer);
f.frametype = -1;
} else if (e->data == p->ringback_timer_id) {
playtone(p->handle, &Ringbacktone);
vpb_timer_stop(p->ringback_timer);
vpb_timer_start(p->ringback_timer);
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
f.frametype = -1;
} else {
f.frametype = -1; /* Ignore. */
}
break;
case VPB_DTMF:
if (p->owner->_state == AST_STATE_UP) {
f.frametype = AST_FRAME_DTMF;
f.subclass = e->data;
} else
f.frametype = -1;
break;
case VPB_TONEDETECT:
if (e->data == VPB_BUSY || e->data == VPB_BUSY_308 || e->data == VPB_BUSY_AUST ) {
f.subclass = AST_CONTROL_HANGUP;
if (p->owner->_state == AST_STATE_UP) {
f.subclass = AST_CONTROL_HANGUP;
}
else {
f.subclass = AST_CONTROL_BUSY;
}
} else if (e->data == VPB_GRUNT) {
if( ( get_time_in_ms() - p->lastgrunt ) > gruntdetect_timeout ) {
// Nothing heard on line for a very long time
// Timeout connection
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "grunt timeout\n");
ast_log(LOG_NOTICE,"%s: Line hangup due of lack of conversation\n",p->dev);
f.subclass = AST_CONTROL_HANGUP;
} else {
p->lastgrunt = get_time_in_ms();
f.frametype = -1;
}
} else
f.frametype = -1;
break;
case VPB_CALLEND:
#ifdef DIAL_WITH_CALL_PROGRESS
if (e->data == VPB_CALL_CONNECTED)
f.subclass = AST_CONTROL_ANSWER;
else if (e->data == VPB_CALL_NO_DIAL_TONE || e->data == VPB_CALL_NO_RING_BACK)
f.subclass = AST_CONTROL_CONGESTION;
else if (e->data == VPB_CALL_NO_ANSWER || e->data == VPB_CALL_BUSY)
f.subclass = AST_CONTROL_BUSY;
else if (e->data == VPB_CALL_DISCONNECTED)
f.subclass = AST_CONTROL_HANGUP;
#else
ast_log(LOG_NOTICE,"%s: Got call progress callback but blind dialing \n", p->dev);
f.frametype = -1;
#endif
break;
case VPB_STATION_OFFHOOK:
f.subclass = AST_CONTROL_ANSWER;
break;
/* not sure why this was commented out */
case VPB_DROP:
if ((p->mode == MODE_FXO)&&(UseLoopDrop)){ /* ignore loop drop on stations */
if (p->owner->_state == AST_STATE_UP)
f.subclass = AST_CONTROL_HANGUP;
else
f.frametype = -1;
}
break;
/* */
case VPB_STATION_ONHOOK:
break;
case VPB_STATION_FLASH:
f.subclass = AST_CONTROL_FLASH;
break;
// Called when dialing has finished and ringing starts
// No indication that call has really been answered when using blind dialing
case VPB_DIALEND:
if (p->state < 5){
f.subclass = AST_CONTROL_ANSWER;
if (option_verbose > 1)
ast_verbose(VERBOSE_PREFIX_2 "%s: Dialend\n", p->dev);
} else {
f.frametype = -1;
}
break;
case VPB_PLAY_UNDERFLOW:
f.frametype = -1;
vpb_reset_play_fifo_alarm(p->handle);
break;
case VPB_RECORD_OVERFLOW:
f.frametype = -1;
vpb_reset_record_fifo_alarm(p->handle);
break;
default:
f.frametype = -1;
break;
}
if (option_verbose > 3)
ast_verbose(VERBOSE_PREFIX_4 "%s: handle_owned: putting frame type[%d]subclass[%d], bridge=%p\n",
p->dev, f.frametype, f.subclass, (void *)p->bridge);
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
ast_mutex_lock(&p->lock); {
if (p->bridge) { /* Check what happened, see if we need to report it. */
switch (f.frametype) {
case AST_FRAME_DTMF:
if (!(p->bridge->c0 == p->owner &&
(p->bridge->flags & AST_BRIDGE_DTMF_CHANNEL_0) ) &&
!(p->bridge->c1 == p->owner &&
(p->bridge->flags & AST_BRIDGE_DTMF_CHANNEL_1) ))
/* Kill bridge, this is interesting. */
endbridge = 1;
break;
case AST_FRAME_CONTROL:
if (!(p->bridge->flags & AST_BRIDGE_IGNORE_SIGS))
#if 0
if (f.subclass == AST_CONTROL_BUSY ||
f.subclass == AST_CONTROL_CONGESTION ||
f.subclass == AST_CONTROL_HANGUP ||
f.subclass == AST_CONTROL_FLASH)
#endif
endbridge = 1;
break;
default:
break;
}
if (endbridge) {
if (p->bridge->fo)
*p->bridge->fo = ast_frisolate(&f);
if (p->bridge->rc)
*p->bridge->rc = p->owner;
ast_mutex_lock(&p->bridge->lock); {
p->bridge->endbridge = 1;
pthread_cond_signal(&p->bridge->cond);
} ast_mutex_unlock(&p->bridge->lock);
}
} ast_mutex_unlock(&p->lock);
if (endbridge) return 0;
// Trylock used here to avoid deadlock that can occur if we
// happen to be in here handling an event when hangup is called
// Problem is that hangup holds p->owner->lock
if (f.frametype >= 0 && f.frametype != AST_FRAME_NULL) {
if (ast_mutex_trylock(&p->owner->lock)==0) {
Mark Spencer
committed
ast_queue_frame(p->owner, &f);
ast_mutex_unlock(&p->owner->lock);
} else {
}
static inline int monitor_handle_notowned(struct vpb_pvt *p, VPB_EVENT *e)
{
if (option_verbose > 3) {
char str[VPB_MAX_STR];
vpb_translate_event(e, str);
ast_verbose(VERBOSE_PREFIX_4 "%s: handle_notowned: mode=%d, event[%d][%s]=[%d]\n",
p->dev, p->mode, e->type,str, e->data);
}
switch(e->type) {
case VPB_RING:
if (p->mode == MODE_FXO) /* FXO port ring, start * */ {
vpb_new(p, AST_STATE_RING, p->context);
get_callerid(p); /* Australian Caller ID only between 1st and 2nd ring */
}
break;
case VPB_RING_OFF:
break;
case VPB_STATION_OFFHOOK:
if (p->mode == MODE_IMMEDIATE)
vpb_new(p,AST_STATE_RING, p->context);
else {
ast_verbose(VERBOSE_PREFIX_4 "%s: handle_notowned: playing dialtone\n",p->dev);
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
playtone(p->handle, &Dialtone);
p->wantdtmf = 1;
p->ext[0] = 0; /* Just to be sure & paranoid.*/
p->state=VPB_STATE_PLAYDIAL;
}
break;
case VPB_DIALEND:
if (p->mode == MODE_DIALTONE){
if (p->state == VPB_STATE_PLAYDIAL) {
playtone(p->handle, &Dialtone);
p->wantdtmf = 1;
p->ext[0] = 0; // Just to be sure & paranoid.
}
/* These are not needed as they have timers to restart them
else if (p->state == VPB_STATE_PLAYBUSY) {
playtone(p->handle, &Busytone);
p->wantdtmf = 1;
p->ext[0] = 0; // Just to be sure & paranoid.
}
else if (p->state == VPB_STATE_PLAYRING) {
playtone(p->handle, &Ringbacktone);
p->wantdtmf = 1;
p->ext[0] = 0; // Just to be sure & paranoid.
}
*/
ast_verbose(VERBOSE_PREFIX_4 "%s: handle_notowned: Got a DIALEND when not really expected\n",p->dev);
}
break;
case VPB_STATION_ONHOOK: /* clear ext */
stoptone(p->handle);
p->wantdtmf = 1 ;
p->ext[0] = 0;
p->state=VPB_STATE_ONHOOK;
break;
case VPB_DTMF:
if (p->state == VPB_STATE_ONHOOK){
/* DTMF's being passed while on-hook maybe Caller ID */
break;
}
if (p->wantdtmf == 1) {
stoptone(p->handle);
p->wantdtmf = 0;
}
p->state=VPB_STATE_GETDTMF;
s[0] = e->data;
strcat(p->ext, s);
if (ast_exists_extension(NULL, p->context, p->ext, 1, p->callerid)){
vpb_new(p,AST_STATE_RING, p->context);
} else if (!ast_canmatch_extension(NULL, p->context, p->ext, 1, p->callerid)){
if (ast_exists_extension(NULL, "default", p->ext, 1, p->callerid)) {
vpb_new(p,AST_STATE_RING, "default");
} else if (!ast_canmatch_extension(NULL, "default", p->ext, 1, p->callerid)) {
if (option_verbose > 3) {
ast_verbose(VERBOSE_PREFIX_4 "%s: handle_notowned: can't match anything in %s or default\n",
p->dev, p->context);
}
playtone(p->handle, &Busytone);
vpb_timer_stop(p->busy_timer);
vpb_timer_start(p->busy_timer);
}
break;
default:
/* Ignore.*/
break;
}
if (option_verbose > 3)
ast_verbose(VERBOSE_PREFIX_4 "%s: handle_notowned: mode=%d, [%d=>%d]\n",
p->dev, p->mode, e->type, e->data);
return 0;
}
static void *do_monitor(void *unused)
{
/* Monitor thread, doesn't die until explicitly killed. */
if (option_verbose > 1)
ast_verbose(VERBOSE_PREFIX_2 "Starting vpb monitor thread[%ld]\n",
pthread_self());
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
for(;;) {
VPB_EVENT e;
VPB_EVENT je;
char str[VPB_MAX_STR];
struct vpb_pvt *p;
if (option_verbose > 3)
ast_verbose(VERBOSE_PREFIX_4 "Monitor waiting for event\n");
int res = vpb_get_event_sync(&e, VPB_WAIT_TIMEOUT);
if( (res==VPB_NO_EVENTS) || (res==VPB_TIME_OUT) ){
/*
if (option_verbose > 3){
if (res == VPB_NO_EVENTS){
ast_verbose(VERBOSE_PREFIX_4 "No events....\n");
} else {
ast_verbose(VERBOSE_PREFIX_4 "No events, timed out....\n");
}
}
*/
if (res != VPB_OK) {
ast_log(LOG_ERROR,"Monitor get event error %s\n", vpb_strerror(res) );
ast_verbose("Monitor get event error %s\n", vpb_strerror(res) );
continue;
str[0] = 0;
p = NULL;
ast_mutex_lock(&monlock); {
vpb_translate_event(&e, str);
if (e.type == VPB_NULL_EVENT) {
if (option_verbose > 3)
ast_verbose(VERBOSE_PREFIX_4 "Monitor got null event\n");
goto done; /* Nothing to do, just a wakeup call.*/
}
if (strlen(str)>1){
str[(strlen(str)-1)]='\0';
}
ast_mutex_lock(&iflock); {
p = iflist;
while (p && p->handle != e.handle)
p = p->next;
} ast_mutex_unlock(&iflock);
if (p && (option_verbose > 3))
ast_verbose(VERBOSE_PREFIX_4 "%s: Event [%d=>%s] \n",
p ? p->dev : "null", e.type, str );
done: (void)0;
} ast_mutex_unlock(&monlock);
if (!p) {
if (e.type != VPB_NULL_EVENT){
ast_log(LOG_WARNING, "Got event [%s][%d], no matching iface!\n", str,e.type);
if (option_verbose > 3){
ast_verbose(VERBOSE_PREFIX_4 "vpb/ERR: No interface for Event [%d=>%s] \n",e.type,str );
}
}
continue;
}
/* flush the event from the channel event Q */
vpb_get_event_ch_async(e.handle,&je);
if (option_verbose > 3){
vpb_translate_event(&je, str);
ast_verbose( VERBOSE_PREFIX_4 "%s: Flushing event [%d]=>%s\n",p->dev,je.type,str);
}
/* Check for ownership and locks */
if ((p->owner)&&(!p->golock)){
/* Need to get owner lock */
/* Safely grab both p->lock and p->owner->lock so that there
cannot be a race with something from the other side */
/*
ast_mutex_lock(&p->lock);
while(ast_mutex_trylock(&p->owner->lock)) {
ast_mutex_unlock(&p->lock);
usleep(1);
ast_mutex_lock(&p->lock);
if (!p->owner)
break;
}
if (p->owner)
p->golock=1;
*/
}
/* Two scenarios: Are you owned or not. */
if (p->owner) {
monitor_handle_owned(p, &e);
} else {
monitor_handle_notowned(p, &e);
}
if ((!p->owner)&&(p->golock)){
ast_mutex_unlock(&p->owner->lock);
ast_mutex_unlock(&p->lock);
}
}
static int restart_monitor(void)
{
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
int error = 0;
/* If we're supposed to be stopped -- stay stopped */
if (mthreadactive == -2)
return 0;
if (option_verbose > 3)
ast_verbose(VERBOSE_PREFIX_4 "Restarting monitor\n");
ast_mutex_lock(&monlock); {
if (monitor_thread == pthread_self()) {
ast_log(LOG_WARNING, "Cannot kill myself\n");
error = -1;
if (option_verbose > 3)
ast_verbose(VERBOSE_PREFIX_4 "Monitor trying to kill monitor\n");
goto done;
}
if (mthreadactive != -1) {
/* Why do other drivers kill the thread? No need says I, simply awake thread with event. */
VPB_EVENT e;
e.handle = 0;
e.type = VPB_NULL_EVENT;
e.data = 0;
if (option_verbose > 3)
ast_verbose(VERBOSE_PREFIX_4 "Trying to reawake monitor\n");
vpb_put_event(&e);
} else {
/* Start a new monitor */
int pid = pthread_create(&monitor_thread, NULL, do_monitor, NULL);
if (option_verbose > 3)
ast_verbose(VERBOSE_PREFIX_4 "Created new monitor thread %d\n",pid);
if (pid < 0) {
ast_log(LOG_ERROR, "Unable to start monitor thread.\n");
error = -1;
goto done;
} else
mthreadactive = 0; /* Started the thread!*/
}
done: (void)0;
} ast_mutex_unlock(&monlock);
if (option_verbose > 3)
ast_verbose(VERBOSE_PREFIX_4 "Monitor restarted\n");
return error;
// Per board config that must be called after vpb_open()
static void mkbrd(vpb_model_t model, int echo_cancel)
if(!bridges) {
if(model==vpb_model_v4pci)
max_bridges = MAX_BRIDGES_V4PCI;
bridges = (vpb_bridge_t *)malloc(max_bridges * sizeof(vpb_bridge_t) );
if(!bridges)
ast_log(LOG_ERROR, "Failed to initialize bridges\n");
else
memset(bridges,0,max_bridges * sizeof(vpb_bridge_t));
if(!echo_cancel) {
if (model==vpb_model_v4pci) {
vpb_echo_canc_disable();
ast_log(LOG_NOTICE, "Voicetronix echo cancellation OFF\n");
}
else {
/* need to it port by port for OpenSwitch*/
}
} else {
if (model==vpb_model_v4pci) {
vpb_echo_canc_enable();
ast_log(LOG_NOTICE, "Voicetronix echo cancellation ON\n");
}
else {
/* need to it port by port for OpenSwitch*/
struct vpb_pvt *mkif(int board, int channel, int mode, float txgain, float rxgain,
float txswgain, float rxswgain, int bal1, int bal2, int bal3,
char * callerid, int echo_cancel, int group )
{
struct vpb_pvt *tmp;
char buf[64];
tmp = (struct vpb_pvt *)calloc(1, sizeof *tmp);