Skip to content
Snippets Groups Projects
channel.c 60.1 KiB
Newer Older
Mark Spencer's avatar
Mark Spencer committed
/*
 * Asterisk -- A telephony toolkit for Linux.
 *
 * Channel Management
 * 
Mark Spencer's avatar
Mark Spencer committed
 * Copyright (C) 1999, Mark Spencer
Mark Spencer's avatar
Mark Spencer committed
 *
 * 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>
Mark Spencer's avatar
Mark Spencer committed
#include <unistd.h>
Mark Spencer's avatar
Mark Spencer committed
#include <math.h>			/* For PI */
Mark Spencer's avatar
Mark Spencer committed
#include <asterisk/pbx.h>
Mark Spencer's avatar
Mark Spencer committed
#include <asterisk/frame.h>
Mark Spencer's avatar
Mark Spencer committed
#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>
Mark Spencer's avatar
Mark Spencer committed
#include <asterisk/translate.h>
Mark Spencer's avatar
Mark Spencer committed
#include <asterisk/manager.h>
#include <asterisk/chanvars.h>
#include <asterisk/linkedlists.h>
Mark Spencer's avatar
Mark Spencer committed
#include <asterisk/indications.h>
#include <asterisk/monitor.h>
#ifdef ZAPTEL_OPTIMIZATIONS
#include <sys/ioctl.h>
#include <linux/zaptel.h>
#endif
Mark Spencer's avatar
Mark Spencer committed
static int shutting_down = 0;
Mark Spencer's avatar
Mark Spencer committed
/* XXX Lock appropriately in more functions XXX */
Mark Spencer's avatar
Mark Spencer committed

#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); 
Mark Spencer's avatar
Mark Spencer committed
	return ast_pthread_mutex_lock(a);
Mark Spencer's avatar
Mark Spencer committed
}

static int __PTHREAD_MUTEX_UNLOCK(char *f, pthread_mutex_t *a) {
	ast_log(LOG_DEBUG, "Unlocking %p (%s)\n", a, f); 
Mark Spencer's avatar
Mark Spencer committed
	return ast_pthread_mutex_unlock(a);
Mark Spencer's avatar
Mark Spencer committed
}
#else
Mark Spencer's avatar
Mark Spencer committed
#define PTHREAD_MUTEX_LOCK(a) ast_pthread_mutex_lock(a)
#define PTHREAD_MUTEX_UNLOCK(a) ast_pthread_mutex_unlock(a)
Mark Spencer's avatar
Mark Spencer committed
#endif

Mark Spencer's avatar
Mark Spencer committed
struct chanlist {
	char type[80];
	char description[80];
	int capabilities;
	struct ast_channel * (*requester)(char *type, int format, void *data);
	int (*devicestate)(void *data);
Mark Spencer's avatar
Mark Spencer committed
	struct chanlist *next;
} *backends = NULL;
Mark Spencer's avatar
Mark Spencer committed
struct ast_channel *channels = NULL;

Mark Spencer's avatar
Mark Spencer committed
/* Protect the channel list (highly unlikely that two things would change
   it at the same time, but still! */
   
