Index: kernel/arch/amd64/include/arch/interrupt.h
===================================================================
--- kernel/arch/amd64/include/arch/interrupt.h	(revision 1ab853969f4ab4bd3b894a43444959889f64873d)
+++ kernel/arch/amd64/include/arch/interrupt.h	(revision 3da166f05b0e0513fe18f82b34f75ef99bef955a)
@@ -53,4 +53,6 @@
 #define IRQ_KBD       1
 #define IRQ_PIC1      2
+/* NS16550 at COM1 */
+#define IRQ_NS16550   4
 #define IRQ_PIC_SPUR  7
 #define IRQ_MOUSE     12
Index: kernel/arch/amd64/src/amd64.c
===================================================================
--- kernel/arch/amd64/src/amd64.c	(revision 1ab853969f4ab4bd3b894a43444959889f64873d)
+++ kernel/arch/amd64/src/amd64.c	(revision 3da166f05b0e0513fe18f82b34f75ef99bef955a)
@@ -49,7 +49,9 @@
 #include <genarch/drivers/ega/ega.h>
 #include <genarch/drivers/i8042/i8042.h>
+#include <genarch/drivers/ns16550/ns16550.h>
 #include <genarch/drivers/legacy/ia32/io.h>
 #include <genarch/fb/bfb.h>
 #include <genarch/kbrd/kbrd.h>
+#include <genarch/srln/srln.h>
 #include <genarch/multiboot/multiboot.h>
 #include <genarch/multiboot/multiboot2.h>
@@ -212,4 +214,35 @@
 	}
 #endif
+
+#if (defined(CONFIG_NS16550) || defined(CONFIG_NS16550_OUT))
+	/*
+	 * Initialize the ns16550 controller.
+	 */
+#ifdef CONFIG_NS16550_OUT
+	outdev_t *ns16550_out;
+	outdev_t **ns16550_out_ptr = &ns16550_out;
+#else
+	outdev_t **ns16550_out_ptr = NULL;
+#endif
+	ns16550_instance_t *ns16550_instance
+	    = ns16550_init((ns16550_t *) NS16550_BASE, IRQ_NS16550, NULL, NULL,
+	    ns16550_out_ptr);
+	if (ns16550_instance) {
+#ifdef CONFIG_NS16550
+		srln_instance_t *srln_instance = srln_init();
+		if (srln_instance) {
+			indev_t *sink = stdin_wire();
+			indev_t *srln = srln_wire(srln_instance, sink);
+			ns16550_wire(ns16550_instance, srln);
+			trap_virtual_enable_irqs(1 << IRQ_NS16550);
+		}
+#endif
+#ifdef CONFIG_NS16550_OUT
+		if (ns16550_out) {
+			stdout_wire(ns16550_out);
+		}
+#endif
+	}
+#endif
 	
 	if (irqs_info != NULL)
Index: kernel/arch/ia32/include/arch/interrupt.h
===================================================================
--- kernel/arch/ia32/include/arch/interrupt.h	(revision 1ab853969f4ab4bd3b894a43444959889f64873d)
+++ kernel/arch/ia32/include/arch/interrupt.h	(revision 3da166f05b0e0513fe18f82b34f75ef99bef955a)
@@ -53,4 +53,6 @@
 #define IRQ_KBD       1
 #define IRQ_PIC1      2
+/* NS16550 at COM1 */
+#define IRQ_NS16550   4
 #define IRQ_PIC_SPUR  7
 #define IRQ_MOUSE     12
Index: kernel/arch/ia32/src/ia32.c
===================================================================
--- kernel/arch/ia32/src/ia32.c	(revision 1ab853969f4ab4bd3b894a43444959889f64873d)
+++ kernel/arch/ia32/src/ia32.c	(revision 3da166f05b0e0513fe18f82b34f75ef99bef955a)
@@ -50,7 +50,9 @@
 #include <genarch/drivers/ega/ega.h>
 #include <genarch/drivers/i8042/i8042.h>
+#include <genarch/drivers/ns16550/ns16550.h>
 #include <genarch/drivers/legacy/ia32/io.h>
 #include <genarch/fb/bfb.h>
 #include <genarch/kbrd/kbrd.h>
+#include <genarch/srln/srln.h>
 #include <genarch/multiboot/multiboot.h>
 #include <genarch/multiboot/multiboot2.h>
@@ -166,4 +168,35 @@
 	}
 #endif
+
+#if (defined(CONFIG_NS16550) || defined(CONFIG_NS16550_OUT))
+	/*
+	 * Initialize the ns16550 controller.
+	 */
+#ifdef CONFIG_NS16550_OUT
+	outdev_t *ns16550_out;
+	outdev_t **ns16550_out_ptr = &ns16550_out;
+#else
+	outdev_t **ns16550_out_ptr = NULL;
+#endif
+	ns16550_instance_t *ns16550_instance
+	    = ns16550_init((ns16550_t *) NS16550_BASE, IRQ_NS16550, NULL, NULL,
+	    ns16550_out_ptr);
+	if (ns16550_instance) {
+#ifdef CONFIG_NS16550
+		srln_instance_t *srln_instance = srln_init();
+		if (srln_instance) {
+			indev_t *sink = stdin_wire();
+			indev_t *srln = srln_wire(srln_instance, sink);
+			ns16550_wire(ns16550_instance, srln);
+			trap_virtual_enable_irqs(1 << IRQ_NS16550);
+		}
+#endif
+#ifdef CONFIG_NS16550_OUT
+		if (ns16550_out) {
+			stdout_wire(ns16550_out);
+		}
+#endif
+	}
+#endif
 	
 	if (irqs_info != NULL)
Index: kernel/arch/ia64/src/ia64.c
===================================================================
--- kernel/arch/ia64/src/ia64.c	(revision 1ab853969f4ab4bd3b894a43444959889f64873d)
+++ kernel/arch/ia64/src/ia64.c	(revision 3da166f05b0e0513fe18f82b34f75ef99bef955a)
@@ -173,5 +173,6 @@
 #ifdef CONFIG_NS16550
 	ns16550_instance_t *ns16550_instance
-	    = ns16550_init((ns16550_t *) NS16550_BASE, NS16550_IRQ, NULL, NULL);
+	    = ns16550_init((ns16550_t *) NS16550_BASE, NS16550_IRQ, NULL, NULL,
+	    NULL);
 	if (ns16550_instance) {
 		srln_instance_t *srln_instance = srln_init();
Index: kernel/arch/sparc64/src/drivers/kbd.c
===================================================================
--- kernel/arch/sparc64/src/drivers/kbd.c	(revision 1ab853969f4ab4bd3b894a43444959889f64873d)
+++ kernel/arch/sparc64/src/drivers/kbd.c	(revision 3da166f05b0e0513fe18f82b34f75ef99bef955a)
@@ -122,5 +122,5 @@
 	
 	ns16550_instance_t *ns16550_instance = ns16550_init(ns16550, inr, cir,
-	    cir_arg);
+	    cir_arg, NULL);
 	if (ns16550_instance) {
 		kbrd_instance_t *kbrd_instance = kbrd_init();
Index: kernel/genarch/include/genarch/drivers/ns16550/ns16550.h
===================================================================
--- kernel/genarch/include/genarch/drivers/ns16550/ns16550.h	(revision 1ab853969f4ab4bd3b894a43444959889f64873d)
+++ kernel/genarch/include/genarch/drivers/ns16550/ns16550.h	(revision 3da166f05b0e0513fe18f82b34f75ef99bef955a)
@@ -38,4 +38,5 @@
 #define KERN_NS16550_H_
 
+#include <ddi/ddi.h>
 #include <ddi/irq.h>
 #include <typedefs.h>
@@ -50,5 +51,8 @@
 /** NS16550 registers. */
 typedef struct {
-	ioport8_t rbr;      /**< Receiver Buffer Register. */
+	union {
+		ioport8_t rbr;      /**< Receiver Buffer Register (read). */
+		ioport8_t thr;      /**< Transmitter Holder Register (write). */
+	} __attribute__ ((packed));
 	ioport8_t ier;      /**< Interrupt Enable Register. */
 	union {
@@ -65,8 +69,11 @@
 	irq_t irq;
 	ns16550_t *ns16550;
-	indev_t *kbrdin;
+	indev_t *input;
+	outdev_t *output;
+	parea_t parea;
 } ns16550_instance_t;
 
-extern ns16550_instance_t *ns16550_init(ns16550_t *, inr_t, cir_t, void *);
+extern ns16550_instance_t *ns16550_init(ns16550_t *, inr_t, cir_t, void *,
+    outdev_t **);
 extern void ns16550_wire(ns16550_instance_t *, indev_t *);
 
Index: kernel/genarch/src/drivers/ns16550/ns16550.c
===================================================================
--- kernel/genarch/src/drivers/ns16550/ns16550.c	(revision 1ab853969f4ab4bd3b894a43444959889f64873d)
+++ kernel/genarch/src/drivers/ns16550/ns16550.c	(revision 3da166f05b0e0513fe18f82b34f75ef99bef955a)
@@ -41,6 +41,8 @@
 #include <mm/slab.h>
 #include <ddi/device.h>
+#include <str.h>
 
 #define LSR_DATA_READY  0x01
+#define LSR_TH_READY    0x20
 
 static irq_ownership_t ns16550_claim(irq_t *irq)
@@ -62,5 +64,5 @@
 	if (pio_read_8(&dev->lsr) & LSR_DATA_READY) {
 		uint8_t data = pio_read_8(&dev->rbr);
-		indev_push_character(instance->kbrdin, data);
+		indev_push_character(instance->input, data);
 	}
 }
@@ -73,16 +75,43 @@
 }
 
