Index: kernel/genarch/src/drivers/via-cuda/cuda.c
===================================================================
--- kernel/genarch/src/drivers/via-cuda/cuda.c	(revision 1f0db02ed7ebf939ec0b8381625ef3129d3539df)
+++ kernel/genarch/src/drivers/via-cuda/cuda.c	(revision 84683fdcff89e57c1a5b8126534bdef325411961)
@@ -42,8 +42,16 @@
 #include <synch/spinlock.h>
 
-static void cuda_packet_handle(cuda_instance_t *instance, uint8_t *buf, size_t len);
+static irq_ownership_t cuda_claim(irq_t *irq);
+static void cuda_irq_handler(irq_t *irq);
+
 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);
+static void cuda_irq_send_start(irq_t *irq);
+static void cuda_irq_send(irq_t *irq);
+
+static void cuda_packet_handle(cuda_instance_t *instance, uint8_t *buf, size_t len);
+static void cuda_send_start(cuda_instance_t *instance);
+static void cuda_autopoll_set(cuda_instance_t *instance, bool enable);
 
 /** B register fields */
@@ -68,134 +76,14 @@
 };
 
-#include <print.h>
-static irq_ownership_t cuda_claim(irq_t *irq)
-{
-	cuda_instance_t *instance = irq->instance;
-	cuda_t *dev = instance->cuda;
-	uint8_t ifr;
-
-	ifr = pio_read_8(&dev->ifr);
-
-	if ((ifr & SR_INT) == 0)
-		return IRQ_DECLINE;
-
-	return IRQ_ACCEPT;
-}
-
-static void cuda_irq_handler(irq_t *irq)
-{
-	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;
-
-	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);
-	} 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;
-
-	/* The packet contains one or two scancodes. */
-	if (data[3] != 0xff)
-		indev_push_character(instance->kbrdin, data[3]);		
-	if (data[4] != 0xff)
-		indev_push_character(instance->kbrdin, data[4]);
-}
+/** Packet types */
+enum {
+	PT_ADB	= 0x00,
+	PT_CUDA	= 0x01
+};
+
+/** CUDA packet types */
+enum {
+	CPT_AUTOPOLL	= 0x01
+};
 
 cuda_instance_t *cuda_init(cuda_t *dev, inr_t inr, cir_t cir, void *cir_arg)
@@ -208,4 +96,5 @@
 		instance->xstate = cx_listen;
 		instance->bidx = 0;
+		instance->snd_bytes = 0;
 
 		spinlock_initialize(&instance->dev_lock, "cuda_dev");
@@ -228,4 +117,5 @@
 }
 
+#include <print.h>
 void cuda_wire(cuda_instance_t *instance, indev_t *kbrdin)
 {
@@ -241,5 +131,229 @@
 	pio_write_8(&dev->ier, TIP | TREQ);
 	pio_write_8(&dev->ier, IER_SET | SR_INT);
-}
+
+	/* Enable ADB autopolling. */
+	cuda_autopoll_set(instance, true);
+}
+
+static irq_ownership_t cuda_claim(irq_t *irq)
+{
+	cuda_instance_t *instance = irq->instance;
+	cuda_t *dev = instance->cuda;
+	uint8_t ifr;
+
+	spinlock_lock(&instance->dev_lock);
+	ifr = pio_read_8(&dev->ifr);
+	spinlock_unlock(&instance->dev_lock);
+
+	if ((ifr & SR_INT) == 0)
+		return IRQ_DECLINE;
+
+	return IRQ_ACCEPT;
+}
+
+static void cuda_irq_handler(irq_t *irq)
+{
+	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;
+	case cx_send_start: cuda_irq_send_start(irq); break;
+	case cx_send: cuda_irq_send(irq); 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;
+
+	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);
+
+	if ((b & TREQ) == 0) {
+		instance->xstate = cx_receive;
+		pio_write_8(&dev->b, b & ~TIP);
+	} else {
+		instance->xstate = cx_listen;
+		cuda_send_start(instance);
+	}
+
+        memcpy(buf, instance->rcv_buf, instance->bidx);
+        *len = instance->bidx;
+	instance->bidx = 0;
+}
+
+/** Interrupt in send_start state.
+ *
+ * Process result of sending first byte (and send second on success).
+ */
+static void cuda_irq_send_start(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) {
+		/* Collision */
+		pio_write_8(&dev->acr, pio_read_8(&dev->acr) & ~SR_OUT);
+		pio_read_8(&dev->sr);
+		pio_write_8(&dev->b, pio_read_8(&dev->b) | TIP | TACK);
+		instance->xstate = cx_listen;
+		return;
+	}
+
+	pio_write_8(&dev->sr, instance->snd_buf[1]);
+	pio_write_8(&dev->b, pio_read_8(&dev->b) ^ TACK);
+	instance->bidx = 2;
+
+	instance->xstate = cx_send;
+}
+
+/** Interrupt in send state.
+ *
+ * Send next byte or terminate transmission.
+ */
+static void cuda_irq_send(irq_t *irq)
+{
+	cuda_instance_t *instance = irq->instance;
+	cuda_t *dev = instance->cuda;
+
+	if (instance->bidx < instance->snd_bytes) {
+		/* Send next byte. */
+		pio_write_8(&dev->sr, instance->snd_buf[instance->bidx++]);
+		pio_write_8(&dev->b, pio_read_8(&dev->b) ^ TACK);
+		return;
+	}
+
+	/* End transfer. */
+	instance->snd_bytes = 0;
+	instance->bidx = 0;
+
+	pio_write_8(&dev->acr, pio_read_8(&dev->acr) & ~SR_OUT);
+	pio_read_8(&dev->sr);
+	pio_write_8(&dev->b, pio_read_8(&dev->b) | TACK | TIP);
+
+	instance->xstate = cx_listen;
+	/* TODO: Match reply with request. */
+}
+
+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;
+
+	/* The packet contains one or two scancodes. */
+	if (data[3] != 0xff)
+		indev_push_character(instance->kbrdin, data[3]);		
+	if (data[4] != 0xff)
+		indev_push_character(instance->kbrdin, data[4]);
+}
+
+static void cuda_autopoll_set(cuda_instance_t *instance, bool enable)
+{
+	instance->snd_buf[0] = PT_CUDA;
+	instance->snd_buf[1] = CPT_AUTOPOLL;
+	instance->snd_buf[2] = enable ? 0x01 : 0x00;
+	instance->snd_bytes = 3;
+	instance->bidx = 0;
+
+	cuda_send_start(instance);
+}
+
+static void cuda_send_start(cuda_instance_t *instance)
+{
+	cuda_t *dev = instance->cuda;
+
+	ASSERT(instance->xstate == cx_listen);
+
+	if (instance->snd_bytes == 0)
+		return;
+
+	/* Check for incoming data. */
+	if ((pio_read_8(&dev->b) & TREQ) == 0)
+		return;
+
+	pio_write_8(&dev->acr, pio_read_8(&dev->acr) | SR_OUT);
+	pio_write_8(&dev->sr, instance->snd_buf[0]);
+	pio_write_8(&dev->b, pio_read_8(&dev->b) & ~TIP);
+
+	instance->xstate = cx_send_start;
+}
+
 
 /** @}