Mark Spencer's avatar
Mark Spencer committed
static pthread_mutex_t chlock = AST_MUTEX_INITIALIZER;
Mark Spencer's avatar
Mark Spencer committed
int ast_check_hangup(struct ast_channel *chan)
{
time_t	myt;

	  /* if soft hangup flag, return true */
Mark Spencer's avatar
Mark Spencer committed
	if (chan->_softhangup) return 1;
	  /* if no private structure, return true */
	if (!chan->pvt->pvt) return 1;
Mark Spencer's avatar
Mark Spencer committed
	  /* 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;
Mark Spencer's avatar
Mark Spencer committed
	chan->_softhangup |= AST_SOFTHANGUP_TIMEOUT;
Mark Spencer's avatar
Mark Spencer committed
	return 1;
}

Mark Spencer's avatar
Mark Spencer committed
void ast_begin_shutdown(int hangup)
{
	struct ast_channel *c;
	shutting_down = 1;
	if (hangup) {
		PTHREAD_MUTEX_LOCK(&chlock);
		c = channels;
		while(c) {
Mark Spencer's avatar
Mark Spencer committed
			ast_softhangup(c, AST_SOFTHANGUP_SHUTDOWN);
Mark Spencer's avatar
Mark Spencer committed
			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;
}

Mark Spencer's avatar
Mark Spencer committed
void ast_channel_setwhentohangup(struct ast_channel *chan, time_t offset)
{
time_t	myt;

	time(&myt);
Mark Spencer's avatar
Mark Spencer committed
        if (offset)
	  chan->whentohangup = myt + offset;
        else
          chan->whentohangup = 0;
Mark Spencer's avatar
Mark Spencer committed
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))
Mark Spencer's avatar
Mark Spencer committed
{
	struct chanlist *chan, *last=NULL;
Mark Spencer's avatar
Mark Spencer committed
	if (PTHREAD_MUTEX_LOCK(&chlock)) {
Mark Spencer's avatar
Mark Spencer committed
		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);
Mark Spencer's avatar
Mark Spencer committed
			PTHREAD_MUTEX_UNLOCK(&chlock);
Mark Spencer's avatar
Mark Spencer committed
			return -1;
		}
		last = chan;
		chan = chan->next;
	}
	chan = malloc(sizeof(struct chanlist));
	if (!chan) {
		ast_log(LOG_WARNING, "Out of memory\n");
Mark Spencer's avatar
Mark Spencer committed
		PTHREAD_MUTEX_UNLOCK(&chlock);
Mark Spencer's avatar
Mark Spencer committed
		return -1;
	}
Mark Spencer's avatar
Mark Spencer committed
	strncpy(chan->type, type, sizeof(chan->type)-1);
	strncpy(chan->description, description, sizeof(chan->description)-1);
Mark Spencer's avatar
Mark Spencer committed
	chan->capabilities = capabilities;
	chan->requester = requester;
	chan->devicestate = devicestate;
Mark Spencer's avatar
Mark Spencer committed
	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);
Mark Spencer's avatar
Mark Spencer committed
	PTHREAD_MUTEX_UNLOCK(&chlock);
Mark Spencer's avatar
Mark Spencer committed
	return 0;
}

Mark Spencer's avatar
Mark Spencer committed
char *ast_state2str(int state)
{
	/* XXX Not reentrant XXX */
	static char localtmp[256];
Mark Spencer's avatar
Mark Spencer committed
	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;
Mark Spencer's avatar
Mark Spencer committed

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,
Mark Spencer's avatar
Mark Spencer committed
		/* iLBC is not too bad */
		AST_FORMAT_ILBC,
Mark Spencer's avatar
Mark Spencer committed
		/* Speex is free, but computationally more expensive than GSM */
		AST_FORMAT_SPEEX,
Mark Spencer's avatar
Mark Spencer committed
		/* Ick, LPC10 sounds terrible, but at least we have code for it, if you're tacky enough
		   to use it */
		AST_FORMAT_LPC10,
Mark Spencer's avatar
Mark Spencer committed
		/* G.729a is faster than 723 and slightly less expensive */
		AST_FORMAT_G729A,
Mark Spencer's avatar
Mark Spencer committed
		/* 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;
}

Mark Spencer's avatar
Mark Spencer committed
struct ast_channel *ast_channel_alloc(int needqueue)
Mark Spencer's avatar
Mark Spencer committed
{
	struct ast_channel *tmp;
	struct ast_channel_pvt *pvt;
Mark Spencer's avatar
Mark Spencer committed
	int x;
Mark Spencer's avatar
Mark Spencer committed
	int flags;
Mark Spencer's avatar
Mark Spencer committed
	struct varshead *headp;        
	        
	
Mark Spencer's avatar
Mark Spencer committed
	/* If shutting down, don't allocate any new channels */
	if (shutting_down)
		return NULL;
Mark Spencer's avatar
Mark Spencer committed
	PTHREAD_MUTEX_LOCK(&chlock);
Mark Spencer's avatar
Mark Spencer committed
	tmp = malloc(sizeof(struct ast_channel));
	memset(tmp, 0, 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) {
Mark Spencer's avatar
Mark Spencer committed
				for (x=0;x<AST_MAX_FDS - 1;x++)
Mark Spencer's avatar
Mark Spencer committed
					tmp->fds[x] = -1;
Mark Spencer's avatar
Mark Spencer committed
				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
Mark Spencer's avatar
Mark Spencer committed
					/* Always watch the alertpipe */
					tmp->fds[AST_MAX_FDS-1] = pvt->alertpipe[0];
					/* And timing pipe */
					tmp->fds[AST_MAX_FDS-2] = tmp->timingfd;
Mark Spencer's avatar
Mark Spencer committed
					strncpy(tmp->name, "**Unknown**", sizeof(tmp->name)-1);
					tmp->pvt = pvt;
Mark Spencer's avatar
Mark Spencer committed
					/* Initial state */
					tmp->_state = AST_STATE_DOWN;
Mark Spencer's avatar
Mark Spencer committed
					tmp->stack = -1;
					tmp->streamid = -1;
					tmp->appl = NULL;
					tmp->data = NULL;
Mark Spencer's avatar
Mark Spencer committed
					tmp->fin = 0;
					tmp->fout = 0;
Mark Spencer's avatar
Mark Spencer committed
					headp=&tmp->varshead;
Mark Spencer's avatar
Mark Spencer committed
					ast_pthread_mutex_init(&tmp->lock);
Mark Spencer's avatar
Mark Spencer committed
				        AST_LIST_HEAD_INIT(headp);
					tmp->vars=ast_var_assign("tempvar","tempval");
					AST_LIST_INSERT_HEAD(headp,tmp->vars,entries);
Mark Spencer's avatar
Mark Spencer committed
					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;
				}
Mark Spencer's avatar
Mark Spencer committed
			} 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");
Mark Spencer's avatar
Mark Spencer committed
	PTHREAD_MUTEX_UNLOCK(&chlock);
Mark Spencer's avatar
Mark Spencer committed
	return tmp;
}

Mark Spencer's avatar
Mark Spencer committed
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;
Mark Spencer's avatar
Mark Spencer committed
	/* Build us a copy and free the original one */
Mark Spencer's avatar
Mark Spencer committed
	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)) {
Mark Spencer's avatar
Mark Spencer committed
		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's avatar
Mark Spencer committed
			if (lock)
				ast_pthread_mutex_unlock(&chan->lock);
			return 0;
		}
	}
Mark Spencer's avatar
Mark Spencer committed
	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))
Mark Spencer's avatar
Mark Spencer committed
			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);
Mark Spencer's avatar
Mark Spencer committed
	}
	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;
Mark Spencer's avatar
Mark Spencer committed
	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);
}

Mark Spencer's avatar
Mark Spencer committed
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;
}

Mark Spencer's avatar
Mark Spencer committed
struct ast_channel *ast_channel_walk(struct ast_channel *prev)
{
	struct ast_channel *l, *ret=NULL;
Mark Spencer's avatar
Mark Spencer committed
	PTHREAD_MUTEX_LOCK(&chlock);
Mark Spencer's avatar
Mark Spencer committed
	l = channels;
	if (!prev) {
Mark Spencer's avatar
Mark Spencer committed
		PTHREAD_MUTEX_UNLOCK(&chlock);
Mark Spencer's avatar
Mark Spencer committed
		return l;
	}
	while(l) {
		if (l == prev)
			ret = l->next;
		l = l->next;
	}
Mark Spencer's avatar
Mark Spencer committed
	PTHREAD_MUTEX_UNLOCK(&chlock);
Mark Spencer's avatar
Mark Spencer committed
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;
}

Mark Spencer's avatar
Mark Spencer committed
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;
}