+static void ns16550_sendb(ns16550_t *dev, uint8_t byte)
+{
+	while (!(pio_read_8(&dev->lsr) & LSR_TH_READY))
+		;
+	pio_write_8(&dev->thr, byte);
+}
+
+static void ns16550_putchar(outdev_t *dev, wchar_t ch)
+{
+	ns16550_instance_t *instance = (ns16550_instance_t *) dev->data;
+	
+	if ((!instance->parea.mapped) || (console_override)) {
+		if (ascii_check(ch))
+			ns16550_sendb(instance->ns16550, (uint8_t) ch);
+		else
+			ns16550_sendb(instance->ns16550, U_SPECIAL);
+	}
+}
+
+static outdev_operations_t ns16550_ops = {
+	.write = ns16550_putchar,
+	.redraw = NULL
+};
+
 /** Initialize ns16550.
  *
  * @param dev      Addrress of the beginning of the device in I/O space.
- * @param devno    Device number.
  * @param inr      Interrupt number.
  * @param cir      Clear interrupt function.
  * @param cir_arg  First argument to cir.
+ * @param output   Where to store pointer to the output device
+ *                 or NULL if the caller is not interested in
+ *                 writing to the serial port.
  *
  * @return Keyboard instance or NULL on failure.
  *
  */
-ns16550_instance_t *ns16550_init(ns16550_t *dev, inr_t inr, cir_t cir, void *cir_arg)
+ns16550_instance_t *ns16550_init(ns16550_t *dev, inr_t inr, cir_t cir,
+    void *cir_arg, outdev_t **output)
 {
 	ns16550_instance_t *instance
@@ -90,5 +119,20 @@
 	if (instance) {
 		instance->ns16550 = dev;
-		instance->kbrdin = NULL;
+		instance->input = NULL;
+		instance->output = NULL;
+		
+		if (output) {
+			instance->output = malloc(sizeof(outdev_t),
+			    FRAME_ATOMIC);
+			if (!instance->output) {
+				free(instance);
+				return NULL;
+			}
+			
+			outdev_initialize("ns16550", instance->output,
+			    &ns16550_ops);
+			instance->output->data = instance;
+			*output = instance->output;
+		}
 		
 		irq_initialize(&instance->irq);
@@ -100,4 +144,10 @@
 		instance->irq.cir = cir;
 		instance->irq.cir_arg = cir_arg;
+		
+		instance->parea.pbase = (uintptr_t) dev;
+		instance->parea.frames = 1;
+		instance->parea.unpriv = false;
+		instance->parea.mapped = false;
+		ddi_parea_register(&instance->parea);
 	}
 	
@@ -105,10 +155,10 @@
 }
 
-void ns16550_wire(ns16550_instance_t *instance, indev_t *kbrdin)
+void ns16550_wire(ns16550_instance_t *instance, indev_t *input)
 {
 	ASSERT(instance);
-	ASSERT(kbrdin);
+	ASSERT(input);
 	
-	instance->kbrdin = kbrdin;
+	instance->input = input;
 	irq_register(&instance->irq);
 	
