Newer
Older
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2003, Paul Bagyenda
* Paul Bagyenda <bagyenda@dsmagic.com>
* Copyright (C) 2004 - 2005, Ben Kramer
* Ben Kramer <ben@voicetronix.com.au>
* Daniel Bichara <daniel@bichara.com.br> - Brazilian CallerID detection (c)2004
*
* Welber Silveira - welberms@magiclink.com.br - (c)2004
* Copying CLID string to propper structure after detection
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
*
* \brief VoiceTronix Interface driver
*
extern "C" {
#include <stdio.h>
#include <string.h>
Kevin P. Fleming
committed
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
Kevin P. Fleming
committed
Kevin P. Fleming
committed
#include "asterisk/lock.h"
#include "asterisk/utils.h"
#include "asterisk/channel.h"
#include "asterisk/config.h"
#include "asterisk/logger.h"
#include "asterisk/module.h"
#include "asterisk/pbx.h"
#include "asterisk/options.h"
#include "asterisk/callerid.h"
#include "asterisk/dsp.h"
#include "asterisk/features.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>
#include <assert.h>
#define DEFAULT_GAIN 0
#define DEFAULT_ECHO_CANCEL 1
#define VPB_SAMPLES 160
#define VPB_MAX_BUF VPB_SAMPLES*4 + AST_FRIENDLY_OFFSET
#define DTMF_CALLERID
#define DTMF_CID_START 'D'
#define DTMF_CID_STOP 'C'
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
static const char desc[] = "VoiceTronix V6PCI/V12PCI/V4PCI API Support";
static const char tdesc[] = "Standard VoiceTronix API Driver";
static const 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;
AST_MUTEX_DEFINE_STATIC(usecnt_lock);
/* Protect the interface list (of vpb_pvt's) */
/* Protect the monitoring thread, so only one process can kill or start it, and not
when it's doing something critical. */
AST_MUTEX_DEFINE_STATIC(monlock);
/* 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
/* These are the tones that are played to the user */
#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
#ifdef TONES_USA
static VPB_TONE Dialtone = {350, 440, 0, -16, -16, -100, 10000, 0};
static VPB_TONE Busytone = {480, 620, 0, -10, -10, -100, 500, 500};
static VPB_TONE Ringbacktone = {440, 480, 0, -20, -20, -100, 2000, 4000};
Kevin P. Fleming
committed
#if 0
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 } } };
Kevin P. Fleming
committed
#endif
static VPB_DETECT toned_ungrunt = { 2, VPB_GRUNT, 1, 2000, 1, 0, 0, -40, 0, 0, 30, 40, { { 0, 0, 0, 0 } } };
/* Use loop polarity detection for CID */
static int UsePolarityCID=0;
/* Use loop drop detection */
static int UseLoopDrop=1;
/* To use or not to use Native bridging */
static int UseNativeBridge=1;
/* Use Asterisk Indication or VPB */
static int use_ast_ind=0;
/* Use Asterisk DTMF detection or VPB */
static int use_ast_dtmfdet=0;
static int relaxdtmf=0;
/* Use Asterisk DTMF play back or VPB */
static int use_ast_dtmf=0;
/* Break for DTMF on native bridge ? */
static int break_for_dtmf=1;
/* Set EC suppression threshold */
static int ec_supp_threshold=-1;
/* Inter Digit Delay for collecting DTMF's */
static int dtmf_idd = 3000;
#define TIMER_PERIOD_RINGBACK 2000
#define TIMER_PERIOD_RING 4000
#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_NODROP (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)
#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
#define VPB_GOT_RXHWG 1
#define VPB_GOT_TXHWG 2
#define VPB_GOT_RXSWG 4
#define VPB_GOT_TXSWG 8
int inuse;
struct ast_channel *c0, *c1, **rc;
struct ast_frame **fo;
int flags;
ast_mutex_t lock;
ast_cond_t cond;
static vpb_bridge_t * bridges;
static int max_bridges = MAX_BRIDGES_V4PCI;
AST_MUTEX_DEFINE_STATIC(bridge_lock);
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 */
ast_group_t callgroup; /* Call group */
ast_group_t pickupgroup; /* Pickup group */
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 callerid_type; /* Caller ID type: 0=>none 1=>vpb 2=>AstV23 3=>AstBell */
char cid_num[AST_MAX_EXTENSION];
char cid_name[AST_MAX_EXTENSION];
int dtmf_caller_pos; /* DTMF CallerID detection (Brazil)*/
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 */
void *ring_timer; /* Void pointer for ring vpb_timer */
int ring_timer_id; /* unique timer ID for ring timer */
void *dtmfidd_timer; /* Void pointer for DTMF IDD vpb_timer */
int dtmfidd_timer_id; /* unique timer ID for DTMF IDD timer */
struct ast_dsp *vad; /* AST Voice Activation Detection dsp */
Kevin P. Fleming
committed
struct timeval lastgrunt; /* time stamp of last grunt event */
ast_mutex_t lock; /* This one just protects bridge ptr below */
vpb_bridge_t *bridge;
int stopreads; /* Stop reading...*/
int read_state; /* Read state */
int chuck_count; /* a count of packets weve chucked away!*/
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 */
struct timeval lastplay; /* Last play time */
ast_mutex_t play_dtmf_lock;
char play_dtmf[16];
int faxhandled; /* has a fax tone been handled ? */
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);
static struct ast_channel *vpb_request(const char *type, int format, void *data, int *cause);
static int vpb_digit(struct ast_channel *ast, char digit);
static int vpb_call(struct ast_channel *ast, char *dest, int timeout);
static int vpb_hangup(struct ast_channel *ast);
static int vpb_answer(struct ast_channel *ast);
static struct ast_frame *vpb_read(struct ast_channel *ast);
static int vpb_write(struct ast_channel *ast, struct ast_frame *frame);
static enum ast_bridge_result ast_vpb_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms);
static int vpb_indicate(struct ast_channel *ast, int condition);
static int vpb_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
Ben Kramer
committed
static struct ast_channel_tech vpb_tech = {
description: tdesc,
capabilities: AST_FORMAT_SLINEAR,
requester: vpb_request,
devicestate: NULL,
send_digit: vpb_digit,
send_digit_begin: NULL,
send_digit_end: NULL,
call: vpb_call,
hangup: vpb_hangup,
answer: vpb_answer,
read: vpb_read,
write: vpb_write,
send_text: NULL,
send_image: NULL,
send_html: NULL,
exception: NULL,
indicate: vpb_indicate,
fixup: vpb_fixup,
setoption: NULL,
queryoption: NULL,
transfer: NULL,
write_video: NULL,
bridged_channel: NULL
};
Ben Kramer
committed
static struct ast_channel_tech vpb_tech_indicate = {
Ben Kramer
committed
description: tdesc,
capabilities: AST_FORMAT_SLINEAR,
Ben Kramer
committed
requester: vpb_request,
devicestate: NULL,
send_digit: vpb_digit,
send_digit_begin: NULL,
send_digit_end: NULL,
Ben Kramer
committed
call: vpb_call,
hangup: vpb_hangup,
answer: vpb_answer,
read: vpb_read,
write: vpb_write,
send_text: NULL,
send_image: NULL,
send_html: NULL,
exception: NULL,
Ben Kramer
committed
indicate: NULL,
fixup: vpb_fixup,
setoption: NULL,
queryoption: NULL,
transfer: NULL,
write_video: NULL,
bridged_channel: NULL
};
/* Can't get ast_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
*/
/* 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 */
/* This is the Native bridge code, which Asterisk will try before using its own bridging code */
static enum ast_bridge_result ast_vpb_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms)
struct vpb_pvt *p0 = (struct vpb_pvt *)c0->tech_pvt;
struct vpb_pvt *p1 = (struct vpb_pvt *)c1->tech_pvt;
struct ast_channel *cs[3];
struct ast_channel *who;
struct ast_frame *f;
cs[0] = c0;
cs[1] = c1;
#ifdef BAD_V4PCI_BRIDGE
if(p0->vpb_model==vpb_model_v4pci)
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_unlock(&bridge_lock);
if (i == max_bridges) {
ast_log(LOG_WARNING, "%s: vpb_bridge: Failed to bridge %s and %s!\n", p0->dev, 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 "%s: vpb_bridge: Bridging call entered with [%s, %s]\n",p0->dev, c0->name, c1->name);
Kevin P. Fleming
committed
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Native bridging %s and %s\n", c0->name, c1->name);
ast_verbose(VERBOSE_PREFIX_2 "%s: vpb_bridge: Starting half-duplex bridge [%s, %s]\n",p0->dev, c0->name, c1->name);
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
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 "%s: vpb_bridge: Finished half-duplex bridge [%s, %s]\n",p0->dev, 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 ) {
/* Are we really ment to be doing nothing ?!?! */
who = ast_waitfor_n(cs, 2, &timeoutms);
Mark Spencer
committed
if (!timeoutms) {
res = AST_BRIDGE_RETRY;
break;
}
ast_log(LOG_DEBUG, "%s: vpb_bridge: Empty frame read...\n",p0->dev);
/* check for hangup / whentohangup */
if (ast_check_hangup(c0) || ast_check_hangup(c1))
break;
continue;
}
f = ast_read(who);
if (!f || ((f->frametype == AST_FRAME_DTMF) &&
(((who == c0) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) ||
((who == c1) && (flags & AST_BRIDGE_DTMF_CHANNEL_1))))) {
*fo = f;
*rc = who;
ast_log(LOG_DEBUG, "%s: vpb_bridge: Got a [%s]\n",p0->dev, f ? "digit" : "hangup");
if ((c0->tech_pvt == pvt0) && (!c0->_softhangup)) {
if (pr0->set_rtp_peer(c0, NULL, NULL, 0))
ast_log(LOG_WARNING, "Channel '%s' failed to revert\n", c0->name);
}
if ((c1->tech_pvt == pvt1) && (!c1->_softhangup)) {
if (pr1->set_rtp_peer(c1, NULL, NULL, 0))
ast_log(LOG_WARNING, "Channel '%s' failed to revert back\n", c1->name);
}
*/
/* That's all we needed */
/* Check if we need to break */
if (break_for_dtmf){
break;
}
else if ((f->frametype == AST_FRAME_DTMF) && ((f->subclass == '#')||(f->subclass == '*'))){
break;
}
} else {
if ((f->frametype == AST_FRAME_DTMF) ||
(f->frametype == AST_FRAME_VOICE) ||
(f->frametype == AST_FRAME_VIDEO))
{
/* Forward voice or DTMF frames if they happen upon us */
/* Actually I dont think we want to forward on any frames!
if (who == c0) {
ast_write(c1, f);
} else if (who == c1) {
ast_write(c0, f);
}
*/
}
ast_frfree(f);
}
/* Swap priority not that it's a big deal at this point */
cs[2] = cs[0];
cs[0] = cs[1];
cs[1] = cs[2];
};
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_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);
return (res==VPB_OK) ? AST_BRIDGE_COMPLETE : AST_BRIDGE_FAILED;
/* 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 */
static void get_callerid(struct vpb_pvt *p)
{
short buf[CID_MSECS*8]; /* 8kHz sampling rate */
Kevin P. Fleming
committed
struct timeval cid_record_time;
struct ast_channel *owner = p->owner;
/*
char callerid[AST_MAX_EXTENSION] = "";
*/
#ifdef ANALYSE_CID
void * ws;
char * file="cidsams.wav";
if( ast_mutex_trylock(&p->record_lock) == 0 ) {
Kevin P. Fleming
committed
cid_record_time = ast_tvnow();
if (option_verbose>3)
ast_verbose(VERBOSE_PREFIX_4 "CID record - start\n");
if (UsePolarityCID != 1){
vpb_sleep(RING_SKIP);
}
ast_verbose(VERBOSE_PREFIX_4 "CID record - skipped %dms trailing ring\n",
Kevin P. Fleming
committed
ast_tvdiff_ms(ast_tvnow(), cid_record_time));
cid_record_time = ast_tvnow();
/* 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);
#ifdef ANALYSE_CID
vpb_wave_open_write(&ws, file, VPB_LINEAR);
vpb_wave_write(ws,(char*)buf,sizeof(buf));
vpb_wave_close_write(ws);
#endif
ast_verbose(VERBOSE_PREFIX_4 "CID record - recorded %dms between rings\n",
Kevin P. Fleming
committed
ast_tvdiff_ms(ast_tvnow(), 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;
}
VPB_CID *cli_struct = new VPB_CID;
cli_struct->ra_cldn[0]=0;
cli_struct->ra_cn[0]=0;
/* This decodes FSK 1200baud type callerid */
if ((rc=vpb_cid_decode2(cli_struct, buf, CID_MSECS*8)) == VPB_OK ) {
if (owner->cid.cid_num)
free(owner->cid.cid_num);
owner->cid.cid_num=NULL;
if (owner->cid.cid_name)
free(owner->cid.cid_name);
owner->cid.cid_name=NULL;
if (cli_struct->ra_cldn[0]=='\0'){
owner->cid.cid_num = strdup(cli_struct->cldn);
owner->cid.cid_name = strdup(cli_struct->cn);
if (owner){
ast_set_callerid(owner, cli_struct->cldn, cli_struct->cn, cli_struct->cldn);
} else {
strcpy(p->cid_num, cli_struct->cldn);
strcpy(p->cid_name, cli_struct->cn);
}
if (option_verbose>3)
ast_verbose(VERBOSE_PREFIX_4 "CID record - got [%s] [%s]\n",owner->cid.cid_num,owner->cid.cid_name );
snprintf(p->callerid,sizeof(p->callerid)-1,"%s %s",cli_struct->cldn,cli_struct->cn);
}
else {
ast_log(LOG_ERROR,"CID record - No caller id avalable on %s \n", p->dev);
}
ast_log(LOG_ERROR, "CID record - Failed to decode caller id on %s - %s\n", p->dev, vpb_strerror(rc) );
strncpy(p->callerid,"unknown", sizeof(p->callerid) - 1);
delete cli_struct;
ast_log(LOG_ERROR, "CID record - Failed to set record mode for caller id on %s\n", p->dev );
static void get_callerid_ast(struct vpb_pvt *p)
{
struct callerid_state *cs;
char buf[1024];
char *name=NULL, *number=NULL;
int flags;
int rc=0,vrc;
int sam_count=0;
struct ast_channel *owner = p->owner;
int which_cid;
void * ws;
char * file="cidsams.wav";
if (option_verbose>3) ast_verbose(VERBOSE_PREFIX_4 "Collected caller ID already\n");
return;
}
which_cid=CID_SIG_V23;
if (option_verbose>3) ast_verbose(VERBOSE_PREFIX_4 "Collecting Caller ID v23...\n");
which_cid=CID_SIG_BELL;
if (option_verbose>3) ast_verbose(VERBOSE_PREFIX_4 "Collecting Caller ID bell...\n");
}
else {
if (option_verbose>3)
ast_verbose(VERBOSE_PREFIX_4 "Caller ID disabled\n");
return;
}
/* vpb_sleep(RING_SKIP); */
/* vpb_record_get_gain(p->handle, &old_gain); */
cs = callerid_new(which_cid);
if (cs){
#ifdef ANALYSE_CID
vpb_wave_open_write(&ws, file, VPB_MULAW);
vpb_record_set_gain(p->handle, 3.0);
vpb_record_set_hw_gain(p->handle,12.0);
#endif
vpb_record_buf_start(p->handle, VPB_MULAW);
while((rc == 0)&&(sam_count<8000*3)){
vrc = vpb_record_buf_sync(p->handle, (char*)buf, sizeof(buf));
if (vrc != VPB_OK)
ast_log(LOG_ERROR, "%s: Caller ID couldnt read audio buffer!\n",p->dev);
rc = callerid_feed(cs,(unsigned char *)buf,sizeof(buf),AST_FORMAT_ULAW);
#ifdef ANALYSE_CID
vpb_wave_write(ws,(char*)buf,sizeof(buf));
#endif
sam_count+=sizeof(buf);
if (option_verbose>3) ast_verbose(VERBOSE_PREFIX_4 "Collecting Caller ID samples [%d][%d]...\n",sam_count,rc);
}
vpb_record_buf_finish(p->handle);
#ifdef ANALYSE_CID
#endif
if (rc == 1){
callerid_get(cs, &name, &number, &flags);
if (option_verbose>0)
ast_verbose(VERBOSE_PREFIX_1 "%s: Caller ID name [%s] number [%s] flags [%d]\n",p->dev,name, number,flags);
}
else {
ast_log(LOG_ERROR, "%s: Failed to decode Caller ID \n", p->dev );
}
/* vpb_record_set_gain(p->handle, old_gain); */
/* vpb_record_set_hw_gain(p->handle,6.0); */
}
else {
ast_log(LOG_ERROR, "%s: Failed to create Caller ID struct\n", p->dev );
}
if (owner->cid.cid_num) {
free(owner->cid.cid_num);
owner->cid.cid_num = NULL;
}
if (owner->cid.cid_name) {
free(owner->cid.cid_name);
owner->cid.cid_name = NULL;
}
if (number)
ast_shrink_phone_number(number);
if (!ast_strlen_zero(number)) {
owner->cid.cid_num = strdup(number);
owner->cid.cid_ani = strdup(number);
if (!ast_strlen_zero(name)){
owner->cid.cid_name = strdup(name);
snprintf(p->callerid,(sizeof(p->callerid)-1),"%s %s",number,name);
}
else {
snprintf(p->callerid,(sizeof(p->callerid)-1),"%s",number);
}
}
if (cs)
callerid_free(cs);
}
/* 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!![%d]\n",je.type);
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;
int res=0;
ast_verbose(VERBOSE_PREFIX_4 "%s: handle_owned: got event: [%d=>%d]\n", p->dev, e->type, e->data);
switch (e->type) {
case VPB_RING:
if (p->mode == MODE_FXO) {
f.subclass = AST_CONTROL_RING;
vpb_timer_stop(p->ring_timer);
vpb_timer_start(p->ring_timer);
} 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);
} else if (e->data == p->ring_timer_id) {
/* We didnt get another ring in time! */
if (p->owner->_state != AST_STATE_UP) {
/* Assume caller has hung up */
vpb_timer_stop(p->ring_timer);
f.subclass = AST_CONTROL_HANGUP;
} else {
vpb_timer_stop(p->ring_timer);
f.frametype = -1;
}
} else {
f.frametype = -1; /* Ignore. */
}
break;
case VPB_DTMF_DOWN:
if (use_ast_dtmfdet){
f.frametype = -1;
} else 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 ) {
ast_verbose(VERBOSE_PREFIX_4 "%s: handle_owned: got event: BUSY\n", p->dev);
if (p->owner->_state == AST_STATE_UP) {
f.subclass = AST_CONTROL_HANGUP;
}
else {
f.subclass = AST_CONTROL_BUSY;
}
}
else if (e->data == VPB_FAX){
if (!p->faxhandled){
if (strcmp(p->owner->exten, "fax")) {
const char *target_context = S_OR(p->owner->macrocontext, p->owner->context);
if (ast_exists_extension(p->owner, target_context, "fax", 1, p->owner->cid.cid_num)) {
ast_verbose(VERBOSE_PREFIX_3 "Redirecting %s to fax extension\n", p->owner->name);
/* Save the DID/DNIS when we transfer the fax call to a "fax" extension */
pbx_builtin_setvar_helper(p->owner, "FAXEXTEN", p->owner->exten);
if (ast_async_goto(p->owner, target_context, "fax", 1))
ast_log(LOG_WARNING, "Failed to async goto '%s' into fax of '%s'\n", p->owner->name, target_context);
} else
ast_log(LOG_NOTICE, "Fax detected, but no fax extension\n");
} else
ast_log(LOG_DEBUG, "Already in a fax extension, not redirecting\n");
} else
ast_log(LOG_DEBUG, "Fax already handled\n");
}
else if (e->data == VPB_GRUNT) {
if ( ast_tvdiff_ms(ast_tvnow(), 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 {
Kevin P. Fleming
committed
p->lastgrunt = ast_tvnow();
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;
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_LOOP_ONHOOK:
if (p->owner->_state == AST_STATE_UP)
f.subclass = AST_CONTROL_HANGUP;
else
f.frametype = -1;
break;
break;
case VPB_STATION_FLASH:
f.subclass = AST_CONTROL_FLASH;