Mark Spencer's avatar
Mark Spencer committed
void ast_channel_free(struct ast_channel *chan)
{
	struct ast_channel *last=NULL, *cur;
Mark Spencer's avatar
Mark Spencer committed
	int fd;
Mark Spencer's avatar
Mark Spencer committed
	struct ast_var_t *vardata;
Mark Spencer's avatar
Mark Spencer committed
	struct ast_frame *f, *fp;
Mark Spencer's avatar
Mark Spencer committed
	struct varshead *headp;
	char name[AST_CHANNEL_NAME];
Mark Spencer's avatar
Mark Spencer committed
	
	headp=&chan->varshead;
	
Mark Spencer's avatar
Mark Spencer committed
	PTHREAD_MUTEX_LOCK(&chlock);
Mark Spencer's avatar
Mark Spencer committed
	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 );
	}
Mark Spencer's avatar
Mark Spencer committed
	/* Free translatosr */
	if (chan->pvt->readtrans)
		ast_translator_free_path(chan->pvt->readtrans);
	if (chan->pvt->writetrans)
		ast_translator_free_path(chan->pvt->writetrans);
Mark Spencer's avatar
Mark Spencer committed
	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);	
Mark Spencer's avatar
Mark Spencer committed
	if (chan->ani)
		free(chan->ani);
Mark Spencer's avatar
Mark Spencer committed
	if (chan->rdnis)
		free(chan->rdnis);
Mark Spencer's avatar
Mark Spencer committed
	pthread_mutex_destroy(&chan->lock);
Mark Spencer's avatar
Mark Spencer committed
	/* Close pipes if appropriate */
	if ((fd = chan->pvt->alertpipe[0]) > -1)
		close(fd);
	if ((fd = chan->pvt->alertpipe[1]) > -1)
		close(fd);
	f = chan->pvt->readq;
Mark Spencer's avatar
Mark Spencer committed
	chan->pvt->readq = NULL;
Mark Spencer's avatar
Mark Spencer committed
	while(f) {
		fp = f;
		f = f->next;
		ast_frfree(fp);
	}
Mark Spencer's avatar
Mark Spencer 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 */
	
	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);
	}
	                                                 

Mark Spencer's avatar
Mark Spencer committed
	free(chan->pvt);
Mark Spencer's avatar
Mark Spencer committed
	chan->pvt = NULL;
Mark Spencer's avatar
Mark Spencer committed
	free(chan);
Mark Spencer's avatar
Mark Spencer committed
	PTHREAD_MUTEX_UNLOCK(&chlock);

	ast_device_state_changed(name);
Mark Spencer's avatar
Mark Spencer committed
int ast_softhangup_nolock(struct ast_channel *chan, int cause)
Mark Spencer's avatar
Mark Spencer committed
{
	int res = 0;
Mark Spencer's avatar
Mark Spencer committed
	struct ast_frame f = { AST_FRAME_NULL };
Mark Spencer's avatar
Mark Spencer committed
	if (option_debug)
		ast_log(LOG_DEBUG, "Soft-Hanging up channel '%s'\n", chan->name);
Mark Spencer's avatar
Mark Spencer committed
	/* Inform channel driver that we need to be hung up, if it cares */
Mark Spencer's avatar
Mark Spencer committed
	chan->_softhangup |= cause;
	ast_queue_frame(chan, &f, 0);
Mark Spencer's avatar
Mark Spencer committed
	/* Interrupt any select call or such */
	if (chan->blocking)
		pthread_kill(chan->blocker, SIGURG);
	return res;
}

Mark Spencer's avatar
Mark Spencer committed
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;
}

Mark Spencer's avatar
Mark Spencer committed
static int ast_do_masquerade(struct ast_channel *original);

Mark Spencer's avatar
Mark Spencer committed
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;
}

Mark Spencer's avatar
Mark Spencer committed
int ast_hangup(struct ast_channel *chan)
{
	int res = 0;
Mark Spencer's avatar
Mark Spencer committed
	/* Don't actually hang up a channel that will masquerade as someone else, or
	   if someone is going to masquerade as us */
Mark Spencer's avatar
Mark Spencer committed
	ast_pthread_mutex_lock(&chan->lock);
	if (chan->masq) {
Mark Spencer's avatar
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);
Mark Spencer's avatar
Mark Spencer committed
		ast_pthread_mutex_unlock(&chan->lock);
Mark Spencer's avatar
Mark Spencer committed
		return 0;
Mark Spencer's avatar
Mark Spencer committed
	}
