Index: channel.c
===================================================================
RCS file: /usr/cvsroot/asterisk/channel.c,v
retrieving revision 1.226
diff -u -p -r1.226 channel.c
--- channel.c	25 Jul 2005 22:13:32 -0000	1.226
+++ channel.c	26 Jul 2005 19:44:00 -0000
@@ -490,7 +490,7 @@ struct ast_channel *ast_channel_alloc(in
 		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
@@ -522,9 +522,9 @@ struct ast_channel *ast_channel_alloc(in
 		tmp->alertpipe[0] = tmp->alertpipe[1] = -1;
 
 	/* Always watch the alertpipe */
-	tmp->fds[AST_MAX_FDS-1] = tmp->alertpipe[0];
+	tmp->fds[AST_ALERT_FD] = tmp->alertpipe[0];
 	/* And timing pipe */
-	tmp->fds[AST_MAX_FDS-2] = tmp->timingfd;
+	tmp->fds[AST_TIMING_FD] = tmp->timingfd;
 	strcpy(tmp->name, "**Unkown**");
 	/* Initial state */
 	tmp->_state = AST_STATE_DOWN;
@@ -1082,6 +1082,7 @@ void ast_deactivate_generator(struct ast
 			chan->generator->release(chan, chan->generatordata);
 		chan->generatordata = NULL;
 		chan->generator = NULL;
+		chan->fds[AST_GENERATOR_FD] = -1;
 		ast_clear_flag(chan, AST_FLAG_WRITE_INT);
 		ast_settimeout(chan, 0, NULL, NULL);
 	}
@@ -1120,6 +1121,13 @@ int ast_activate_generator(struct ast_ch
 	if ((chan->generatordata = gen->alloc(chan, params))) {
 		ast_settimeout(chan, 160, generator_force, chan);
 		chan->generator = gen;
+		/* if the generator supplies an fd, register it */
+		if (gen->magic == AST_GEN_MAGIC) {
+			int gfd = gen->get_fd(chan, chan->generatordata);
+
+			if (gfd > -1)
+				chan->fds[AST_GENERATOR_FD] = gfd;
+		}
 	} else {
 		res = -1;
 	}
@@ -1127,59 +1135,25 @@ int ast_activate_generator(struct ast_ch
 	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;
+}
+
 /*--- ast_waitfor_n_fd: Wait for x amount of time on a file descriptor to have input.  */
 int ast_waitfor_n_fd(int *fds, int n, int *ms, int *exception)
 {
-	struct timeval start = { 0 , 0 };
-	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)
-		start = ast_tvnow();
-	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) {
-		*ms -= ast_tvdiff_ms(ast_tvnow(), start);
-		if (*ms < 0)
-			*ms = 0;
-	}
+
+	ast_waitfor_nandfds(NULL, 0, fds, n, exception, &winner, ms);
 	return winner;
 }
 
@@ -1189,38 +1163,35 @@ struct ast_channel *ast_waitfor_nandfds(
 {
 	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;
+	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 */
 
-	pfds = alloca(sizeof(struct pollfd) * (n * AST_MAX_FDS + nfds));
-	if (!pfds) {
-		ast_log(LOG_ERROR, "Out of memory\n");
+	if (outfd)
 		*outfd = -1;
+	if (exception)
+		*exception = 0;
+
+	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;
 
-	if (outfd)
-		*outfd = -99999;
-	if (exception)
-		*exception = 0;
-	
-	/* Perform any pending masquerades */
-	for (x=0; x < n; x++) {
 		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");
@@ -1229,95 +1200,85 @@ struct ast_channel *ast_waitfor_nandfds(
 				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) 
 		start = ast_tvnow();
 	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 fd ready, 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) {
 		*ms -= ast_tvdiff_ms(ast_tvnow(), start);
@@ -1334,16 +1295,11 @@ struct ast_channel *ast_waitfor_n(struct
 
 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;
 }
 
@@ -1501,7 +1457,7 @@ struct ast_frame *ast_read(struct ast_ch
 		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_FD && 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 */
@@ -1545,6 +1501,15 @@ struct ast_frame *ast_read(struct ast_ch
 			ast_log(LOG_NOTICE, "No/unknown event '%d' on timer for '%s'?\n", blah, chan->name);
 	}
 #endif
+	/* check for pending generator call */
+	if (chan->fds[AST_GENERATOR_FD] > -1 && chan->fdno == AST_GENERATOR_FD) {
+		void *tmp = chan->generatordata;
+
+		chan->generatordata = NULL;     /* reset to 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;
@@ -1654,7 +1619,7 @@ struct ast_frame *ast_read(struct ast_ch
 			ast_settimeout(chan, 0, NULL, NULL);
 		}
 		tmp = chan->generatordata;
-		chan->generatordata = NULL;
+		chan->generatordata = NULL;		/* reset to let ast_write pass through */
 		generate = chan->generator->generate;
 		res = generate(chan, tmp, f->datalen, f->samples);
 		chan->generatordata = tmp;
@@ -2681,7 +2646,7 @@ int ast_do_masquerade(struct ast_channel
 	clone->cid = tmpcid;
 	
 	/* Restore original timing file descriptor */
-	original->fds[AST_MAX_FDS - 2] = original->timingfd;
+	original->fds[AST_TIMING_FD] = 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.146
diff -u -p -r1.146 chan_agent.c
--- channels/chan_agent.c	25 Jul 2005 16:53:28 -0000	1.146
+++ channels/chan_agent.c	26 Jul 2005 19:44:03 -0000
@@ -194,10 +194,10 @@ static struct agent_pvt {
 	int x; \
 	if (p->chan) { \
 		for (x=0;x<AST_MAX_FDS;x++) {\
-			if (x != AST_MAX_FDS - 2) \
+			if (x != AST_TIMING_FD) \
 				ast->fds[x] = p->chan->fds[x]; \
 		} \
-		ast->fds[AST_MAX_FDS - 3] = p->chan->fds[AST_MAX_FDS - 2]; \
+		ast->fds[AST_AGENT_FD] = p->chan->fds[AST_TIMING_FD]; \
 	} \
 } while(0)
 
@@ -403,10 +403,7 @@ static struct ast_frame *agent_read(stru
 	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_FD) ? AST_TIMING_FD : 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.16
diff -u -p -r1.16 chan_features.c
--- channels/chan_features.c	25 Jul 2005 20:04:46 -0000	1.16
+++ channels/chan_features.c	26 Jul 2005 19:44:03 -0000
@@ -157,8 +157,8 @@ static void restore_channel(struct featu
 	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_FD] = p->subs[index].alertpipebackup[0];
+	p->subs[index].owner->fds[AST_TIMING_FD] = 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.94
diff -u -p -r1.94 channel.h
--- include/asterisk/channel.h	25 Jul 2005 22:13:32 -0000	1.94
+++ include/asterisk/channel.h	26 Jul 2005 19:44:09 -0000
@@ -52,6 +52,24 @@ extern "C" {
 #define MAX_MUSICCLASS		20
 
 #define AST_MAX_FDS		8
+/*
+ * We have AST_MAX_FDS file descriptors in a channel.
+ * Some of them have a fixed use:
+ */
+#define AST_ALERT_FD	(AST_MAX_FDS-1)		/* used for alertpipe */
+#define AST_TIMING_FD	(AST_MAX_FDS-2)		/* used for timingfd */
+#define AST_AGENT_FD	(AST_MAX_FDS-3)		/* XXX used by agent ? */
+
+/*
+ * 'generators' that provide a timed output can use their own file
+ * descriptors instead of relying on AST_TIMING_FD.
+ * In order to be backward compatible, generators of this kind should
+ * set the 'magic' field to AST_GEN_MAGIC, and get_fd() (to be run
+ * after the alloc() call) should return the file descriptor to set
+ * in the AST_GENERATOR_FD entry of the channel.
+ */
+#define AST_GENERATOR_FD	(AST_MAX_FDS-4) /* used by generator */
+#define AST_GEN_MAGIC	0xa9ea61c
 
 typedef unsigned long long ast_group_t;
 
@@ -59,6 +77,9 @@ 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 get_fd is valid */
+	int (*get_fd)(struct ast_channel *chan, void *data);
 };
 
 struct ast_callerid {
@@ -975,12 +996,16 @@ void ast_set_variables(struct ast_channe
 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;
