Newer
Older
Joshua Colp
committed
/* Get rid of each of the data stores on the channel */
Joshua Colp
committed
while ((datastore = AST_LIST_REMOVE_HEAD(&chan->datastores, entry)))
Joshua Colp
committed
/* Free the data store */
ast_channel_datastore_free(datastore);
Joshua Colp
committed
AST_LIST_HEAD_INIT_NOLOCK(&chan->datastores);
Joshua Colp
committed
/* loop over the variables list, freeing all data and deleting list items */
/* no need to lock the list, as the channel is already locked */
Kevin P. Fleming
committed
while ((vardata = AST_LIST_REMOVE_HEAD(headp, entries)))
ast_var_delete(vardata);
Russell Bryant
committed
/* Destroy the jitterbuffer */
ast_jb_destroy(chan);
ast_string_field_free_all(chan);
AST_LIST_UNLOCK(&channels);
Mark Spencer
committed
ast_device_state_changed_literal(name);
Joshua Colp
committed
struct ast_datastore *ast_channel_datastore_alloc(const struct ast_datastore_info *info, char *uid)
{
struct ast_datastore *datastore = NULL;
/* Make sure we at least have type so we can identify this */
if (info == NULL) {
return NULL;
}
/* Allocate memory for datastore and clear it */
datastore = ast_calloc(1, sizeof(*datastore));
if (datastore == NULL) {
return NULL;
}
datastore->info = info;
datastore->uid = ast_strdup(uid);
Joshua Colp
committed
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
return datastore;
}
int ast_channel_datastore_free(struct ast_datastore *datastore)
{
int res = 0;
/* Using the destroy function (if present) destroy the data */
if (datastore->info->destroy != NULL && datastore->data != NULL) {
datastore->info->destroy(datastore->data);
datastore->data = NULL;
}
/* Free allocated UID memory */
if (datastore->uid != NULL) {
free(datastore->uid);
datastore->uid = NULL;
}
/* Finally free memory used by ourselves */
free(datastore);
return res;
}
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
{
int res = 0;
Joshua Colp
committed
AST_LIST_INSERT_HEAD(&chan->datastores, datastore, entry);
Joshua Colp
committed
return res;
}
int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
{
struct ast_datastore *datastore2 = NULL;
int res = -1;
/* Find our position and remove ourselves */
Joshua Colp
committed
AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->datastores, datastore2, entry) {
Joshua Colp
committed
if (datastore2 == datastore) {
Joshua Colp
committed
AST_LIST_REMOVE_CURRENT(&chan->datastores, entry);
Joshua Colp
committed
res = 0;
break;
}
}
AST_LIST_TRAVERSE_SAFE_END
return res;
}
struct ast_datastore *ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, char *uid)
{
struct ast_datastore *datastore = NULL;
if (info == NULL)
return NULL;
Joshua Colp
committed
AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->datastores, datastore, entry) {
Joshua Colp
committed
if (datastore->info == info) {
if (uid != NULL && datastore->uid != NULL) {
if (!strcasecmp(uid, datastore->uid)) {
/* Matched by type AND uid */
break;
}
} else {
/* Matched by type at least */
break;
}
}
}
AST_LIST_TRAVERSE_SAFE_END
return datastore;
}
Kevin P. Fleming
committed
int ast_channel_spy_add(struct ast_channel *chan, struct ast_channel_spy *spy)
Kevin P. Fleming
committed
if (!ast_test_flag(spy, CHANSPY_FORMAT_AUDIO)) {
ast_log(LOG_WARNING, "Could not add channel spy '%s' to channel '%s', only audio format spies are supported.\n",
spy->type, chan->name);
return -1;
}
Kevin P. Fleming
committed
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
if (ast_test_flag(spy, CHANSPY_READ_VOLADJUST) && (spy->read_queue.format != AST_FORMAT_SLINEAR)) {
ast_log(LOG_WARNING, "Cannot provide volume adjustment on '%s' format spies\n",
ast_getformatname(spy->read_queue.format));
return -1;
}
if (ast_test_flag(spy, CHANSPY_WRITE_VOLADJUST) && (spy->write_queue.format != AST_FORMAT_SLINEAR)) {
ast_log(LOG_WARNING, "Cannot provide volume adjustment on '%s' format spies\n",
ast_getformatname(spy->write_queue.format));
return -1;
}
if (ast_test_flag(spy, CHANSPY_MIXAUDIO) &&
((spy->read_queue.format != AST_FORMAT_SLINEAR) ||
(spy->write_queue.format != AST_FORMAT_SLINEAR))) {
ast_log(LOG_WARNING, "Cannot provide audio mixing on '%s'-'%s' format spies\n",
ast_getformatname(spy->read_queue.format), ast_getformatname(spy->write_queue.format));
return -1;
}
if (!chan->spies) {
if (!(chan->spies = ast_calloc(1, sizeof(*chan->spies)))) {
Kevin P. Fleming
committed
return -1;
Kevin P. Fleming
committed
AST_LIST_HEAD_INIT_NOLOCK(&chan->spies->list);
AST_LIST_INSERT_HEAD(&chan->spies->list, spy, list);
} else {
AST_LIST_INSERT_TAIL(&chan->spies->list, spy, list);
Kevin P. Fleming
committed
if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE) {
ast_cond_init(&spy->trigger, NULL);
ast_set_flag(spy, CHANSPY_TRIGGER_READ);
ast_clear_flag(spy, CHANSPY_TRIGGER_WRITE);
}
ast_log(LOG_DEBUG, "Spy %s added to channel %s\n",
spy->type, chan->name);
return 0;
}
void ast_channel_spy_stop_by_type(struct ast_channel *chan, const char *type)
{
struct ast_channel_spy *spy;
if (!chan->spies)
return;
AST_LIST_TRAVERSE(&chan->spies->list, spy, list) {
Kevin P. Fleming
committed
ast_mutex_lock(&spy->lock);
if ((spy->type == type) && (spy->status == CHANSPY_RUNNING)) {
Kevin P. Fleming
committed
if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE)
ast_cond_signal(&spy->trigger);
}
ast_mutex_unlock(&spy->lock);
Kevin P. Fleming
committed
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
}
}
void ast_channel_spy_trigger_wait(struct ast_channel_spy *spy)
{
ast_cond_wait(&spy->trigger, &spy->lock);
}
void ast_channel_spy_remove(struct ast_channel *chan, struct ast_channel_spy *spy)
{
struct ast_frame *f;
if (!chan->spies)
return;
AST_LIST_REMOVE(&chan->spies->list, spy, list);
ast_mutex_lock(&spy->lock);
for (f = spy->read_queue.head; f; f = spy->read_queue.head) {
spy->read_queue.head = f->next;
ast_frfree(f);
}
for (f = spy->write_queue.head; f; f = spy->write_queue.head) {
spy->write_queue.head = f->next;
ast_frfree(f);
}
if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE)
ast_cond_destroy(&spy->trigger);
ast_mutex_unlock(&spy->lock);
ast_log(LOG_DEBUG, "Spy %s removed from channel %s\n",
spy->type, chan->name);
if (AST_LIST_EMPTY(&chan->spies->list)) {
if (chan->spies->read_translator.path)
ast_translator_free_path(chan->spies->read_translator.path);
if (chan->spies->write_translator.path)
ast_translator_free_path(chan->spies->write_translator.path);
free(chan->spies);
chan->spies = NULL;
}
}
static void detach_spies(struct ast_channel *chan)
Kevin P. Fleming
committed
{
struct ast_channel_spy *spy;
if (!chan->spies)
return;
/* Marking the spies as done is sufficient. Chanspy or spy users will get the picture. */
AST_LIST_TRAVERSE(&chan->spies->list, spy, list) {
ast_mutex_lock(&spy->lock);
if (spy->status == CHANSPY_RUNNING)
spy->status = CHANSPY_DONE;
if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE)
ast_cond_signal(&spy->trigger);
ast_mutex_unlock(&spy->lock);
}
AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->spies->list, spy, list)
ast_channel_spy_remove(chan, spy);
AST_LIST_TRAVERSE_SAFE_END;
/*! \brief Softly hangup a channel, don't lock */
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 */
ast_queue_frame(chan, &ast_null_frame);
/* Interrupt any poll call or such */
if (ast_test_flag(chan, AST_FLAG_BLOCKING))
/*! \brief Softly hangup a channel, lock */
int ast_softhangup(struct ast_channel *chan, int cause)
{
int res;
Kevin P. Fleming
committed
enum spy_direction {
SPY_READ,
SPY_WRITE,
};
#define SPY_QUEUE_SAMPLE_LIMIT 4000 /* half of one second */
static void queue_frame_to_spies(struct ast_channel *chan, struct ast_frame *f, enum spy_direction dir)
Kevin P. Fleming
committed
struct ast_frame *translated_frame = NULL;
struct ast_channel_spy *spy;
struct channel_spy_trans *trans;
Kevin P. Fleming
committed
trans = (dir == SPY_READ) ? &chan->spies->read_translator : &chan->spies->write_translator;
AST_LIST_TRAVERSE(&chan->spies->list, spy, list) {
struct ast_frame *last;
struct ast_frame *f1; /* the frame to append */
struct ast_channel_spy_queue *queue;
Kevin P. Fleming
committed
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
ast_mutex_lock(&spy->lock);
queue = (dir == SPY_READ) ? &spy->read_queue : &spy->write_queue;
if ((queue->format == AST_FORMAT_SLINEAR) && (f->subclass != AST_FORMAT_SLINEAR)) {
if (!translated_frame) {
if (trans->path && (trans->last_format != f->subclass)) {
ast_translator_free_path(trans->path);
trans->path = NULL;
}
if (!trans->path) {
ast_log(LOG_DEBUG, "Building translator from %s to SLINEAR for spies on channel %s\n",
ast_getformatname(f->subclass), chan->name);
if ((trans->path = ast_translator_build_path(AST_FORMAT_SLINEAR, f->subclass)) == NULL) {
ast_log(LOG_WARNING, "Cannot build a path from %s to %s\n",
ast_getformatname(f->subclass), ast_getformatname(AST_FORMAT_SLINEAR));
ast_mutex_unlock(&spy->lock);
continue;
} else {
trans->last_format = f->subclass;
}
}
if (!(translated_frame = ast_translate(trans->path, f, 0))) {
ast_log(LOG_ERROR, "Translation to %s failed, dropping frame for spies\n",
ast_getformatname(AST_FORMAT_SLINEAR));
ast_mutex_unlock(&spy->lock);
break;
}
Kevin P. Fleming
committed
}
f1 = translated_frame;
Kevin P. Fleming
committed
} else {
if (f->subclass != queue->format) {
ast_log(LOG_WARNING, "Spy '%s' on channel '%s' wants format '%s', but frame is '%s', dropping\n",
spy->type, chan->name,
ast_getformatname(queue->format), ast_getformatname(f->subclass));
ast_mutex_unlock(&spy->lock);
continue;
}
/* duplicate and append f1 to the tail */
f1 = ast_frdup(f1);
for (last = queue->head; last && last->next; last = last->next)
;
if (last)
last->next = f1;
else
queue->head = f1;
Kevin P. Fleming
committed
queue->samples += f->samples;
if (queue->samples > SPY_QUEUE_SAMPLE_LIMIT) {
if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE) {
switch (ast_test_flag(spy, CHANSPY_TRIGGER_MODE)) {
case CHANSPY_TRIGGER_READ:
if (dir == SPY_WRITE) {
ast_set_flag(spy, CHANSPY_TRIGGER_WRITE);
ast_clear_flag(spy, CHANSPY_TRIGGER_READ);
if (option_debug)
ast_log(LOG_DEBUG, "Switching spy '%s' on '%s' to write-trigger mode\n",
spy->type, chan->name);
Kevin P. Fleming
committed
}
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
break;
case CHANSPY_TRIGGER_WRITE:
if (dir == SPY_READ) {
ast_set_flag(spy, CHANSPY_TRIGGER_READ);
ast_clear_flag(spy, CHANSPY_TRIGGER_WRITE);
if (option_debug)
ast_log(LOG_DEBUG, "Switching spy '%s' on '%s' to read-trigger mode\n",
spy->type, chan->name);
}
break;
}
if (option_debug)
ast_log(LOG_DEBUG, "Triggering queue flush for spy '%s' on '%s'\n",
spy->type, chan->name);
ast_set_flag(spy, CHANSPY_TRIGGER_FLUSH);
ast_cond_signal(&spy->trigger);
} else {
if (option_debug)
ast_log(LOG_DEBUG, "Spy '%s' on channel '%s' %s queue too long, dropping frames\n",
spy->type, chan->name, (dir == SPY_READ) ? "read" : "write");
while (queue->samples > SPY_QUEUE_SAMPLE_LIMIT) {
struct ast_frame *drop = queue->head;
queue->samples -= drop->samples;
queue->head = drop->next;
ast_frfree(drop);
Kevin P. Fleming
committed
}
}
} else {
switch (ast_test_flag(spy, CHANSPY_TRIGGER_MODE)) {
case CHANSPY_TRIGGER_READ:
if (dir == SPY_READ)
ast_cond_signal(&spy->trigger);
break;
case CHANSPY_TRIGGER_WRITE:
if (dir == SPY_WRITE)
ast_cond_signal(&spy->trigger);
break;
}
}
ast_mutex_unlock(&spy->lock);
Kevin P. Fleming
committed
if (translated_frame)
ast_frfree(translated_frame);
static void free_translation(struct ast_channel *clone)
{
if (clone->writetrans)
ast_translator_free_path(clone->writetrans);
if (clone->readtrans)
ast_translator_free_path(clone->readtrans);
clone->writetrans = NULL;
clone->readtrans = NULL;
clone->rawwriteformat = clone->nativeformats;
clone->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 */
Kevin P. Fleming
committed
detach_spies(chan); /* get rid of spies */
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) /* Close audio stream */
if (chan->vstream) /* Close video stream */
if (chan->sched) {
chan->sched = NULL;
}
if (chan->generatordata) /* Clear any tone stuff remaining */
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_detach(chan->cdr); /* Post and Free the CDR */
Kevin P. Fleming
committed
chan->cdr = NULL;
if (ast_test_flag(chan, AST_FLAG_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",
(long)pthread_self(), chan->name, (long)chan->blocker, chan->blockproc);
if (!ast_test_flag(chan, AST_FLAG_ZOMBIE)) {
if (option_debug)
ast_log(LOG_DEBUG, "Hanging up channel '%s'\n", chan->name);
if (chan->tech->hangup)
res = chan->tech->hangup(chan);
if (option_debug)
ast_log(LOG_DEBUG, "Hanging up zombie '%s'\n", chan->name);
manager_event(EVENT_FLAG_CALL, "Hangup",
"Cause: %d\r\n"
"Cause-txt: %s\r\n",
chan->name,
chan->uniqueid,
chan->hangupcause,
ast_cause2str(chan->hangupcause)
);
return res;
}
int ast_answer(struct ast_channel *chan)
{
Mark Spencer
committed
/* You can't answer an outbound call */
if (ast_test_flag(chan, AST_FLAG_OUTGOING)) {
ast_channel_unlock(chan);
Mark Spencer
committed
return 0;
/* Stop if we're a zombie or need a soft hangup */
if (ast_test_flag(chan, AST_FLAG_ZOMBIE) || ast_check_hangup(chan)) {
if (chan->tech->answer)
res = chan->tech->answer(chan);
ast_cdr_answer(chan->cdr);
ast_cdr_answer(chan->cdr);
void ast_deactivate_generator(struct ast_channel *chan)
if (chan->generator && chan->generator->release)
chan->generator->release(chan, chan->generatordata);
chan->fds[AST_GENERATOR_FD] = -1;
ast_clear_flag(chan, AST_FLAG_WRITE_INT);
ast_settimeout(chan, 0, NULL, NULL);
static int generator_force(void *data)
{
/* Called if generator doesn't have data */
void *tmp;
int res;
int (*generate)(struct ast_channel *chan, void *tmp, int datalen, int samples);
struct ast_channel *chan = data;
tmp = chan->generatordata;
chan->generatordata = NULL;
generate = chan->generator->generate;
res = generate(chan, tmp, 0, 160);
chan->generatordata = tmp;
if (res) {
ast_log(LOG_DEBUG, "Auto-deactivating generator\n");
ast_deactivate_generator(chan);
}
return 0;
}
int ast_activate_generator(struct ast_channel *chan, struct ast_generator *gen, void *params)
{
Kevin P. Fleming
committed
Kevin P. Fleming
committed
if (chan->generator && chan->generator->release)
chan->generator->release(chan, chan->generatordata);
Kevin P. Fleming
committed
if (gen->alloc && !(chan->generatordata = gen->alloc(chan, params))) {
res = -1;
Kevin P. Fleming
committed
}
if (!res) {
ast_settimeout(chan, 160, generator_force, chan);
Kevin P. Fleming
committed
Kevin P. Fleming
committed
/*! \brief Wait for x amount of time on a file descriptor to have input. */
int ast_waitfor_n_fd(int *fds, int n, int *ms, int *exception)
ast_waitfor_nandfds(NULL, 0, fds, n, exception, &winner, ms);
/*! \brief Wait for x amount of time on a file descriptor to have input. */
struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds, int nfds,
struct pollfd *pfds;
long rms;
int x, y, max;
time_t now = 0;
struct fdmap {
int chan;
int fdno;
} *fdmap;
sz = n * AST_MAX_FDS + nfds;
Russell Bryant
committed
pfds = alloca(sizeof(*pfds) * sz);
fdmap = alloca(sizeof(*fdmap) * sz);
Mark Spencer
committed
if (ast_do_masquerade(c[x])) {
ast_log(LOG_WARNING, "Masquerade failed\n");
*ms = -1;
if (c[x]->whentohangup) {
if (!whentohangup)
time(&now);
diff = c[x]->whentohangup - now;
if (diff < 1) {
/* Should already be hungup */
c[x]->_softhangup |= AST_SOFTHANGUP_TIMEOUT;
return c[x];
}
if (!whentohangup || (diff < whentohangup))
whentohangup = diff;
}
if (whentohangup) {
rms = (whentohangup - now) * 1000; /* timeout in milliseconds */
if (*ms >= 0 && *ms < rms) /* original *ms still smaller */
rms = *ms;
/*
* Build the pollfd array, putting the channels' fds first,
* followed by individual fds. Order is important because
* individual fd's must have priority over channel fds.
*/
for (x=0; x<n; x++) {
for (y=0; y<AST_MAX_FDS; y++) {
fdmap[max].fdno = y; /* fd y is linked to this pfds */
fdmap[max].chan = x; /* channel x is linked to this pfds */
max += ast_add_fd(&pfds[max], c[x]->fds[y]);
/* Add the individual fds */
for (x=0; x<nfds; x++) {
fdmap[max].chan = -1;
max += ast_add_fd(&pfds[max], fds[x]);
if (*ms > 0)
Kevin P. Fleming
committed
start = ast_tvnow();
if (sizeof(int) == 4) { /* XXX fix timeout > 600000 on linux x86-32 */
do {
int kbrms = rms;
if (kbrms > 600000)
kbrms = 600000;
res = poll(pfds, max, kbrms);
if (!res)
rms -= kbrms;
} while (!res && (rms > 0));
} else {
res = poll(pfds, max, rms);
}
for (x=0; x<n; x++)
ast_clear_flag(c[x], AST_FLAG_BLOCKING);
if (res < 0) { /* Simulate a timeout if we were interrupted */
if (errno != EINTR)
*ms = -1;
if (whentohangup) { /* if we have a timeout, check who expired */
time(&now);
for (x=0; x<n; x++) {
if (c[x]->whentohangup && now >= c[x]->whentohangup) {
c[x]->_softhangup |= AST_SOFTHANGUP_TIMEOUT;
if (winner == NULL)
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
if (res == 0) { /* no fd ready, reset timeout and done */
*ms = 0; /* XXX use 0 since we may not have an exact timeout. */
return winner;
}
/*
* Then check if any channel or fd has a pending event.
* Remember to check channels first and fds last, as they
* must have priority on setting 'winner'
*/
for (x = 0; x < max; x++) {
res = pfds[x].revents;
if (res == 0)
continue;
if (fdmap[x].chan >= 0) { /* this is a channel */
winner = c[fdmap[x].chan]; /* override previous winners */
if (res & POLLPRI)
ast_set_flag(winner, AST_FLAG_EXCEPTION);
else
ast_clear_flag(winner, AST_FLAG_EXCEPTION);
winner->fdno = fdmap[x].fdno;
} else { /* this is an fd */
if (outfd)
*outfd = pfds[x].fd;
if (exception)
*exception = (res & POLLPRI) ? -1 : 0;
winner = NULL;
}
}
if (*ms > 0) {
Kevin P. Fleming
committed
*ms -= ast_tvdiff_ms(ast_tvnow(), start);
if (*ms < 0)
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)
{
int oldms = ms; /* -1 if no timeout */
ast_waitfor_nandfds(&c, 1, NULL, 0, NULL, NULL, &ms);
if ((ms < 0) && (oldms < 0))
ms = 0;
/* XXX never to be called with ms = -1 */
Kevin P. Fleming
committed
int ast_waitfordigit(struct ast_channel *c, int ms)
return ast_waitfordigit_full(c, ms, -1, -1);
int ast_settimeout(struct ast_channel *c, int samples, int (*func)(void *data), void *data)
Kevin P. Fleming
committed
#ifdef HAVE_ZAPTEL
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;
}
Kevin P. Fleming
committed
int ast_waitfordigit_full(struct ast_channel *c, int ms, int audiofd, int cmdfd)
/* Stop if we're a zombie or need a soft hangup */
if (ast_test_flag(c, AST_FLAG_ZOMBIE) || ast_check_hangup(c))
return -1;
/* Wait for a digit, no more than ms milliseconds total. */
while (ms) {
struct ast_channel *rchan;
int outfd;
Kevin P. Fleming
committed
errno = 0;
rchan = ast_waitfor_nandfds(&c, 1, &cmdfd, (cmdfd > -1) ? 1 : 0, NULL, &outfd, &ms);
if (!rchan && outfd < 0 && ms) {
Kevin P. Fleming
committed
if (errno == 0 || errno == EINTR)
continue;
ast_log(LOG_WARNING, "Wait failed (%s)\n", strerror(errno));
return -1;
} else if (outfd > -1) {
/* The FD we were watching has something waiting */
return 1;
int res;
struct ast_frame *f = ast_read(c);
if (!f)
return -1;
switch(f->frametype) {
case AST_FRAME_DTMF:
res = f->subclass;
return res;
case AST_FRAME_CONTROL:
switch(f->subclass) {
case AST_CONTROL_HANGUP:
ast_frfree(f);
return -1;
case AST_CONTROL_RINGING:
case AST_CONTROL_ANSWER:
/* Unimportant */
break;
default:
ast_log(LOG_WARNING, "Unexpected control subclass '%d'\n", f->subclass);
}
case AST_FRAME_VOICE:
/* Write audio if appropriate */
if (audiofd > -1)
write(audiofd, f->data, f->datalen);
}
/* Ignore */
ast_frfree(f);
return 0; /* Time is up */
static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
struct ast_frame *f = NULL; /* the return value */
/* this function is very long so make sure there is only one return
* point at the end (there is only one exception to this).
*/
Mark Spencer
committed
if (ast_do_masquerade(chan)) {
ast_log(LOG_WARNING, "Failed to perform masquerade\n");
} else {
f = &ast_null_frame;
}
goto done;
}
/* Stop if we're a zombie or need a soft hangup */
if (ast_test_flag(chan, AST_FLAG_ZOMBIE) || ast_check_hangup(chan)) {
goto done;
if (!ast_test_flag(chan, AST_FLAG_DEFER_DTMF) && !ast_strlen_zero(chan->dtmfq)) {
/* We have DTMF that has been deferred. Return it now */
chan->dtmff.frametype = AST_FRAME_DTMF;
chan->dtmff.subclass = chan->dtmfq[0];
/* Drop first digit from the buffer */
memmove(chan->dtmfq, chan->dtmfq + 1, sizeof(chan->dtmfq) - 1);
f = &chan->dtmff;
goto done;
/* Read and ignore anything on the alertpipe, but read only
one sizeof(blah) per frame that we send from it */
if (chan->alertpipe[0] > -1)
read(chan->alertpipe[0], &blah, sizeof(blah));
Kevin P. Fleming
committed
#ifdef HAVE_ZAPTEL
if (chan->timingfd > -1 && chan->fdno == AST_TIMING_FD && ast_test_flag(chan, AST_FLAG_EXCEPTION)) {
ast_clear_flag(chan, AST_FLAG_EXCEPTION);
/* IF we can't get event, assume it's an expired as-per the old interface */
res = ioctl(chan->timingfd, ZT_GETEVENT, &blah);
blah = ZT_EVENT_TIMER_EXPIRED;
if (blah == ZT_EVENT_TIMER_PING) {
if (!chan->readq || !chan->readq->next) {
/* Acknowledge PONG unless we need it again */
if (ioctl(chan->timingfd, ZT_TIMERPONG, &blah)) {
ast_log(LOG_WARNING, "Failed to pong timer on '%s': %s\n", chan->name, strerror(errno));
}
}
} else if (blah == ZT_EVENT_TIMER_EXPIRED) {
ioctl(chan->timingfd, ZT_TIMERACK, &blah);
if (chan->timingfunc) {
/* save a copy of func/data before unlocking the channel */
int (*func)(void *) = chan->timingfunc;
void *data = chan->timingdata;
ast_channel_unlock(chan);
func(data);
} else {
blah = 0;
ioctl(chan->timingfd, ZT_TIMERCONFIG, &blah);
chan->timingdata = NULL;
}
/* cannot 'goto done' because the channel is already unlocked */
return &ast_null_frame;
} else
ast_log(LOG_NOTICE, "No/unknown event '%d' on timer for '%s'?\n", blah, chan->name);
if (chan->fds[AST_GENERATOR_FD] > -1 && chan->fdno == AST_GENERATOR_FD) {
/* if the AST_GENERATOR_FD is set, call the generator with args
* set to -1 so it can do whatever it needs to.
*/
void *tmp = chan->generatordata;
chan->generatordata = NULL; /* reset to let ast_write get through */
chan->generator->generate(chan, tmp, -1, -1);
chan->generatordata = tmp;
f = &ast_null_frame;
goto done;
if (chan->readq) {
f = chan->readq;
chan->readq = f->next;
Kevin P. Fleming
committed
f->next = NULL;
/* XXX why not the same for frames from the channel ? */
if (f->frametype == AST_FRAME_CONTROL && f->subclass == AST_CONTROL_HANGUP) {
if (ast_test_flag(chan, AST_FLAG_EXCEPTION)) {
if (chan->tech->exception)
f = chan->tech->exception(chan);
else {
ast_log(LOG_WARNING, "Exception flag set on '%s', but no exception handler\n", chan->name);
f = &ast_null_frame;
ast_clear_flag(chan, AST_FLAG_EXCEPTION);
} else if (chan->tech->read)
f = chan->tech->read(chan);
else
ast_log(LOG_WARNING, "No read routine on channel %s\n", chan->name);
Kevin P. Fleming
committed
if (f) {
/* if the channel driver returned more than one frame, stuff the excess
into the readq for the next ast_read call
*/
if (f->next) {
chan->readq = f->next;
f->next = NULL;
}
Kevin P. Fleming
committed
switch (f->frametype) {
case AST_FRAME_CONTROL:
if (f->subclass == AST_CONTROL_ANSWER) {
Mark Spencer
committed
if (!ast_test_flag(chan, AST_FLAG_OUTGOING)) {
ast_log(LOG_DEBUG, "Ignoring answer on an inbound call!\n");
Mark Spencer
committed
f = &ast_null_frame;
} else if (prestate == AST_STATE_UP) {
Kevin P. Fleming
committed
ast_log(LOG_DEBUG, "Dropping duplicate answer!\n");
f = &ast_null_frame;
Mark Spencer
committed
} else {
/* Answer the CDR */
ast_setstate(chan, AST_STATE_UP);
ast_cdr_answer(chan->cdr);
Kevin P. Fleming
committed
}
}
break;
case AST_FRAME_DTMF:
ast_log(LOG_DTMF, "DTMF '%c' received on %s\n", f->subclass, chan->name);
if (ast_test_flag(chan, AST_FLAG_DEFER_DTMF)) {
if (strlen(chan->dtmfq) < sizeof(chan->dtmfq) - 2)
chan->dtmfq[strlen(chan->dtmfq)] = f->subclass;
else
ast_log(LOG_WARNING, "Dropping deferred DTMF digits on %s\n", chan->name);
f = &ast_null_frame;