--- linux-2.2.10/include/linux/serialP.h	Tue May 11 19:35:44 1999
+++ linux-2.2.10-ub/include/linux/serialP.h	Thu Aug  5 22:21:54 1999
@@ -61,6 +61,7 @@
 	struct tty_struct 	*tty;
 	int			read_status_mask;
 	int			ignore_status_mask;
+	int                     wakeup_reason;
 	int			timeout;
 	int			quot;
 	int			x_char;	/* xon/xoff character */
--- linux-2.2.10/include/asm-i386/termios.h	Wed Mar 24 20:45:59 1999
+++ linux-2.2.10-ub/include/asm-i386/termios.h	Thu Aug  5 20:43:48 1999
@@ -36,6 +36,25 @@
 #define TIOCM_OUT1	0x2000
 #define TIOCM_OUT2	0x4000
 
+/*
+** ioctl(fd, TIOCMIWAIT, condition)  (wait for serial interrupt)
+** condition flags are defined as bitwise OR of the values below.
+** If TIOCMIWAIT_RETFLAGS is specified, these flags are also the
+** indicate the triggering condition on return.
+** ioctl(fd, TIOCMIWAIT, 0) will clear any current conditions
+** and return immediately with return value indicating the conditions.
+*/
+#define TIOCMIWAIT_RNG   TIOCM_RNG
+#define TIOCMIWAIT_DSR   TIOCM_DSR
+#define TIOCMIWAIT_DCD   TIOCM_CD
+#define TIOCMIWAIT_CTS   TIOCM_CTS
+#define TIOCMIWAIT_RX    0x1000
+#define TIOCMIWAIT_BRK   0x2000
+#define TIOCMIWAIT_TX    0x4000
+#define TIOCMIWAIT_ERR   0x8000
+
+#define TIOCMIWAIT_RETFLAGS 0x800000
+
 /* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */
 
 /* line disciplines */
--- linux-2.2.10/drivers/char/serial.c	Tue Mar 23 22:13:58 1999
+++ linux-2.2.10-ub/drivers/char/serial.c	Thu Aug  5 22:34:58 1999
@@ -17,7 +17,7 @@
  *
  *  rs_set_termios fixed to look also for changes of the input
  *      flags INPCK, BRKINT, PARMRK, IGNPAR and IGNBRK.
- *                                            Bernd Anhäupl 05/17/96.
+ *                                            Bernd Anhäupl 05/17/96.
  *
  *  1/97:  Extended dumb serial ports are a config option now.  
  *         Saves 4k.   Michael A. Griffith <grif@acm.org>
@@ -32,6 +32,8 @@
  *  4/98: Added changes to support the ARM architecture proposed by
  * 	  Russell King
  *
+ *  03/99: TIOCMIWAIT enhanced by Michael McCormack <mccormack@ozemail.com.au>
+ *
  * This module exports the following rs232 io functions:
  *
  *	int rs_init(void);
@@ -154,7 +156,7 @@
 #endif
 	
 static char *serial_name = "Serial driver";
-static char *serial_version = "4.27";
+static char *serial_version = "4.27b";
 
 static DECLARE_TASK_QUEUE(tq_serial);
 
@@ -402,12 +404,18 @@
 			if (*status & UART_LSR_BI) {
 				*status &= ~(UART_LSR_FE | UART_LSR_PE);
 				icount->brk++;
-			} else if (*status & UART_LSR_PE)
+				info->wakeup_reason |= TIOCMIWAIT_BRK;
+			} else if (*status & UART_LSR_PE) {
 				icount->parity++;
-			else if (*status & UART_LSR_FE)
+				info->wakeup_reason |= TIOCMIWAIT_ERR;
+			} else if (*status & UART_LSR_FE) {
 				icount->frame++;
-			if (*status & UART_LSR_OE)
+				info->wakeup_reason |= TIOCMIWAIT_ERR;
+			}
+			if (*status & UART_LSR_OE) {
 				icount->overrun++;
+				info->wakeup_reason |= TIOCMIWAIT_ERR;
+			}
 
 			/*
 			 * Now check to see if character should be
@@ -453,6 +461,9 @@
 		*status = serial_inp(info, UART_LSR);
 	} while (*status & UART_LSR_DR);
 	tty_flip_buffer_push(tty);
+
+	info->wakeup_reason |= TIOCMIWAIT_RX;
+
 }
 
 static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done)
@@ -486,6 +497,10 @@
 	if (info->xmit_cnt < WAKEUP_CHARS)
 		rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
 
+        /* enable waiting on tx queue empty */
+        if (info->xmit_cnt == 0)
+                info->wakeup_reason |= TIOCMIWAIT_TX;
+
 #ifdef SERIAL_DEBUG_INTR
 	printk("THRE...");
 #endif
