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.
*/
/*! \file
*
* \brief VoiceTronix Interface driver
*
* \ingroup channel_drivers
*/
/*! \li \ref chan_vpb.cc uses the configuration file \ref vpb.conf
* \addtogroup configuration_file
*/
/*! \page vpb.conf vpb.conf
* \verbinclude vpb.conf.sample
*/
Richard Mudgett
committed
/*
* XXX chan_vpb needs its native bridge code converted to the
* new bridge technology scheme. The chan_dahdi native bridge
* code can be used as an example. It is unlikely that this
* will ever get done.
*
* The existing native bridge code is marked with the
* VPB_NATIVE_BRIDGING conditional.
*/
Kevin P. Fleming
committed
<depend>vpb</depend>
<defaultenabled>no</defaultenabled>
<support_level>extended</support_level>
#include <vpbapi.h>
extern "C" {
#include "asterisk.h"
#include "asterisk/lock.h"
#include "asterisk/utils.h"
#include "asterisk/channel.h"
#include "asterisk/config.h"
#include "asterisk/module.h"
#include "asterisk/pbx.h"
#include "asterisk/callerid.h"
#include "asterisk/dsp.h"
#include "asterisk/features.h"
#include "asterisk/musiconhold.h"
#include "asterisk/format_cache.h"
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
}
#include <sys/socket.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <ctype.h>
#include <assert.h>
#ifdef pthread_create
#undef pthread_create
#endif
#define DEFAULT_GAIN 0
#define DEFAULT_ECHO_CANCEL 1
#define VPB_SAMPLES 160
#define VPB_MAX_BUF VPB_SAMPLES*4 + AST_FRIENDLY_OFFSET
#define VPB_NULL_EVENT 200
#define VPB_WAIT_TIMEOUT 4000
#define MAX_VPB_GAIN 12.0
#define MIN_VPB_GAIN -12.0
#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 gruntdetect_timeout = 3600000; /* Grunt detect timeout is 1hr. */
/* Protect the interface list (of vpb_pvt's) */
AST_MUTEX_DEFINE_STATIC(iflock);
/* 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 pthread_t monitor_thread;
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! */
/* These are the tones that are played to the user */
#define TONES_AU
/* #define TONES_USA */
#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};
#endif
/* grunt tone defn's */
#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 } } };
#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 short ec_supp_threshold=-1;
/* Inter Digit Delay for collecting DTMF's */
static int dtmf_idd = 3000;
#define TIMER_PERIOD_RINGBACK 2000
#define TIMER_PERIOD_BUSY 700
#define TIMER_PERIOD_RING 4000
static int timer_period_ring = TIMER_PERIOD_RING;
#define VPB_EVENTS_ALL (VPB_MRING|VPB_MDIGIT|VPB_MDTMF|VPB_MTONEDETECT|VPB_MTIMEREXP \
|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_MSTATION_OFFHOOK|VPB_MSTATION_ONHOOK \
#define VPB_EVENTS_NODTMF (VPB_MRING|VPB_MDIGIT|VPB_MTONEDETECT|VPB_MTIMEREXP \
|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_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, VPB_CALL_DISCONNECT, 0 },
{ VPB_RINGBACK, VPB_CALL_RINGBACK, 0 },
{ VPB_BUSY, VPB_CALL_BUSY, 0 },
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
{ 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
typedef struct {
int inuse;
struct ast_channel *c0, *c1, **rc;
struct ast_frame **fo;
int flags;
ast_mutex_t lock;
ast_cond_t cond;
int endbridge;
} vpb_bridge_t;
static vpb_bridge_t * bridges;
static int max_bridges = MAX_BRIDGES_V4PCI;
AST_MUTEX_DEFINE_STATIC(bridge_lock);
typedef enum {
vpb_model_unknown = 0,
vpb_model_v4pci,
vpb_model_v12pci
} vpb_model_t;
static struct vpb_pvt {
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 */
int dialtone; /*!< NOT USED */
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 */
struct timeval lastgrunt; /*!< time stamp of last grunt event */
ast_mutex_t lock; /*!< This one just protects bridge ptr below */
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 */
int play_buf_time; /*!< How long the last play_buf took */
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, enum ast_channel_state state, const char *context, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor);
static void *do_chanreads(void *pvt);
static struct ast_channel *vpb_request(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause);
static int vpb_digit_begin(struct ast_channel *ast, char digit);
static int vpb_digit_end(struct ast_channel *ast, char digit, unsigned int duration);
static int vpb_call(struct ast_channel *ast, const 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 int vpb_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen);
static int vpb_fixup(struct ast_channel *oldchan, struct ast_channel *newchan);
static struct ast_channel_tech vpb_tech = {
type: "vpb",
description: tdesc,
capabilities: NULL,
properties: 0,
requester: vpb_request,
presencestate: NULL,
send_digit_begin: vpb_digit_begin,
send_digit_end: vpb_digit_end,
call: vpb_call,
hangup: vpb_hangup,
answer: vpb_answer,
read: vpb_read,
send_text: NULL,
send_image: NULL,
send_html: NULL,
exception: NULL,
early_bridge: NULL,
indicate: vpb_indicate,
fixup: vpb_fixup,
setoption: NULL,
queryoption: NULL,
transfer: NULL,
write_video: NULL,
write_text: NULL,
func_channel_read: NULL,
func_channel_write: NULL,
};
static struct ast_channel_tech vpb_tech_indicate = {
type: "vpb",
description: tdesc,
capabilities: NULL,
properties: 0,
requester: vpb_request,
presencestate: NULL,
send_digit_begin: vpb_digit_begin,
send_digit_end: vpb_digit_end,
call: vpb_call,
hangup: vpb_hangup,
answer: vpb_answer,
read: vpb_read,
send_text: NULL,
send_image: NULL,
send_html: NULL,
exception: NULL,
early_bridge: NULL,
indicate: NULL,
fixup: vpb_fixup,
setoption: NULL,
queryoption: NULL,
transfer: NULL,
write_video: NULL,
write_text: NULL,
func_channel_read: NULL,
func_channel_write: NULL,
Richard Mudgett
committed
#if defined(VPB_NATIVE_BRIDGING)
/* 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
*/
#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 */
/* 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 *)ast_channel_tech_pvt(c0);
struct vpb_pvt *p1 = (struct vpb_pvt *)ast_channel_tech_pvt(c1);
int i;
int res;
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)
return AST_BRIDGE_FAILED_NOWARN;
#endif
if (UseNativeBridge != 1) {
return AST_BRIDGE_FAILED_NOWARN;
}
/*
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);
ast_log(LOG_WARNING, "%s: vpb_bridge: Failed to bridge %s and %s!\n", p0->dev, ast_channel_name(c0), ast_channel_name(c1));
ast_mutex_unlock(&p0->lock);
ast_mutex_unlock(&p1->lock);
return AST_BRIDGE_FAILED_NOWARN;
} else {
/* 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);
ast_verb(2, "%s: vpb_bridge: Bridging call entered with [%s, %s]\n", p0->dev, ast_channel_name(c0), ast_channel_name(c1));
ast_verb(3, "Native bridging %s and %s\n", ast_channel_name(c0), ast_channel_name(c1));
ast_debug(2, "%s: vpb_bridge: Starting half-duplex bridge [%s, %s]\n", p0->dev, ast_channel_name(c0), ast_channel_name(c1));
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) {
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);
ast_debug(2, "%s: vpb_bridge: Finished half-duplex bridge [%s, %s]\n", p0->dev, ast_channel_name(c0), ast_channel_name(c1));
res = vpb_bridge(p0->handle, p1->handle, VPB_BRIDGE_ON);
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);
if (!who) {
if (!timeoutms) {
res = AST_BRIDGE_RETRY;
break;
}
ast_debug(1, "%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_debug(1, "%s: vpb_bridge: Got a [%s]\n", p0->dev, f ? "digit" : "hangup");
#if 0
if ((c0->tech_pvt == pvt0) && (!ast_check_hangup(c0))) {
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) && (!ast_check_hangup(c1))) {
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 */
return 0;
#endif
if (break_for_dtmf) {
} else if ((f->frametype == AST_FRAME_DTMF) && ((f->subclass.integer == '#') || (f->subclass.integer == '*'))) {
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);
ast_mutex_lock(&bridge_lock);
bridges[i].inuse = 0;
ast_mutex_unlock(&bridge_lock);
p0->bridge = NULL;
p1->bridge = NULL;
ast_verb(2, "Bridging call done with [%s, %s] => %d\n", ast_channel_name(c0), ast_channel_name(c1), res);
/*
ast_mutex_unlock(&p0->lock);
ast_mutex_unlock(&p1->lock);
*/
return (res == VPB_OK) ? AST_BRIDGE_COMPLETE : AST_BRIDGE_FAILED;
Richard Mudgett
committed
#endif /* defined(VPB_NATIVE_BRIDGING) */
/* 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 300
#define CID_MSECS 2000
static void get_callerid(struct vpb_pvt *p)
{
short buf[CID_MSECS*8]; /* 8kHz sampling rate */
struct timeval cid_record_time;
int rc;
struct ast_channel *owner = p->owner;
/*
char callerid[AST_MAX_EXTENSION] = "";
*/
#ifdef ANALYSE_CID
void * ws;
char * file="cidsams.wav";
#endif
if (ast_mutex_trylock(&p->record_lock) == 0) {
cid_record_time = ast_tvnow();
ast_verb(4, "CID record - start\n");
/* Skip any trailing ringtone */
if (UsePolarityCID != 1){
vpb_sleep(RING_SKIP);
}
Tilghman Lesher
committed
ast_verb(4, "CID record - skipped %lldms trailing ring\n",
(long long int) 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
Tilghman Lesher
committed
ast_verb(4, "CID record - recorded %lldms between rings\n",
(long long int) 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)
ast_free(owner->cid.cid_num);
owner->cid.cid_num=NULL;
if (owner->cid.cid_name)
ast_free(owner->cid.cid_name);
owner->cid.cid_name=NULL;
*/
if (cli_struct->ra_cldn[0] == '\0') {
/*
owner->cid.cid_num = ast_strdup(cli_struct->cldn);
owner->cid.cid_name = ast_strdup(cli_struct->cn);
*/
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);
}
ast_verb(4, "CID record - got [%s] [%s]\n",
S_COR(ast_channel_caller(owner)->id.number.valid, ast_channel_caller(owner)->id.number.str, ""),
S_COR(ast_channel_caller(owner)->id.name.valid, ast_channel_caller(owner)->id.name.str, ""));
snprintf(p->callerid, sizeof(p->callerid), "%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 - %d\n", p->dev, rc);
ast_copy_string(p->callerid, "unknown", sizeof(p->callerid));
}
delete cli_struct;
} else
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 rc = 0, vrc;
int sam_count = 0;
struct ast_channel *owner = p->owner;
int which_cid;
/*
float old_gain;
*/
#ifdef ANALYSE_CID
void * ws;
char * file = "cidsams.wav";
if (p->callerid_type == 1) {
ast_verb(4, "Collected caller ID already\n");
return;
}
else if (p->callerid_type == 2 ) {
which_cid = CID_SIG_V23;
ast_verb(4, "Collecting Caller ID v23...\n");
}
else if (p->callerid_type == 3) {
which_cid = CID_SIG_BELL;
ast_verb(4, "Collecting Caller ID bell...\n");
ast_verb(4, "Caller ID disabled\n");
return;
}
/* vpb_sleep(RING_SKIP); */
/* vpb_record_get_gain(p->handle, &old_gain); */
cs = callerid_new(which_cid);
#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 couldn't read audio buffer!\n", p->dev);
rc = callerid_feed(cs, (unsigned char *)buf, sizeof(buf), ast_format_ulaw);
vpb_wave_write(ws, (char *)buf, sizeof(buf));
sam_count += sizeof(buf);
ast_verb(4, "Collecting Caller ID samples [%d][%d]...\n", sam_count, rc);
}
vpb_record_buf_finish(p->handle);
#ifdef ANALYSE_CID
vpb_wave_close_write(ws);
if (rc == 1) {
callerid_get(cs, &name, &number, &flags);
ast_debug(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);
ast_party_number_free(&ast_channel_caller(owner)->id.number);
ast_party_number_init(&ast_channel_caller(owner)->id.number);
ast_party_name_free(&ast_channel_caller(owner)->id.name);
ast_party_name_init(&ast_channel_caller(owner)->id.name);
if (number)
ast_shrink_phone_number(number);
ast_set_callerid(owner,
number, name,
ast_channel_caller(owner)->ani.number.valid ? NULL : number);
snprintf(p->callerid, sizeof(p->callerid), "%s %s", number, name);
ast_copy_string(p->callerid, number, sizeof(p->callerid));
}
if (cs)
callerid_free(cs);
}
/* Terminate any tones we are presently playing */
static void stoptone(int handle)
while (vpb_playtone_state(handle) != VPB_OK) {
ret = vpb_get_event_ch_async(handle, &je);
if ((ret == VPB_OK) && (je.type != VPB_DIALEND)) {
ast_verb(4, "Stop tone collected a wrong event!![%d]\n", je.type);
/* vpb_put_event(&je); */
}
vpb_sleep(10);
}
}
/* Safe vpb_playtone_async */
static int playtone( int handle, VPB_TONE *tone)
{
int ret = VPB_OK;
stoptone(handle);
ast_verb(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;
ast_verb(4, "%s: handle_owned: got event: [%d=>%d]\n", p->dev, e->type, e->data);
f.src = "vpb";
switch (e->type) {
case VPB_RING:
if (p->mode == MODE_FXO) {
f.subclass.integer = AST_CONTROL_RING;
vpb_timer_stop(p->ring_timer);
vpb_timer_start(p->ring_timer);
} else
f.frametype = AST_FRAME_NULL; /* ignore ring on station port. */
break;
case VPB_RING_OFF:
f.frametype = AST_FRAME_NULL;
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);
} else if (e->data == p->ringback_timer_id) {
playtone(p->handle, &Ringbacktone);
vpb_timer_stop(p->ringback_timer);
vpb_timer_start(p->ringback_timer);
f.frametype = AST_FRAME_NULL;
} else if (e->data == p->ring_timer_id) {
/* We didnt get another ring in time! */
if (ast_channel_state(p->owner) != AST_STATE_UP) {
/* Assume caller has hung up */
vpb_timer_stop(p->ring_timer);
f.subclass.integer = AST_CONTROL_HANGUP;
} else {
vpb_timer_stop(p->ring_timer);
f.frametype = AST_FRAME_NULL; /* Ignore. */
case VPB_DTMF_DOWN:
case VPB_DTMF:
if (use_ast_dtmfdet) {
f.frametype = AST_FRAME_NULL;
} else if (ast_channel_state(p->owner) == AST_STATE_UP) {
f.frametype = AST_FRAME_DTMF;
f.subclass.integer = e->data;
} else
f.frametype = AST_FRAME_NULL;
break;
case VPB_TONEDETECT:
if (e->data == VPB_BUSY || e->data == VPB_BUSY_308 || e->data == VPB_BUSY_AUST ) {
ast_debug(4, "%s: handle_owned: got event: BUSY\n", p->dev);
if (ast_channel_state(p->owner) == AST_STATE_UP) {
f.subclass.integer = AST_CONTROL_HANGUP;
f.subclass.integer = AST_CONTROL_BUSY;
}
} else if (e->data == VPB_FAX) {
if (!p->faxhandled) {
if (strcmp(ast_channel_exten(p->owner), "fax")) {
const char *target_context = S_OR(ast_channel_macrocontext(p->owner), ast_channel_context(p->owner));
if (ast_exists_extension(p->owner, target_context, "fax", 1,
S_COR(ast_channel_caller(p->owner)->id.number.valid, ast_channel_caller(p->owner)->id.number.str, NULL))) {
ast_verb(3, "Redirecting %s to fax extension\n", ast_channel_name(p->owner));
/* Save the DID/DNIS when we transfer the fax call to a "fax" extension */
pbx_builtin_setvar_helper(p->owner, "FAXEXTEN", ast_channel_exten(p->owner));
if (ast_async_goto(p->owner, target_context, "fax", 1)) {
ast_log(LOG_WARNING, "Failed to async goto '%s' into fax of '%s'\n", ast_channel_name(p->owner), target_context);
}
} else {
ast_log(LOG_NOTICE, "Fax detected, but no fax extension\n");
}
ast_debug(1, "Already in a fax extension, not redirecting\n");
} else {
ast_debug(1, "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 */
ast_verb(3, "grunt timeout\n");
ast_log(LOG_NOTICE, "%s: Line hangup due of lack of conversation\n", p->dev);
f.subclass.integer = AST_CONTROL_HANGUP;
} else {
p->lastgrunt = ast_tvnow();
f.frametype = AST_FRAME_NULL;
}
} else {
case VPB_CALLEND:
#ifdef DIAL_WITH_CALL_PROGRESS
if (e->data == VPB_CALL_CONNECTED) {
f.subclass.integer = AST_CONTROL_ANSWER;
} else if (e->data == VPB_CALL_NO_DIAL_TONE || e->data == VPB_CALL_NO_RING_BACK) {
f.subclass.integer = AST_CONTROL_CONGESTION;
} else if (e->data == VPB_CALL_NO_ANSWER || e->data == VPB_CALL_BUSY) {
f.subclass.integer = AST_CONTROL_BUSY;
} else if (e->data == VPB_CALL_DISCONNECTED) {
f.subclass.integer = AST_CONTROL_HANGUP;
}
#else
ast_log(LOG_NOTICE, "%s: Got call progress callback but blind dialing \n", p->dev);
f.frametype = AST_FRAME_NULL;
#endif
break;
case VPB_STATION_OFFHOOK:
f.subclass.integer = AST_CONTROL_ANSWER;
case VPB_DROP:
if ((p->mode == MODE_FXO) && (UseLoopDrop)) { /* ignore loop drop on stations */
if (ast_channel_state(p->owner) == AST_STATE_UP) {
f.subclass.integer = AST_CONTROL_HANGUP;
} else {
f.frametype = AST_FRAME_NULL;
}
}
break;
case VPB_LOOP_ONHOOK:
if (ast_channel_state(p->owner) == AST_STATE_UP) {
f.subclass.integer = AST_CONTROL_HANGUP;
}
break;
case VPB_STATION_ONHOOK:
f.subclass.integer = AST_CONTROL_HANGUP;
case VPB_STATION_FLASH:
f.subclass.integer = AST_CONTROL_FLASH;
/* Called when dialing has finished and ringing starts
* No indication that call has really been answered when using blind dialing
*/
case VPB_DIALEND: