Skip to content
Snippets Groups Projects
channel.c 74.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	if (*ms > 0) {
    		long diff;
    		gettimeofday(&end, NULL);
    		diff = (end.tv_sec - start.tv_sec) * 1000;
    		diff += (end.tv_usec - start.tv_usec) / 1000;
    		if (diff < *ms)
    			*ms -= diff;
    		else
    			*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;
    
    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 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;
    
    char 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 (c->zombie || ast_check_hangup(c)) 
    		return -1;
    	/* Wait for a digit, no more than ms milliseconds total. */
    
    	while(ms) {
    		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;
    
    #ifdef ZAPTEL_OPTIMIZATIONS
    	int (*func)(void *);
    	void *data;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	static struct ast_frame null_frame = 
    	{
    		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 */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (chan->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;
    	}
    
    	if (!chan->deferdtmf && !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->pvt->alertpipe[0] > -1) {
    		read(chan->pvt->alertpipe[0], &blah, sizeof(blah));
    	}
    
    #ifdef ZAPTEL_OPTIMIZATIONS
    	if ((chan->timingfd > -1) && (chan->fdno == AST_MAX_FDS - 2) && chan->exception) {
    		chan->exception = 0;
    		blah = -1;
    
    		/* 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->pvt->readq || !chan->pvt->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->pvt->readq) {
    		f = chan->pvt->readq;
    		chan->pvt->readq = f->next;
    		/* 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 (chan->exception) {
    			if (chan->pvt->exception) 
    				f = chan->pvt->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 */
    			chan->exception = 0;
    		} else
    		if (chan->pvt->read)
    			f = chan->pvt->read(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		else
    
    Mark Spencer's avatar
    Mark Spencer committed
    			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;
    
    		} else {
    			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->pvt->readtrans) {
    				f = ast_translate(chan->pvt->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 (chan->deferdtmf && 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)) {
    		/* 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 */
    		void *tmp;
    		int res;
    
    		int (*generate)(struct ast_channel *chan, void *tmp, int datalen, int samples);
    
    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);
    
    	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;
    	/* 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;
    
    	ast_mutex_lock(&chan->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (chan->pvt->indicate)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		res = chan->pvt->indicate(chan, condition);
    
    	ast_mutex_unlock(&chan->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (!chan->pvt->indicate || res) {
    		/*
    		 * Device does not support (that) indication, lets fake
    		 * it by doing our own tone generation. (PM2002)
    		 */
    		if (condition >= 0) {
    			const struct tone_zone_sound *ts = NULL;
    			switch (condition) {
    			 case AST_CONTROL_RINGING:
    				ts = ast_get_indication_tone(chan->zone, "ring");
    				break;
    			 case AST_CONTROL_BUSY:
    				ts = ast_get_indication_tone(chan->zone, "busy");
    				break;
    			 case AST_CONTROL_CONGESTION:
    				ts = ast_get_indication_tone(chan->zone, "congestion");
    				break;
    			}
    			if (ts && ts->data[0]) {
    				ast_log(LOG_DEBUG, "Driver for channel '%s' does not support indication %d, emulating it\n", chan->name, condition);
    
    				ast_playtones_start(chan,0,ts->data, 1);
    
    			} else if (condition == AST_CONTROL_PROGRESS) {
    				/* ast_playtones_stop(chan); */
    			} else {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				/* not handled */
    				ast_log(LOG_WARNING, "Unable to handle indication %d for '%s'\n", condition, chan->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    		}
    		else ast_playtones_stop(chan);
    	}
    
    	return res;
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_recvchar(struct ast_channel *chan, int timeout)
    {
    	int res,ourto,c;
    	struct ast_frame *f;
    	
    	ourto = timeout;
    	for(;;)
    	   {
    		if (ast_check_hangup(chan)) return -1;
    		res = ast_waitfor(chan,ourto);
    		if (res <= 0) /* if timeout */
    		   {
    			return 0;
    		   }
    		ourto = res;
    		f = ast_read(chan);
    		if (f == NULL) return -1; /* if hangup */
    		if ((f->frametype == AST_FRAME_CONTROL) &&
    
    		    (f->subclass == AST_CONTROL_HANGUP)) return -1; /* if hangup */
    		if (f->frametype == AST_FRAME_TEXT)  /* if a text frame */
    
    			c = *((char *)f->data);  /* get the data */
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_frfree(f);
    			return(c);
    		   }
    		ast_frfree(f);
    	}
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_sendtext(struct ast_channel *chan, char *text)
    {
    	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
    	CHECK_BLOCKING(chan);
    	if (chan->pvt->send_text)
    		res = chan->pvt->send_text(chan, text);
    	chan->blocking = 0;
    	return res;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int do_senddigit(struct ast_channel *chan, char digit)
    {
    	int res = -1;
    
    	if (chan->pvt->send_digit)
    		res = chan->pvt->send_digit(chan, digit);
    	if (!chan->pvt->send_digit || res) {
    		/*
    		 * Device does not support DTMF tones, lets fake
    		 * it by doing our own generation. (PM2002)
    		 */
    		static const char* dtmf_tones[] = {
    
    			"!941+1336/100,!0/100",	/* 0 */
    			"!697+1209/100,!0/100",	/* 1 */
    			"!697+1336/100,!0/100",	/* 2 */
    			"!697+1477/100,!0/100",	/* 3 */
    			"!770+1209/100,!0/100",	/* 4 */
    			"!770+1336/100,!0/100",	/* 5 */
    			"!770+1477/100,!0/100",	/* 6 */
    			"!852+1209/100,!0/100",	/* 7 */
    			"!852+1336/100,!0/100",	/* 8 */
    			"!852+1477/100,!0/100",	/* 9 */
    			"!697+1633/100,!0/100",	/* A */
    			"!770+1633/100,!0/100",	/* B */
    			"!852+1633/100,!0/100",	/* C */
    			"!941+1633/100,!0/100",	/* D */
    			"!941+1209/100,!0/100",	/* * */
    
    			"!941+1477/100,!0/100" };	/* # */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (digit >= '0' && digit <='9')
    
    			ast_playtones_start(chan,0,dtmf_tones[digit-'0'], 0);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		else if (digit >= 'A' && digit <= 'D')
    
    			ast_playtones_start(chan,0,dtmf_tones[digit-'A'+10], 0);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		else if (digit == '*')
    
    			ast_playtones_start(chan,0,dtmf_tones[14], 0);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		else if (digit == '#')
    
    			ast_playtones_start(chan,0,dtmf_tones[15], 0);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		else {
    			/* not handled */
    
    			ast_log(LOG_DEBUG, "Unable to handle DTMF tone '%c' for '%s'\n", digit, chan->name);
    
    int ast_senddigit(struct ast_channel *chan, char digit)
    {
      return do_senddigit(chan, digit);
    }
    
    
    int ast_prod(struct ast_channel *chan)
    {
    	struct ast_frame a = { AST_FRAME_VOICE };
    	char nothing[128];
    	/* Send an empty audio frame to get things moving */
    	if (chan->_state != AST_STATE_UP) {
    
    		ast_log(LOG_DEBUG, "Prodding channel '%s'\n", chan->name);
    
    		a.subclass = chan->pvt->rawwriteformat;
    		a.data = nothing + AST_FRIENDLY_OFFSET;
    
    		if (ast_write(chan, &a))
    			ast_log(LOG_WARNING, "Prodding channel '%s' failed\n", chan->name);
    
    int ast_write_video(struct ast_channel *chan, struct ast_frame *fr)
    {
    	int res;
    	if (!chan->pvt->write_video)
    		return 0;
    	res = ast_write(chan, fr);
    	if (!res)
    		res = 1;
    	return res;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_write(struct ast_channel *chan, struct ast_frame *fr)
    {
    	int res = -1;
    
    	struct ast_frame *f = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Stop if we're a zombie or need a soft hangup */
    
    	if (chan->zombie || ast_check_hangup(chan))  {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Handle any pending masquerades */
    	if (chan->masq) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_log(LOG_WARNING, "Failed to perform masquerade\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return -1;
    		}
    	}
    
    	if (chan->masqr) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (chan->generatordata) {
    		if (chan->writeinterrupt)
    
    			ast_deactivate_generator(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return 0;
    
    	if (chan->fout & 0x80000000)
    		ast_frame_dump(chan->name, fr, ">>");
    
    Mark Spencer's avatar
    Mark Spencer committed
    	CHECK_BLOCKING(chan);
    	switch(fr->frametype) {
    	case AST_FRAME_CONTROL:
    		/* XXX Interpret control frames XXX */
    		ast_log(LOG_WARNING, "Don't know how to handle control frames yet\n");
    		break;
    	case AST_FRAME_DTMF:
    
    Mark Spencer's avatar
    Mark Spencer committed
    		chan->blocking = 0;
    		ast_mutex_unlock(&chan->lock);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		res = do_senddigit(chan,fr->subclass);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_mutex_lock(&chan->lock);
    		CHECK_BLOCKING(chan);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	case AST_FRAME_TEXT:
    		if (chan->pvt->send_text)
    			res = chan->pvt->send_text(chan, (char *) fr->data);
    		break;
    
    	case AST_FRAME_VIDEO:
    		/* XXX Handle translation of video codecs one day XXX */
    		if (chan->pvt->write_video)
    			res = chan->pvt->write_video(chan, fr);
    		else
    			res = 0;
    		break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	default:
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (chan->pvt->write) {
    			if (chan->pvt->writetrans) {
    
    				f = ast_translate(chan->pvt->writetrans, fr, 0);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			} else
    				f = fr;
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    			if (f) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				res = chan->pvt->write(chan, f);
    
    				if( chan->monitor &&
    						chan->monitor->write_stream &&
    						f && ( f->frametype == AST_FRAME_VOICE ) ) {
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    #ifndef MONITOR_CONSTANT_DELAY
    					int jump = chan->insmpl - chan->outsmpl - 2 * f->samples;
    					if (jump >= 0) {
    						if (ast_seekstream(chan->monitor->write_stream, jump + f->samples, SEEK_FORCECUR) == -1)
    							ast_log(LOG_WARNING, "Failed to perform seek in monitoring write stream, synchronization between the files may be broken\n");
    						chan->outsmpl += jump + 2 * f->samples;
    					} else
    						chan->outsmpl += f->samples;
    #else
    					int jump = chan->insmpl - chan->outsmpl;
    					if (jump - MONITOR_DELAY >= 0) {
    						if (ast_seekstream(chan->monitor->write_stream, jump - f->samples, SEEK_FORCECUR) == -1)
    							ast_log(LOG_WARNING, "Failed to perform seek in monitoring write stream, synchronization between the files may be broken\n");
    						chan->outsmpl += jump;
    					} else
    						chan->outsmpl += f->samples;
    #endif
    				if (ast_writestream(chan->monitor->write_stream, f) < 0)
    
    						ast_log(LOG_WARNING, "Failed to write data to channel monitor write stream\n");
    				}
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    			} else
    
    Mark Spencer's avatar
    Mark Spencer committed
    				res = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	if (f && (f != fr))
    		ast_frfree(f);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	chan->blocking = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Consider a write failure to force a soft hangup */
    	if (res < 0)
    
    Mark Spencer's avatar
    Mark Spencer committed
    		chan->_softhangup |= AST_SOFTHANGUP_DEV;
    
    	else {
    		if ((chan->fout & 0x7fffffff) == 0x7fffffff)
    			chan->fout &= 0x80000000;
    		else
    			chan->fout++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		chan->fout++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
    int ast_set_write_format(struct ast_channel *chan, int fmts)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int fmt;
    	int native;
    	int res;
    	
    
    Mark Spencer's avatar
    Mark Spencer committed
    	native = chan->nativeformats;
    	fmt = fmts;
    	
    	res = ast_translator_best_choice(&native, &fmt);
    	if (res < 0) {
    
    		ast_log(LOG_NOTICE, "Unable to find a path from %s to %s\n",
    			ast_getformatname(fmts), ast_getformatname(chan->nativeformats));
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    	
    
    	/* Now we have a good choice for both.  We'll write using our native format. */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	chan->pvt->rawwriteformat = native;
    	/* User perspective is fmt */
    	chan->writeformat = fmt;
    	/* Free any write translation we have right now */
    	if (chan->pvt->writetrans)
    		ast_translator_free_path(chan->pvt->writetrans);
    	/* Build a translation path from the user write format to the raw writing format */
    	chan->pvt->writetrans = ast_translator_build_path(chan->pvt->rawwriteformat, chan->writeformat);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (option_debug)
    
    		ast_log(LOG_DEBUG, "Set channel %s to write format %s\n", chan->name, ast_getformatname(chan->writeformat));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    int ast_set_read_format(struct ast_channel *chan, int fmts)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int fmt;
    	int native;
    	int res;
    	
    
    Mark Spencer's avatar
    Mark Spencer committed
    	native = chan->nativeformats;
    	fmt = fmts;
    	/* Find a translation path from the native read format to one of the user's read formats */
    	res = ast_translator_best_choice(&fmt, &native);
    	if (res < 0) {
    
    		ast_log(LOG_NOTICE, "Unable to find a path from %s to %s\n",
    			ast_getformatname(chan->nativeformats), ast_getformatname(fmts));
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    	
    
    	/* Now we have a good choice for both.  We'll write using our native format. */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	chan->pvt->rawreadformat = native;
    	/* User perspective is fmt */
    	chan->readformat = fmt;
    	/* Free any read translation we have right now */
    	if (chan->pvt->readtrans)
    		ast_translator_free_path(chan->pvt->readtrans);
    	/* Build a translation path from the raw read format to the user reading format */
    	chan->pvt->readtrans = ast_translator_build_path(chan->readformat, chan->pvt->rawreadformat);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (option_debug)
    
    		ast_log(LOG_DEBUG, "Set channel %s to read format %s\n", 
    			chan->name, ast_getformatname(chan->readformat));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    struct ast_channel *__ast_request_and_dial(char *type, int format, void *data, int timeout, int *outstate, char *callerid, struct outgoing_helper *oh)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int state = 0;
    	struct ast_channel *chan;
    	struct ast_frame *f;
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    	int res = 0;
    
    	char *variable;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	chan = ast_request(type, format, data);
    	if (chan) {
    
    			if (oh->variable)
    				variable = ast_strdupa(oh->variable);
    			else
    				variable = NULL;
    			tmp = variable;
    
    			/* FIXME replace this call with strsep  NOT*/
    
    			while( (var = strtok_r(NULL, "|", &tmp)) ) {
    				pbx_builtin_setvar( chan, var );
    			} /* /JDG */
    
    			if (oh->account && *oh->account)
    				ast_cdr_setaccount(chan, oh->account);
    
    		if (callerid && !ast_strlen_zero(callerid))
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_set_callerid(chan, callerid, 1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!ast_call(chan, data, 0)) {
    			while(timeout && (chan->_state != AST_STATE_UP)) {
    				res = ast_waitfor(chan, timeout);
    				if (res < 0) {
    					/* Something not cool, or timed out */
    					break;
    				}
    				/* If done, break out */
    				if (!res)
    					break;
    				if (timeout > -1)
    					timeout = res;
    				f = ast_read(chan);
    				if (!f) {
    					state = AST_CONTROL_HANGUP;
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    					res = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    					break;
    				}
    				if (f->frametype == AST_FRAME_CONTROL) {
    					if (f->subclass == AST_CONTROL_RINGING)
    						state = AST_CONTROL_RINGING;
    					else if ((f->subclass == AST_CONTROL_BUSY) || (f->subclass == AST_CONTROL_CONGESTION)) {
    						state = f->subclass;
    
    						ast_frfree(f);
    
    Mark Spencer's avatar
    Mark Spencer committed
    						break;
    					} else if (f->subclass == AST_CONTROL_ANSWER) {
    						state = f->subclass;
    
    						ast_frfree(f);
    
    Mark Spencer's avatar
    Mark Spencer committed
    						break;
    
    					} else if (f->subclass == AST_CONTROL_PROGRESS) {
    						/* Ignore */
    
    					} else if (f->subclass == -1) {
    						/* Ignore -- just stopping indications */
    
    Mark Spencer's avatar
    Mark Spencer committed
    					} else {
    						ast_log(LOG_NOTICE, "Don't know what to do with control frame %d\n", f->subclass);
    					}
    				}
    				ast_frfree(f);
    			}
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    		} else
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data);
    	} else
    		ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (chan) {
    		/* Final fixups */
    		if (oh) {
    			if (oh->context && *oh->context)
    				strncpy(chan->context, oh->context, sizeof(chan->context) - 1);
    			if (oh->exten && *oh->exten)
    				strncpy(chan->exten, oh->exten, sizeof(chan->exten) - 1);
    			chan->priority = oh->priority;
    		}
    		if (chan->_state == AST_STATE_UP) 
    			state = AST_CONTROL_ANSWER;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (outstate)
    		*outstate = state;
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    	if (chan && res <= 0) {
    		if (!chan->cdr) {
    			chan->cdr = ast_cdr_alloc();
    			if (chan->cdr)
    				ast_cdr_init(chan->cdr, chan);
    		}
    		if (chan->cdr) {
    			char tmp[256];
    			sprintf(tmp, "%s/%s",type,(char *)data);
    			ast_cdr_setapp(chan->cdr,"Dial",tmp);
    			ast_cdr_update(chan);
    			ast_cdr_start(chan->cdr);
    			ast_cdr_end(chan->cdr);
    			/* If the cause wasn't handled properly */
    			if (ast_cdr_disposition(chan->cdr,chan->hangupcause))
    				ast_cdr_failed(chan->cdr);
    		} else 
    			ast_log(LOG_WARNING, "Unable to create Call Detail Record\n");
    		ast_hangup(chan);
    		chan = NULL;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return chan;
    }
    
    
    struct ast_channel *ast_request_and_dial(char *type, int format, void *data, int timeout, int *outstate, char *callerid)
    {
    	return __ast_request_and_dial(type, format, data, timeout, outstate, callerid, NULL);
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    struct ast_channel *ast_request(char *type, int format, void *data)
    {
    	struct chanlist *chan;
    	struct ast_channel *c = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int capabilities;
    	int fmt;
    	int res;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Unable to lock channel list\n");
    		return NULL;
    	}
    	chan = backends;
    	while(chan) {
    		if (!strcasecmp(type, chan->type)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			capabilities = chan->capabilities;
    			fmt = format;
    			res = ast_translator_best_choice(&fmt, &capabilities);
    			if (res < 0) {
    				ast_log(LOG_WARNING, "No translator path exists for channel type %s (native %d) to %d\n", type, chan->capabilities, format);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				return NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (chan->requester)
    
    Mark Spencer's avatar
    Mark Spencer committed
    				c = chan->requester(type, capabilities, data);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (c) {
    
    				if (c->_state == AST_STATE_DOWN) {
    					manager_event(EVENT_FLAG_CALL, "Newchannel",
    					"Channel: %s\r\n"
    					"State: %s\r\n"
    					"Callerid: %s\r\n"
    					"Uniqueid: %s\r\n",
    					c->name, ast_state2str(c->_state), c->callerid ? c->callerid : "<unknown>", c->uniqueid);
    				}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return c;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    		chan = chan->next;
    	}
    	if (!chan)
    		ast_log(LOG_WARNING, "No channel type registered for '%s'\n", type);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return c;
    }
    
    
    int ast_parse_device_state(char *device)
    {
    	char name[AST_CHANNEL_NAME] = "";
    	char *cut;
    	struct ast_channel *chan;
    
    
    	chan = ast_channel_walk_locked(NULL);
    
    	while (chan) {
    		strncpy(name, chan->name, sizeof(name)-1);
    
    		ast_mutex_unlock(&chan->lock);
    
    		cut = strchr(name,'-');
    		if (cut)
    
    		        *cut = 0;
    
    		if (!strcmp(name, device))
    
    		        return AST_DEVICE_INUSE;
    
    		chan = ast_channel_walk_locked(chan);
    
    	}
    	return AST_DEVICE_UNKNOWN;
    }
    
    int ast_device_state(char *device)
    {
    	char tech[AST_MAX_EXTENSION] = "";
    	char *number;
    	struct chanlist *chanls;
    	int res = 0;
    	
    	strncpy(tech, device, sizeof(tech)-1);
    	number = strchr(tech, '/');
    	if (!number) {
    
    	    return AST_DEVICE_INVALID;
    
    		ast_log(LOG_WARNING, "Unable to lock channel list\n");
    		return -1;
    	}
    	chanls = backends;
    	while(chanls) {
    		if (!strcasecmp(tech, chanls->type)) {
    
    			if (!chanls->devicestate) 
    				return ast_parse_device_state(device);
    			else {
    				res = chanls->devicestate(number);
    				if (res == AST_DEVICE_UNKNOWN)
    					return ast_parse_device_state(device);
    				else
    					return res;
    			}
    		}
    		chanls = chanls->next;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_call(struct ast_channel *chan, char *addr, int timeout) 
    {
    	/* Place an outgoing call, but don't wait any longer than timeout ms before returning. 
    	   If the remote end does not answer within the timeout, then do NOT hang up, but 
    	   return anyway.  */
    	int res = -1;
    
    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
    		if (chan->pvt->call)
    			res = chan->pvt->call(chan, addr, timeout);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
    int ast_transfer(struct ast_channel *chan, char *dest) 
    {
    	/* Place an outgoing call, but don't wait any longer than timeout ms before returning. 
    	   If the remote end does not answer within the timeout, then do NOT hang up, but 
    	   return anyway.  */
    	int res = -1;
    	/* Stop if we're a zombie or need a soft hangup */
    
    	if (!chan->zombie && !ast_check_hangup(chan)) {
    		if (chan->pvt->transfer) {
    			res = chan->pvt->transfer(chan, dest);
    			if (!res)
    				res = 1;
    		} else
    			res = 0;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_readstring(struct ast_channel *c, char *s, int len, int timeout, int ftimeout, char *enders)
    {
    	int pos=0;
    	int to = ftimeout;
    	char d;
    
    	/* XXX Merge with full version? XXX */
    
    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
    	if (!len)
    		return -1;
    	do {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (c->stream) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			d = ast_waitstream(c, AST_DIGIT_ANY);
    			ast_stopstream(c);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			usleep(1000);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (!d)
    				d = ast_waitfordigit(c, to);
    		} else {
    			d = ast_waitfordigit(c, to);
    		}
    		if (d < 0)
    			return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (d == 0) {
    			s[pos]='\0';
    			return 1;
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!strchr(enders, d))
    			s[pos++] = d;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (strchr(enders, d) || (pos >= len)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			s[pos]='\0';
    
    			return 0;
    		}
    		to = timeout;
    	} while(1);
    	/* Never reached */
    	return 0;
    }
    
    int ast_readstring_full(struct ast_channel *c, char *s, int len, int timeout, int ftimeout, char *enders, int audiofd, int ctrlfd)
    {
    	int pos=0;
    	int to = ftimeout;
    	char d;
    	/* Stop if we're a zombie or need a soft hangup */
    	if (c->zombie || ast_check_hangup(c)) 
    		return -1;
    	if (!len)
    		return -1;
    	do {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (c->stream) {
    
    			d = ast_waitstream_full(c, AST_DIGIT_ANY, audiofd, ctrlfd);
    			ast_stopstream(c);
    			usleep(1000);
    			if (!d)
    				d = ast_waitfordigit_full(c, to, audiofd, ctrlfd);
    		} else {
    			d = ast_waitfordigit_full(c, to, audiofd, ctrlfd);
    		}
    		if (d < 0)
    			return -1;
    		if (d == 0) {
    			s[pos]='\0';
    			return 1;
    		}
    		if (d == 1) {
    			s[pos]='\0';
    			return 2;
    		}
    		if (!strchr(enders, d))
    			s[pos++] = d;
    		if (strchr(enders, d) || (pos >= len)) {
    			s[pos]='\0';
    
    Mark Spencer's avatar
    Mark Spencer committed
    			return 0;
    		}
    		to = timeout;