@@ -508,21 +523,27 @@
 	if (status & UART_MSR_ANY_DELTA) {
 		icount = &info->state->icount;
 		/* update input line counters */
-		if (status & UART_MSR_TERI)
+		if (status & UART_MSR_TERI) {
 			icount->rng++;
-		if (status & UART_MSR_DDSR)
+			info->wakeup_reason |= TIOCMIWAIT_RNG;
+                }
+		if (status & UART_MSR_DDSR) {
 			icount->dsr++;
+			info->wakeup_reason |= TIOCMIWAIT_DSR;
+                }
 		if (status & UART_MSR_DDCD) {
 			icount->dcd++;
+			info->wakeup_reason |= TIOCMIWAIT_DCD;
 #ifdef CONFIG_HARD_PPS
 			if ((info->flags & ASYNC_HARDPPS_CD) &&
 			    (status & UART_MSR_DCD))
 				hardpps();
 #endif
 		}
-		if (status & UART_MSR_DCTS)
+		if (status & UART_MSR_DCTS) {
 			icount->cts++;
-		wake_up_interruptible(&info->delta_msr_wait);
+			info->wakeup_reason |= TIOCMIWAIT_CTS;
+                }
 	}
 
 	if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) {
@@ -616,6 +637,9 @@
 		if (status & UART_LSR_THRE)
 			transmit_chars(info, 0);
 
+                /* on any int wake up a waiting task - if one exists */
+		wake_up_interruptible(&info->delta_msr_wait);
+
 	next:
 		info = info->next_port;
 		if (!info) {
@@ -686,6 +710,7 @@
 			break;
 		}
 	} while (!(serial_in(info, UART_IIR) & UART_IIR_NO_INT));
+	wake_up_interruptible(&info->delta_msr_wait);
 	info->last_active = jiffies;
 #ifdef CONFIG_SERIAL_MULTIPORT	
 	if (multi->port_monitor)
@@ -743,6 +768,7 @@
 		if (status & UART_LSR_THRE)
 			transmit_chars(info, 0);
 
+                wake_up_interruptible(&info->delta_msr_wait);
 	next:
 		info = info->next_port;
 		if (info)
@@ -1147,6 +1173,7 @@
 	 * clear delta_msr_wait queue to avoid mem leaks: we may free the irq
 	 * here so the queue might never be waken up
 	 */
+	info->wakeup_reason = 0;
 	wake_up_interruptible(&info->delta_msr_wait);
 	
 	/*
@@ -2028,12 +2055,56 @@
 }
 #endif
 
+
+static int wait_for_serial_interrupt(struct async_struct * info, int arg) {
+	int reason;
+	unsigned long flags;
+
+	save_flags(flags); cli();
+	reason = info->wakeup_reason;
+	info->wakeup_reason = 0;
+	restore_flags(flags);
+
+	/* 
+	 * interpret arg==0 as 
+	 * Tell me what interrupts have happened and
+	 * Clear the slate.
+	 */
+	if( (arg==0) || (arg==TIOCMIWAIT_RETFLAGS))
+		return reason;
+
+	/* check that we can wake up */
+	if( !( arg & (TIOCMIWAIT_RNG | TIOCMIWAIT_DSR |
+		      TIOCMIWAIT_DCD | TIOCMIWAIT_CTS |
+		      TIOCMIWAIT_RX  | TIOCMIWAIT_BRK | 
+		      TIOCMIWAIT_TX  | TIOCMIWAIT_ERR ) ) ) {
+		return -EPERM;
+	}
+	info->flags |=ASYNC_CALLOUT_ACTIVE|ASYNC_CALLOUT_NOHUP;
+	while (! (arg & reason)) {
+		interruptible_sleep_on(&info->delta_msr_wait);
+		/* see if a signal did it */
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		save_flags(flags); cli();
+		reason = info->wakeup_reason;
+		info->wakeup_reason = 0;
+		restore_flags(flags);
+	}
+	info->flags &= ~(ASYNC_CALLOUT_ACTIVE|ASYNC_CALLOUT_NOHUP);
+
+	if(arg & TIOCMIWAIT_RETFLAGS)
+		return reason;
+
+	return 0;
+}
+
 static int rs_ioctl(struct tty_struct *tty, struct file * file,
 		    unsigned int cmd, unsigned long arg)
 {
 	int error;
 	struct async_struct * info = (struct async_struct *)tty->driver_data;
-	struct async_icount cprev, cnow;	/* kernel counter temps */
+	struct async_icount cnow;	        /* kernel counter temps */
 	struct serial_icounter_struct *p_cuser;	/* user space */
 	unsigned long flags;
 	
@@ -2086,33 +2157,12 @@
 		 * - mask passed in arg for lines of interest
  		 *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
 		 * Caller should use TIOCGICOUNT to see which one it was
+		 *
+		 * First check to see if anything happened since the last time
+		 * we were here.
 		 */
 		case TIOCMIWAIT:
-			save_flags(flags); cli();
-			/* note the counters on entry */
-			cprev = info->state->icount;
-			restore_flags(flags);
-			while (1) {
-				interruptible_sleep_on(&info->delta_msr_wait);
-				/* see if a signal did it */
-				if (signal_pending(current))
-					return -ERESTARTSYS;
-				save_flags(flags); cli();
-				cnow = info->state->icount; /* atomic copy */
-				restore_flags(flags);
-				if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && 
-				    cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
-					return -EIO; /* no change => error */
-				if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
-				     ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
-				     ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd)) ||
-				     ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
-					return 0;
-				}
-				cprev = cnow;
-			}
-			/* NOTREACHED */
-
+			return wait_for_serial_interrupt(info,arg);
 		/* 
 		 * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
 		 * Return: write counters to the user passed counter struct