Mark Spencer's avatar
Mark Spencer committed
	/* 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) {
Mark Spencer's avatar
Mark Spencer committed
		ast_pthread_mutex_unlock(&chan->lock);
Mark Spencer's avatar
Mark Spencer committed
		chan->zombie=1;
		return 0;
	}
Mark Spencer's avatar
Mark Spencer committed
	free_translation(chan);
Mark Spencer's avatar
Mark Spencer committed
	if (chan->stream)
		ast_stopstream(chan);
	if (chan->sched)
		sched_context_destroy(chan->sched);
Mark Spencer's avatar
Mark Spencer committed
	/* Clear any tone stuff remaining */
	if (chan->generatordata)
		chan->generator->release(chan, chan->generatordata);
	chan->generatordata = NULL;
	chan->generator = NULL;
Mark Spencer's avatar
Mark Spencer committed
	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);
	}
Mark Spencer's avatar
Mark Spencer committed
	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;
	}
Mark Spencer's avatar
Mark Spencer committed
	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);
			
Mark Spencer's avatar
Mark Spencer committed
	ast_pthread_mutex_unlock(&chan->lock);
Mark Spencer's avatar
Mark Spencer committed
	manager_event(EVENT_FLAG_CALL, "Hangup", 
			"Channel: %s\r\n",
			chan->name);
Mark Spencer's avatar
Mark Spencer committed
	ast_channel_free(chan);
Mark Spencer's avatar
Mark Spencer committed
	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);
Mark Spencer's avatar
Mark Spencer committed
	if (PTHREAD_MUTEX_LOCK(&chlock)) {
Mark Spencer's avatar
Mark Spencer committed
		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);
Mark Spencer's avatar
Mark Spencer committed
			PTHREAD_MUTEX_UNLOCK(&chlock);
Mark Spencer's avatar
Mark Spencer committed
			return;
		}
		last = chan;
		chan = chan->next;
	}
Mark Spencer's avatar
Mark Spencer committed
	PTHREAD_MUTEX_UNLOCK(&chlock);
Mark Spencer's avatar
Mark Spencer committed
}

int ast_answer(struct ast_channel *chan)
{
Mark Spencer's avatar
Mark Spencer committed
	int res = 0;
Mark Spencer's avatar
Mark Spencer committed
	/* Stop if we're a zombie or need a soft hangup */
Mark Spencer's avatar
Mark Spencer committed
	if (chan->zombie || ast_check_hangup(chan)) 
Mark Spencer's avatar
Mark Spencer committed
		return -1;
Mark Spencer's avatar
Mark Spencer committed
	switch(chan->_state) {
Mark Spencer's avatar
Mark Spencer committed
	case AST_STATE_RINGING:
Mark Spencer's avatar
Mark Spencer committed
	case AST_STATE_RING:
Mark Spencer's avatar
Mark Spencer committed
		if (chan->pvt->answer)
Mark Spencer's avatar
Mark Spencer committed
			res = chan->pvt->answer(chan);
Mark Spencer's avatar
Mark Spencer committed
		ast_setstate(chan, AST_STATE_UP);
Mark Spencer's avatar
Mark Spencer committed
		if (chan->cdr)
			ast_cdr_answer(chan->cdr);
Mark Spencer's avatar
Mark Spencer committed
		return res;
Mark Spencer's avatar
Mark Spencer committed
		break;
	case AST_STATE_UP:
Mark Spencer's avatar
Mark Spencer committed
		if (chan->cdr)
			ast_cdr_answer(chan->cdr);
Mark Spencer's avatar
Mark Spencer committed
		break;
Mark Spencer's avatar
Mark Spencer committed
	}
	return 0;
}

