--- /home/sapan/devel/uml/linux/include/asm-um/irq.h	Tue Mar 19 18:52:32 2002
+++ /tmp/work/um/linux/include/asm-um/irq.h	Sat Mar 16 19:33:21 2002
@@ -24,7 +24,7 @@
 #define LAST_IRQ MCONSOLE_IRQ
 #define NR_IRQS (LAST_IRQ + 1)
 
-extern int um_request_irq(unsigned int irq, int fd,
+extern int um_request_irq(unsigned int irq, int fd, int input, int output,
 			  void (*handler)(int, void *, struct pt_regs *),
 			  unsigned long irqflags,  const char * devname,
 			  void *dev_id);
--- /home/sapan/devel/uml/linux/arch/um/drivers/stdio_console.h	Tue Mar 19 18:52:31 2002
+++ /tmp/work/um/linux/arch/um/drivers/stdio_console.h	Wed Mar 20 03:02:04 2002
@@ -5,8 +5,10 @@
 
 #ifndef __STDIO_CONSOLE_H
 #define __STDIO_CONSOLE_H
+#define STDIO_BUFFER_SIZE 4096
 
 extern void save_console_flags(void);
+
 #endif
 
 /*
--- /home/sapan/devel/uml/linux/arch/um/include/chan_kern.h	Tue Mar 19 18:52:31 2002
+++ /tmp/work/um/linux/arch/um/include/chan_kern.h	Sun Mar 17 23:28:06 2002
@@ -32,8 +32,8 @@
 extern void close_chan(struct list_head *chans);
 extern void enable_chan(struct list_head *chans, 
 			int (*irq_setup)(int fd, int input, int output, 
-					 void *data), 
-			void *data);
+					 void *read_data, void *write_data), 
+			void *read_data, void *write_data);
 extern void disable_chan(struct list_head *chans);
 extern int chan_window_size(struct list_head *chans, 
 			     unsigned short *rows_out, 
--- /home/sapan/devel/uml/linux/arch/um/include/irq_user.h	Tue Mar 19 18:52:31 2002
+++ /tmp/work/um/linux/arch/um/include/irq_user.h	Tue Mar 19 05:23:14 2002
@@ -7,13 +7,15 @@
 #define __IRQ_USER_H__
 
 extern void sigio_handler(int sig, struct uml_pt_regs *regs);
-extern int activate_fd(int irq, int fd, void *dev_id);
+extern int activate_fd(int irq, int fd, int input, int output, void *dev_id);
 extern void free_irq_by_dev(void *dev_id);
 extern void free_irq_by_fd(int fd);
 extern void reactivate_fd(int fd);
 extern void forward_interrupts(int pid);
 extern void init_irq_signals(int on_sigstack);
 extern void forward_ipi(int fd, int pid);
+extern void flip_write_fd(int fd);
+extern void flop_write_fd(int fd);
 
 #endif
 
--- /home/sapan/devel/uml/linux/arch/um/include/line.h	Tue Mar 19 18:52:32 2002
+++ /tmp/work/um/linux/arch/um/include/line.h	Mon Mar 18 16:50:09 2002
@@ -6,6 +6,7 @@
 #ifndef __LINE_H__
 #define __LINE_H__
 
+#include "async.h"
 #include "linux/list.h"
 #include "asm/semaphore.h"
 #include "chan_user.h"
@@ -18,6 +19,7 @@
 	struct tty_struct *tty;
 	struct semaphore sem;
 	int initialized;
+	struct async_struct *async;
 };
 
 #define LINE_INIT(str) \
@@ -26,6 +28,7 @@
 	  chan_list : 	{ }, \
 	  count :	0, \
 	  tty :	NULL, \
+	  async : NULL, \
 	  sem : { } }
 
 struct winch_lines {
@@ -36,9 +39,10 @@
 
 extern void line_interrupt(int irq, void *data, struct pt_regs *unused);
 extern void line_close(struct line *lines, int n);
-extern int line_open(struct line *lines, int n, struct tty_struct *tty, 
+extern int line_open(struct line *lines, int n, struct tty_struct *tty,
+		struct async_struct *async,
 		     int (*setup_irq)(int fd, int input, int output, 
-				      void *data),
+				      void *read_data, void *write_data),
 		     struct chan_opts *opts);
 extern void line_setup(struct line *lines, int num, char *init);
 extern void run_winch_handlers(void);
--- /home/sapan/devel/uml/linux/drivers/char/serial.c	Tue Feb 26 01:07:57 2002
+++ /tmp/work/um/linux/drivers/char/serial.c	Sun Mar 17 17:16:18 2002
@@ -3104,7 +3104,9 @@
 		sstate->count--;
 		return -ENOMEM;
 	}
+
 	memset(info, 0, sizeof(struct async_struct));
+
 	init_waitqueue_head(&info->open_wait);
 	init_waitqueue_head(&info->close_wait);
 	init_waitqueue_head(&info->delta_msr_wait);
--- /home/sapan/devel/uml/linux/arch/um/drivers/chan_kern.c	Tue Mar 19 18:52:31 2002
+++ /tmp/work/um/linux/arch/um/drivers/chan_kern.c	Wed Mar 20 03:10:14 2002
@@ -1,6 +1,10 @@
 /* 
  * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
  * Licensed under the GPL
+ * 
+ * Changelog: 
+ *            15.3.2002        Sapan Bhatia            Changes in write_chan
+ *			       <sapan@corewars.org>    for write interrupts
  */
 
 #include <linux/stddef.h>
@@ -63,18 +67,28 @@
 	return(err);
 }
 
