Index: channel.c
===================================================================
RCS file: /usr/cvsroot/asterisk/channel.c,v
retrieving revision 1.207
diff -u -r1.207 channel.c
--- channel.c	21 Jun 2005 23:10:29 -0000	1.207
+++ channel.c	23 Jun 2005 15:51:40 -0000
@@ -370,7 +370,7 @@
 		return NULL;
 	}
 	
-	for (x=0; x<AST_MAX_FDS - 1; x++)
+	for (x=0; x<AST_MAX_FDS; x++)
 		tmp->fds[x] = -1;
 
 #ifdef ZAPTEL_OPTIMIZATIONS
@@ -402,9 +402,9 @@
 		tmp->alertpipe[0] = tmp->alertpipe[1] = -1;
 
 	/* Always watch the alertpipe */
-	tmp->fds[AST_MAX_FDS-1] = tmp->alertpipe[0];
+	tmp->fds[AST_ALERT_FDS] = tmp->alertpipe[0];
 	/* And timing pipe */
-	tmp->fds[AST_MAX_FDS-2] = tmp->timingfd;
+	tmp->fds[AST_TIMING_FDS] = tmp->timingfd;
 	ast_copy_string(tmp->name, "**Unknown**", sizeof(tmp->name));
 	/* Initial state */
 	tmp->_state = AST_STATE_DOWN;
@@ -971,6 +971,7 @@
 			chan->generator->release(chan, chan->generatordata);
 		chan->generatordata = NULL;
 		chan->generator = NULL;
+		chan->fds[AST_GENERATOR_FDS] = -1;
 		ast_clear_flag(chan, AST_FLAG_WRITE_INT);
 		ast_settimeout(chan, 0, NULL, NULL);
 	}
@@ -1007,8 +1008,14 @@
 	}
 	ast_prod(chan);
 	if ((chan->generatordata = gen->alloc(chan, params))) {
-		ast_settimeout(chan, 160, generator_force, chan);
+		int gfd;
+
 		chan->generator = gen;
+		/* if the generator supplies an fd, register it */
+		if (gen->magic == AST_GEN_MAGIC &&
+				(gfd = gen->get_fd(chan, chan->generatordata)) >= 0)
+			chan->fds[AST_GENERATOR_FDS] = gfd;
+		ast_settimeout(chan, 160, generator_force, chan);
 	} else {
 		res = -1;
 	}
@@ -1016,64 +1023,23 @@
 	return res;
 }
 
+/* if fd is a valid descriptor, set *pfd with the descriptor
+ * Return 1 (not -1!) if added, 0 otherwise (so we can add the
+ * return value to the index into the array)
+ */
+static int add_fd(struct pollfd *pfd, int fd)
+{
+	if (fd < 0)
+		return 0;
+	pfd->fd = fd;
+	pfd->events = POLLIN | POLLPRI;
+	return 1;
+}
+
 int ast_waitfor_n_fd(int *fds, int n, int *ms, int *exception)
 {
-	/* Wait for x amount of time on a file descriptor to have input.  */
-	struct timeval start, now;
-	int res;
-	int x, y;
 	int winner = -1;
-	int spoint;
-	struct pollfd *pfds;
-	
-	pfds = alloca(sizeof(struct pollfd) * n);
-	if (!pfds) {
-		ast_log(LOG_ERROR, "Out of memory\n");
-		return -1;
-	}
-	if (*ms > 0)
-		gettimeofday(&start, NULL);
-	y = 0;
-	for (x=0;x<n;x++) {
-		if (fds[x] > -1) {
-			pfds[y].fd = fds[x];
-			pfds[y].events = POLLIN | POLLPRI;
-			y++;
-		}
-	}
-	res = poll(pfds, y, *ms);
-	if (res < 0) {
-		/* Simulate a timeout if we were interrupted */
-		if (errno != EINTR)
-			*ms = -1;
-		else
-			*ms = 0;
-		return -1;
-	}
-	spoint = 0;
-	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;
-				}
-			}
-		}
-	}
-	if (*ms > 0) {
-		long passed;
-		gettimeofday(&now, NULL);
-		passed = (now.tv_sec - start.tv_sec) * 1000;
-		passed += (now.tv_usec - start.tv_usec) / 1000;
-		if (passed <= *ms)
-			*ms -= passed;
-		else
-			*ms = 0;
-	}
+	ast_waitfor_nandfds(NULL, 0, fds, n, exception, &winner, ms);
 	return winner;
 }
 