Mark Spencer's avatar
Mark Spencer committed
void ast_deactivate_generator(struct ast_channel *chan)
{
	if (chan->generatordata) {
		chan->generator->release(chan, chan->generatordata);
		chan->generatordata = NULL;
Mark Spencer's avatar
Mark Spencer committed
		chan->generator = NULL;
Mark Spencer's avatar
Mark Spencer committed
		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;
	}
	ast_prod(chan);
Mark Spencer's avatar
Mark Spencer committed
	if ((chan->generatordata = gen->alloc(chan, params))) {
		chan->generator = gen;
	} else {
		return -1;
	}
	return 0;
}

Mark Spencer's avatar
Mark Spencer committed
int ast_waitfor_n_fd(int *fds, int n, int *ms, int *exception)
Mark Spencer's avatar
Mark Spencer committed
{
	/* 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++) {
Mark Spencer's avatar
Mark Spencer committed
		if (fds[x] > -1) {
			FD_SET(fds[x], &rfds);
			FD_SET(fds[x], &efds);
			if (fds[x] > max)
				max = fds[x];
		}
Mark Spencer's avatar
Mark Spencer committed
	}
Mark Spencer's avatar
Mark Spencer committed
	if (*ms >= 0)
		res = ast_select(max + 1, &rfds, NULL, &efds, &tv);
Mark Spencer's avatar
Mark Spencer committed
	else
Mark Spencer's avatar
Mark Spencer committed
		res = ast_select(max + 1, &rfds, NULL, &efds, NULL);
Mark Spencer's avatar
Mark Spencer committed

	if (res < 0) {
		/* Simulate a timeout if we were interrupted */
		if (errno != EINTR)
			*ms = -1;
		else
			*ms = 0;
		return -1;
	}

Mark Spencer's avatar
Mark Spencer committed
	for (x=0;x<n;x++) {
Mark Spencer's avatar
Mark Spencer committed
		if ((fds[x] > -1) && (FD_ISSET(fds[x], &rfds) || FD_ISSET(fds[x], &efds)) && (winner < 0)) {
Mark Spencer's avatar
Mark Spencer committed
			if (exception)
				*exception = FD_ISSET(fds[x], &efds);
Mark Spencer's avatar
Mark Spencer committed
			winner = fds[x];
Mark Spencer's avatar
Mark Spencer committed
		}
Mark Spencer's avatar
Mark Spencer committed
	}
	*ms = tv.tv_sec * 1000 + tv.tv_usec / 1000;
	return winner;
}

Mark Spencer's avatar
Mark Spencer committed
struct ast_channel *ast_waitfor_nandfds(struct ast_channel **c, int n, int *fds, int nfds, 
	int *exception, int *outfd, int *ms)
Mark Spencer's avatar
Mark Spencer committed
{
	/* Wait for x amount of time on a file descriptor to have input.  */
	struct timeval tv;
	fd_set rfds, efds;
	int res;
Mark Spencer's avatar
Mark Spencer committed
	int x, y, max=-1;
Mark Spencer's avatar
Mark Spencer committed
	struct ast_channel *winner = NULL;
Mark Spencer's avatar
Mark Spencer committed
	if (outfd)
		*outfd = -1;
	if (exception)
		*exception = 0;
Mark Spencer's avatar
Mark Spencer committed
	/* 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;
				return NULL;
			}
		}
	}
	
Mark Spencer's avatar
Mark Spencer committed
	tv.tv_sec = *ms / 1000;
	tv.tv_usec = (*ms % 1000) * 1000;
	FD_ZERO(&rfds);
	FD_ZERO(&efds);
Mark Spencer's avatar
Mark Spencer committed
	for (x=0;x<n;x++) {
Mark Spencer's avatar
Mark Spencer committed
		for (y=0;y<AST_MAX_FDS;y++) {
Mark Spencer's avatar
Mark Spencer committed
			if (c[x]->fds[y] > -1) {
Mark Spencer's avatar
Mark Spencer committed
				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];
			}
		}
Mark Spencer's avatar
Mark Spencer committed
		CHECK_BLOCKING(c[x]);
	}
Mark Spencer's avatar
Mark Spencer committed
	for (x=0;x<nfds; x++) {
		FD_SET(fds[x], &rfds);
		FD_SET(fds[x], &efds);
		if (fds[x] > max)
			max = fds[x];
	}
Mark Spencer's avatar
Mark Spencer committed
	if (*ms >= 0)
		res = ast_select(max + 1, &rfds, NULL, &efds, &tv);
Mark Spencer's avatar
Mark Spencer committed
	else
Mark Spencer's avatar
Mark Spencer committed
		res = ast_select(max + 1, &rfds, NULL, &efds, NULL);
Mark Spencer's avatar
Mark Spencer committed

	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;
Mark Spencer's avatar
Mark Spencer committed
		else {
			/* Just an interrupt */
#if 0
Mark Spencer's avatar
Mark Spencer committed
			*ms = 0;
Mark Spencer's avatar
Mark Spencer committed
#endif			
		}
