Skip to content
Snippets Groups Projects
channel.c 82.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • 				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;
    
    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) {
    
    				pfds[max].fd = c[x]->fds[y];
    				pfds[max].events = POLLIN | POLLPRI;
    				max++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		CHECK_BLOCKING(c[x]);
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	for (x=0;x<nfds; x++) {
    
    		if (fds[x] > -1) {
    			pfds[max].fd = fds[x];
    			pfds[max].events = POLLIN | POLLPRI;
    			max++;
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	if (*ms > 0) 
    		gettimeofday(&start, NULL);
    	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;
    	}
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	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];
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		for (y=0;y<AST_MAX_FDS;y++) {
    			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
    	}
    
    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) {
    		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 */
    
    	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;
    
    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 (ast_test_flag(c, AST_FLAG_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 */
    
    	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;
    	}
    
    	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->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) && 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->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 (ast_test_flag(chan, AST_FLAG_EXCEPTION)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			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 */
    
    			ast_clear_flag(chan, AST_FLAG_EXCEPTION);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		} 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 (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 (chan->_state == 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);
    		}
    
    	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 */
    
    	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);
    
    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 if (condition == AST_CONTROL_PROCEEDING) {
    				/* Do nothing, really */
    
    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 */
    
    	if (ast_test_flag(chan, AST_FLAG_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);
    
    	ast_clear_flag(chan, AST_FLAG_BLOCKING);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	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;
    
    		a.src = "ast_prod";
    
    		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 (ast_test_flag(chan, AST_FLAG_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 (ast_test_flag(chan, AST_FLAG_WRITE_INT))
    
    			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:
    
    		ast_clear_flag(chan, AST_FLAG_BLOCKING);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		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);
    
    	ast_clear_flag(chan, AST_FLAG_BLOCKING);
    
    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(const char *type, int format, void *data, int timeout, int *outstate, const char *cid_num, const char *cid_name, struct outgoing_helper *oh)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	int state = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_channel *chan;
    	struct ast_frame *f;
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    	int res = 0;
    
    	char *variable;
    
    	chan = ast_request(type, format, data, &cause);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	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 */
    
    			ast_set_callerid(chan, oh->cid_num, oh->cid_name, oh->cid_num);
    
    			if (oh->account && *oh->account)
    				ast_cdr_setaccount(chan, oh->account);
    
    		ast_set_callerid(chan, cid_num, cid_name, cid_num);
    
    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
    
    			ast_log(LOG_NOTICE, "Unable to call channel %s/%s\n", type, (char *)data);
    	} else {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data);
    
    		switch(cause) {
    		case AST_CAUSE_BUSY:
    			state = AST_CONTROL_BUSY;
    			break;
    		case AST_CAUSE_CONGESTION:
    			state = AST_CONTROL_CONGESTION;
    			break;
    		}
    	}
    
    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];
    
    			snprintf(tmp, 256, "%s/%s", type, (char *)data);
    
    Martin Pycko's avatar
     
    Martin Pycko committed
    			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(const char *type, int format, void *data, int timeout, int *outstate, const char *cidnum, const char *cidname)
    
    	return __ast_request_and_dial(type, format, data, timeout, outstate, cidnum, cidname, NULL);
    
    struct ast_channel *ast_request(const char *type, int format, void *data, int *cause)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct chanlist *chan;
    	struct ast_channel *c = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int capabilities;
    	int fmt;
    	int res;
    
    	int foo;
    	if (!cause)
    		cause = &foo;
    	*cause = AST_CAUSE_NOTDEFINED;
    
    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)
    
    				c = chan->requester(type, capabilities, data, cause);
    
    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"
    					"CallerIDName: %s\r\n"
    
    					"Uniqueid: %s\r\n",
    
    					c->name, ast_state2str(c->_state), c->cid.cid_num ? c->cid.cid_num : "<unknown>", c->cid.cid_name ? c->cid.cid_name : "<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;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		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)) {