Newer
Older
Mark Spencer
committed
/*
* Asterisk -- A telephony toolkit for Linux.
*
* Channel Management
*
* Copyright (C) 1999 - 2005, Digium, Inc.
* Mark Spencer <markster@digium.com>
*
* This program is free software, distributed under the terms of
* the GNU General Public License
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <signal.h>
#include <errno.h>
#ifdef ZAPTEL_OPTIMIZATIONS
#include <sys/ioctl.h>
#ifdef __linux__
#include <linux/zaptel.h>
#else
#include <zaptel.h>
#endif /* __linux__ */
#ifndef ZT_TIMERPING
#error "You need newer zaptel! Please cvs update zaptel"
#endif
#endif
Kevin P. Fleming
committed
#include "asterisk/pbx.h"
#include "asterisk/frame.h"
#include "asterisk/sched.h"
#include "asterisk/options.h"
#include "asterisk/channel.h"
#include "asterisk/musiconhold.h"
#include "asterisk/logger.h"
#include "asterisk/say.h"
#include "asterisk/file.h"
#include "asterisk/cli.h"
#include "asterisk/translate.h"
#include "asterisk/manager.h"
#include "asterisk/chanvars.h"
#include "asterisk/linkedlists.h"
#include "asterisk/indications.h"
#include "asterisk/monitor.h"
#include "asterisk/causes.h"
#include "asterisk/utils.h"
#include "asterisk/lock.h"
#include "asterisk/app.h"
#include "asterisk/transcap.h"
/* uncomment if you have problems with 'monitoring' synchronized files */
#if 0
#define MONITOR_CONSTANT_DELAY
#define MONITOR_DELAY 150 * 8 /* 150 ms of MONITORING DELAY */
#endif
Kevin P. Fleming
committed
/*
* Prevent new channel allocation if shutting down.
*/
Kevin P. Fleming
committed
Mark Spencer
committed
unsigned long global_fin = 0, global_fout = 0;
const struct ast_channel_tech *tech;
Kevin P. Fleming
committed
};
static struct chanlist *backends = NULL;
/*
* the list of channels we have
*/
static struct ast_channel *channels = NULL;
Kevin P. Fleming
committed
/* Protect the channel list, both backends and channels.
*/
static int show_channeltypes(int fd, int argc, char *argv[])
{
Kevin P. Fleming
committed
#define FORMAT "%-10.10s %-50.50s %-12.12s\n"
struct chanlist *cl = backends;
Kevin P. Fleming
committed
ast_cli(fd, FORMAT, "Type", "Description", "Devicestate");
ast_cli(fd, FORMAT, "----------", "-----------", "-----------");
if (ast_mutex_lock(&chlock)) {
ast_log(LOG_WARNING, "Unable to lock channel list\n");
return -1;
}
while (cl) {
Kevin P. Fleming
committed
ast_cli(fd, FORMAT, cl->tech->type, cl->tech->description, (cl->tech->devicestate)?"yes":"no");
cl = cl->next;
}
ast_mutex_unlock(&chlock);
return RESULT_SUCCESS;
#undef FORMAT
}
static char show_channeltypes_usage[] =
"Usage: show channeltypes\n"
" Shows available channel types registered in your Asterisk server.\n";
static struct ast_cli_entry cli_show_channeltypes =
{ { "show", "channeltypes", NULL }, show_channeltypes, "Show available channel types", show_channeltypes_usage };
/*--- ast_check_hangup: Checks to see if a channel is needing hang up */
int ast_check_hangup(struct ast_channel *chan)
{
time_t myt;
/* if soft hangup flag, return true */
if (chan->_softhangup)
return 1;
/* if no technology private data, return true */
if (!chan->tech_pvt)
return 1;
/* if no hangup scheduled, just return here */
if (!chan->whentohangup)
return 0;
/* return, if not yet */
if (chan->whentohangup > myt)
return 0;
static int ast_check_hangup_locked(struct ast_channel *chan)
{
int res;
ast_mutex_lock(&chan->lock);
res = ast_check_hangup(chan);
ast_mutex_unlock(&chan->lock);
/*--- ast_begin_shutdown: Initiate system shutdown */
void ast_begin_shutdown(int hangup)
{
struct ast_channel *c;
shutting_down = 1;
if (hangup) {
ast_mutex_lock(&chlock);
ast_mutex_unlock(&chlock);
/*--- ast_active_channels: returns number of active/allocated channels */
int ast_active_channels(void)
{
struct ast_channel *c;
int cnt = 0;
ast_mutex_lock(&chlock);
c = channels;
while(c) {
cnt++;
c = c->next;
}
ast_mutex_unlock(&chlock);
/*--- ast_cancel_shutdown: Cancel a shutdown in progress */
void ast_cancel_shutdown(void)
{
shutting_down = 0;
}
/*--- ast_shutting_down: Returns non-zero if Asterisk is being shut down */
int ast_shutting_down(void)
{
return shutting_down;
}
/*--- ast_channel_setwhentohangup: Set when to hangup channel */
void ast_channel_setwhentohangup(struct ast_channel *chan, time_t offset)
{
if (offset)
chan->whentohangup = myt + offset;
else
chan->whentohangup = 0;
/*--- ast_channel_register: Register a new telephony channel in Asterisk */
int ast_channel_register(const struct ast_channel_tech *tech)
struct chanlist *chan;
ast_mutex_lock(&chlock);
if (!strcasecmp(tech->type, chan->tech->type)) {
ast_log(LOG_WARNING, "Already have a handler for type '%s'\n", tech->type);
ast_mutex_unlock(&chlock);
chan = malloc(sizeof(*chan));
if (!chan) {
ast_log(LOG_WARNING, "Out of memory\n");
ast_mutex_unlock(&chlock);
chan->tech = tech;
chan->next = backends;
backends = chan;
ast_log(LOG_DEBUG, "Registered handler for '%s' (%s)\n", chan->tech->type, chan->tech->description);
if (option_verbose > 1)
ast_verbose(VERBOSE_PREFIX_2 "Registered channel type '%s' (%s)\n", chan->tech->type,
chan->tech->description);
ast_mutex_unlock(&chlock);
/*--- ast_state2str: Gives the string form of a given channel state */
/* 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;
/*--- ast_transfercapability2str: Gives the string form of a given transfer capability */
char *ast_transfercapability2str(int transfercapability)
{
switch(transfercapability) {
case AST_TRANS_CAP_SPEECH:
return "SPEECH";
case AST_TRANS_CAP_DIGITAL:
return "DIGITAL";
case AST_TRANS_CAP_RESTRICTED_DIGITAL:
return "RESTRICTED_DIGITAL";
case AST_TRANS_CAP_3_1K_AUDIO:
return "3K1AUDIO";
case AST_TRANS_CAP_DIGITAL_W_TONES:
return "DIGITAL_W_TONES";
case AST_TRANS_CAP_VIDEO:
return "VIDEO";
default:
return "UNKNOWN";
}
}
/*--- ast_best_codec: Pick the best codec */
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,
/* G.726 is standard ADPCM */
AST_FORMAT_G726,
/* 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,
};
/* Find the first prefered codec in the format given */
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;
}
static const struct ast_channel_tech null_tech = {
.type = "NULL",
.description = "Null channel (should not see this)",
};
/*--- ast_channel_alloc: Create a new channel structure */
struct ast_channel *ast_channel_alloc(int needqueue)
/* If shutting down, don't allocate any new channels */
if (shutting_down) {
ast_log(LOG_WARNING, "Channel allocation failed: Refusing due to active shutdown\n");
}
if (!tmp) {
ast_log(LOG_WARNING, "Channel allocation failed: Out of memory\n");
return NULL;
}
memset(tmp, 0, sizeof(struct ast_channel));
tmp->sched = sched_context_create();
if (!tmp->sched) {
ast_log(LOG_WARNING, "Channel allocation failed: Unable to create schedule context\n");
free(tmp);
return NULL;
}
for (x=0; x<AST_MAX_FDS - 1; x++)
tmp->fds[x] = -1;
#ifdef ZAPTEL_OPTIMIZATIONS
tmp->timingfd = open("/dev/zap/timer", O_RDWR);
if (tmp->timingfd > -1) {
/* Check if timing interface supports new
ping/pong scheme */
flags = 1;
if (!ioctl(tmp->timingfd, ZT_TIMERPONG, &flags))
needqueue = 0;
}
#else
tmp->timingfd = -1;
#endif
if (needqueue) {
if (pipe(tmp->alertpipe)) {
ast_log(LOG_WARNING, "Channel allocation failed: Can't create alert pipe!\n");
return NULL;
} else {
flags = fcntl(tmp->alertpipe[0], F_GETFL);
fcntl(tmp->alertpipe[0], F_SETFL, flags | O_NONBLOCK);
flags = fcntl(tmp->alertpipe[1], F_GETFL);
fcntl(tmp->alertpipe[1], F_SETFL, flags | O_NONBLOCK);
/* Make sure we've got it done right if they don't */
tmp->alertpipe[0] = tmp->alertpipe[1] = -1;
/* Always watch the alertpipe */
tmp->fds[AST_MAX_FDS-1] = tmp->alertpipe[0];
/* And timing pipe */
tmp->fds[AST_MAX_FDS-2] = tmp->timingfd;
ast_copy_string(tmp->name, "**Unknown**", sizeof(tmp->name));
/* Initial state */
tmp->_state = AST_STATE_DOWN;
tmp->streamid = -1;
tmp->appl = NULL;
tmp->data = NULL;
tmp->fin = global_fin;
tmp->fout = global_fout;
snprintf(tmp->uniqueid, sizeof(tmp->uniqueid), "%li.%d", (long)time(NULL), uniqueint++);
headp = &tmp->varshead;
ast_mutex_init(&tmp->lock);
AST_LIST_HEAD_INIT(headp);
ast_copy_string(tmp->context, "default", sizeof(tmp->context));
ast_copy_string(tmp->language, defaultlanguage, sizeof(tmp->language));
ast_copy_string(tmp->exten, "s", sizeof(tmp->exten));
tmp->priority = 1;
tmp->amaflags = ast_default_amaflags;
ast_copy_string(tmp->accountcode, ast_default_accountcode, sizeof(tmp->accountcode));
tmp->tech = &null_tech;
Kevin P. Fleming
committed
ast_mutex_lock(&chlock);
tmp->next = channels;
channels = tmp;
ast_mutex_unlock(&chlock);
/*--- ast_queue_frame: Queue an outgoing media frame */
Mark Spencer
committed
int ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin)
{
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;
}
Mark Spencer
committed
ast_mutex_lock(&chan->lock);
cur = chan->readq;
Mark Spencer
committed
if ((cur->frametype == AST_FRAME_CONTROL) && (cur->subclass == AST_CONTROL_HANGUP)) {
/* Don't bother actually queueing anything after a hangup */
ast_frfree(f);
ast_mutex_unlock(&chan->lock);
return 0;
}
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);
Mark Spencer
committed
ast_mutex_unlock(&chan->lock);
chan->readq = f;
if (chan->alertpipe[1] > -1) {
if (write(chan->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));
#ifdef ZAPTEL_OPTIMIZATIONS
} else if (chan->timingfd > -1) {
ioctl(chan->timingfd, ZT_TIMERPING, &blah);
#endif
} else if (ast_test_flag(chan, AST_FLAG_BLOCKING)) {
pthread_kill(chan->blocker, SIGURG);
Mark Spencer
committed
ast_mutex_unlock(&chan->lock);
/*--- ast_queue_hangup: Queue a hangup frame for channel */
Mark Spencer
committed
int ast_queue_hangup(struct ast_channel *chan)
{
struct ast_frame f = { AST_FRAME_CONTROL, AST_CONTROL_HANGUP };
chan->_softhangup |= AST_SOFTHANGUP_DEV;
Mark Spencer
committed
return ast_queue_frame(chan, &f);
/*--- ast_queue_control: Queue a control frame */
Mark Spencer
committed
int ast_queue_control(struct ast_channel *chan, int control)
{
struct ast_frame f = { AST_FRAME_CONTROL, };
f.subclass = control;
Mark Spencer
committed
return ast_queue_frame(chan, &f);
/*--- ast_channel_defer_dtmf: Set defer DTMF flag on channel */
int ast_channel_defer_dtmf(struct ast_channel *chan)
{
int pre = 0;
if (chan) {
pre = ast_test_flag(chan, AST_FLAG_DEFER_DTMF);
ast_set_flag(chan, AST_FLAG_DEFER_DTMF);
/*--- ast_channel_undefer_dtmf: Unset defer DTMF flag on channel */
void ast_channel_undefer_dtmf(struct ast_channel *chan)
{
if (chan)
ast_clear_flag(chan, AST_FLAG_DEFER_DTMF);
Kevin P. Fleming
committed
/*
* Helper function to return the channel after prev, or the one matching name,
* with the channel's lock held. If getting the individual lock fails,
* unlock and retry quickly up to 10 times, then give up.
*
* XXX Note that this code has cost O(N) because of the need to verify
* that the object is still on the global list.
*
* XXX also note that accessing fields (e.g. c->name in ast_log())
* can only be done with the lock held or someone could delete the
* object while we work on it. This causes some ugliness in the code.
* Note that removing the first ast_log() may be harmful, as it would
* shorten the retry period and possibly cause failures.
* We should definitely go for a better scheme that is deadlock-free.
*/
static struct ast_channel *channel_find_locked(const struct ast_channel *prev,
const char *name)
const char *msg = prev ? "deadlock" : "initial deadlock";
Kevin P. Fleming
committed
int retries, done;
struct ast_channel *c;
for (retries = 0; retries < 10; retries++) {
ast_mutex_lock(&chlock);
for (c = channels; c; c = c->next) {
if (prev == NULL) {
/* want either head of list or match by name */
if (name == NULL || !strcasecmp(name, c->name))
break;
} else if (c == prev) { /* found, return c->next */
c = c->next;
break;
Kevin P. Fleming
committed
/* exit if chan not found or mutex acquired successfully */
done = (c == NULL) || (ast_mutex_trylock(&c->lock) == 0);
/* this is slightly unsafe, as we _should_ hold the lock to access c->name */
if (!done && c)
ast_log(LOG_DEBUG, "Avoiding %s for '%s'\n", msg, c->name);
ast_mutex_unlock(&chlock);
Kevin P. Fleming
committed
if (done)
return c;
usleep(1);
Mark Spencer
committed
}
Kevin P. Fleming
committed
/*
* c is surely not null, but we don't have the lock so cannot
* access c->name
*/
ast_log(LOG_WARNING, "Avoided %s for '%p', %d retries!\n",
msg, c, retries);
return NULL;
}
/*--- ast_channel_walk_locked: Browse channels in use */
struct ast_channel *ast_channel_walk_locked(const struct ast_channel *prev)
{
return channel_find_locked(prev, NULL);
/*--- ast_get_channel_by_name_locked: Get channel by name and lock it */
Kevin P. Fleming
committed
struct ast_channel *ast_get_channel_by_name_locked(const char *name)
Kevin P. Fleming
committed
return channel_find_locked(NULL, name);
/*--- ast_safe_sleep_conditional: Wait, look for hangups and condition arg */
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;
}
/*--- ast_safe_sleep: Wait, look for hangups */
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;
}
static void free_cid(struct ast_callerid *cid)
{
if (cid->cid_dnid)
free(cid->cid_dnid);
if (cid->cid_num)
free(cid->cid_num);
if (cid->cid_name)
free(cid->cid_name);
if (cid->cid_ani)
free(cid->cid_ani);
if (cid->cid_rdnis)
free(cid->cid_rdnis);
}
/*--- ast_channel_free: Free a channel structure */
void ast_channel_free(struct ast_channel *chan)
{
struct ast_channel *last=NULL, *cur;
char name[AST_CHANNEL_NAME];
ast_mutex_lock(&chlock);
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");
else {
/* Lock and unlock the channel just to be sure nobody
has it locked still */
ast_mutex_lock(&cur->lock);
ast_mutex_unlock(&cur->lock);
}
if (chan->tech_pvt) {
ast_log(LOG_WARNING, "Channel '%s' may not have been hung up properly\n", chan->name);
free(chan->tech_pvt);
}
ast_copy_string(name, chan->name, sizeof(name));
/* Stop monitoring */
if (chan->monitor) {
chan->monitor->stop( chan, 0 );
}
Mark Spencer
committed
/* If there is native format music-on-hold state, free it */
if(chan->music_state)
ast_moh_cleanup(chan);
if (chan->readtrans)
ast_translator_free_path(chan->readtrans);
if (chan->writetrans)
ast_translator_free_path(chan->writetrans);
if (chan->pbx)
ast_log(LOG_WARNING, "PBX may not have been terminated properly on '%s'\n", chan->name);
ast_mutex_destroy(&chan->lock);
if ((fd = chan->alertpipe[0]) > -1)
if ((fd = chan->alertpipe[1]) > -1)
if ((fd = chan->timingfd) > -1)
close(fd);
f = chan->readq;
chan->readq = NULL;
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_REMOVE_HEAD(headp, entries);
ast_mutex_unlock(&chlock);
ast_device_state_changed(name);
static void ast_spy_detach(struct ast_channel *chan)
{
struct ast_channel_spy *chanspy;
int to=3000;
int sleepms = 100;
for (chanspy = chan->spiers; chanspy; chanspy = chanspy->next) {
if (chanspy->status == CHANSPY_RUNNING) {
chanspy->status = CHANSPY_DONE;
}
}
/* signal all the spys to get lost and allow them time to unhook themselves
god help us if they don't......
*/
while (chan->spiers && to >= 0) {
ast_safe_sleep(chan, sleepms);
to -= sleepms;
}
chan->spiers = NULL;
return;
}
/*--- 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);
static void ast_queue_spy_frame(struct ast_channel_spy *spy, struct ast_frame *f, int pos)
{
struct ast_frame *tmpf = NULL;
int count = 0;
ast_mutex_lock(&spy->lock);
for (tmpf=spy->queue[pos]; tmpf && tmpf->next; tmpf=tmpf->next) {
count++;
}
ast_log(LOG_ERROR, "Too Many frames queued at once, flushing cache.\n");
headf = spy->queue[pos];
/* deref the queue right away so it looks empty */
spy->queue[pos] = NULL;
tmpf = headf;
/* free the wasted frames */
while (tmpf) {
freef = tmpf;
tmpf = tmpf->next;
ast_frfree(freef);
}
ast_mutex_unlock(&spy->lock);
return;
}
if (tmpf) {
tmpf->next = ast_frdup(f);
} else {
spy->queue[pos] = ast_frdup(f);
}
ast_mutex_unlock(&spy->lock);
}
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);
ast_spy_detach(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 */
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);
} else
if (option_debug)
ast_log(LOG_DEBUG, "Hanging up zombie '%s'\n", chan->name);
ast_mutex_unlock(&chan->lock);
Kevin P. Fleming
committed
"Cause: %d\r\n",
chan->name,
chan->uniqueid,
chan->hangupcause);
void ast_channel_unregister(const struct ast_channel_tech *tech)
ast_log(LOG_DEBUG, "Unregistering channel type '%s'\n", tech->type);
ast_mutex_lock(&chlock);
while (chan) {
if (chan->tech == tech) {
if (last)
last->next = chan->next;
else
backends = backends->next;
free(chan);
ast_mutex_unlock(&chlock);
if (option_verbose > 1)
ast_verbose( VERBOSE_PREFIX_2 "Unregistered channel type '%s'\n", tech->type);
return;
}
last = chan;
chan = chan->next;
}
ast_mutex_unlock(&chlock);
}
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)
{