Index: kernel/arch/sparc64/src/console.c
===================================================================
--- kernel/arch/sparc64/src/console.c	(revision e60293d6039a24b3d36a35e0b4d938dacd250a49)
+++ kernel/arch/sparc64/src/console.c	(revision 8513ad77da21520006dc3e18de1e95a2b6f59c6f)
@@ -106,6 +106,10 @@
 
 #ifdef CONFIG_Z8530
-	if (kbd_type == KBD_Z8530)
+	if (kbd_type == KBD_Z8530) {
+		/*
+		 * The z8530 driver is interrupt-driven.
+		 */
 		return;
+	}
 #endif
 
@@ -124,8 +128,18 @@
 void arch_grab_console(void)
 {
+	switch (kbd_type) {
 #ifdef CONFIG_Z8530
-	if (kbd_type == KBD_Z8530)
+	case KBD_Z8530:
 		z8530_grab();
+		break;
 #endif
+#ifdef CONFIG_NS16550
+	case KBD_NS16550:
+		ns16550_grab();
+		break;
+#endif
+	default:
+		break;
+	}
 }
 
@@ -135,8 +149,18 @@
 void arch_release_console(void)
 {
+	switch (kbd_type) {
 #ifdef CONFIG_Z8530
-	if (kbd_type == KBD_Z8530)
+	case KBD_Z8530:
 		z8530_release();
+		break;
 #endif
+#ifdef CONFIG_NS16550
+	case KBD_NS16550:
+		ns16550_release();
+		break;
+#endif
+	default:
+		break;
+	}
 }
 
Index: kernel/genarch/src/kbd/ns16550.c
===================================================================
--- kernel/genarch/src/kbd/ns16550.c	(revision e60293d6039a24b3d36a35e0b4d938dacd250a49)
+++ kernel/genarch/src/kbd/ns16550.c	(revision 8513ad77da21520006dc3e18de1e95a2b6f59c6f)
@@ -39,7 +39,8 @@
 #include <genarch/kbd/scanc.h>
 #include <genarch/kbd/scanc_sun.h>
+#include <arch/drivers/kbd.h>
 #include <arch/drivers/ns16550.h>
 #include <ddi/irq.h>
-#include <arch/interrupt.h>
+#include <ipc/irq.h>
 #include <cpu.h>
 #include <arch/asm.h>
@@ -49,5 +50,7 @@
 #include <console/console.h>
 #include <interrupt.h>
+#include <arch/interrupt.h>
 #include <sysinfo/sysinfo.h>
+#include <synch/spinlock.h>
 
 #define LSR_DATA_READY	0x01
@@ -58,4 +61,6 @@
 /** Structure for ns16550's IRQ. */
 static irq_t ns16550_irq;
+
+static ipc_notif_cfg_t saved_notif_cfg;
 
 /*
@@ -73,11 +78,21 @@
 };
 
-void ns16550_interrupt(int n, istate_t *istate);
-void ns16550_wait(void);
+void ns16550_interrupt(void);
 
 /** Initialize keyboard and service interrupts using kernel routine */
 void ns16550_grab(void)
 {
-	/* TODO */
+	ns16550_ier_write(&ns16550, IER_ERBFI);		/* enable receiver interrupt */
+	
+	while (ns16550_lsr_read(&ns16550) & LSR_DATA_READY)
+		(void) ns16550_rbr_read(&ns16550);
+
+	if (ns16550_irq.notif_cfg.answerbox) {
+		saved_notif_cfg = ns16550_irq.notif_cfg;
+		ns16550_irq.notif_cfg.answerbox = NULL;
+		ns16550_irq.notif_cfg.code = NULL;
+		ns16550_irq.notif_cfg.method = 0;
+		ns16550_irq.notif_cfg.counter = 0;
+	}
 }
 
@@ -85,5 +100,6 @@
 void ns16550_release(void)
 {
-	/* TODO */
+	if (saved_notif_cfg.answerbox)
+		ns16550_irq.notif_cfg = saved_notif_cfg;
 }
 
@@ -96,5 +112,4 @@
 void ns16550_init(devno_t devno, inr_t inr, uintptr_t vaddr)
 {
-	ns16550_grab();
 	chardev_initialize("ns16550_kbd", &kbrd, &ops);
 	stdin = &kbrd;
@@ -111,27 +126,19 @@
 	
 	sysinfo_set_item_val("kbd", NULL, true);
+	sysinfo_set_item_val("kbd.type", NULL, KBD_NS16550);
 	sysinfo_set_item_val("kbd.devno", NULL, devno);
 	sysinfo_set_item_val("kbd.inr", NULL, inr);
 	sysinfo_set_item_val("kbd.address.virtual", NULL, vaddr);
 	
-	ns16550_ier_write(&ns16550, IER_ERBFI);		/* enable receiver interrupt */
-	
-	while (ns16550_lsr_read(&ns16550) & LSR_DATA_READY)
-		(void) ns16550_rbr_read(&ns16550);
-}
-
-/** Process ns16550 interrupt.
- *
- * @param n Interrupt vector.
- * @param istate Interrupted state.
- */
-void ns16550_interrupt(int n, istate_t *istate)
-{
-	/* TODO */
-}
-
-/** Wait until the controller reads its data. */
-void ns16550_wait(void)
-{
+	ns16550_grab();
+}
+
+/** Process ns16550 interrupt. */
+void ns16550_interrupt(void)
+{
+	/* TODO
+	 *
+	 * ns16550 works in the polled mode so far.
+	 */
 }
 