-void enable_chan(struct list_head *chans, 
-		 int (*irq_setup)(int fd, int input, int output, void *data), 
-		 void *data)
+void enable_chan(struct list_head *chans,
+		 int (*irq_setup)(int fd, int input, int output,
+			 void *read_data, void *write_data),
+		 void *read_data, void *write_data)
 {
 	struct list_head *ele;
 	struct chan *chan;
-
+	
 	list_for_each(ele, chans){
 		chan = list_entry(ele, struct chan, list);
 		if(!chan->opened) continue;
 
-		(*irq_setup)(chan->fd, chan->input, chan->output, data);
+		/* The chan->input and chan->output values are a bit
+		 * unpredictable (irq_setup gets called with an 
+		 * arbitrary long in ->output for the initial console, so
+		 * I'm hardcoding 0 and 1 here, I guess if this 
+		 * is fixed (or I'm wrong :) then they may be
+		 * reset. Also, the write handlers won't work
+		 * unless enabled with flip_write_fd, so no harm done -SB */
+
+		(*irq_setup)(chan->fd, /*chan->input*/1, 
+			     /*chan->output*/1, read_data, write_data);
 	}
 }
 
@@ -119,6 +133,21 @@
 		chan = list_entry(ele, struct chan, list);
 		if(!chan->output) continue;
 		n = chan->ops->write(chan->fd, buf, len, chan->data);
+
+		
+		/* This check is correct. The write handler is not
+		 * enabled only on EAGAIN but when partial data is 
+		 * written, since the line discipline goes to sleep
+		 * on the write wait queue when this happens -SB
+		 */
+
+		if ((n < len) | (n == 0))
+			flip_write_fd(chan->fd);
+		else
+		if (n == len) {
+			flop_write_fd(chan->fd);
+		}
+
 		if(chan->primary) ret = n;
 	}
 	return(ret);
--- /home/sapan/devel/uml/linux/arch/um/drivers/line.c	Tue Mar 19 18:52:31 2002
+++ /tmp/work/um/linux/arch/um/drivers/line.c	Wed Mar 20 03:05:44 2002
@@ -1,6 +1,10 @@
 /* 
  * Copyright (C) 2001 Jeff Dike (jdike@karaya.com)
  * Licensed under the GPL
+ *
+ * Changelog:  
+ *         15.3.2002   Sapan Bhatia   		Added async
+ *                     <sapan@corewars.org>     struct initialization       
  */
 
 #include "linux/sched.h"