@@ -1081,40 +1047,36 @@
 	int *exception, int *outfd, int *ms)
 {
 	/* Wait for x amount of time on a file descriptor to have input.  */
-	struct timeval start, end;
+	struct timeval start;
 	struct pollfd *pfds;
-	int res;
-	long rms;
-	int x, y, max;
-	int spoint;
-	time_t now = 0;
-	long whentohangup = 0, havewhen = 0, diff;
 	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");
-		*outfd = -1;
-		return NULL;
-	}
+	int x, max, res, rms, sz;
+	time_t t0 = 0; /* timeout, 0 means unset */
+	int *ci, *fdno;	/* map pfds entries to channel index and fd index */
 
 	if (outfd)
-		*outfd = -99999;
+		*outfd = -1;
 	if (exception)
 		*exception = 0;
 	
-	/* Perform any pending masquerades */
-	for (x=0;x<n;x++) {
+	sz = (n * AST_MAX_FDS + nfds);
+	pfds = alloca(sizeof(struct pollfd) * sz);
+	ci = alloca(sizeof(int *) * sz);	/* pfds to channel map */
+	fdno = alloca(sizeof(int *) * sz);	/* pfds to fd index */
+
+	if (!pfds || !ci || !fdno) {
+		ast_log(LOG_ERROR, "Out of memory\n");
+		return NULL;
+	}
+	for (x=0; x < sz; x++)
+		ci[x] = -1;	/* no channel linked to this pfd */
+
+	/*
+	 * Perform any pending masquerades, and take the min timeout in t0
+	 */
+	for (x=0; x<n; x++) {
+		time_t t;
 		ast_mutex_lock(&c[x]->lock);
-		if (c[x]->whentohangup) {
-			if (!havewhen)
-				time(&now);
-			diff = c[x]->whentohangup - now;
-			if (!havewhen || (diff < whentohangup)) {
-				havewhen++;
-				whentohangup = diff;
-			}
-		}
 		if (c[x]->masq) {
 			if (ast_do_masquerade(c[x])) {
 				ast_log(LOG_WARNING, "Masquerade failed\n");
@@ -1123,104 +1085,91 @@
 				return NULL;
 			}
 		}
+		t = c[x]->whentohangup;
+		if (t > 0 && (t0 == 0 || t < t0))
+			t0 = t;
 		ast_mutex_unlock(&c[x]->lock);
 	}
 
-	rms = *ms;
-	
-	if (havewhen) {
-		if ((*ms < 0) || (whentohangup * 1000 < *ms)) {
-			rms =  whentohangup * 1000;
-		}
+	if (t0 == 0)	/* no timeout, use *ms */
+		rms = *ms;
+	else {
+		rms = (t0 - time(NULL)) * 1000; /* timeout in milliseconds */
+		if (*ms >= 0 && *ms < rms)		/* original *ms still smaller */
+			rms =  *ms;
 	}
+	/*
+	 * Build the pollfd array, putting the channels' fds first,
+	 * followed by individual fds. Order is important because
+	 * individual fd's must have priority over channel fds.
+	 */
 	max = 0;
-	for (x=0;x<n;x++) {
-		for (y=0;y<AST_MAX_FDS;y++) {
-			if (c[x]->fds[y] > -1) {
-				pfds[max].fd = c[x]->fds[y];
-				pfds[max].events = POLLIN | POLLPRI;
-				max++;
+	for (x=0; x<n; x++) {
+		int y;
+
+		for (y=0; y<AST_MAX_FDS; y++)
+			if (add_fd(&pfds[max], c[x]->fds[y])) {
+				fdno[max] = y;	/* fd y is linked to this pfds */
+				ci[max++] = x;  /* channel x is linked to this pfds */
 			}
-		}
 		CHECK_BLOCKING(c[x]);
 	}
-	for (x=0;x<nfds; x++) {
-		if (fds[x] > -1) {
-			pfds[max].fd = fds[x];
-			pfds[max].events = POLLIN | POLLPRI;
-			max++;
-		}
-	}
+	/* add the individual fds */
+	for (x=0; x<nfds; x++)
+		max += add_fd(&pfds[max], fds[x]);
+
 	if (*ms > 0) 
-		gettimeofday(&start, NULL);
+		start = ast_tvnow();	/* timestamp before the poll */
 	res = poll(pfds, max, rms);
-	if (res < 0) {
-		for (x=0;x<n;x++) 
-			ast_clear_flag(c[x], AST_FLAG_BLOCKING);
-		/* Simulate a timeout if we were interrupted */
-		if (errno != EINTR)
-			*ms = -1;
-		else {
-			/* Just an interrupt */
-#if 0
-			*ms = 0;
-#endif			
-		}
+	for (x=0; x<n; x++) 
+		ast_clear_flag(c[x], AST_FLAG_BLOCKING);
+	if (res < 0) { /* Simulate a timeout if we were interrupted */
+		*ms = (errno != EINTR) ? -1 : 0;
 		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;
 	}
 
-	if (havewhen)
-		time(&now);
-	spoint = 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++) {
-			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);
-					else
-						ast_clear_flag(c[x], AST_FLAG_EXCEPTION);
-					c[x]->fdno = y;
+	if (t0 > 0)	{ /* if we have a timeout, check who expired */
+		time(&t0);
+		for (x=0; x<n; x++) {
+			if (c[x]->whentohangup && t0 > c[x]->whentohangup) {
+				c[x]->_softhangup |= AST_SOFTHANGUP_TIMEOUT;
+				if (winner == NULL)
 					winner = c[x];
-				}
 			}
 		}
 	}
-	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 (res == 0) {	/* no fds signalled, reset timeout and done */
+		*ms = 0;	/* XXX use 0 since we may not have an exact timeout. */
+		return winner;
+	}
+
+	/*
+	 * Then check if any channel or fd has a pending event.
+	 * Remember to check channels first and fds last, as they
+	 * must have priority on setting 'winner'
+	 */
+	for (x = 0; x < max; x++) {
+		res = pfds[x].revents;
+		if (res == 0)
+			continue;
+		if (ci[x] >= 0) {	/* this is a channel */
+			winner = c[ci[x]];	/* override previous winners */
+			if (res & POLLPRI)
+				ast_set_flag(winner, AST_FLAG_EXCEPTION);
+			else
+				ast_clear_flag(winner, AST_FLAG_EXCEPTION);
+			winner->fdno = fdno[x];
+		} else { /* this is an fd */
+			if (outfd)
+				*outfd = pfds[x].fd;
+			if (exception)
+				*exception = (res & POLLPRI) ? -1 : 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 -= ast_tvdiff(ast_tvnow(), start);
+		if (*ms < 0)
 			*ms = 0;
 	}
 	return winner;
@@ -1233,15 +1182,10 @@
 
 int ast_waitfor(struct ast_channel *c, int ms)
 {
-	struct ast_channel *chan;
 	int oldms = ms;
-	chan = ast_waitfor_n(&c, 1, &ms);
-	if (ms < 0) {
-		if (oldms < 0)
-			return 0;
-		else
-			return -1;
-	}
+	ast_waitfor_nandfds(&c, 1, NULL, 0, NULL, NULL, &ms);
+	if (ms < 0)		/* !EINTR */
+		return (oldms < 0) ? 0 : -1;
 	return ms;
 }
 
@@ -1396,7 +1344,7 @@
 		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)) {
+	if ((chan->timingfd > -1) && (chan->fdno == AST_TIMING_FDS) && ast_test_flag(chan, AST_FLAG_EXCEPTION)) {
 		ast_clear_flag(chan, AST_FLAG_EXCEPTION);
 		blah = -1;
 		/* IF we can't get event, assume it's an expired as-per the old interface */
@@ -1405,14 +1353,10 @@
 			blah = ZT_EVENT_TIMER_EXPIRED;
 
 		if (blah == ZT_EVENT_TIMER_PING) {
-#if 0
-			ast_log(LOG_NOTICE, "Oooh, there's a PING!\n");
-#endif			
+			// 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				
+				// ast_log(LOG_NOTICE, "Sending a PONG!\n");
 				if (ioctl(chan->timingfd, ZT_TIMERPONG, &blah)) {
 					ast_log(LOG_WARNING, "Failed to pong timer on '%s': %s\n", chan->name, strerror(errno));
 				}
@@ -1423,9 +1367,7 @@
 			data = chan->timingdata;
 			ast_mutex_unlock(&chan->lock);
 			if (func) {
-#if 0
-				ast_log(LOG_DEBUG, "Calling private function\n");
-#endif			
+				// ast_log(LOG_DEBUG, "Calling private function\n");
 				func(data);
 			} else {
 				blah = 0;
@@ -1440,6 +1382,14 @@
 			ast_log(LOG_NOTICE, "No/unknown event '%d' on timer for '%s'?\n", blah, chan->name);
 	}
 #endif
+	/* XXX check for pending generator call */
+	if (chan->fds[AST_GENERATOR_FDS] > -1 && chan->fdno == AST_GENERATOR_FDS) {
+		void *tmp = chan->generatordata;
+		// ast_log(LOG_WARNING, "calling moh process\n");
+		chan->generatordata = NULL;     /* let ast_write get through... */
+		chan->generator->generate(chan, tmp, 0, 160);
+		chan->generatordata = tmp;
+	}
 	/* Check for pending read queue */
 	if (chan->readq) {
 		f = chan->readq;
@@ -1547,7 +1497,7 @@
 			ast_settimeout(chan, 0, NULL, NULL);
 		}
 		tmp = chan->generatordata;
-		chan->generatordata = NULL;
+		chan->generatordata = NULL;	/* XXX nasty trick to let ast_write pass through */
 		generate = chan->generator->generate;
 		res = generate(chan, tmp, f->datalen, f->samples);
 		chan->generatordata = tmp;
@@ -2667,7 +2610,7 @@
 	clone->cid = tmpcid;
 	
 	/* Restore original timing file descriptor */
-	original->fds[AST_MAX_FDS - 2] = original->timingfd;
+	original->fds[AST_TIMING_FDS] = original->timingfd;
 	
 	/* Our native formats are different now */
 	original->nativeformats = clone->nativeformats;
Index: channels/chan_agent.c
===================================================================
RCS file: /usr/cvsroot/asterisk/channels/chan_agent.c,v
retrieving revision 1.139
diff -u -r1.139 chan_agent.c
--- channels/chan_agent.c	18 Jun 2005 16:38:48 -0000	1.139
+++ channels/chan_agent.c	23 Jun 2005 15:51:56 -0000
@@ -192,10 +192,10 @@
 	int x; \
 	if (p->chan) { \
 		for (x=0;x<AST_MAX_FDS;x++) {\
-			if (x != AST_MAX_FDS - 2) \
+			if (x != AST_TIMING_FDS) \
 				ast->fds[x] = p->chan->fds[x]; \
 		} \
-		ast->fds[AST_MAX_FDS - 3] = p->chan->fds[AST_MAX_FDS - 2]; \
+		ast->fds[AST_AGENT_FDS] = p->chan->fds[AST_TIMING_FDS]; \
 	} \
 } while(0)
 
@@ -401,10 +401,7 @@
 	CHECK_FORMATS(ast, p);
 	if (p->chan) {
 		ast_copy_flags(p->chan, ast, AST_FLAG_EXCEPTION);
-		if (ast->fdno == AST_MAX_FDS - 3)
-			p->chan->fdno = AST_MAX_FDS - 2;
-		else
-			p->chan->fdno = ast->fdno;
+		p->chan->fdno = (ast->fdno == AST_AGENT_FDS) ? AST_TIMING_FDS : ast->fdno;
 		f = ast_read(p->chan);
 	} else
 		f = &null_frame;
Index: channels/chan_features.c
===================================================================
RCS file: /usr/cvsroot/asterisk/channels/chan_features.c,v
retrieving revision 1.14
diff -u -r1.14 chan_features.c
--- channels/chan_features.c	7 Jun 2005 17:06:33 -0000	1.14
+++ channels/chan_features.c	23 Jun 2005 15:51:57 -0000
@@ -155,8 +155,8 @@
 	p->subs[index].owner->timingfd = p->subs[index].timingfdbackup;
 	p->subs[index].owner->alertpipe[0] = p->subs[index].alertpipebackup[0];
 	p->subs[index].owner->alertpipe[1] = p->subs[index].alertpipebackup[1];
-	p->subs[index].owner->fds[AST_MAX_FDS-1] = p->subs[index].alertpipebackup[0];
-	p->subs[index].owner->fds[AST_MAX_FDS-2] = p->subs[index].timingfdbackup;
+	p->subs[index].owner->fds[AST_ALERT_FDS] = p->subs[index].alertpipebackup[0];
+	p->subs[index].owner->fds[AST_TIMING_FDS] = p->subs[index].timingfdbackup;
 }
 
 static void update_features(struct feature_pvt *p, int index)
Index: include/asterisk/channel.h
===================================================================
RCS file: /usr/cvsroot/asterisk/include/asterisk/channel.h,v
retrieving revision 1.88
diff -u -r1.88 channel.h
--- include/asterisk/channel.h	21 Jun 2005 01:16:18 -0000	1.88
+++ include/asterisk/channel.h	23 Jun 2005 15:52:36 -0000
@@ -49,13 +49,24 @@
 
 
 #define AST_MAX_FDS 8
+#define	AST_ALERT_FDS	(AST_MAX_FDS-1)		/* used for alertpipe */
+#define	AST_TIMING_FDS	(AST_MAX_FDS-2)		/* used for timingfd */
+#define	AST_AGENT_FDS	(AST_MAX_FDS-3)		/* XXX used by agent ? */
+#define	AST_GENERATOR_FDS	(AST_MAX_FDS-3)	/* used by generator */
 
 typedef unsigned long long ast_group_t;
 
+/*
+ get_fd() returns an fd to activate in the channel
+ Must be run after alloc.
+ */
+#define	AST_GEN_MAGIC	0xa9ea61c
 struct ast_generator {
 	void *(*alloc)(struct ast_channel *chan, void *params);
 	void (*release)(struct ast_channel *chan, void *data);
 	int (*generate)(struct ast_channel *chan, void *data, int len, int samples);
+	int magic;	/* set AST_GEN_MAGIC to indicate fd is valid */
+	int (*get_fd)(struct ast_channel *chan, void *data);
 };
 
 struct ast_callerid {
@@ -970,12 +981,16 @@
 static inline int ast_fdisset(struct pollfd *pfds, int fd, int max, int *start)
 {
 	int x;
-	for (x=start ? *start : 0;x<max;x++)
+	int dummy=0;
+
+	if (fd < 0)
+		return 0;
+	if (start == NULL)
+		start = &dummy;
+	for (x = *start; x<max; x++)
 		if (pfds[x].fd == fd) {
-			if (start) {
-				if (x==*start)
-					(*start)++;
-			}
+			if (x==*start)
+				(*start)++;
 			return pfds[x].revents;
 		}
 	return 0;