@@ -171,7 +178,27 @@
 void ns16550_poll(void)
 {
-	uint8_t x;
+	ipl_t ipl;
+
+	ipl = interrupts_disable();
+	spinlock_lock(&ns16550_irq.lock);
+
+	if (ns16550_lsr_read(&ns16550) & LSR_DATA_READY) {
+		if (ns16550_irq.notif_cfg.answerbox) {
+			/*
+			 * Send IPC notification.
+			 */
+			ipc_irq_send_notif(&ns16550_irq);
+			spinlock_unlock(&ns16550_irq.lock);
+			interrupts_restore(ipl);
+			return;
+		}
+	}
+
+	spinlock_unlock(&ns16550_irq.lock);
+	interrupts_restore(ipl);
 
 	while (ns16550_lsr_read(&ns16550) & LSR_DATA_READY) {
+		uint8_t x;
+		
 		x = ns16550_rbr_read(&ns16550);
 		if (x != IGNORE_CODE) {
@@ -186,11 +213,10 @@
 irq_ownership_t ns16550_claim(void)
 {
-	/* TODO */
-	return IRQ_ACCEPT;
+	return (ns16550_lsr_read(&ns16550) & LSR_DATA_READY);
 }
 
 void ns16550_irq_handler(irq_t *irq, void *arg, ...)
 {
-	panic("Not yet implemented.\n");
+	panic("Not yet implemented, ns16550 works in polled mode.\n");
 }
 
Index: kernel/genarch/src/kbd/z8530.c
===================================================================
--- kernel/genarch/src/kbd/z8530.c	(revision e60293d6039a24b3d36a35e0b4d938dacd250a49)
+++ kernel/genarch/src/kbd/z8530.c	(revision 8513ad77da21520006dc3e18de1e95a2b6f59c6f)
@@ -125,4 +125,5 @@
 
 	sysinfo_set_item_val("kbd", NULL, true);
+	sysinfo_set_item_val("kbd.type", NULL, KBD_Z8530);
 	sysinfo_set_item_val("kbd.devno", NULL, devno);
 	sysinfo_set_item_val("kbd.inr", NULL, inr);
Index: uspace/kbd/arch/sparc64/src/kbd.c
===================================================================
--- uspace/kbd/arch/sparc64/src/kbd.c	(revision e60293d6039a24b3d36a35e0b4d938dacd250a49)
+++ uspace/kbd/arch/sparc64/src/kbd.c	(revision 8513ad77da21520006dc3e18de1e95a2b6f59c6f)
@@ -47,6 +47,12 @@
 #define KBD_ALL_KEYS_UP		0x7f
 
+/** Top-half pseudocode for z8530. */
 irq_cmd_t z8530_cmds[] = {
-	{ CMD_MEM_READ_1, 0, 0, 1 }
+	{
+		CMD_MEM_READ_1,
+		0,			/**< Address. Will be patched in run-time. */
+		0,			/**< Value. Not used. */
+		1			/**< Arg 1 will contain the result. */
+	}
 };
 	
@@ -56,8 +62,37 @@
 };
 
+/** Top-half pseudocode for ns16550. */
+irq_cmd_t ns16550_cmds[] = {
+	{
+		CMD_MEM_READ_1,
+		0,			/**< Address. Will be patched in run-time. */
+		0,			/**< Value. Not used. */
+		1			/**< Arg 1 will contain the result. */
+	}
+};
+	
+irq_code_t ns16550_kbd = {
+	1,
+	ns16550_cmds
+};
+
+#define KBD_Z8530	1
+#define KBD_NS16550	2
+
 int kbd_arch_init(void)
 {
-	z8530_cmds[0].addr = (void *) sysinfo_value("kbd.address.virtual") + 6;
-	ipc_register_irq(sysinfo_value("kbd.inr"), sysinfo_value("kbd.devno"), 0, &z8530_kbd);
+	int type = sysinfo_value("kbd.type");
+	switch (type) {
+	case KBD_Z8530:
+		z8530_cmds[0].addr = (void *) sysinfo_value("kbd.address.virtual") + 6;
+		ipc_register_irq(sysinfo_value("kbd.inr"), sysinfo_value("kbd.devno"), 0, &z8530_kbd);
+		break;
+	case KBD_NS16550:
+		ns16550_cmds[0].addr = (void *) sysinfo_value("kbd.address.virtual");
+		ipc_register_irq(sysinfo_value("kbd.inr"), sysinfo_value("kbd.devno"), 0, &ns16550_kbd);
+		break;
+	default:
+		break;
+	}
 	return 0;
 }