@@ -14,9 +18,10 @@
 	if(dev->count > 0) chan_interrupt(&dev->chan_list, dev->tty);
 }
 
-int line_open(struct line *lines, int n, struct tty_struct *tty, 
-	      int (*setup_irq)(int fd, int input, int output, void *data),
-	      struct chan_opts *opts)
+int line_open(struct line *lines, int n, struct tty_struct *tty,
+		struct async_struct *async, int (*setup_irq)
+		(int fd, int input, int output, 
+		 void *read_data, void *write_data), struct chan_opts *opts)
 {
 	struct line *line = &lines[n];
 	int err = 0;
@@ -24,18 +29,24 @@
 	down(&line->sem);
 	if(line->count == 0){
 		if(list_empty(&line->chan_list)){
-			err = parse_chan_pair(line->init_str, &line->chan_list,
-					      line->init_pri, n, opts);
+			err = parse_chan_pair(line->init_str, 
+					&line->chan_list,
+					line->init_pri, n, opts);
 			if(err) goto out;
 			err = open_chan(&line->chan_list);
 			if(err) goto out;
 		}
-		enable_chan(&line->chan_list, setup_irq, line);
+		enable_chan(&line->chan_list, setup_irq, line, async);
 	}
 	/* This is outside the if because the initial console is opened
 	 * with tty == NULL
 	 */
 	line->tty = tty;
+	if (async) {
+		async->tty = tty;
+		line->async = async;
+	}
+
 	if(tty != NULL) tty->driver_data = line;
 
 	line->count++;
--- /home/sapan/devel/uml/linux/arch/um/drivers/mconsole_kern.c	Tue Mar 19 18:52:31 2002
+++ /tmp/work/um/linux/arch/um/drivers/mconsole_kern.c	Sun Mar 17 20:44:31 2002
@@ -214,7 +214,7 @@
 
 	register_reboot_notifier(&reboot_notifier);
 
-	err = um_request_irq(MCONSOLE_IRQ, sock, mconsole_interrupt,
+	err = um_request_irq(MCONSOLE_IRQ, sock, 1, 0, mconsole_interrupt,
 			     SA_INTERRUPT | SA_SHIRQ, "mconsole",
 			     (void *)sock);
 	if (err) {
--- /home/sapan/devel/uml/linux/arch/um/drivers/net_kern.c	Tue Mar 19 18:52:31 2002
+++ /tmp/work/um/linux/arch/um/drivers/net_kern.c	Sun Mar 17 20:43:01 2002
@@ -234,7 +234,7 @@
 		goto out;
 	}
 
-	err = um_request_irq(dev->irq, lp->fd, uml_net_interrupt,
+	err = um_request_irq(dev->irq, lp->fd, 1, 0, uml_net_interrupt,
 			     SA_INTERRUPT | SA_SHIRQ, dev->name, dev);
 	if(err != 0){
 		printk(KERN_ERR "uml_net_open: failed to get irq(%d)\n", err);
--- /home/sapan/devel/uml/linux/arch/um/drivers/port_kern.c	Tue Mar 19 18:52:31 2002
+++ /tmp/work/um/linux/arch/um/drivers/port_kern.c	Sun Mar 17 20:50:37 2002
@@ -84,7 +84,7 @@
 		       port_num, -fd);
 		return(NULL);
 	}
-	if(um_request_irq(ACCEPT_IRQ, fd, port_interrupt, 
+	if(um_request_irq(ACCEPT_IRQ, fd, 1, 0, port_interrupt, 
 			  SA_INTERRUPT | SA_SHIRQ, "port", port)){
 		printk(KERN_ERR "Failed to get IRQ for port %d\n", port_num);
 		return(NULL);
--- /home/sapan/devel/uml/linux/arch/um/drivers/ssl.c	Tue Mar 19 18:52:31 2002
+++ /tmp/work/um/linux/arch/um/drivers/ssl.c	Sun Mar 17 20:52:10 2002
@@ -49,10 +49,11 @@
 static struct line serial_lines[NR_PORTS] = 
 	{ [0 ... NR_PORTS - 1] = LINE_INIT(CONFIG_SSL_CHAN) };
 
-static int setup_ssl_irq(int fd, int input, int output, void *data)
+static int setup_ssl_irq(int fd, int input, int output, void *read_data,
+		void *write_data)
 {
-	return(um_request_irq(SSL_IRQ, fd, line_interrupt, 
-			      SA_INTERRUPT | SA_SHIRQ, "ssl", data));
+	return(um_request_irq(SSL_IRQ, fd, 1, 0, line_interrupt, 
+			      SA_INTERRUPT | SA_SHIRQ, "ssl", read_data));
 }
 
 int ssl_open(struct tty_struct *tty, struct file *filp)
@@ -62,7 +63,7 @@
 	line = minor(tty->device) - tty->driver.minor_start;
 	if ((line < 0) || (line >= NR_PORTS))
 		return -ENODEV;
-	return(line_open(serial_lines, line, tty, setup_ssl_irq, &opts));
+	return(line_open(serial_lines, line, tty, NULL, setup_ssl_irq, &opts));
 }
 
 static void ssl_close(struct tty_struct *tty, struct file * filp)
--- /home/sapan/devel/uml/linux/arch/um/drivers/stdio_console.c	Tue Mar 19 18:52:31 2002
+++ /tmp/work/um/linux/arch/um/drivers/stdio_console.c	Wed Mar 20 03:10:19 2002
@@ -1,6 +1,10 @@
 /* 
  * Copyright (C) 2000, 2001 Jeff Dike (jdike@karaya.com)
  * Licensed under the GPL
+ *
+ * Changelog:	15.3.2002      Sapan Bhatia           Added write interrupt
+ *			       sapan@corewars.org     handlers, bufferred
+ *			                              IO
  */
 
 #include "linux/config.h"
@@ -17,10 +21,14 @@
 #include "linux/init.h"
 #include "linux/interrupt.h"
 #include "linux/devfs_fs_kernel.h"
+#include "linux/circ_buf.h"
+#include "linux/spinlock.h"
+#include "linux/mm.h"
 #include "asm/current.h"
 #include "asm/softirq.h"
 #include "asm/hardirq.h"
 #include "stdio_console.h"
+#include "async.h"
 #include "line.h"
 #include "chan_kern.h"
 #include "user_util.h"
@@ -36,6 +44,9 @@
 static struct termios *console_termios_locked[MAX_TTYS];
 static int console_refcount;
 
+struct async_struct *async_ptr; // For passing an async operation into a BH
+static DECLARE_MUTEX(xmit_sem);
+
 static struct chan_ops init_console_ops = {
 	init : 		NULL,
 	open : 		NULL,
@@ -75,23 +86,111 @@
 			      [ 1 ... MAX_TTYS - 1 ] = 
 			      LINE_INIT(CONFIG_CON_CHAN) };
 
-static int setup_console_irq(int fd, int input, int output, void *data)
+static void write_bh(unsigned long write_data)
 {
-	return(um_request_irq(CONSOLE_IRQ, fd, line_interrupt, 
-			      SA_INTERRUPT | SA_SHIRQ, "console", data));
+	struct async_struct *async = (struct async_struct *) write_data;
+	struct tty_struct *tty = async->tty;
+	int c, line;
+	int count=0;
+	int ret;
+
+	if (!tty)
+		return;
+
+	line = async->line;
+
+	spin_lock(&async->xmit_lock);
+        while (1) {
+				c = CIRC_CNT_TO_END(async->xmit.head,
+					      async->xmit.tail,
+					      STDIO_BUFFER_SIZE);
+				if (!c)
+					break;
+				memcpy(async->buf + count, async->xmit.buf + 
+						async->xmit.tail, c);
+				async->xmit.tail = ((async->xmit.tail + c) & 
+						STDIO_BUFFER_SIZE - 1);
+				count += c;
+				
+			}
+
+
+	ret = write_chan(&vts[line].chan_list, async->buf, count);
+	spin_unlock(&async->xmit_lock);
+
+	if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags) &&
+			tty->ldisc.write_wakeup)
+		(tty->ldisc.write_wakeup)(tty);
+	
+	wake_up_interruptible(&tty->write_wait);
+}
+
+DECLARE_TASKLET(write_bh_task, write_bh, 0);
+
+static void write_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct async_struct *async = (struct async_struct *) dev_id;
+	async_ptr = async;
+
+	if (!async->tty)
+		return;
+
+	if (async->xmit.head == async->xmit.tail || 
+			async->tty->stopped ||
+			async->tty->hw_stopped) {
+		return;
+	}
+	write_bh_task.data = (unsigned long) async_ptr;
+	tasklet_schedule(&write_bh_task);
+}
+
+static int setup_console_irq(int fd, int input, 
+		int output, void *read_data, void *write_data)
+{
+	int ret=-1;
+
+	if (input) {
+		ret = um_request_irq(CONSOLE_IRQ, fd, input, 0, 
+				line_interrupt,
+				SA_INTERRUPT | SA_SHIRQ, 
+				"console_r", read_data);
+	
+		if (ret) return(ret);
+	}
+
+	if (output) {
+		ret = um_request_irq(CONSOLE_IRQ, fd, 0, output, 
+				write_interrupt,
+				SA_INTERRUPT | SA_SHIRQ, 
+				"console_w", write_data);
+		if (ret) {
+			return (ret);
+		}
+	}
+
+	return(ret);
 }
 
-static int open_console(int line, struct tty_struct *tty)
+static int open_console(int line, struct tty_struct *tty,
+		struct async_struct *async)
 {
-	return(line_open(vts, line, tty, setup_console_irq, &opts));
+	return(line_open(vts, line, tty, async, setup_console_irq, &opts));
 }
 
 static int con_open(struct tty_struct * tty, struct file * filp)
 {
 	int line, ret;
+	struct async_struct *async;
 
 	line = minor(tty->device) - tty->driver.minor_start;
-	ret = open_console(line, tty);
+	if (vts[line].async)
+		async=vts[line].async;
+	else {
+		ret = setup_async(line, &async, tty);
+                if (ret) return ret;
+	}
+	
+	ret = open_console(line, tty, async);
 	chan_window_size(&vts[line].chan_list, &tty->winsize.ws_row, 
 			 &tty->winsize.ws_col);	
 	return(ret);
@@ -105,20 +204,82 @@
 static int con_write(struct tty_struct * tty, int from_user, 
 		     const unsigned char *buf, int count)
 {
-	int line;
+	int line, ret, c;
+	unsigned long flags;
+	struct line *cur_line = (struct line *) tty->driver_data;
+	struct async_struct *async = (struct async_struct *) cur_line->async;
+	int balance;
 
 	if(in_interrupt() && tty->stopped) return 0;
 	while(tty->stopped) schedule();
 
 	line = minor(tty->device) - tty->driver.minor_start;
-	return(write_chan(&vts[line].chan_list, buf, count));
+	ret = write_chan(&vts[line].chan_list, buf, count);
+	
+	balance = (ret==-1) ? count : count - ret;
+
+	if (balance) {
+	if (from_user)
+	{
+		save_flags(flags);
+		if (from_user) {
+			down(&xmit_sem); // semaphore for xmit
+			while (balance) {
+				c = CIRC_SPACE_TO_END(async->xmit.head,
+					      async->xmit.tail,
+					      STDIO_BUFFER_SIZE);
+				if (!c)
+					break;
+				if (balance < c)
+					c = balance;
+				cli();
+				memcpy(async->xmit.buf + async->xmit.head, 
+						buf, c);
+				async->xmit.head = ((async->xmit.head + c) &
+					   (STDIO_BUFFER_SIZE - 1));
+				restore_flags(flags);
+				buf += c;
+				balance -= c;
+				ret += c;
+			}
+		}
+		up(&xmit_sem);
+	} else {
+		cli();
+		while (balance) {
+			c = CIRC_SPACE_TO_END(async->xmit.head,
+					      async->xmit.tail,
+					      STDIO_BUFFER_SIZE);
+			if (!c)
+				break;
+			if (balance < c)
+				c = balance;
+			memcpy(async->xmit.buf + async->xmit.head, buf, c);
+			async->xmit.head = ((async->xmit.head + c) &
+					   (STDIO_BUFFER_SIZE - 1));
+			buf += c;
+			balance -= c;
+			ret += c;
+		}
+		restore_flags(flags);
+	}
+	}
+
+	return(ret);
 }
 
 static int write_room(struct tty_struct *tty)
 {
-	return(1024);
+	struct line *line = (struct line *) tty->driver_data;
+	struct async_struct *async = (struct async_struct *) line->async;
+	return CIRC_SPACE(async->xmit.head, async->xmit.tail, STDIO_BUFFER_SIZE);
 }
 
+
+
+
+
+
 static void set_termios(struct tty_struct *tty, struct termios * old)
 {
 }
@@ -137,6 +298,7 @@
 int stdio_init(void)
 {
 	int i, err;
+	struct async_struct *async;
 
 	printk(KERN_INFO "Initializing stdio console driver\n");
 	memset(&console_driver, 0, sizeof(struct tty_driver));
@@ -189,7 +351,12 @@
 		sema_init(&vts[i].sem, 1);
 	}
 	
-	open_console(0, NULL);
+
+	err = setup_async(0, &async, NULL);
+	if (err)
+		return(err);
+
+	open_console(0, NULL, async);
         register_winch(&winch);
 	return(0);
 }
--- /home/sapan/devel/uml/linux/arch/um/drivers/ubd.c	Tue Mar 19 18:52:31 2002
+++ /tmp/work/um/linux/arch/um/drivers/ubd.c	Sun Mar 17 20:43:16 2002
@@ -491,8 +491,8 @@
 		       "falling back to synchronous I/O\n", -io_pid);
 		return(0);
 	}
-	err = um_request_irq(UBD_IRQ, thread_fd, ubd_intr, SA_INTERRUPT, 
-			     "ubd", ubd_dev);
+	err = um_request_irq(UBD_IRQ, thread_fd, 1, 0, ubd_intr, 
+			SA_INTERRUPT, "ubd", ubd_dev);
 	if(err != 0) printk(KERN_ERR 
 			    "um_request_irq failed - errno = %d\n", -err);
 	return(err);
--- /home/sapan/devel/uml/linux/arch/um/kernel/irq.c	Tue Mar 19 18:52:32 2002
+++ /tmp/work/um/linux/arch/um/kernel/irq.c	Sat Mar 16 19:33:34 2002
@@ -413,7 +413,7 @@
 	return retval;
 }
 
-int um_request_irq(unsigned int irq, int fd,
+int um_request_irq(unsigned int irq, int fd, int input, int output,
 		   void (*handler)(int, void *, struct pt_regs *),
 		   unsigned long irqflags,  const char * devname,
 		   void *dev_id)
@@ -422,7 +422,7 @@
 
 	retval = request_irq(irq, handler, irqflags, devname, dev_id);
 	if(retval) return(retval);
-	return(activate_fd(irq, fd, dev_id));
+	return(activate_fd(irq, fd, input, output, dev_id));
 }
 
 /* this was setup_x86_irq but it seems pretty generic */