Mark Spencer's avatar
Mark Spencer committed
		return NULL;
	}

Mark Spencer's avatar
Mark Spencer committed
	for (x=0;x<n;x++) {
		c[x]->blocking = 0;
Mark Spencer's avatar
Mark Spencer committed
		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];
				}
			}
Mark Spencer's avatar
Mark Spencer committed
		}
Mark Spencer's avatar
Mark Spencer committed
	}
Mark Spencer's avatar
Mark Spencer committed
	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;
		}
	}
Mark Spencer's avatar
Mark Spencer committed
	*ms = tv.tv_sec * 1000 + tv.tv_usec / 1000;
	return winner;
}

Mark Spencer's avatar
Mark Spencer committed
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);
}

Mark Spencer's avatar
Mark Spencer committed
int ast_waitfor(struct ast_channel *c, int ms)
{
Mark Spencer's avatar
Mark Spencer committed
	struct ast_channel *chan;
Mark Spencer's avatar
Mark Spencer committed
	int oldms = ms;
Mark Spencer's avatar
Mark Spencer committed
	chan = ast_waitfor_n(&c, 1, &ms);
Mark Spencer's avatar
Mark Spencer committed
	if (ms < 0) {
		if (oldms < 0)
			return 0;
		else
			return -1;
	}
Mark Spencer's avatar
Mark Spencer committed
	return ms;
Mark Spencer's avatar
Mark Spencer committed
}

char ast_waitfordigit(struct ast_channel *c, int ms)
{
	/* XXX Should I be merged with waitfordigit_full XXX */
Mark Spencer's avatar
Mark Spencer committed
	struct ast_frame *f;
	char result = 0;
Mark Spencer's avatar
Mark Spencer committed
	/* Stop if we're a zombie or need a soft hangup */
Mark Spencer's avatar
Mark Spencer committed
	if (c->zombie || ast_check_hangup(c)) 
Mark Spencer's avatar
Mark Spencer committed
		return -1;
Mark Spencer's avatar
Mark Spencer committed
	/* 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 ms)
{
	int res = -1;
#ifdef ZAPTEL_OPTIMIZATIONS
	if (c->timingfd > -1) {
		ms *= 8;
		res = ioctl(c->timingfd, ZT_TIMERCONFIG, &ms);
	}
#endif	
	return res;
}
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;
}

Mark Spencer's avatar
Mark Spencer committed
struct ast_frame *ast_read(struct ast_channel *chan)
{
	struct ast_frame *f = NULL;
Mark Spencer's avatar
Mark Spencer committed
	int blah;
Mark Spencer's avatar
Mark Spencer committed
	static struct ast_frame null_frame = 
	{
		AST_FRAME_NULL,
	};
Mark Spencer's avatar
Mark Spencer committed
	ast_pthread_mutex_lock(&chan->lock);
Mark Spencer's avatar
Mark Spencer committed
	if (chan->masq) {
		if (ast_do_masquerade(chan)) {
			ast_log(LOG_WARNING, "Failed to perform masquerade\n");
			f = NULL;
		} else
			f =  &null_frame;
		pthread_mutex_unlock(&chan->lock);
		return f;
	}

	/* Stop if we're a zombie or need a soft hangup */
Mark Spencer's avatar
Mark Spencer committed
	if (chan->zombie || ast_check_hangup(chan)) {