Newer
Older
* Asterisk -- A telephony toolkit for Linux.
*
* Channel Management
*
*
* Mark Spencer <markster@linux-support.net>
*
* This program is free software, distributed under the terms of
* the GNU General Public License
*/
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <sys/time.h>
#include <signal.h>
#include <errno.h>
#include <asterisk/sched.h>
#include <asterisk/options.h>
#include <asterisk/channel.h>
#include <asterisk/channel_pvt.h>
#include <asterisk/logger.h>
#include <asterisk/file.h>
#include <asterisk/manager.h>
#include <asterisk/chanvars.h>
#include <asterisk/linkedlists.h>
#ifdef ZAPTEL_OPTIMIZATIONS
#include <sys/ioctl.h>
#include <linux/zaptel.h>
#endif
#ifdef DEBUG_MUTEX
/* Convenient mutex debugging functions */
#define PTHREAD_MUTEX_LOCK(a) __PTHREAD_MUTEX_LOCK(__FUNCTION__, a)
#define PTHREAD_MUTEX_UNLOCK(a) __PTHREAD_MUTEX_UNLOCK(__FUNCTION__, a)
static int __PTHREAD_MUTEX_LOCK(char *f, pthread_mutex_t *a) {
ast_log(LOG_DEBUG, "Locking %p (%s)\n", a, f);
}
static int __PTHREAD_MUTEX_UNLOCK(char *f, pthread_mutex_t *a) {
ast_log(LOG_DEBUG, "Unlocking %p (%s)\n", a, f);
#define PTHREAD_MUTEX_LOCK(a) ast_pthread_mutex_lock(a)
#define PTHREAD_MUTEX_UNLOCK(a) ast_pthread_mutex_unlock(a)
struct chanlist {
char type[80];
char description[80];
int capabilities;
struct ast_channel * (*requester)(char *type, int format, void *data);
int (*devicestate)(void *data);
/* Protect the channel list (highly unlikely that two things would change
it at the same time, but still! */
static pthread_mutex_t chlock = AST_MUTEX_INITIALIZER;
int ast_check_hangup(struct ast_channel *chan)
{
time_t myt;
/* if soft hangup flag, return true */
if (chan->_softhangup) return 1;
/* if no private structure, return true */
if (!chan->pvt->pvt) return 1;
/* if no hangup scheduled, just return here */
if (!chan->whentohangup) return 0;
time(&myt); /* get current time */
/* return, if not yet */
if (chan->whentohangup > myt) return 0;
static int ast_check_hangup_locked(struct ast_channel *chan)
{
int res;
ast_pthread_mutex_lock(&chan->lock);
res = ast_check_hangup(chan);
ast_pthread_mutex_unlock(&chan->lock);
return res;
}
void ast_begin_shutdown(int hangup)
{
struct ast_channel *c;
shutting_down = 1;
if (hangup) {
PTHREAD_MUTEX_LOCK(&chlock);
c = channels;
while(c) {
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
c = c->next;
}
PTHREAD_MUTEX_UNLOCK(&chlock);
}
}
int ast_active_channels(void)
{
struct ast_channel *c;
int cnt = 0;
PTHREAD_MUTEX_LOCK(&chlock);
c = channels;
while(c) {
cnt++;
c = c->next;
}
PTHREAD_MUTEX_UNLOCK(&chlock);
return cnt;
}
void ast_cancel_shutdown(void)
{
shutting_down = 0;
}
int ast_shutting_down(void)
{
return shutting_down;
}
void ast_channel_setwhentohangup(struct ast_channel *chan, time_t offset)
{
time_t myt;
time(&myt);
if (offset)
chan->whentohangup = myt + offset;
else
chan->whentohangup = 0;
int ast_channel_register(char *type, char *description, int capabilities,
struct ast_channel *(*requester)(char *type, int format, void *data))
{
return ast_channel_register_ex(type, description, capabilities, requester, NULL);
}
int ast_channel_register_ex(char *type, char *description, int capabilities,
struct ast_channel *(*requester)(char *type, int format, void *data),
int (*devicestate)(void *data))
ast_log(LOG_WARNING, "Unable to lock channel list\n");
return -1;
}
chan = backends;
while(chan) {
if (!strcasecmp(type, chan->type)) {
ast_log(LOG_WARNING, "Already have a handler for type '%s'\n", type);
return -1;
}
last = chan;
chan = chan->next;
}
chan = malloc(sizeof(struct chanlist));
if (!chan) {
ast_log(LOG_WARNING, "Out of memory\n");
strncpy(chan->type, type, sizeof(chan->type)-1);
strncpy(chan->description, description, sizeof(chan->description)-1);
chan->capabilities = capabilities;
chan->requester = requester;
chan->devicestate = devicestate;
chan->next = NULL;
if (last)
last->next = chan;
else
backends = chan;
if (option_debug)
ast_log(LOG_DEBUG, "Registered handler for '%s' (%s)\n", chan->type, chan->description);
else if (option_verbose > 1)
ast_verbose( VERBOSE_PREFIX_2 "Registered channel type '%s' (%s)\n", chan->type, chan->description);
/* XXX Not reentrant XXX */
static char localtmp[256];
switch(state) {
case AST_STATE_DOWN:
return "Down";
case AST_STATE_RESERVED:
return "Rsrvd";
case AST_STATE_OFFHOOK:
return "OffHook";
case AST_STATE_DIALING:
return "Dialing";
case AST_STATE_RING:
return "Ring";
case AST_STATE_RINGING:
return "Ringing";
case AST_STATE_UP:
return "Up";
case AST_STATE_BUSY:
return "Busy";
default:
snprintf(localtmp, sizeof(localtmp), "Unknown (%d)\n", state);
return localtmp;
int ast_best_codec(int fmts)
{
/* This just our opinion, expressed in code. We are asked to choose
the best codec to use, given no information */
int x;
static int prefs[] =
{
/* Okay, ulaw is used by all telephony equipment, so start with it */
AST_FORMAT_ULAW,
/* Unless of course, you're a silly European, so then prefer ALAW */
AST_FORMAT_ALAW,
/* Okay, well, signed linear is easy to translate into other stuff */
AST_FORMAT_SLINEAR,
/* ADPCM has great sound quality and is still pretty easy to translate */
AST_FORMAT_ADPCM,
/* Okay, we're down to vocoders now, so pick GSM because it's small and easier to
translate and sounds pretty good */
AST_FORMAT_GSM,
/* Speex is free, but computationally more expensive than GSM */
AST_FORMAT_SPEEX,
/* Ick, LPC10 sounds terrible, but at least we have code for it, if you're tacky enough
to use it */
AST_FORMAT_LPC10,
/* G.729a is faster than 723 and slightly less expensive */
AST_FORMAT_G729A,
/* Down to G.723.1 which is proprietary but at least designed for voice */
AST_FORMAT_G723_1,
/* Last and least, MP3 which was of course never designed for real-time voice */
AST_FORMAT_MP3,
};
for (x=0;x<sizeof(prefs) / sizeof(prefs[0]); x++)
if (fmts & prefs[x])
return prefs[x];
ast_log(LOG_WARNING, "Don't know any of 0x%x formats\n", fmts);
return 0;
}
struct ast_channel *ast_channel_alloc(int needqueue)
{
struct ast_channel *tmp;
struct ast_channel_pvt *pvt;
/* If shutting down, don't allocate any new channels */
if (shutting_down)
return NULL;
tmp = malloc(sizeof(struct ast_channel));
if (tmp) {
pvt = malloc(sizeof(struct ast_channel_pvt));
if (pvt) {
memset(pvt, 0, sizeof(struct ast_channel_pvt));
tmp->sched = sched_context_create();
if (tmp->sched) {
if (needqueue &&
pipe(pvt->alertpipe)) {
ast_log(LOG_WARNING, "Alert pipe creation failed!\n");
free(pvt);
free(tmp);
tmp = NULL;
pvt = NULL;
} else {
/* Make sure we've got it done right if they don't */
if (needqueue) {
flags = fcntl(pvt->alertpipe[0], F_GETFL);
fcntl(pvt->alertpipe[0], F_SETFL, flags | O_NONBLOCK);
flags = fcntl(pvt->alertpipe[1], F_GETFL);
fcntl(pvt->alertpipe[1], F_SETFL, flags | O_NONBLOCK);
} else
pvt->alertpipe[0] = pvt->alertpipe[1] = -1;
#ifdef ZAPTEL_OPTIMIZATIONS
tmp->timingfd = open("/dev/zap/timer", O_RDWR);
#else
tmp->timingfd = -1;
#endif
/* Always watch the alertpipe */
tmp->fds[AST_MAX_FDS-1] = pvt->alertpipe[0];
/* And timing pipe */
tmp->fds[AST_MAX_FDS-2] = tmp->timingfd;
strncpy(tmp->name, "**Unknown**", sizeof(tmp->name)-1);
tmp->pvt = pvt;
/* Initial state */
tmp->_state = AST_STATE_DOWN;
tmp->stack = -1;
tmp->streamid = -1;
tmp->appl = NULL;
tmp->data = NULL;
snprintf(tmp->uniqueid, sizeof(tmp->uniqueid), "%li.%d", time(NULL), uniqueint++);
AST_LIST_HEAD_INIT(headp);
tmp->vars=ast_var_assign("tempvar","tempval");
AST_LIST_INSERT_HEAD(headp,tmp->vars,entries);
strncpy(tmp->context, "default", sizeof(tmp->context)-1);
strncpy(tmp->language, defaultlanguage, sizeof(tmp->language)-1);
strncpy(tmp->exten, "s", sizeof(tmp->exten)-1);
tmp->priority=1;
tmp->amaflags = ast_default_amaflags;
strncpy(tmp->accountcode, ast_default_accountcode, sizeof(tmp->accountcode)-1);
tmp->next = channels;
channels= tmp;
}
} else {
ast_log(LOG_WARNING, "Unable to create schedule context\n");
free(tmp);
tmp = NULL;
}
} else {
ast_log(LOG_WARNING, "Out of memory\n");
free(tmp);
tmp = NULL;
}
} else
ast_log(LOG_WARNING, "Out of memory\n");
int ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin, int lock)
{
struct ast_frame *f;
struct ast_frame *prev, *cur;
int blah = 1;
int qlen = 0;
f = ast_frdup(fin);
if (!f) {
ast_log(LOG_WARNING, "Unable to duplicate frame\n");
return -1;
}
if (lock)
ast_pthread_mutex_lock(&chan->lock);
prev = NULL;
cur = chan->pvt->readq;
while(cur) {
prev = cur;
cur = cur->next;
qlen++;
}
/* Allow up to 96 voice frames outstanding, and up to 128 total frames */
if (((fin->frametype == AST_FRAME_VOICE) && (qlen > 96)) || (qlen > 128)) {
if (fin->frametype != AST_FRAME_VOICE) {
ast_log(LOG_WARNING, "Exceptionally long queue length queuing to %s\n", chan->name);
CRASH;
} else {
ast_log(LOG_DEBUG, "Dropping voice to exceptionally long queue on %s\n", chan->name);
if (lock)
ast_pthread_mutex_unlock(&chan->lock);
return 0;
}
}
if (prev)
prev->next = f;
else
chan->pvt->readq = f;
if (chan->pvt->alertpipe[1] > -1) {
if (write(chan->pvt->alertpipe[1], &blah, sizeof(blah)) != sizeof(blah))
ast_log(LOG_WARNING, "Unable to write to alert pipe on %s, frametype/subclass %d/%d (qlen = %d): %s!\n",
chan->name, f->frametype, f->subclass, qlen, strerror(errno));
} else if (chan->blocking) {
pthread_kill(chan->blocker, SIGURG);
}
if (lock)
ast_pthread_mutex_unlock(&chan->lock);
return 0;
}
int ast_queue_hangup(struct ast_channel *chan, int lock)
{
struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_HANGUP };
chan->_softhangup |= AST_SOFTHANGUP_DEV;
return ast_queue_frame(chan, &f, lock);
}
int ast_queue_control(struct ast_channel *chan, int control, int lock)
{
struct ast_frame f = { AST_FRAME_CONTROL, };
f.subclass = control;
return ast_queue_frame(chan, &f, lock);
}
int ast_channel_defer_dtmf(struct ast_channel *chan)
{
int pre = 0;
if (chan) {
pre = chan->deferdtmf;
chan->deferdtmf = 1;
}
return pre;
}
void ast_channel_undefer_dtmf(struct ast_channel *chan)
{
if (chan)
chan->deferdtmf = 0;
}
struct ast_channel *ast_channel_walk(struct ast_channel *prev)
{
struct ast_channel *l, *ret=NULL;
return l;
}
while(l) {
if (l == prev)
ret = l->next;
l = l->next;
}
int ast_safe_sleep_conditional( struct ast_channel *chan, int ms,
int (*cond)(void*), void *data )
{
struct ast_frame *f;
while(ms > 0) {
if( cond && ((*cond)(data) == 0 ) )
return 0;
ms = ast_waitfor(chan, ms);
if (ms <0)
return -1;
if (ms > 0) {
f = ast_read(chan);
if (!f)
return -1;
ast_frfree(f);
}
}
return 0;
}
int ast_safe_sleep(struct ast_channel *chan, int ms)
{
struct ast_frame *f;
while(ms > 0) {
ms = ast_waitfor(chan, ms);
if (ms <0)
return -1;
if (ms > 0) {
f = ast_read(chan);
if (!f)
return -1;
ast_frfree(f);
}
}
return 0;
}
void ast_channel_free(struct ast_channel *chan)
{
struct ast_channel *last=NULL, *cur;
char name[AST_CHANNEL_NAME];
cur = channels;
while(cur) {
if (cur == chan) {
if (last)
last->next = cur->next;
else
channels = cur->next;
break;
}
last = cur;
cur = cur->next;
}
if (!cur)
ast_log(LOG_WARNING, "Unable to find channel in list\n");
if (chan->pvt->pvt)
ast_log(LOG_WARNING, "Channel '%s' may not have been hung up properly\n", chan->name);
strncpy(name, chan->name, sizeof(name)-1);
/* Stop monitoring */
if (chan->monitor) {
chan->monitor->stop( chan, 0 );
}
/* Free translatosr */
if (chan->pvt->readtrans)
ast_translator_free_path(chan->pvt->readtrans);
if (chan->pvt->writetrans)
ast_translator_free_path(chan->pvt->writetrans);
if (chan->pbx)
ast_log(LOG_WARNING, "PBX may not have been terminated properly on '%s'\n", chan->name);
if (chan->dnid)
free(chan->dnid);
if (chan->callerid)
free(chan->callerid);
/* Close pipes if appropriate */
if ((fd = chan->pvt->alertpipe[0]) > -1)
close(fd);
if ((fd = chan->pvt->alertpipe[1]) > -1)
close(fd);
if ((fd = chan->timingfd) > -1)
close(fd);
while(f) {
fp = f;
f = f->next;
ast_frfree(fp);
}
/* loop over the variables list, freeing all data and deleting list items */
/* no need to lock the list, as the channel is already locked */
while (!AST_LIST_EMPTY(headp)) { /* List Deletion. */
vardata = AST_LIST_FIRST(headp);
AST_LIST_REMOVE_HEAD(headp, entries);
// printf("deleting var %s=%s\n",ast_var_name(vardata),ast_var_value(vardata));
ast_var_delete(vardata);
}
ast_device_state_changed(name);
int ast_softhangup_nolock(struct ast_channel *chan, int cause)
if (option_debug)
ast_log(LOG_DEBUG, "Soft-Hanging up channel '%s'\n", chan->name);
/* Inform channel driver that we need to be hung up, if it cares */
chan->_softhangup |= cause;
ast_queue_frame(chan, &f, 0);
/* Interrupt any select call or such */
if (chan->blocking)
pthread_kill(chan->blocker, SIGURG);
return res;
}
int ast_softhangup(struct ast_channel *chan, int cause)
{
int res;
ast_pthread_mutex_lock(&chan->lock);
res = ast_softhangup_nolock(chan, cause);
ast_pthread_mutex_unlock(&chan->lock);
return res;
}
static int ast_do_masquerade(struct ast_channel *original);
static void free_translation(struct ast_channel *clone)
{
if (clone->pvt->writetrans)
ast_translator_free_path(clone->pvt->writetrans);
if (clone->pvt->readtrans)
ast_translator_free_path(clone->pvt->readtrans);
clone->pvt->writetrans = NULL;
clone->pvt->readtrans = NULL;
clone->pvt->rawwriteformat = clone->nativeformats;
clone->pvt->rawreadformat = clone->nativeformats;
}
int ast_hangup(struct ast_channel *chan)
{
int res = 0;
/* Don't actually hang up a channel that will masquerade as someone else, or
if someone is going to masquerade as us */
ast_pthread_mutex_lock(&chan->lock);
if (chan->masq) {
if (ast_do_masquerade(chan))
ast_log(LOG_WARNING, "Failed to perform masquerade\n");
}
if (chan->masq) {
ast_log(LOG_WARNING, "%s getting hung up, but someone is trying to masq into us?!?\n", chan->name);
/* If this channel is one which will be masqueraded into something,
mark it as a zombie already, so we know to free it later */
if (chan->masqr) {
if (chan->stream)
ast_stopstream(chan);
if (chan->sched)
sched_context_destroy(chan->sched);
/* Clear any tone stuff remaining */
if (chan->generatordata)
chan->generator->release(chan, chan->generatordata);
chan->generatordata = NULL;
chan->generator = NULL;
if (chan->cdr) {
/* End the CDR if it hasn't already */
ast_cdr_end(chan->cdr);
/* Post and Free the CDR */
ast_cdr_post(chan->cdr);
ast_cdr_free(chan->cdr);
}
if (chan->blocking) {
ast_log(LOG_WARNING, "Hard hangup called by thread %ld on %s, while fd "
"is blocked by thread %ld in procedure %s! Expect a failure\n",
pthread_self(), chan->name, chan->blocker, chan->blockproc);
CRASH;
}
if (!chan->zombie) {
if (option_debug)
ast_log(LOG_DEBUG, "Hanging up channel '%s'\n", chan->name);
if (chan->pvt->hangup)
res = chan->pvt->hangup(chan);
} else
if (option_debug)
ast_log(LOG_DEBUG, "Hanging up zombie '%s'\n", chan->name);
"Channel: %s\r\n"
"Uniqueid: %s\r\n",
chan->name, chan->uniqueid);
return res;
}
void ast_channel_unregister(char *type)
{
struct chanlist *chan, *last=NULL;
if (option_debug)
ast_log(LOG_DEBUG, "Unregistering channel type '%s'\n", type);
ast_log(LOG_WARNING, "Unable to lock channel list\n");
return;
}
chan = backends;
while(chan) {
if (!strcasecmp(chan->type, type)) {
if (last)
last->next = chan->next;
else
backends = backends->next;
free(chan);
return;
}
last = chan;
chan = chan->next;
}
}
int ast_answer(struct ast_channel *chan)
{
/* Stop if we're a zombie or need a soft hangup */
void ast_deactivate_generator(struct ast_channel *chan)
{
if (chan->generatordata) {
chan->generator->release(chan, chan->generatordata);
chan->generatordata = NULL;
chan->writeinterrupt = 0;
}
}
int ast_activate_generator(struct ast_channel *chan, struct ast_generator *gen, void *params)
{
if (chan->generatordata) {
chan->generator->release(chan, chan->generatordata);
chan->generatordata = NULL;
}
if ((chan->generatordata = gen->alloc(chan, params))) {
chan->generator = gen;
} else {
return -1;
}
return 0;
}
int ast_waitfor_n_fd(int *fds, int n, int *ms, int *exception)
{
/* Wait for x amount of time on a file descriptor to have input. */
struct timeval tv;
fd_set rfds, efds;
int res;
int x, max=-1;
int winner = -1;
tv.tv_sec = *ms / 1000;
tv.tv_usec = (*ms % 1000) * 1000;
FD_ZERO(&rfds);
FD_ZERO(&efds);
for (x=0;x<n;x++) {
if (fds[x] > -1) {
FD_SET(fds[x], &rfds);
FD_SET(fds[x], &efds);
if (fds[x] > max)
max = fds[x];
}
if (*ms >= 0)
res = ast_select(max + 1, &rfds, NULL, &efds, &tv);
res = ast_select(max + 1, &rfds, NULL, &efds, NULL);
if (res < 0) {
/* Simulate a timeout if we were interrupted */
if (errno != EINTR)
*ms = -1;
else
*ms = 0;
return -1;
}
if ((fds[x] > -1) && (FD_ISSET(fds[x], &rfds) || FD_ISSET(fds[x], &efds)) && (winner < 0)) {
if (exception)
*exception = FD_ISSET(fds[x], &efds);
}
*ms = tv.tv_sec * 1000 + tv.tv_usec / 1000;
return winner;
}
struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds, int nfds,
int *exception, int *outfd, int *ms)
{
/* Wait for x amount of time on a file descriptor to have input. */
struct timeval tv;
fd_set rfds, efds;
int res;
if (outfd)
*outfd = -1;
if (exception)
*exception = 0;
/* Perform any pending masquerades */
for (x=0;x<n;x++) {
if (c[x]->masq) {
if (ast_do_masquerade(c[x])) {
ast_log(LOG_WARNING, "Masquerade failed\n");
*ms = -1;
ast_pthread_mutex_unlock(&c[x]->lock);
ast_pthread_mutex_unlock(&c[x]->lock);
tv.tv_sec = *ms / 1000;
tv.tv_usec = (*ms % 1000) * 1000;
FD_ZERO(&rfds);
FD_ZERO(&efds);
FD_SET(c[x]->fds[y], &rfds);
FD_SET(c[x]->fds[y], &efds);
if (c[x]->fds[y] > max)
max = c[x]->fds[y];
}
}
for (x=0;x<nfds; x++) {
FD_SET(fds[x], &rfds);
FD_SET(fds[x], &efds);
if (fds[x] > max)
max = fds[x];
}
if (*ms >= 0)
res = ast_select(max + 1, &rfds, NULL, &efds, &tv);
res = ast_select(max + 1, &rfds, NULL, &efds, NULL);
if (res < 0) {
for (x=0;x<n;x++)
c[x]->blocking = 0;
/* Simulate a timeout if we were interrupted */
if (errno != EINTR)
*ms = -1;
for (y=0;y<AST_MAX_FDS;y++) {
if (c[x]->fds[y] > -1) {
if ((FD_ISSET(c[x]->fds[y], &rfds) || FD_ISSET(c[x]->fds[y], &efds)) && !winner) {
/* Set exception flag if appropriate */
if (FD_ISSET(c[x]->fds[y], &efds))
c[x]->exception = 1;
c[x]->fdno = y;
winner = c[x];
}
}
for (x=0;x<nfds;x++) {
if ((FD_ISSET(fds[x], &rfds) || FD_ISSET(fds[x], &efds)) && !winner) {
if (outfd)
*outfd = fds[x];
if (FD_ISSET(fds[x], &efds) && exception)
*exception = 1;
winner = NULL;
}
}
*ms = tv.tv_sec * 1000 + tv.tv_usec / 1000;
return winner;
}
struct ast_channel *ast_waitfor_n(struct ast_channel **c, int n, int *ms)
{
return ast_waitfor_nandfds(c, n, NULL, 0, NULL, NULL, ms);
}
int ast_waitfor(struct ast_channel *c, int ms)
{
if (ms < 0) {
if (oldms < 0)
return 0;
else
return -1;
}
}
char ast_waitfordigit(struct ast_channel *c, int ms)
{
/* XXX Should I be merged with waitfordigit_full XXX */
/* Stop if we're a zombie or need a soft hangup */
/* Wait for a digit, no more than ms milliseconds total. */
while(ms && !result) {
ms = ast_waitfor(c, ms);
if (ms < 0) /* Error */
result = -1;
else if (ms > 0) {
/* Read something */
f = ast_read(c);
if (f) {
if (f->frametype == AST_FRAME_DTMF)
result = f->subclass;
ast_frfree(f);
} else
result = -1;
}
}
return result;
}
int ast_settimeout(struct ast_channel *c, int samples, int (*func)(void *data), void *data)
{
int res = -1;
#ifdef ZAPTEL_OPTIMIZATIONS
if (c->timingfd > -1) {
if (!func) {
samples = 0;
data = 0;
}
ast_log(LOG_DEBUG, "Scheduling timer at %d sample intervals\n", samples);
res = ioctl(c->timingfd, ZT_TIMERCONFIG, &samples);
c->timingfunc = func;
c->timingdata = data;
}
#endif
return res;
}
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
char ast_waitfordigit_full(struct ast_channel *c, int ms, int audio, int ctrl)
{
struct ast_frame *f;
char result = 0;
struct ast_channel *rchan;
int outfd;
/* Stop if we're a zombie or need a soft hangup */
if (c->zombie || ast_check_hangup(c))
return -1;
/* Wait for a digit, no more than ms milliseconds total. */
while(ms && !result) {
rchan = ast_waitfor_nandfds(&c, 1, &audio, (audio > -1) ? 1 : 0, NULL, &outfd, &ms);
if ((!rchan) && (outfd < 0) && (ms)) /* Error */
result = -1;
else if (outfd > -1) {
result = 1;
} else if (rchan) {
/* Read something */
f = ast_read(c);
if (f) {
if (f->frametype == AST_FRAME_DTMF)
result = f->subclass;
ast_frfree(f);
} else
result = -1;
}
}
return result;
}