Skip to content
Snippets Groups Projects
channel.c 108 KiB
Newer Older
  • Learn to ignore specific revisions
  • 		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 */
    
    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;
    
    	/* Interrupt any poll call or such */
    
    	if (ast_test_flag(chan, AST_FLAG_BLOCKING))
    
    Mark Spencer's avatar
    Mark Spencer committed
    		pthread_kill(chan->blocker, SIGURG);
    	return res;
    }
    
    
    /*--- ast_softhangup_nolock: Softly hangup a channel, lock */
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_softhangup(struct ast_channel *chan, int cause)
    {
    	int res;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	res = ast_softhangup_nolock(chan, cause);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
    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)
    
    	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;
    
    	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);
    
    		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);
    
    	if (translated_frame)
    		ast_frfree(translated_frame);
    
    Mark Spencer's avatar
    Mark Spencer committed
    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 */
    
    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 */
    
    	detach_spies(chan);		/* get rid of spies */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (chan->masq) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			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
    		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) {
    
    		ast_set_flag(chan, AST_FLAG_ZOMBIE);
    
    		ast_mutex_unlock(&chan->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	free_translation(chan);
    
    	if (chan->stream) 		/* Close audio stream */
    
    		ast_closestream(chan->stream);
    
    	if (chan->vstream)		/* Close video stream */
    
    		ast_closestream(chan->vstream);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (chan->sched)
    		sched_context_destroy(chan->sched);
    
    	
    	if (chan->generatordata)	/* Clear any tone stuff remaining */ 
    
    Mark Spencer's avatar
    Mark Spencer committed
    		chan->generator->release(chan, chan->generatordata);
    	chan->generatordata = NULL;
    	chan->generator = NULL;
    
    	if (chan->cdr) {		/* End the CDR if it hasn't already */ 
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_cdr_end(chan->cdr);
    
    		ast_cdr_detach(chan->cdr);	/* Post and Free the CDR */ 
    
    	if (ast_test_flag(chan, AST_FLAG_BLOCKING)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		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);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		CRASH;
    	}
    
    	if (!ast_test_flag(chan, AST_FLAG_ZOMBIE)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (option_debug)
    			ast_log(LOG_DEBUG, "Hanging up channel '%s'\n", chan->name);
    
    		if (chan->tech->hangup)
    			res = chan->tech->hangup(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (option_debug)
    			ast_log(LOG_DEBUG, "Hanging up zombie '%s'\n", chan->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	manager_event(EVENT_FLAG_CALL, "Hangup", 
    
    Mark Spencer's avatar
    Mark Spencer committed
    			"Channel: %s\r\n"
    
    James Golovich's avatar
    James Golovich committed
    			"Uniqueid: %s\r\n"
    
    			"Cause: %d\r\n"
    			"Cause-txt: %s\r\n",
    
    			chan->hangupcause,
    			ast_cause2str(chan->hangupcause)
    			);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ast_channel_free(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    int ast_answer(struct ast_channel *chan)
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int res = 0;
    
    	ast_mutex_lock(&chan->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* 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);
    
    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:
    
    		if (chan->tech->answer)
    			res = chan->tech->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
    		ast_mutex_unlock(&chan->lock);
    
    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
    	}
    
    	ast_mutex_unlock(&chan->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    void ast_deactivate_generator(struct ast_channel *chan)
    
    	ast_mutex_lock(&chan->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (chan->generatordata) {
    
    		if (chan->generator && chan->generator->release) 
    			chan->generator->release(chan, chan->generatordata);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		chan->generatordata = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		chan->generator = NULL;
    
    		ast_clear_flag(chan, AST_FLAG_WRITE_INT);
    
    		ast_settimeout(chan, 0, NULL, NULL);
    
    	ast_mutex_unlock(&chan->lock);
    }
    
    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;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_activate_generator(struct ast_channel *chan, struct ast_generator *gen, void *params)
    {
    
    	int res = 0;
    
    	ast_mutex_lock(&chan->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (chan->generatordata) {
    
    		if (chan->generator && chan->generator->release)
    			chan->generator->release(chan, chan->generatordata);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		chan->generatordata = NULL;
    	}
    
    	ast_prod(chan);
    
    	if (gen->alloc) {
    		if (!(chan->generatordata = gen->alloc(chan, params)))
    			res = -1;
    	}
    	
    	if (!res) {
    
    		ast_settimeout(chan, 160, generator_force, chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		chan->generator = gen;
    	}
    
    	ast_mutex_unlock(&chan->lock);
    
    	return res;
    
    /*--- ast_waitfor_n_fd: Wait for x amount of time on a file descriptor to have input.  */
    
    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
    {
    
    Russell Bryant's avatar
    Russell Bryant committed
    	struct timeval start = { 0 , 0 };
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int res;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int winner = -1;
    
    	int spoint;
    	struct pollfd *pfds;
    
    	pfds = alloca(sizeof(struct pollfd) * n);
    	if (!pfds) {
    
    		ast_log(LOG_ERROR, "Out of memory\n");
    
    	for (x=0; x < n; x++) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (fds[x] > -1) {
    
    			pfds[y].fd = fds[x];
    			pfds[y].events = POLLIN | POLLPRI;
    			y++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	res = poll(pfds, y, *ms);
    
    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;
    	}
    
    	for (x=0; x < n; x++) {
    
    		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;
    				}
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    		*ms -= ast_tvdiff_ms(ast_tvnow(), start);
    		if (*ms < 0)
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return winner;
    }
    
    
    /*--- ast_waitfor_nanfds: Wait for x amount of time on a file descriptor to have input.  */
    
    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
    {
    
    Russell Bryant's avatar
    Russell Bryant committed
    	struct timeval start = { 0 , 0 };
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int res;
    
    	long rms;
    	int x, y, max;
    	int spoint;
    
    	long whentohangup = 0, havewhen = 0, diff;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_channel *winner = NULL;
    
    
    	pfds = alloca(sizeof(struct pollfd) * (n * AST_MAX_FDS + nfds));
    	if (!pfds) {
    
    		ast_log(LOG_ERROR, "Out of memory\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (outfd)
    
    		*outfd = -99999;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (exception)
    		*exception = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Perform any pending masquerades */
    
    	for (x=0; x < n; x++) {
    
    		if (c[x]->whentohangup) {
    
    			diff = c[x]->whentohangup - now;
    			if (!havewhen || (diff < whentohangup)) {
    				havewhen++;
    				whentohangup = diff;
    			}
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (c[x]->masq) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				ast_log(LOG_WARNING, "Masquerade failed\n");
    				*ms = -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    				return NULL;
    			}
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	
    	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++) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (c[x]->fds[y] > -1) {
    
    				pfds[max].fd = c[x]->fds[y];
    				pfds[max].events = POLLIN | POLLPRI;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		CHECK_BLOCKING(c[x]);
    	}
    
    	for (x=0; x < nfds; x++) {
    
    		if (fds[x] > -1) {
    			pfds[max].fd = fds[x];
    			pfds[max].events = POLLIN | POLLPRI;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	
    	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);
    	}
    	
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (res < 0) {
    
    		for (x=0; x < n; x++) 
    
    			ast_clear_flag(c[x], AST_FLAG_BLOCKING);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* 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;
    
            } else {
            	/* If no fds signalled, then timeout. So set ms = 0
    		   since we may not have an exact timeout.
    		*/
    		if (res == 0)
    			*ms = 0;
    
    	for (x=0; x < n; x++) {
    
    		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];
    		}
    
    		for (y=0; y < AST_MAX_FDS; y++) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (c[x]->fds[y] > -1) {
    
    				if ((res = ast_fdisset(pfds, c[x]->fds[y], max, &spoint))) {
    					if (res & POLLPRI)
    
    						ast_set_flag(c[x], AST_FLAG_EXCEPTION);
    
    						ast_clear_flag(c[x], AST_FLAG_EXCEPTION);
    
    					c[x]->fdno = y;
    
    Mark Spencer's avatar
    Mark Spencer committed
    					winner = c[x];
    				}
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	for (x=0; x < nfds; x++) {
    
    		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) {
    
    		*ms -= ast_tvdiff_ms(ast_tvnow(), start);
    		if (*ms < 0)
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	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;
    
    int ast_waitfordigit(struct ast_channel *c, int ms)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	/* XXX Should I be merged with waitfordigit_full XXX */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_frame *f;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Stop if we're a zombie or need a soft hangup */
    
    	if (ast_test_flag(c, AST_FLAG_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 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;
    
    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. */
    
    		rchan = ast_waitfor_nandfds(&c, 1, &cmdfd, (cmdfd > -1) ? 1 : 0, NULL, &outfd, &ms);
    		if ((!rchan) && (outfd < 0) && (ms)) { 
    
    			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;
    
    				ast_frfree(f);
    
    				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 */
    
    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;
    
    	int prestate;
    
    #ifdef ZAPTEL_OPTIMIZATIONS
    	int (*func)(void *);
    	void *data;
    
    	static struct ast_frame null_frame = {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		AST_FRAME_NULL,
    	};
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (chan->masq) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_log(LOG_WARNING, "Failed to perform masquerade\n");
    			f = NULL;
    		} else
    
    			f =  &null_frame;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		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)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (chan->generator)
    
    			ast_deactivate_generator(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return NULL;
    	}
    
    	prestate = chan->_state;
    
    	if (!ast_test_flag(chan, AST_FLAG_DEFER_DTMF) && !ast_strlen_zero(chan->dtmfq)) {
    
    		/* We have DTMF that has been deferred.  Return it now */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		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);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return &chan->dtmff;
    	}
    	
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* 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));
    
    #ifdef ZAPTEL_OPTIMIZATIONS
    
    	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;
    
    			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);
    			}
    
    			f =  &null_frame;
    
    			return f;
    		} else
    			ast_log(LOG_NOTICE, "No/unknown event '%d' on timer for '%s'?\n", blah, chan->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Check for pending read queue */
    
    	if (chan->readq) {
    		f = chan->readq;
    		chan->readq = f->next;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Interpret hangup and return NULL */
    
    		if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
    			ast_frfree(f);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			f = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else {
    		chan->blocker = pthread_self();
    
    		if (ast_test_flag(chan, AST_FLAG_EXCEPTION)) {
    
    			if (chan->tech->exception) 
    				f = chan->tech->exception(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			else {
    				ast_log(LOG_WARNING, "Exception flag set on '%s', but no exception handler\n", chan->name);
    				f = &null_frame;
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* Clear the exception flag */
    
    			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);
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (f && (f->frametype == AST_FRAME_VOICE)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		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));
    
    			ast_frfree(f);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			f = &null_frame;
    
    			if (chan->spies)
    				queue_frame_to_spies(chan, f, SPY_READ);
    
    
    			if (chan->monitor && chan->monitor->read_stream ) {
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    #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;
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Make sure we always return NULL in the future */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!f) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		chan->_softhangup |= AST_SOFTHANGUP_DEV;
    		if (chan->generator)
    
    			ast_deactivate_generator(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* 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) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		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;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	} else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_ANSWER)) {
    
    		if (prestate == AST_STATE_UP) {
    
    			ast_log(LOG_DEBUG, "Dropping duplicate answer!\n");
    			f = &null_frame;
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Answer the CDR */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_setstate(chan, AST_STATE_UP);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_cdr_answer(chan->cdr);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Run any generator sitting on the line */
    
    	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 */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		void *tmp;
    		int res;
    
    		int (*generate)(struct ast_channel *chan, void *tmp, int datalen, int samples);
    
    		if (chan->timingfunc) {
    			ast_log(LOG_DEBUG, "Generator got voice, switching to phase locked mode\n");
    			ast_settimeout(chan, 0, NULL, NULL);
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		tmp = chan->generatordata;
    		chan->generatordata = NULL;
    
    		generate = chan->generator->generate;
    		res = generate(chan, tmp, f->datalen, f->samples);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		chan->generatordata = tmp;
    		if (res) {
    			ast_log(LOG_DEBUG, "Auto-deactivating generator\n");
    
    			ast_deactivate_generator(chan);
    
    	} 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++;
    
    	ast_mutex_unlock(&chan->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return f;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_indicate(struct ast_channel *chan, int condition)
    {
    	int res = -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Stop if we're a zombie or need a soft hangup */
    
    	if (ast_test_flag(chan, AST_FLAG_ZOMBIE) || ast_check_hangup(chan)) 
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    
    	ast_mutex_lock(&chan->lock);
    
    	if (chan->tech->indicate)
    		res = chan->tech->indicate(chan, condition);
    
    	ast_mutex_unlock(&chan->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/*
    		 * Device does not support (that) indication, lets fake
    		 * it by doing our own tone generation. (PM2002)