Index: kernel/genarch/src/drivers/via-cuda/cuda.c
===================================================================
--- kernel/genarch/src/drivers/via-cuda/cuda.c	(revision 80a3bd946652bcf9cf7b6469e7be56639abcc853)
+++ kernel/genarch/src/drivers/via-cuda/cuda.c	(revision ec8b0dac0a89d85fb1256e7e6a702141cc630506)
@@ -42,5 +42,8 @@
 #include <synch/spinlock.h>
 
-static void cuda_packet_handle(cuda_instance_t *instance, size_t len);
+static void cuda_packet_handle(cuda_instance_t *instance, uint8_t *buf, size_t len);
+static void cuda_irq_listen(irq_t *irq);
+static void cuda_irq_receive(irq_t *irq);
+static void cuda_irq_rcv_end(irq_t *irq, void *buf, size_t *len);
 
 /** B register fields */
@@ -53,8 +56,17 @@
 /** IER register fields */
 enum {
+	IER_CLR	= 0x00,
 	IER_SET	= 0x80,
-	SR_INT	= 0x04
+
+	SR_INT	= 0x04,
+	ALL_INT	= 0x7f
 };
 
+/** ACR register fields */
+enum {
+	SR_OUT	= 0x10
+};
+
+#include <print.h>
 static irq_ownership_t cuda_claim(irq_t *irq)
 {
@@ -65,8 +77,8 @@
 	ifr = pio_read_8(&dev->ifr);
 
-	if ((ifr & SR_INT) != 0)
-		return IRQ_ACCEPT;
-	else
+	if ((ifr & SR_INT) == 0)
 		return IRQ_DECLINE;
+
+	return IRQ_ACCEPT;
 }
 
@@ -74,44 +86,108 @@
 {
 	cuda_instance_t *instance = irq->instance;
+	uint8_t rbuf[CUDA_RCV_BUF_SIZE];
+	size_t len;
+	bool handle;
+
+	handle = false;
+	len = 0;
+
+	spinlock_lock(&instance->dev_lock);
+
+	/* Lower IFR.SR_INT so that CUDA can generate next int by raising it. */
+	pio_write_8(&instance->cuda->ifr, SR_INT);
+
+	switch (instance->xstate) {
+	case cx_listen: cuda_irq_listen(irq); break;
+	case cx_receive: cuda_irq_receive(irq); break;
+	case cx_rcv_end: cuda_irq_rcv_end(irq, rbuf, &len);
+	    handle = true; break;
+	}
+
+	spinlock_unlock(&instance->dev_lock);
+
+	/* Handle an incoming packet. */
+	if (handle)
+		cuda_packet_handle(instance, rbuf, len);
+}
+
+/** Interrupt in listen state.
+ *
+ * Start packet reception.
+ */
+static void cuda_irq_listen(irq_t *irq)
+{
+	cuda_instance_t *instance = irq->instance;
+	cuda_t *dev = instance->cuda;
+	uint8_t b;
+
+	b = pio_read_8(&dev->b);
+
+	if ((b & TREQ) != 0) {
+		printf("cuda_irq_listen: no TREQ?!\n");
+		return;
+	}
+
+	pio_read_8(&dev->sr);
+	pio_write_8(&dev->b, pio_read_8(&dev->b) & ~TIP);
+	instance->xstate = cx_receive;
+}
+
+/** Interrupt in receive state.
+ *
+ * Receive next byte of packet.
+ */
+static void cuda_irq_receive(irq_t *irq)
+{
+	cuda_instance_t *instance = irq->instance;
 	cuda_t *dev = instance->cuda;
 	uint8_t b, data;
-	size_t pos;
-
-	spinlock_lock(&instance->dev_lock);
-
-	/* We have received one or more CUDA packets. Process them all. */
-	while (true) {
-		b = pio_read_8(&dev->b);
-
-		if ((b & TREQ) != 0)
-			break;	/* No data */
-
+
+	data = pio_read_8(&dev->sr);
+	if (instance->bidx < CUDA_RCV_BUF_SIZE)
+		instance->rcv_buf[instance->bidx++] = data;
+
+	b = pio_read_8(&dev->b);
+
+	if ((b & TREQ) == 0) {
+		pio_write_8(&dev->b, b ^ TACK);
+	} else {
+		pio_write_8(&dev->b, b | TACK | TIP);
+		instance->xstate = cx_rcv_end;
+	}
+}
+
+/** Interrupt in rcv_end state.
+ *
+ * Terminate packet reception. Either go back to listen state or start
+ * receiving another packet if CUDA has one for us.
+ */
+static void cuda_irq_rcv_end(irq_t *irq, void *buf, size_t *len)
+{
+	cuda_instance_t *instance = irq->instance;
+	cuda_t *dev = instance->cuda;
+	uint8_t data, b;
+
+	b = pio_read_8(&dev->b);
+	data = pio_read_8(&dev->sr);
+
+	instance->xstate = cx_listen;
+
+	if ((b & TREQ) == 0) {
+		instance->xstate = cx_receive;
 		pio_write_8(&dev->b, b & ~TIP);
-
-		/* Read one packet. */
-
-		pos = 0;
-		do {
-			data = pio_read_8(&dev->sr);
-			b = pio_read_8(&dev->b);
-			pio_write_8(&dev->b, b ^ TACK);
-
-			if (pos < CUDA_RCV_BUF_SIZE)
-				instance->rcv_buf[pos++] = data;
-		} while ((b & TREQ) == 0);
-
-		pio_write_8(&dev->b, b | TACK | TIP);
-
-		cuda_packet_handle(instance, pos);
-	}
-
-	spinlock_unlock(&instance->dev_lock);
-}
-
-static void cuda_packet_handle(cuda_instance_t *instance, size_t len)
-{
-	uint8_t *data = instance->rcv_buf;
-
-	if (data[0] != 0x00 || data[1] != 0x40 || data[2] != 0x2c)
+	} else {
+		instance->xstate = cx_listen;
+	}
+
+        memcpy(buf, instance->rcv_buf, instance->bidx);
+        *len = instance->bidx;
+	instance->bidx = 0;
+}
+
+static void cuda_packet_handle(cuda_instance_t *instance, uint8_t *data, size_t len)
+{
+	if (data[0] != 0x00 || data[1] != 0x40 || (data[2] != 0x2c
+		&& data[2] != 0x8c))
 		return;
 
@@ -130,6 +206,11 @@
 		instance->cuda = dev;
 		instance->kbrdin = NULL;
+		instance->xstate = cx_listen;
+		instance->bidx = 0;
 
 		spinlock_initialize(&instance->dev_lock, "cuda_dev");
+
+		/* Disable all interrupts from CUDA. */
+		pio_write_8(&dev->ier, IER_CLR | ALL_INT);
 
 		irq_initialize(&instance->irq);
@@ -141,4 +222,5 @@
 		instance->irq.cir = cir;
 		instance->irq.cir_arg = cir_arg;
+		instance->irq.preack = true;
 	}
 	
@@ -148,4 +230,6 @@
 void cuda_wire(cuda_instance_t *instance, indev_t *kbrdin)
 {
+	cuda_t *dev = instance->cuda;
+
 	ASSERT(instance);
 	ASSERT(kbrdin);
@@ -155,5 +239,6 @@
 
 	/* Enable SR interrupt. */
-	pio_write_8(&instance->cuda->ier, IER_SET | SR_INT);
+	pio_write_8(&dev->ier, TIP | TREQ);
+	pio_write_8(&dev->ier, IER_SET | SR_INT);
 }
 