--- /home/sapan/devel/uml/linux/arch/um/kernel/irq_user.c	Tue Mar 19 18:52:32 2002
+++ /tmp/work/um/linux/arch/um/kernel/irq_user.c	Wed Mar 20 03:10:26 2002
@@ -1,6 +1,11 @@
 /* 
  * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
  * Licensed under the GPL
+ * 
+ * Changelog:
+ *	12-03-02      Sapan Bhatia    		Added functionality
+ *	              sapan@corewars.org        for write interrupts 
+ *						using SIGIO POLLOUT
  */
 
 #include <stdlib.h>
@@ -26,6 +31,7 @@
 	int pid;
 	int events;
 	int current_events;
+	int saved_events;
 };
 
 static struct irq_fd *active_fds = NULL;
@@ -41,7 +47,7 @@
 {
 	struct irq_fd *irq_fd, *next;
 	int i, n;
-
+	
 	if(smp_sigio_handler()) return;
 	while(1){
 		if((n = poll(pollfds, pollfds_num, 0)) < 0){
@@ -56,6 +62,7 @@
 		for(i = 0; i < pollfds_num; i++){
 			if(pollfds[i].revents != 0){
 				irq_fd->current_events = pollfds[i].revents;
+				irq_fd->saved_events = pollfds[i].events;
 				pollfds[i].events = 0;
 			}
 			irq_fd = irq_fd->next;
@@ -66,6 +73,12 @@
 			 * the irq handler freeing the irq from under us.
 			 */
 			next = irq_fd->next;
+
+		/* XXX - should change to current_events & info passed
+		 * in signal handler- although flip/flop_write_fd slash
+		 * off most of the cases in which the IRQ's generated
+		 * without any reason -SB*/
+
 			if(irq_fd->current_events != 0){
 				irq_fd->current_events = 0;
 				do_IRQ(irq_fd->irq, regs);
@@ -99,18 +112,37 @@
 	return prepare_fd_async(fd, pid);
 }
 
-int activate_fd(int irq, int fd, void *dev_id)
+int activate_fd(int irq, int fd, int input, int output, void *dev_id)
 {
 	struct irq_fd *new_fd;
-	int pid, retval, events = POLLIN | POLLPRI;
+	int pid, retval;
+	int events=POLLPRI | (input ? POLLIN : 0) | (output ? POLLOUT : 0);
+	int i=0;
 
 	for(new_fd = active_fds;new_fd;new_fd = new_fd->next){
-		if(new_fd->fd == fd){
-			printk("Registering fd %d twice\n", fd);
-			printk("Irqs : %d, %d\n", new_fd->irq, irq);
-			printk("Ids : 0x%x, 0x%x\n", new_fd->id, dev_id);
-			return(-EIO);
+		if(new_fd->fd == fd) {
+			if (new_fd->events == events){
+				printk("Registering fd %d is already \
+				registered for input: %d and \
+					output: %d\n", fd, input, output);
+				printk("Irqs : %d, %d\n", new_fd->irq, irq);
+				printk("Ids : 0x%x, 0x%x\n", new_fd->id, 
+						dev_id);
+				return(-EIO);
+			}
+			else { 	
+				/* use the same irq_fd, since all 
+				 * the handlers are called anyway */
+				new_fd->events |= events;
+
+				/* we don't want to poll for POLLOUTS
+				 * every time a POLLIN comes in because
+				 * they'll always be there waiting */
+				pollfds[i].events = new_fd->events & ~POLLOUT;
+				return (0);
+			}
 		}
+		i++; 	
 	}
 	pid = cpu_tasks[0].pid;
 	if ((retval = prepare_fd_async(fd, pid)) != 0)
@@ -146,7 +178,7 @@
 	last_irq_ptr = &new_fd->next;
 
 	pollfds[pollfds_num - 1].fd = fd;
-	pollfds[pollfds_num - 1].events  = events;
+	pollfds[pollfds_num - 1].events  = events & ~POLLOUT;
 	pollfds[pollfds_num - 1].revents = 0;
 	return(0);
 }
@@ -223,6 +255,37 @@
 	return(irq);
 }
 
+/* The two functions below are to enable and disable an fd for
+ * POLLOUT. This is essential since a POLLOUT is something you
+ * invariably get when you poll an fd irrespective of what
+ * the interrupt was
+ */
+
+void flip_write_fd(int fd)
+{
+	struct irq_fd *irq;
+	int i;
+	irq = find_irq_by_fd(fd, &i);
+	if (irq == NULL) return;
+	pollfds[i].events |= (irq->events & POLLOUT);
+}
+
+void flop_write_fd(int fd)
+{
+	struct irq_fd *irq;
+	int i;
+	irq = find_irq_by_fd(fd, &i);
+	if (irq == NULL) return;
+	pollfds[i].events &= ~POLLOUT;
+}
+
+/* Incoming interrupts disable both the read as well as write handler.
+ * This is essential in the present config since both handlers are 
+ * called every time, and irrespective of what the interrupt is, if 
+ * it comes in while something's being handled, it'll get read stuck
+ * in an infinite loop
+ */
+
 void reactivate_fd(int fd)
 {
 	struct irq_fd *irq;
@@ -230,7 +293,7 @@
 
 	irq = find_irq_by_fd(fd, &i);
 	if(irq == NULL) return;
-	pollfds[i].events = irq->events;
+	pollfds[i].events = irq->saved_events;
 }
 
 void forward_ipi(int fd, int pid)
--- /home/sapan/devel/uml/linux/arch/um/drivers/async.c	Wed Mar 20 03:38:56 2002
+++ /tmp/work/um/linux/arch/um/drivers/async.c	Wed Mar 20 02:59:03 2002
@@ -0,0 +1,36 @@
+/* 
+ * Created     		13/3/2002       by Sapan Bhatia <sapan@corewars.org
+ *
+ * Changelog:
+ *
+ */
+
+#include "async.h"
+#include "linux/slab.h"
+
+int setup_async(int line, struct async_struct **async, 
+		struct tty_struct *tty)
+{
+	struct async_struct *ret_async;
+	char *buf;
+
+	ret_async = kmalloc(sizeof(struct async_struct), GFP_KERNEL);
+	if (!ret_async)
+		return -ENOMEM;
+	buf = (char *) kmalloc(4096, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+	memset(ret_async, 0, sizeof(struct async_struct));
+	ret_async->xmit.buf = buf;
+        buf = (char *) kmalloc(4096, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	ret_async->buf = buf;
+	ret_async->line = line;
+	ret_async->tty = tty;
+	ret_async->xmit.head = ret_async->xmit.tail = 0;
+	spin_lock_init(&ret_async->xmit_lock);
+	*async = ret_async;
+	return 0;
+}
--- /home/sapan/devel/uml/linux/arch/um/include/async.h	Wed Mar 20 03:39:02 2002
+++ /tmp/work/um/linux/arch/um/include/async.h	Wed Mar 20 02:59:33 2002
@@ -0,0 +1,26 @@
+/* 
+ * Created:     13.3.2002     by Sapan Bhatia <sapan@corewars.org>
+ * Changelog:
+ *
+ */
+
+#ifndef __ASYNC_H__
+#define __ASYNC_H__
+
+#include "linux/circ_buf.h"
+#include "linux/spinlock.h"
+#include "asm/semaphore.h"
+
+struct async_struct {
+	int line;
+	struct tty_struct *tty;
+	struct circ_buf xmit;
+	spinlock_t xmit_lock;
+	unsigned char *buf;
+	struct semaphore buf_sem;
+};
+
+extern int setup_async(int line, struct async_struct **async, 
+		struct tty_struct *tty);
+
+#endif
