Newer
Older
Kevin P. Fleming
committed
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
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
1075
1076
1077
1078
1079
1080
1081
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) {
if ((spy->type == type) && (spy->status == CHANSPY_RUNNING))
spy->status = CHANSPY_DONE;
}
}
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)
{
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;
/*--- ast_softhangup_nolock: 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 */
Mark Spencer
committed
ast_queue_frame(chan, &f);
/* Interrupt any poll call or such */
if (ast_test_flag(chan, AST_FLAG_BLOCKING))
pthread_kill(chan->blocker, SIGURG);
return res;
}
/*--- ast_softhangup_nolock: Softly hangup a channel, lock */
int ast_softhangup(struct ast_channel *chan, int cause)
{
int res;
ast_mutex_lock(&chan->lock);
ast_mutex_unlock(&chan->lock);
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 ast_channel_spy_queue *queue;
struct ast_channel_spy_queue *other_queue;
struct channel_spy_trans *trans;
struct ast_frame *last;
Kevin P. Fleming
committed
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
trans = (dir == SPY_READ) ? &chan->spies->read_translator : &chan->spies->write_translator;
AST_LIST_TRAVERSE(&chan->spies->list, spy, list) {
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;
}
}
translated_frame = ast_translate(trans->path, f, 0);
}
for (last = queue->head; last && last->next; last = last->next);
if (last)
last->next = ast_frdup(translated_frame);
else
queue->head = ast_frdup(translated_frame);
} 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;
}
for (last = queue->head; last && last->next; last = last->next);
if (last)
last->next = ast_frdup(f);
else
queue->head = ast_frdup(f);
Kevin P. Fleming
committed
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
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
1235
queue->samples += f->samples;
if (queue->samples > SPY_QUEUE_SAMPLE_LIMIT) {
if (ast_test_flag(spy, CHANSPY_TRIGGER_MODE) != CHANSPY_TRIGGER_NONE) {
other_queue = (dir == SPY_WRITE) ? &spy->read_queue : &spy->write_queue;
if (other_queue->samples == 0) {
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);
}
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);
ast_mutex_unlock(&spy->lock);
continue;
}
}
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);
}
} 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;
/*--- ast_hangup: Hangup a channel */
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_mutex_lock(&chan->lock);
Kevin P. Fleming
committed
detach_spies(chan); /* get rid of spies */
Mark Spencer
committed
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);
ast_mutex_unlock(&chan->lock);
/* 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)
sched_context_destroy(chan->sched);
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);
ast_mutex_unlock(&chan->lock);
"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)
{
/* 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);
void ast_deactivate_generator(struct ast_channel *chan)
if (chan->generator && chan->generator->release)
chan->generator->release(chan, chan->generatordata);
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
Kevin P. Fleming
committed
if (gen->alloc) {
if (!(chan->generatordata = gen->alloc(chan, params)))
res = -1;
}
if (!res) {
ast_settimeout(chan, 160, generator_force, chan);
Kevin P. Fleming
committed
Kevin P. Fleming
committed
/*--- ast_waitfor_n_fd: 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)
int spoint;
struct pollfd *pfds;
pfds = alloca(sizeof(struct pollfd) * n);
if (!pfds) {
ast_log(LOG_ERROR, "Out of memory\n");
return -1;
}
if (*ms > 0)
Kevin P. Fleming
committed
start = ast_tvnow();
pfds[y].fd = fds[x];
pfds[y].events = POLLIN | POLLPRI;
y++;
res = poll(pfds, y, *ms);
if (res < 0) {
/* Simulate a timeout if we were interrupted */
if (errno != EINTR)
*ms = -1;
else
*ms = 0;
return -1;
}
if (fds[x] > -1) {
if ((res = ast_fdisset(pfds, fds[x], y, &spoint))) {
winner = fds[x];
if (exception) {
if (res & POLLPRI)
*exception = -1;
else
*exception = 0;
}
}
Kevin P. Fleming
committed
*ms -= ast_tvdiff_ms(ast_tvnow(), start);
if (*ms < 0)
/*--- ast_waitfor_nanfds: 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,
int *exception, int *outfd, int *ms)
struct pollfd *pfds;
long rms;
int x, y, max;
int spoint;
time_t now = 0;
long whentohangup = 0, havewhen = 0, diff;
pfds = alloca(sizeof(struct pollfd) * (n * AST_MAX_FDS + nfds));
if (!pfds) {
ast_log(LOG_ERROR, "Out of memory\n");
*outfd = -1;
return NULL;
}
ast_mutex_lock(&c[x]->lock);
if (!havewhen)
time(&now);
diff = c[x]->whentohangup - now;
if (!havewhen || (diff < whentohangup)) {
havewhen++;
whentohangup = diff;
}
}
Mark Spencer
committed
if (ast_do_masquerade(c[x])) {
ast_log(LOG_WARNING, "Masquerade failed\n");
*ms = -1;
ast_mutex_unlock(&c[x]->lock);
ast_mutex_unlock(&c[x]->lock);
if (havewhen) {
if ((*ms < 0) || (whentohangup * 1000 < *ms)) {
rms = whentohangup * 1000;
for (x=0; x < n; x++) {
for (y=0; y< AST_MAX_FDS; y++) {
pfds[max].fd = c[x]->fds[y];
pfds[max].events = POLLIN | POLLPRI;
pfds[max].revents = 0;
if (fds[x] > -1) {
pfds[max].fd = fds[x];
pfds[max].events = POLLIN | POLLPRI;
pfds[max].revents = 0;
Kevin P. Fleming
committed
start = ast_tvnow();
if (sizeof(int) == 4) {
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);
}
ast_clear_flag(c[x], AST_FLAG_BLOCKING);
/* Simulate a timeout if we were interrupted */
if (errno != EINTR)
*ms = -1;
} else {
/* If no fds signalled, then timeout. So set ms = 0
since we may not have an exact timeout.
*/
if (res == 0)
*ms = 0;
if (havewhen)
time(&now);
ast_clear_flag(c[x], AST_FLAG_BLOCKING);
if (havewhen && c[x]->whentohangup && (now > c[x]->whentohangup)) {
c[x]->_softhangup |= AST_SOFTHANGUP_TIMEOUT;
if (!winner)
winner = c[x];
}
if ((res = ast_fdisset(pfds, c[x]->fds[y], max, &spoint))) {
if (res & POLLPRI)
ast_clear_flag(c[x], AST_FLAG_EXCEPTION);
if (fds[x] > -1) {
if ((res = ast_fdisset(pfds, fds[x], max, &spoint))) {
if (outfd)
*outfd = fds[x];
if (exception) {
if (res & POLLPRI)
*exception = -1;
else
*exception = 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)
{
if (ms < 0) {
if (oldms < 0)
return 0;
else
return -1;
}
Kevin P. Fleming
committed
int ast_waitfordigit(struct ast_channel *c, int ms)
/* XXX Should I be merged with waitfordigit_full XXX */
Kevin P. Fleming
committed
int result = 0;
/* Stop if we're a zombie or need a soft hangup */
if (ast_test_flag(c, AST_FLAG_ZOMBIE) || ast_check_hangup(c))
/* 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;
}
Kevin P. Fleming
committed
int ast_waitfordigit_full(struct ast_channel *c, int ms, int audiofd, int cmdfd)
{
struct ast_frame *f;
struct ast_channel *rchan;
int outfd;
/* 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. */
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;
} else if (rchan) {
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 */
struct ast_frame *ast_read(struct ast_channel *chan)
{
struct ast_frame *f = NULL;
#ifdef ZAPTEL_OPTIMIZATIONS
int (*func)(void *);
void *data;
int res;
static struct ast_frame null_frame = {
ast_mutex_lock(&chan->lock);
Mark Spencer
committed
if (ast_do_masquerade(chan)) {
ast_log(LOG_WARNING, "Failed to perform masquerade\n");
f = NULL;
} else
ast_mutex_unlock(&chan->lock);
return f;
}
/* Stop if we're a zombie or need a soft hangup */
if (ast_test_flag(chan, AST_FLAG_ZOMBIE) || ast_check_hangup(chan)) {
ast_mutex_unlock(&chan->lock);
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 */
memmove(chan->dtmfq, chan->dtmfq + 1, sizeof(chan->dtmfq) - 1);
ast_mutex_unlock(&chan->lock);
/* 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));
if ((chan->timingfd > -1) && (chan->fdno == AST_MAX_FDS - 2) && 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);
if (res)
blah = ZT_EVENT_TIMER_EXPIRED;
if (blah == ZT_EVENT_TIMER_PING) {
ast_log(LOG_NOTICE, "Oooh, there's a PING!\n");
if (!chan->readq || !chan->readq->next) {
/* Acknowledge PONG unless we need it again */
#if 0
ast_log(LOG_NOTICE, "Sending a PONG!\n");
#endif
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);
func = chan->timingfunc;
data = chan->timingdata;
ast_mutex_unlock(&chan->lock);
if (func) {
#if 0
ast_log(LOG_DEBUG, "Calling private function\n");
#endif
func(data);
} else {
blah = 0;
ast_mutex_lock(&chan->lock);
ioctl(chan->timingfd, ZT_TIMERCONFIG, &blah);
chan->timingdata = NULL;
ast_mutex_unlock(&chan->lock);
}
return f;
} else
ast_log(LOG_NOTICE, "No/unknown event '%d' on timer for '%s'?\n", blah, chan->name);
if (chan->readq) {
f = chan->readq;
chan->readq = f->next;
if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
ast_frfree(f);
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 = &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);
}
if (!(f->subclass & chan->nativeformats)) {
/* This frame can't be from the current native formats -- drop it on the
floor */
ast_log(LOG_NOTICE, "Dropping incompatible voice frame on %s of format %s since our native format has changed to %s\n", chan->name, ast_getformatname(f->subclass), ast_getformatname(chan->nativeformats));
Kevin P. Fleming
committed
if (chan->spies)
queue_frame_to_spies(chan, f, SPY_READ);
if (chan->monitor && chan->monitor->read_stream ) {
#ifndef MONITOR_CONSTANT_DELAY
int jump = chan->outsmpl - chan->insmpl - 2 * f->samples;
if (jump >= 0) {
if (ast_seekstream(chan->monitor->read_stream, jump + f->samples, SEEK_FORCECUR) == -1)
ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");
chan->insmpl += jump + 2 * f->samples;
} else
chan->insmpl+= f->samples;
#else
int jump = chan->outsmpl - chan->insmpl;
if (jump - MONITOR_DELAY >= 0) {
if (ast_seekstream(chan->monitor->read_stream, jump - f->samples, SEEK_FORCECUR) == -1)
ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");
chan->insmpl += jump;
} else
chan->insmpl += f->samples;
#endif
if (ast_writestream(chan->monitor->read_stream, f) < 0)
ast_log(LOG_WARNING, "Failed to write data to channel monitor read stream\n");
}
if (chan->readtrans) {
f = ast_translate(chan->readtrans, f, 1);
if (!f)
f = &null_frame;
}
/* Make sure we always return NULL in the future */
chan->_softhangup |= AST_SOFTHANGUP_DEV;
if (chan->generator)
/* End the CDR if appropriate */
if (chan->cdr)
ast_cdr_end(chan->cdr);
} else if (ast_test_flag(chan, AST_FLAG_DEFER_DTMF) && f->frametype == AST_FRAME_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 = &null_frame;
} else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_ANSWER)) {
ast_log(LOG_DEBUG, "Dropping duplicate answer!\n");
f = &null_frame;
}
Mark Spencer
committed
if (f && (f->frametype == AST_FRAME_VOICE) && chan->generatordata) {
/* Mask generator data temporarily and apply. If there is a timing function, it
will be calling the generator instead */
int (*generate)(struct ast_channel *chan, void *tmp, int datalen, int samples);
Mark Spencer
committed
if (chan->timingfunc) {
ast_log(LOG_DEBUG, "Generator got voice, switching to phase locked mode\n");
ast_settimeout(chan, 0, NULL, NULL);
}
tmp = chan->generatordata;
chan->generatordata = NULL;
generate = chan->generator->generate;
res = generate(chan, tmp, f->datalen, f->samples);
chan->generatordata = tmp;
if (res) {
ast_log(LOG_DEBUG, "Auto-deactivating generator\n");
Mark Spencer
committed
} else if (f && (f->frametype == AST_FRAME_CNG)) {
if (chan->generator && !chan->timingfunc && (chan->timingfd > -1)) {
ast_log(LOG_DEBUG, "Generator got CNG, switching to zap timed mode\n");
ast_settimeout(chan, 160, generator_force, chan);
}
/* High bit prints debugging */
if (chan->fin & 0x80000000)
ast_frame_dump(chan->name, f, "<<");
if ((chan->fin & 0x7fffffff) == 0x7fffffff)
chan->fin &= 0x80000000;
else
chan->fin++;
int ast_indicate(struct ast_channel *chan, int condition)
{
int res = -1;
/* 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->indicate)
res = chan->tech->indicate(chan, condition);
ast_mutex_unlock(&chan->lock);
if (!chan->tech->indicate || res) {
/*
* Device does not support (that) indication, lets fake
* it by doing our own tone generation. (PM2002)