shithub: riscv

Download patch

ref: 26c2845917cb4661a6e04e3b41d52fdc1338a2c4
parent: 67856b45077ec20116f1cb658b6686d4cdecbdfa
author: cinap_lenrek <cinap_lenrek@centraldogma>
date: Mon May 9 04:32:14 EDT 2011

kbdfs

--- a/lib/namespace
+++ b/lib/namespace
@@ -14,6 +14,7 @@
 
 # mount points
 mount -a /srv/slashn /n
+mount -b /srv/cons /dev
 
 # authentication
 mount -a /srv/factotum /mnt
--- a/sys/src/9/boot/bootrc
+++ b/sys/src/9/boot/bootrc
@@ -2,6 +2,12 @@
 
 bind -q '#p' /proc
 
+if(test -e '#b' && ! test -e /dev/kbd){
+	bind -a '#b' /dev
+	aux/kbdfs -s cons
+	exec /rc/bin/bootrc </dev/cons >/dev/cons >[2]/dev/cons
+}
+
 bind -qa '#S' /dev
 bind -qa '#f' /dev
 bind -qa '#k' /dev
--- /dev/null
+++ b/sys/src/9/pc/devkbd.c
@@ -1,0 +1,481 @@
+/*
+ * keyboard input
+ */
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/error.h"
+
+enum {
+	Data=		0x60,		/* data port */
+
+	Status=		0x64,		/* status port */
+	 Inready=	0x01,		/*  input character ready */
+	 Outbusy=	0x02,		/*  output busy */
+	 Sysflag=	0x04,		/*  system flag */
+	 Cmddata=	0x08,		/*  cmd==0, data==1 */
+	 Inhibit=	0x10,		/*  keyboard/mouse inhibited */
+	 Minready=	0x20,		/*  mouse character ready */
+	 Rtimeout=	0x40,		/*  general timeout */
+	 Parity=	0x80,
+
+	Cmd=		0x64,		/* command port (write only) */
+
+	Spec=		0xF800,		/* Unicode private space */
+	PF=		Spec|0x20,	/* num pad function key */
+	View=		Spec|0x00,	/* view (shift window up) */
+	KF=		0xF000,		/* function key (begin Unicode private space) */
+	Shift=		Spec|0x60,
+	Break=		Spec|0x61,
+	Ctrl=		Spec|0x62,
+	Latin=		Spec|0x63,
+	Caps=		Spec|0x64,
+	Num=		Spec|0x65,
+	Middle=		Spec|0x66,
+	Altgr=		Spec|0x67,
+	Kmouse=		Spec|0x100,
+	No=		0x00,		/* peter */
+
+	Home=		KF|13,
+	Up=		KF|14,
+	Pgup=		KF|15,
+	Print=		KF|16,
+	Left=		KF|17,
+	Right=		KF|18,
+	End=		KF|24,
+	Down=		View,
+	Pgdown=		KF|19,
+	Ins=		KF|20,
+	Del=		0x7F,
+	Scroll=		KF|21,
+
+	Nscan=	128,
+
+	Int=	0,			/* kbscans indices */
+	Ext,
+	Nscans,
+};
+
+enum
+{
+	/* controller command byte */
+	Cscs1=		(1<<6),		/* scan code set 1 */
+	Cauxdis=	(1<<5),		/* mouse disable */
+	Ckbddis=	(1<<4),		/* kbd disable */
+	Csf=		(1<<2),		/* system flag */
+	Cauxint=	(1<<1),		/* mouse interrupt enable */
+	Ckbdint=	(1<<0),		/* kbd interrupt enable */
+};
+
+enum {
+	Qdir,
+	Qscancode,
+	Qleds,
+};
+
+static Dirtab kbdtab[] = {
+	".",		{Qdir, 0, QTDIR},	0,	0555,
+	"scancode",	{Qscancode, 0},		0,	0440,
+	"leds",		{Qleds, 0},		0,	0220,
+};
+
+static Lock i8042lock;
+static uchar ccc;
+static void kbdputc(int);
+static void (*auxputc)(int, int);
+static int nokbd = 1;			/* flag: no PS/2 keyboard */
+
+static struct {
+	Ref ref;
+	Queue *q;
+} kbd;
+
+/*
+ *  wait for output no longer busy
+ */
+static int
+outready(void)
+{
+	int tries;
+
+	for(tries = 0; (inb(Status) & Outbusy); tries++){
+		if(tries > 500)
+			return -1;
+		delay(2);
+	}
+	return 0;
+}
+
+/*
+ *  wait for input
+ */
+static int
+inready(void)
+{
+	int tries;
+
+	for(tries = 0; !(inb(Status) & Inready); tries++){
+		if(tries > 500)
+			return -1;
+		delay(2);
+	}
+	return 0;
+}
+
+/*
+ *  ask 8042 to reset the machine
+ */
+void
+i8042reset(void)
+{
+	int i, x;
+
+	if(nokbd)
+		return;
+
+	*((ushort*)KADDR(0x472)) = 0x1234;	/* BIOS warm-boot flag */
+
+	/*
+	 *  newer reset the machine command
+	 */
+	outready();
+	outb(Cmd, 0xFE);
+	outready();
+
+	/*
+	 *  Pulse it by hand (old somewhat reliable)
+	 */
+	x = 0xDF;
+	for(i = 0; i < 5; i++){
+		x ^= 1;
+		outready();
+		outb(Cmd, 0xD1);
+		outready();
+		outb(Data, x);	/* toggle reset */
+		delay(100);
+	}
+}
+
+int
+i8042auxcmd(int cmd)
+{
+	unsigned int c;
+	int tries;
+	static int badkbd;
+
+	if(badkbd)
+		return -1;
+	c = 0;
+	tries = 0;
+
+	ilock(&i8042lock);
+	do{
+		if(tries++ > 2)
+			break;
+		if(outready() < 0)
+			break;
+		outb(Cmd, 0xD4);
+		if(outready() < 0)
+			break;
+		outb(Data, cmd);
+		if(outready() < 0)
+			break;
+		if(inready() < 0)
+			break;
+		c = inb(Data);
+	} while(c == 0xFE || c == 0);
+	iunlock(&i8042lock);
+
+	if(c != 0xFA){
+		print("i8042: %2.2ux returned to the %2.2ux command\n", c, cmd);
+		badkbd = 1;	/* don't keep trying; there might not be one */
+		return -1;
+	}
+	return 0;
+}
+
+int
+i8042auxcmds(uchar *cmd, int ncmd)
+{
+	int i;
+
+	ilock(&i8042lock);
+	for(i=0; i<ncmd; i++){
+		if(outready() < 0)
+			break;
+		outb(Cmd, 0xD4);
+		if(outready() < 0)
+			break;
+		outb(Data, cmd[i]);
+	}
+	iunlock(&i8042lock);
+	return i;
+}
+
+/*
+ * set keyboard's leds for lock states (scroll, numeric, caps).
+ *
+ * at least one keyboard (from Qtronics) also sets its numeric-lock
+ * behaviour to match the led state, though it has no numeric keypad,
+ * and some BIOSes bring the system up with numeric-lock set and no
+ * setting to change that.  this combination steals the keys for these
+ * characters and makes it impossible to generate them: uiolkjm&*().
+ * thus we'd like to be able to force the numeric-lock led (and behaviour) off.
+ */
+static void
+setleds(int leds)
+{
+	static int old = -1;
+
+	if(nokbd || leds == old)
+		return;
+	leds &= 7;
+	ilock(&i8042lock);
+	for(;;){
+		if(outready() < 0)
+			break;
+		outb(Data, 0xed);		/* `reset keyboard lock states' */
+		if(outready() < 0)
+			break;
+		outb(Data, leds);
+		if(outready() < 0)
+			break;
+		old = leds;
+		break;
+	}
+	iunlock(&i8042lock);
+}
+
+/*
+ *  keyboard interrupt
+ */
+static void
+i8042intr(Ureg*, void*)
+{
+	int s, c;
+	uchar b;
+
+	/*
+	 *  get status
+	 */
+	ilock(&i8042lock);
+	s = inb(Status);
+	if(!(s&Inready)){
+		iunlock(&i8042lock);
+		return;
+	}
+
+	/*
+	 *  get the character
+	 */
+	c = inb(Data);
+	iunlock(&i8042lock);
+
+	/*
+	 *  if it's the aux port...
+	 */
+	if(s & Minready){
+		if(auxputc != nil)
+			auxputc(c, 0);
+		return;
+	}
+
+	b = c & 0xff;
+	qproduce(kbd.q, &b, 1);
+}
+
+void
+i8042auxenable(void (*putc)(int, int))
+{
+	char *err = "i8042: aux init failed\n";
+
+	/* enable kbd/aux xfers and interrupts */
+	ccc &= ~Cauxdis;
+	ccc |= Cauxint;
+
+	ilock(&i8042lock);
+	if(outready() < 0)
+		print(err);
+	outb(Cmd, 0x60);			/* write control register */
+	if(outready() < 0)
+		print(err);
+	outb(Data, ccc);
+	if(outready() < 0)
+		print(err);
+	outb(Cmd, 0xA8);			/* auxiliary device enable */
+	if(outready() < 0){
+		iunlock(&i8042lock);
+		return;
+	}
+	auxputc = putc;
+	intrenable(IrqAUX, i8042intr, 0, BUSUNKNOWN, "kbdaux");
+	iunlock(&i8042lock);
+}
+
+static Chan *
+kbdattach(char *spec)
+{
+	return devattach(L'b', spec);
+}
+
+static Walkqid*
+kbdwalk(Chan *c, Chan *nc, char **name, int nname)
+{
+	return devwalk(c, nc, name, nname, kbdtab, nelem(kbdtab), devgen);
+}
+
+static int
+kbdstat(Chan *c, uchar *dp, int n)
+{
+	return devstat(c, dp, n, kbdtab, nelem(kbdtab), devgen);
+}
+
+static Chan*
+kbdopen(Chan *c, int omode)
+{
+	if(!iseve())
+		error(Eperm);
+	if(c->qid.path == Qscancode){
+		if(incref(&kbd.ref) != 1){
+			decref(&kbd.ref);
+			error(Einuse);
+		}
+	}
+	return devopen(c, omode, kbdtab, nelem(kbdtab), devgen);
+}
+
+static void
+kbdclose(Chan *c)
+{
+	if(c->qid.path == Qscancode)
+		decref(&kbd.ref);
+}
+
+static Block*
+kbdbread(Chan *c, long n, ulong)
+{
+	if(c->qid.path != Qscancode)
+		error(Egreg);
+
+	return qbread(kbd.q, n);
+}
+
+static long
+kbdread(Chan *c, void *a, long n, vlong)
+{
+	if(c->qid.path != Qscancode)
+		error(Egreg);
+
+	return qread(kbd.q, a, n);
+}
+
+static long
+kbdwrite(Chan *c, void *a, long n, vlong)
+{
+	char tmp[8+1], *p;
+
+	if(c->qid.path != Qleds)
+		error(Egreg);
+
+	p = tmp + n;
+	if(n >= sizeof(tmp))
+		p = tmp + sizeof(tmp)-1;
+	memmove(tmp, a, p - tmp);
+	*p = 0;
+
+	setleds(atoi(tmp));
+
+	return n;
+}
+
+Dev kbddevtab = {
+	L'b',
+	"kbd",
+
+	devreset,
+	devinit,
+	devshutdown,
+	kbdattach,
+	kbdwalk,
+	kbdstat,
+	kbdopen,
+	devcreate,
+	kbdclose,
+	kbdread,
+	kbdbread,
+	kbdwrite,
+	devbwrite,
+	devremove,
+	devwstat,
+};
+
+
+static char *initfailed = "i8042: kbdinit failed\n";
+
+static int
+outbyte(int port, int c)
+{
+	outb(port, c);
+	if(outready() < 0) {
+		print(initfailed);
+		return -1;
+	}
+	return 0;
+}
+
+void
+kbdenable(void)
+{
+	kbd.q = qopen(4*1024, 0, 0, 0);
+	if(kbd.q == nil)
+		panic("kbdenable");
+	qnoblock(kbd.q, 1);
+
+	ioalloc(Data, 1, 0, "kbd");
+	ioalloc(Cmd, 1, 0, "kbd");
+
+	intrenable(IrqKBD, i8042intr, 0, BUSUNKNOWN, "kbd");
+}
+
+void
+kbdinit(void)
+{
+	int c, try;
+
+	/* wait for a quiescent controller */
+	try = 1000;
+	while(try-- > 0 && (c = inb(Status)) & (Outbusy | Inready)) {
+		if(c & Inready)
+			inb(Data);
+		delay(1);
+	}
+	if (try <= 0) {
+		print(initfailed);
+		return;
+	}
+
+	/* get current controller command byte */
+	outb(Cmd, 0x20);
+	if(inready() < 0){
+		print("i8042: kbdinit can't read ccc\n");
+		ccc = 0;
+	} else
+		ccc = inb(Data);
+
+	/* enable kbd xfers and interrupts */
+	ccc &= ~Ckbddis;
+	ccc |= Csf | Ckbdint | Cscs1;
+	if(outready() < 0) {
+		print(initfailed);
+		return;
+	}
+
+	nokbd = 0;
+
+	/* disable mouse */
+	if (outbyte(Cmd, 0x60) < 0 || outbyte(Data, ccc) < 0)
+		print("i8042: kbdinit mouse disable failed\n");
+}
--- a/sys/src/9/pc/kbd.c
+++ /dev/null
@@ -1,715 +1,0 @@
-/*
- * keyboard input
- */
-#include	"u.h"
-#include	"../port/lib.h"
-#include	"mem.h"
-#include	"dat.h"
-#include	"fns.h"
-#include	"io.h"
-#include	"../port/error.h"
-
-enum {
-	Data=		0x60,		/* data port */
-
-	Status=		0x64,		/* status port */
-	 Inready=	0x01,		/*  input character ready */
-	 Outbusy=	0x02,		/*  output busy */
-	 Sysflag=	0x04,		/*  system flag */
-	 Cmddata=	0x08,		/*  cmd==0, data==1 */
-	 Inhibit=	0x10,		/*  keyboard/mouse inhibited */
-	 Minready=	0x20,		/*  mouse character ready */
-	 Rtimeout=	0x40,		/*  general timeout */
-	 Parity=	0x80,
-
-	Cmd=		0x64,		/* command port (write only) */
-
-	Spec=		0xF800,		/* Unicode private space */
-	PF=		Spec|0x20,	/* num pad function key */
-	View=		Spec|0x00,	/* view (shift window up) */
-	KF=		0xF000,		/* function key (begin Unicode private space) */
-	Shift=		Spec|0x60,
-	Break=		Spec|0x61,
-	Ctrl=		Spec|0x62,
-	Latin=		Spec|0x63,
-	Caps=		Spec|0x64,
-	Num=		Spec|0x65,
-	Middle=		Spec|0x66,
-	Altgr=		Spec|0x67,
-	Kmouse=		Spec|0x100,
-	No=		0x00,		/* peter */
-
-	Home=		KF|13,
-	Up=		KF|14,
-	Pgup=		KF|15,
-	Print=		KF|16,
-	Left=		KF|17,
-	Right=		KF|18,
-	End=		KF|24,
-	Down=		View,
-	Pgdown=		KF|19,
-	Ins=		KF|20,
-	Del=		0x7F,
-	Scroll=		KF|21,
-
-	Nscan=	128,
-
-	Int=	0,			/* kbscans indices */
-	Ext,
-	Nscans,
-};
-
-/*
- * The codes at 0x79 and 0x7b are produced by the PFU Happy Hacking keyboard.
- * A 'standard' keyboard doesn't produce anything above 0x58.
- */
-Rune kbtab[Nscan] = 
-{
-[0x00]	No,	0x1b,	'1',	'2',	'3',	'4',	'5',	'6',
-[0x08]	'7',	'8',	'9',	'0',	'-',	'=',	'\b',	'\t',
-[0x10]	'q',	'w',	'e',	'r',	't',	'y',	'u',	'i',
-[0x18]	'o',	'p',	'[',	']',	'\n',	Ctrl,	'a',	's',
-[0x20]	'd',	'f',	'g',	'h',	'j',	'k',	'l',	';',
-[0x28]	'\'',	'`',	Shift,	'\\',	'z',	'x',	'c',	'v',
-[0x30]	'b',	'n',	'm',	',',	'.',	'/',	Shift,	'*',
-[0x38]	Latin,	' ',	Ctrl,	KF|1,	KF|2,	KF|3,	KF|4,	KF|5,
-[0x40]	KF|6,	KF|7,	KF|8,	KF|9,	KF|10,	Num,	Scroll,	'7',
-[0x48]	'8',	'9',	'-',	'4',	'5',	'6',	'+',	'1',
-[0x50]	'2',	'3',	'0',	'.',	No,	No,	No,	KF|11,
-[0x58]	KF|12,	No,	No,	No,	No,	No,	No,	No,
-[0x60]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x68]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x70]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x78]	No,	View,	No,	Up,	No,	No,	No,	No,
-};
-
-Rune kbtabshift[Nscan] =
-{
-[0x00]	No,	0x1b,	'!',	'@',	'#',	'$',	'%',	'^',
-[0x08]	'&',	'*',	'(',	')',	'_',	'+',	'\b',	'\t',
-[0x10]	'Q',	'W',	'E',	'R',	'T',	'Y',	'U',	'I',
-[0x18]	'O',	'P',	'{',	'}',	'\n',	Ctrl,	'A',	'S',
-[0x20]	'D',	'F',	'G',	'H',	'J',	'K',	'L',	':',
-[0x28]	'"',	'~',	Shift,	'|',	'Z',	'X',	'C',	'V',
-[0x30]	'B',	'N',	'M',	'<',	'>',	'?',	Shift,	'*',
-[0x38]	Latin,	' ',	Ctrl,	KF|1,	KF|2,	KF|3,	KF|4,	KF|5,
-[0x40]	KF|6,	KF|7,	KF|8,	KF|9,	KF|10,	Num,	Scroll,	'7',
-[0x48]	'8',	'9',	'-',	'4',	'5',	'6',	'+',	'1',
-[0x50]	'2',	'3',	'0',	'.',	No,	No,	No,	KF|11,
-[0x58]	KF|12,	No,	No,	No,	No,	No,	No,	No,
-[0x60]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x68]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x70]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x78]	No,	Up,	No,	Up,	No,	No,	No,	No,
-};
-
-Rune kbtabesc1[Nscan] =
-{
-[0x00]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x08]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x10]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x18]	No,	No,	No,	No,	'\n',	Ctrl,	No,	No,
-[0x20]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x28]	No,	No,	Shift,	No,	No,	No,	No,	No,
-[0x30]	No,	No,	No,	No,	No,	'/',	No,	Print,
-[0x38]	Altgr,	No,	No,	No,	No,	No,	No,	No,
-[0x40]	No,	No,	No,	No,	No,	No,	Break,	Home,
-[0x48]	Up,	Pgup,	No,	Left,	No,	Right,	No,	End,
-[0x50]	Down,	Pgdown,	Ins,	Del,	No,	No,	No,	No,
-[0x58]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x60]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x68]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x70]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x78]	No,	Up,	No,	No,	No,	No,	No,	No,
-};
-
-Rune kbtabaltgr[Nscan] =
-{
-[0x00]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x08]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x10]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x18]	No,	No,	No,	No,	'\n',	Ctrl,	No,	No,
-[0x20]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x28]	No,	No,	Shift,	No,	No,	No,	No,	No,
-[0x30]	No,	No,	No,	No,	No,	'/',	No,	Print,
-[0x38]	Altgr,	No,	No,	No,	No,	No,	No,	No,
-[0x40]	No,	No,	No,	No,	No,	No,	Break,	Home,
-[0x48]	Up,	Pgup,	No,	Left,	No,	Right,	No,	End,
-[0x50]	Down,	Pgdown,	Ins,	Del,	No,	No,	No,	No,
-[0x58]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x60]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x68]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x70]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x78]	No,	Up,	No,	No,	No,	No,	No,	No,
-};
-
-Rune kbtabctrl[Nscan] =
-{
-[0x00]	No,	'', 	'', 	'', 	'', 	'', 	'', 	'', 
-[0x08]	'', 	'', 	'', 	'', 	'
', 	'', 	'\b',	'\t',
-[0x10]	'', 	'', 	'', 	'', 	'', 	'', 	'', 	'\t',
-[0x18]	'', 	'', 	'', 	'', 	'\n',	Ctrl,	'', 	'', 
-[0x20]	'', 	'', 	'', 	'\b',	'\n',	'', 	'', 	'', 
-[0x28]	'', 	No, 	Shift,	'', 	'', 	'', 	'', 	'', 
-[0x30]	'', 	'', 	'
', 	'', 	'', 	'', 	Shift,	'\n',
-[0x38]	Latin,	No, 	Ctrl,	'', 	'', 	'', 	'', 	'', 
-[0x40]	'', 	'', 	'', 	'
', 	'', 	'', 	'', 	'', 
-[0x48]	'', 	'', 	'
', 	'', 	'', 	'', 	'', 	'', 
-[0x50]	'', 	'', 	'', 	'', 	No,	No,	No,	'', 
-[0x58]	'', 	No,	No,	No,	No,	No,	No,	No,
-[0x60]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x68]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x70]	No,	No,	No,	No,	No,	No,	No,	No,
-[0x78]	No,	'', 	No,	'\b',	No,	No,	No,	No,
-};
-
-enum
-{
-	/* controller command byte */
-	Cscs1=		(1<<6),		/* scan code set 1 */
-	Cauxdis=	(1<<5),		/* mouse disable */
-	Ckbddis=	(1<<4),		/* kbd disable */
-	Csf=		(1<<2),		/* system flag */
-	Cauxint=	(1<<1),		/* mouse interrupt enable */
-	Ckbdint=	(1<<0),		/* kbd interrupt enable */
-};
-
-int mouseshifted;
-void (*kbdmouse)(int);
-
-static Lock i8042lock;
-static uchar ccc;
-static void (*auxputc)(int, int);
-static int nokbd = 1;			/* flag: no PS/2 keyboard */
-
-/*
- *  wait for output no longer busy
- */
-static int
-outready(void)
-{
-	int tries;
-
-	for(tries = 0; (inb(Status) & Outbusy); tries++){
-		if(tries > 500)
-			return -1;
-		delay(2);
-	}
-	return 0;
-}
-
-/*
- *  wait for input
- */
-static int
-inready(void)
-{
-	int tries;
-
-	for(tries = 0; !(inb(Status) & Inready); tries++){
-		if(tries > 500)
-			return -1;
-		delay(2);
-	}
-	return 0;
-}
-
-/*
- *  ask 8042 to reset the machine
- */
-void
-i8042reset(void)
-{
-	int i, x;
-
-	if(nokbd)
-		return;
-
-	*((ushort*)KADDR(0x472)) = 0x1234;	/* BIOS warm-boot flag */
-
-	/*
-	 *  newer reset the machine command
-	 */
-	outready();
-	outb(Cmd, 0xFE);
-	outready();
-
-	/*
-	 *  Pulse it by hand (old somewhat reliable)
-	 */
-	x = 0xDF;
-	for(i = 0; i < 5; i++){
-		x ^= 1;
-		outready();
-		outb(Cmd, 0xD1);
-		outready();
-		outb(Data, x);	/* toggle reset */
-		delay(100);
-	}
-}
-
-int
-i8042auxcmd(int cmd)
-{
-	unsigned int c;
-	int tries;
-	static int badkbd;
-
-	if(badkbd)
-		return -1;
-	c = 0;
-	tries = 0;
-
-	ilock(&i8042lock);
-	do{
-		if(tries++ > 2)
-			break;
-		if(outready() < 0)
-			break;
-		outb(Cmd, 0xD4);
-		if(outready() < 0)
-			break;
-		outb(Data, cmd);
-		if(outready() < 0)
-			break;
-		if(inready() < 0)
-			break;
-		c = inb(Data);
-	} while(c == 0xFE || c == 0);
-	iunlock(&i8042lock);
-
-	if(c != 0xFA){
-		print("i8042: %2.2ux returned to the %2.2ux command\n", c, cmd);
-		badkbd = 1;	/* don't keep trying; there might not be one */
-		return -1;
-	}
-	return 0;
-}
-
-int
-i8042auxcmds(uchar *cmd, int ncmd)
-{
-	int i;
-
-	ilock(&i8042lock);
-	for(i=0; i<ncmd; i++){
-		if(outready() < 0)
-			break;
-		outb(Cmd, 0xD4);
-		if(outready() < 0)
-			break;
-		outb(Data, cmd[i]);
-	}
-	iunlock(&i8042lock);
-	return i;
-}
-
-typedef struct Kbscan Kbscan;
-struct Kbscan {
-	int	esc1;
-	int	esc2;
-	int	alt;
-	int	altgr;
-	int	caps;
-	int	ctl;
-	int	num;
-	int	shift;
-	int	collecting;
-	int	nk;
-	Rune	kc[5];
-	int	buttons;
-};
-
-Kbscan kbscans[Nscans];	/* kernel and external scan code state */
-
-static int kdebug;
-
-/*
- * set keyboard's leds for lock states (scroll, numeric, caps).
- *
- * at least one keyboard (from Qtronics) also sets its numeric-lock
- * behaviour to match the led state, though it has no numeric keypad,
- * and some BIOSes bring the system up with numeric-lock set and no
- * setting to change that.  this combination steals the keys for these
- * characters and makes it impossible to generate them: uiolkjm&*().
- * thus we'd like to be able to force the numeric-lock led (and behaviour) off.
- */
-static void
-setleds(Kbscan *kbscan)
-{
-	int leds;
-
-	if(nokbd || kbscan != &kbscans[Int])
-		return;
-	leds = 0;
-	if(kbscan->num)
-		leds |= 1<<1;
-	if(0 && kbscan->caps)		/* we don't implement caps lock */
-		leds |= 1<<2;
-	ilock(&i8042lock);
-	outready();
-	outb(Data, 0xed);		/* `reset keyboard lock states' */
-	outready();
-	outb(Data, leds);
-	outready();
-	iunlock(&i8042lock);
-}
-
-/*
- * Scan code processing
- */
-void
-kbdputsc(int c, int external)
-{
-	int i, keyup;
-	Kbscan *kbscan;
-
-	if(external)
-		kbscan = &kbscans[Ext];
-	else
-		kbscan = &kbscans[Int];
-
-	if(kdebug)
-		print("sc %x ms %d\n", c, mouseshifted);
-	/*
-	 *  e0's is the first of a 2 character sequence, e1 the first
-	 *  of a 3 character sequence (on the safari)
-	 */
-	if(c == 0xe0){
-		kbscan->esc1 = 1;
-		return;
-	} else if(c == 0xe1){
-		kbscan->esc2 = 2;
-		return;
-	}
-
-	keyup = c & 0x80;
-	c &= 0x7f;
-	if(c > sizeof kbtab){
-		c |= keyup;
-		if(c != 0xFF)	/* these come fairly often: CAPSLOCK U Y */
-			print("unknown key %ux\n", c);
-		return;
-	}
-
-	if(kbscan->esc1){
-		c = kbtabesc1[c];
-		kbscan->esc1 = 0;
-	} else if(kbscan->esc2){
-		kbscan->esc2--;
-		return;
-	} else if(kbscan->shift)
-		c = kbtabshift[c];
-	else if(kbscan->altgr)
-		c = kbtabaltgr[c];
-	else if(kbscan->ctl)
-		c = kbtabctrl[c];
-	else
-		c = kbtab[c];
-
-	if(kbscan->caps && c<='z' && c>='a')
-		c += 'A' - 'a';
-
-	/*
-	 *  keyup only important for shifts
-	 */
-	if(keyup){
-		switch(c){
-		case Latin:
-			kbscan->alt = 0;
-			break;
-		case Shift:
-			kbscan->shift = 0;
-			mouseshifted = 0;
-			if(kdebug)
-				print("shiftclr\n");
-			break;
-		case Ctrl:
-			kbscan->ctl = 0;
-			break;
-		case Altgr:
-			kbscan->altgr = 0;
-			break;
-		case Kmouse|1:
-		case Kmouse|2:
-		case Kmouse|3:
-		case Kmouse|4:
-		case Kmouse|5:
-			kbscan->buttons &= ~(1<<(c-Kmouse-1));
-			if(kbdmouse)
-				kbdmouse(kbscan->buttons);
-			break;
-		}
-		return;
-	}
-
-	/*
-	 *  normal character
-	 */
-	if(!(c & (Spec|KF))){
-		if(kbscan->ctl)
-			if(kbscan->alt && c == Del)
-				exit(0);
-		if(!kbscan->collecting){
-			kbdputc(kbdq, c);
-			return;
-		}
-		kbscan->kc[kbscan->nk++] = c;
-		c = latin1(kbscan->kc, kbscan->nk);
-		if(c < -1)	/* need more keystrokes */
-			return;
-		if(c != -1)	/* valid sequence */
-			kbdputc(kbdq, c);
-		else	/* dump characters */
-			for(i=0; i<kbscan->nk; i++)
-				kbdputc(kbdq, kbscan->kc[i]);
-		kbscan->nk = 0;
-		kbscan->collecting = 0;
-		return;
-	} else {
-		switch(c){
-		case Caps:
-			kbscan->caps ^= 1;
-			return;
-		case Num:
-			kbscan->num ^= 1;
-			if(!external)
-				setleds(kbscan);
-			return;
-		case Shift:
-			kbscan->shift = 1;
-			if(kdebug)
-				print("shift\n");
-			mouseshifted = 1;
-			return;
-		case Latin:
-			kbscan->alt = 1;
-			/*
-			 * VMware and Qemu use Ctl-Alt as the key combination
-			 * to make the VM give up keyboard and mouse focus.
-			 * This has the unfortunate side effect that when you
-			 * come back into focus, Plan 9 thinks you want to type
-			 * a compose sequence (you just typed alt). 
-			 *
-			 * As a clumsy hack around this, we look for ctl-alt
-			 * and don't treat it as the start of a compose sequence.
-			 */
-			if(!kbscan->ctl){
-				kbscan->collecting = 1;
-				kbscan->nk = 0;
-			}
-			return;
-		case Ctrl:
-			kbscan->ctl = 1;
-			return;
-		case Altgr:
-			kbscan->altgr = 1;
-			return;
-		case Kmouse|1:
-		case Kmouse|2:
-		case Kmouse|3:
-		case Kmouse|4:
-		case Kmouse|5:
-			kbscan->buttons |= 1<<(c-Kmouse-1);
-			if(kbdmouse)
-				kbdmouse(kbscan->buttons);
-			return;
-		case KF|11:
-			print("kbd debug on, F12 turns it off\n");
-			kdebug = 1;
-			break;
-		case KF|12:
-			kdebug = 0;
-			break;
-		}
-	}
-	kbdputc(kbdq, c);
-}
-
-/*
- *  keyboard interrupt
- */
-static void
-i8042intr(Ureg*, void*)
-{
-	int s, c;
-
-	/*
-	 *  get status
-	 */
-	ilock(&i8042lock);
-	s = inb(Status);
-	if(!(s&Inready)){
-		iunlock(&i8042lock);
-		return;
-	}
-
-	/*
-	 *  get the character
-	 */
-	c = inb(Data);
-	iunlock(&i8042lock);
-
-	/*
-	 *  if it's the aux port...
-	 */
-	if(s & Minready){
-		if(auxputc != nil)
-			auxputc(c, kbscans[Int].shift);
-		return;
-	}
-
-	kbdputsc(c, Int);
-}
-
-void
-i8042auxenable(void (*putc)(int, int))
-{
-	char *err = "i8042: aux init failed\n";
-
-	/* enable kbd/aux xfers and interrupts */
-	ccc &= ~Cauxdis;
-	ccc |= Cauxint;
-
-	ilock(&i8042lock);
-	if(outready() < 0)
-		print(err);
-	outb(Cmd, 0x60);			/* write control register */
-	if(outready() < 0)
-		print(err);
-	outb(Data, ccc);
-	if(outready() < 0)
-		print(err);
-	outb(Cmd, 0xA8);			/* auxiliary device enable */
-	if(outready() < 0){
-		iunlock(&i8042lock);
-		return;
-	}
-	auxputc = putc;
-	intrenable(IrqAUX, i8042intr, 0, BUSUNKNOWN, "kbdaux");
-	iunlock(&i8042lock);
-}
-
-static char *initfailed = "i8042: kbdinit failed\n";
-
-static int
-outbyte(int port, int c)
-{
-	outb(port, c);
-	if(outready() < 0) {
-		print(initfailed);
-		return -1;
-	}
-	return 0;
-}
-
-void
-kbdinit(void)
-{
-	int c, try;
-
-	/* wait for a quiescent controller */
-	try = 1000;
-	while(try-- > 0 && (c = inb(Status)) & (Outbusy | Inready)) {
-		if(c & Inready)
-			inb(Data);
-		delay(1);
-	}
-	if (try <= 0) {
-		print(initfailed);
-		return;
-	}
-
-	/* get current controller command byte */
-	outb(Cmd, 0x20);
-	if(inready() < 0){
-		print("i8042: kbdinit can't read ccc\n");
-		ccc = 0;
-	} else
-		ccc = inb(Data);
-
-	/* enable kbd xfers and interrupts */
-	ccc &= ~Ckbddis;
-	ccc |= Csf | Ckbdint | Cscs1;
-	if(outready() < 0) {
-		print(initfailed);
-		return;
-	}
-
-	nokbd = 0;
-
-	/* disable mouse */
-	if (outbyte(Cmd, 0x60) < 0 || outbyte(Data, ccc) < 0)
-		print("i8042: kbdinit mouse disable failed\n");
-}
-
-void
-kbdenable(void)
-{
-	kbdq = qopen(4*1024, 0, 0, 0);
-	if(kbdq == nil)
-		panic("kbdinit");
-	qnoblock(kbdq, 1);
-
-	ioalloc(Data, 1, 0, "kbd");
-	ioalloc(Cmd, 1, 0, "kbd");
-
-	intrenable(IrqKBD, i8042intr, 0, BUSUNKNOWN, "kbd");
-
-	kbscans[Int].num = 0;
-	setleds(&kbscans[Int]);
-}
-
-void
-kbdputmap(ushort m, ushort scanc, Rune r)
-{
-	if(scanc >= Nscan)
-		error(Ebadarg);
-	switch(m) {
-	default:
-		error(Ebadarg);
-	case 0:
-		kbtab[scanc] = r;
-		break;
-	case 1:
-		kbtabshift[scanc] = r;
-		break;
-	case 2:
-		kbtabesc1[scanc] = r;
-		break;
-	case 3:
-		kbtabaltgr[scanc] = r;
-		break;
-	case 4:	
-		kbtabctrl[scanc] = r;
-		break;
-	}
-}
-
-int
-kbdgetmap(uint offset, int *t, int *sc, Rune *r)
-{
-	if ((int)offset < 0)
-		error(Ebadarg);
-	*t = offset/Nscan;
-	*sc = offset%Nscan;
-	switch(*t) {
-	default:
-		return 0;
-	case 0:
-		*r = kbtab[*sc];
-		return 1;
-	case 1:
-		*r = kbtabshift[*sc];
-		return 1;
-	case 2:
-		*r = kbtabesc1[*sc];
-		return 1;
-	case 3:
-		*r = kbtabaltgr[*sc];
-		return 1;
-	case 4:
-		*r = kbtabctrl[*sc];
-		return 1;
-	}
-}
--- a/sys/src/9/pc/mkfile
+++ b/sys/src/9/pc/mkfile
@@ -1,6 +1,6 @@
-CONF=pc
-CONFLIST=pc pccpu pcf pccpuf
-CRAPLIST=pccd pcflop
+CONF=pcf
+CONFLIST=pcf pccpuf
+CRAPLIST=pc pccpu pccd pcflop
 EXTRACOPIES=
 #EXTRACOPIES=lookout boundary	# copy to these servers on install
 
@@ -49,7 +49,6 @@
 	cga.$O\
 	i8253.$O\
 	i8259.$O\
-	kbd.$O\
 	main.$O\
 	memory.$O\
 	mmu.$O\
--- a/sys/src/9/pc/mouse.c
+++ b/sys/src/9/pc/mouse.c
@@ -22,8 +22,6 @@
 	MousePS2=	2,
 };
 
-extern int mouseshifted;
-
 static QLock mousectlqlock;
 static int mousetype;
 static int intellimouse;
@@ -95,11 +93,6 @@
 	ulong m;
 	int buttons, dx, dy;
 
-	/*
-	 * non-ps2 keyboards might not set shift
-	 * but still set mouseshifted.
-	 */
-	shift |= mouseshifted;
 	/*
 	 * Resynchronize in stream with timing; see comment above.
 	 */
--- a/sys/src/9/pc/pc
+++ /dev/null
@@ -1,134 +1,0 @@
-dev
-	root
-	cons
-
-	arch
-	pnp		pci
-	env
-	pipe
-	proc
-	mnt
-	srv
-	dup
-	rtc
-	ssl
-	tls
-	cap
-	kprof
-	fs
-
-	ether		netif
-	ip		arp chandial ip ipv6 ipaux iproute netlog nullmedium pktmedium ptclbsum386 inferno
-
-	draw		screen vga vgax
-	mouse		mouse
-	vga
-	kbmap
-	kbin
-
-	sd
-	floppy		dma
-	lpt
-
-	audio		dma
-	pccard
-	i82365		cis
-	uart
-	usb
-
-
-link
-	realmode
-	devpccard
-	devi82365
-	ether2000	ether8390
-	ether2114x	pci
-	ether589	etherelnk3
-	ether79c970	pci
-	ether8003	ether8390
-	ether8139	pci
-	ether8169	pci ethermii
-	ether82543gc	pci
-	ether82563	pci
-	ether82557	pci
-	ether83815	pci
-	etherbcm        pci
-	etherdp83820	pci
-	etherec2t	ether8390
-	etherelnk3	pci
-	etherga620	pci
-	etherigbe	pci ethermii
-	ethervgbe	pci ethermii
-	ethervt6102	pci ethermii
-	ethervt6105m	pci ethermii
-	ethersink
-	ethersmc	devi82365 cis
-	etherwavelan	wavelan devi82365 cis pci
-	ethermedium
-#	etherm10g
-	ether82598	pci
-	pcmciamodem
-	netdevmedium
-	loopbackmedium
-	usbuhci
-	usbohci
-	usbehci		usbehcipc
-
-misc
-	archmp		mp apic
-	mtrr
-
-	sdata		pci sdscsi
-	sd53c8xx	pci sdscsi
-	sdmylex		pci sdscsi
-	sdiahci		pci sdscsi
-
-	uarti8250
-	uartpci		pci
-	uartisa
-
-	vga3dfx		+cur
-	vgaark2000pv	+cur
-	vgabt485	=cur
-	vgaclgd542x	+cur
-	vgaclgd546x	+cur
-	vgact65545	+cur
-	vgacyber938x	+cur
-	vgaet4000	+cur
-	vgahiqvideo	+cur
-	vgai81x	+cur
-	vgamach64xx	+cur
-	vgamga2164w	+cur
-	vgamga4xx	+cur
-	vganeomagic	+cur
-	vganvidia	+cur
-	vgargb524	=cur
-	vgas3		+cur vgasavage
-	vgat2r4		+cur
-	vgatvp3020	=cur
-	vgatvp3026	=cur
-	vgavesa
-	vgavmware	+cur
-	vgageode	+cur
-
-ip
-	tcp
-	udp
-	ipifc
-	icmp
-	icmp6
-	gre
-	ipmux
-	esp
-
-port
-	int cpuserver = 0;
-
-boot
-	tcp
-
-bootdir
-	bootpc.out boot
-	/386/bin/ip/ipconfig
-	/386/bin/auth/factotum
-	/386/bin/usb/usbd
--- a/sys/src/9/pc/pccpu
+++ /dev/null
@@ -1,99 +1,0 @@
-# pccpu - cpu server kernel
-dev
-	root
-	cons
-	arch
-	pnp		pci
-	env
-	pipe
-	proc
-	mnt
-	srv
-	dup
-	rtc
-	ssl
-	tls
-	bridge		log
-	sdp		thwack unthwack
-	cap
-	kprof
-	fs
-	segment
-
-	ether		netif
-	ip		arp chandial ip ipv6 ipaux iproute netlog nullmedium pktmedium ptclbsum386 inferno
-
-	sd
-	floppy		dma
-	aoe
-
-	uart
-	usb
-	kbin
-	audio
-
-link
-	realmode
-	ether2000	ether8390
-	ether2114x	pci
-	ether79c970	pci
-	ether8003	ether8390
-	ether8139	pci
-	ether8169	pci ethermii
-	ether82543gc	pci
-	ether82563	pci
-	ether82557	pci
-	ether83815	pci
-	etherdp83820	pci
-	etherelnk3	pci
-	etherga620	pci
-	etherigbe	pci ethermii
-	ethervgbe	pci ethermii
-	ethervt6102	pci ethermii
-	ethervt6105m	pci ethermii
-#	etherm10g	pci ethermii
-	ether82598	pci
-	ethersink
-	ethermedium
-	loopbackmedium
-	usbuhci
-	usbohci
-	usbehci		usbehcipc
-
-misc
-	archmp		mp apic
-	mtrr
-
-	uarti8250
-	uartpci		pci
-	uartaxp		pci
-
-	sdata		pci sdscsi
-	sd53c8xx	pci sdscsi
-	sdmv50xx	pci sdscsi
-	sdmylex		pci sdscsi
-	sdiahci		pci sdscsi
-	sdaoe		sdscsi
-
-ip
-	tcp
-	udp
-	ipifc
-	icmp
-	icmp6
-	gre
-	ipmux
-	esp
-	rudp
-
-port
-	int cpuserver = 1;
-
-boot cpu
-	tcp
-
-bootdir
-	bootpccpu.out boot
-	/386/bin/ip/ipconfig ipconfig
-	/386/bin/auth/factotum
-	/386/bin/usb/usbd
--- a/sys/src/9/pc/pccpuf
+++ b/sys/src/9/pc/pccpuf
@@ -24,6 +24,7 @@
 
 	draw		screen vga vgax
 	mouse		mouse
+	kbd
 	vga
 
 	sd
@@ -32,7 +33,6 @@
 
 	uart
 	usb
-	kbin
 
 link
 	realmode
--- a/sys/src/9/pc/pcf
+++ b/sys/src/9/pc/pcf
@@ -22,9 +22,8 @@
 
 	draw		screen vga vgax
 	mouse		mouse
+	kbd
 	vga
-	kbmap
-	kbin
 
 	sd
 	floppy		dma
--- a/sys/src/9/port/auth.c
+++ b/sys/src/9/port/auth.c
@@ -133,6 +133,7 @@
 	buf[n] = 0;
 
 	renameuser(eve, buf);
+	srvrenameuser(eve, buf);
 	kstrdup(&eve, buf);
 	kstrdup(&up->user, buf);
 	up->basepri = PriNormal;
--- a/sys/src/9/port/bootfs.proto
+++ b/sys/src/9/port/bootfs.proto
@@ -2,6 +2,8 @@
 	bin
 		9660srv
 		awk
+		aux
+			kbdfs
 		bind
 		bzfs
 		cat
--- a/sys/src/9/port/devcons.c
+++ b/sys/src/9/port/devcons.c
@@ -11,8 +11,6 @@
 void	(*consdebug)(void) = nil;
 void	(*screenputs)(char*, int) = nil;
 
-Queue*	kbdq;			/* unprocessed console input */
-Queue*	lineq;			/* processed console input */
 Queue*	serialoq;		/* serial console output */
 Queue*	kprintoq;		/* console output, for /dev/kprint */
 ulong	kprintinuse;		/* test and set whether /dev/kprint is open */
@@ -20,30 +18,6 @@
 
 int	panicking;
 
-static struct
-{
-	QLock;
-
-	int	raw;		/* true if we shouldn't process input */
-	Ref	ctl;		/* number of opens to the control file */
-	int	x;		/* index into line */
-	char	line[1024];	/* current input line */
-
-	int	count;
-	int	ctlpoff;
-
-	/* a place to save up characters at interrupt time before dumping them in the queue */
-	Lock	lockputc;
-	char	istage[1024];
-	char	*iw;
-	char	*ir;
-	char	*ie;
-} kbd = {
-	.iw	= kbd.istage,
-	.ir	= kbd.istage,
-	.ie	= kbd.istage + sizeof(kbd.istage),
-};
-
 char	*sysname;
 vlong	fasthz;
 
@@ -70,10 +44,6 @@
 void
 printinit(void)
 {
-	lineq = qopen(2*1024, 0, nil, nil);
-	if(lineq == nil)
-		panic("printinit");
-	qnoblock(lineq, 1);
 }
 
 int
@@ -179,7 +149,7 @@
 
 	while(n > 0) {
 		t = memchr(str, '\n', n);
-		if(t && !kbd.raw) {
+		if(t) {
 			m = t-str;
 			if(usewrite){
 				qwrite(serialoq, str, m);
@@ -206,8 +176,6 @@
 	putstrn0(str, n, 0);
 }
 
-int noprint;
-
 int
 print(char *fmt, ...)
 {
@@ -215,9 +183,6 @@
 	va_list arg;
 	char buf[PRINTSIZE];
 
-	if(noprint)
-		return -1;
-
 	va_start(arg, fmt);
 	n = vseprint(buf, buf+sizeof(buf), fmt, arg) - buf;
 	va_end(arg);
@@ -350,231 +315,10 @@
 	return n;
 }
 
-static void
-echoscreen(char *buf, int n)
-{
-	char *e, *p;
-	char ebuf[128];
-	int x;
-
-	p = ebuf;
-	e = ebuf + sizeof(ebuf) - 4;
-	while(n-- > 0){
-		if(p >= e){
-			screenputs(ebuf, p - ebuf);
-			p = ebuf;
-		}
-		x = *buf++;
-		if(x == 0x15){
-			*p++ = '^';
-			*p++ = 'U';
-			*p++ = '\n';
-		} else
-			*p++ = x;
-	}
-	if(p != ebuf)
-		screenputs(ebuf, p - ebuf);
-}
-
-static void
-echoserialoq(char *buf, int n)
-{
-	char *e, *p;
-	char ebuf[128];
-	int x;
-
-	p = ebuf;
-	e = ebuf + sizeof(ebuf) - 4;
-	while(n-- > 0){
-		if(p >= e){
-			qiwrite(serialoq, ebuf, p - ebuf);
-			p = ebuf;
-		}
-		x = *buf++;
-		if(x == '\n'){
-			*p++ = '\r';
-			*p++ = '\n';
-		} else if(x == 0x15){
-			*p++ = '^';
-			*p++ = 'U';
-			*p++ = '\n';
-		} else
-			*p++ = x;
-	}
-	if(p != ebuf)
-		qiwrite(serialoq, ebuf, p - ebuf);
-}
-
-static void
-echo(char *buf, int n)
-{
-	static int ctrlt, pid;
-	int x;
-	char *e, *p;
-
-	if(n == 0)
-		return;
-
-	e = buf+n;
-	for(p = buf; p < e; p++){
-		switch(*p){
-		case 0x10:	/* ^P */
-			if(cpuserver && !kbd.ctlpoff){
-				active.exiting = 1;
-				return;
-			}
-			break;
-		case 0x14:	/* ^T */
-			ctrlt++;
-			if(ctrlt > 2)
-				ctrlt = 2;
-			continue;
-		}
-
-		if(ctrlt != 2)
-			continue;
-
-		/* ^T escapes */
-		ctrlt = 0;
-		switch(*p){
-		case 'S':
-			x = splhi();
-			dumpstack();
-			procdump();
-			splx(x);
-			return;
-		case 's':
-			dumpstack();
-			return;
-		case 'x':
-			xsummary();
-			ixsummary();
-			mallocsummary();
-		//	memorysummary();
-			pagersummary();
-			return;
-		case 'd':
-			if(consdebug == nil)
-				consdebug = rdb;
-			else
-				consdebug = nil;
-			print("consdebug now %#p\n", consdebug);
-			return;
-		case 'D':
-			if(consdebug == nil)
-				consdebug = rdb;
-			consdebug();
-			return;
-		case 'p':
-			x = spllo();
-			procdump();
-			splx(x);
-			return;
-		case 'q':
-			scheddump();
-			return;
-		case 'k':
-			killbig("^t ^t k");
-			return;
-		case 'r':
-			exit(0);
-			return;
-		}
-	}
-
-	qproduce(kbdq, buf, n);
-	if(kbd.raw)
-		return;
-	kmesgputs(buf, n);
-	if(screenputs != nil)
-		echoscreen(buf, n);
-	if(serialoq)
-		echoserialoq(buf, n);
-}
-
-/*
- *  Called by a uart interrupt for console input.
- *
- *  turn '\r' into '\n' before putting it into the queue.
- */
-int
-kbdcr2nl(Queue*, int ch)
-{
-	char *next;
-
-	ilock(&kbd.lockputc);		/* just a mutex */
-	if(ch == '\r' && !kbd.raw)
-		ch = '\n';
-	next = kbd.iw+1;
-	if(next >= kbd.ie)
-		next = kbd.istage;
-	if(next != kbd.ir){
-		*kbd.iw = ch;
-		kbd.iw = next;
-	}
-	iunlock(&kbd.lockputc);
-	return 0;
-}
-
-/*
- *  Put character, possibly a rune, into read queue at interrupt time.
- *  Called at interrupt time to process a character.
- */
-int
-kbdputc(Queue*, int ch)
-{
-	int i, n;
-	char buf[3];
-	Rune r;
-	char *next;
-
-	if(kbd.ir == nil)
-		return 0;		/* in case we're not inited yet */
-	
-	ilock(&kbd.lockputc);		/* just a mutex */
-	r = ch;
-	n = runetochar(buf, &r);
-	for(i = 0; i < n; i++){
-		next = kbd.iw+1;
-		if(next >= kbd.ie)
-			next = kbd.istage;
-		if(next == kbd.ir)
-			break;
-		*kbd.iw = buf[i];
-		kbd.iw = next;
-	}
-	iunlock(&kbd.lockputc);
-	return 0;
-}
-
-/*
- *  we save up input characters till clock time to reduce
- *  per character interrupt overhead.
- */
-static void
-kbdputcclock(void)
-{
-	char *iw;
-
-	/* this amortizes cost of qproduce */
-	if(kbd.iw != kbd.ir){
-		iw = kbd.iw;
-		if(iw < kbd.ir){
-			echo(kbd.ir, kbd.ie-kbd.ir);
-			kbd.ir = kbd.istage;
-		}
-		if(kbd.ir != iw){
-			echo(kbd.ir, iw-kbd.ir);
-			kbd.ir = iw;
-		}
-	}
-}
-
 enum{
 	Qdir,
 	Qbintime,
 	Qcons,
-	Qconsctl,
 	Qcputime,
 	Qdrivers,
 	Qkmesg,
@@ -607,7 +351,6 @@
 	".",	{Qdir, 0, QTDIR},	0,		DMDIR|0555,
 	"bintime",	{Qbintime},	24,		0664,
 	"cons",		{Qcons},	0,		0660,
-	"consctl",	{Qconsctl},	0,		0220,
 	"cputime",	{Qcputime},	6*NUMSIZE,	0444,
 	"drivers",	{Qdrivers},	0,		0444,
 	"hostdomain",	{Qhostdomain},	DOMLEN,		0664,
@@ -665,11 +408,6 @@
 {
 	todinit();
 	randominit();
-	/*
-	 * at 115200 baud, the 1024 char buffer takes 56 ms to process,
-	 * processing it every 22 ms should be fine
-	 */
-	addclock0link(kbdputcclock, 22);
 }
 
 static Chan*
@@ -696,10 +434,6 @@
 	c->aux = nil;
 	c = devopen(c, omode, consdir, nelem(consdir), devgen);
 	switch((ulong)c->qid.path){
-	case Qconsctl:
-		incref(&kbd.ctl);
-		break;
-
 	case Qkprint:
 		if(tas(&kprintinuse) != 0){
 			c->flag &= ~COPEN;
@@ -724,14 +458,6 @@
 consclose(Chan *c)
 {
 	switch((ulong)c->qid.path){
-	/* last close of control file turns off raw */
-	case Qconsctl:
-		if(c->flag&COPEN){
-			if(decref(&kbd.ctl) == 0)
-				kbd.raw = 0;
-		}
-		break;
-
 	/* close of kprint allows other opens */
 	case Qkprint:
 		if(c->flag & COPEN){
@@ -747,9 +473,9 @@
 {
 	ulong l;
 	Mach *mp;
-	char *b, *bp, ch;
+	char *b, *bp;
 	char tmp[256];		/* must be >= 18*NUMSIZE (Qswap) */
-	int i, k, id, send;
+	int i, k, id;
 	vlong offset = off;
 	extern char configfile[];
 
@@ -761,49 +487,7 @@
 		return devdirread(c, buf, n, consdir, nelem(consdir), devgen);
 
 	case Qcons:
-		qlock(&kbd);
-		if(waserror()) {
-			qunlock(&kbd);
-			nexterror();
-		}
-		while(!qcanread(lineq)){
-			if(qread(kbdq, &ch, 1) == 0)
-				continue;
-			send = 0;
-			if(ch == 0){
-				/* flush output on rawoff -> rawon */
-				if(kbd.x > 0)
-					send = !qcanread(kbdq);
-			}else if(kbd.raw){
-				kbd.line[kbd.x++] = ch;
-				send = !qcanread(kbdq);
-			}else{
-				switch(ch){
-				case '\b':
-					if(kbd.x > 0)
-						kbd.x--;
-					break;
-				case 0x15:	/* ^U */
-					kbd.x = 0;
-					break;
-				case '\n':
-				case 0x04:	/* ^D */
-					send = 1;
-				default:
-					if(ch != 0x04)
-						kbd.line[kbd.x++] = ch;
-					break;
-				}
-			}
-			if(send || kbd.x == sizeof kbd.line){
-				qwrite(lineq, kbd.line, kbd.x);
-				kbd.x = 0;
-			}
-		}
-		n = qread(lineq, buf, n);
-		qunlock(&kbd);
-		poperror();
-		return n;
+		error(Egreg);
 
 	case Qcputime:
 		k = offset;
@@ -980,7 +664,7 @@
 static long
 conswrite(Chan *c, void *va, long n, vlong off)
 {
-	char buf[256], ch;
+	char buf[256];
 	long l, bp;
 	char *a;
 	Mach *mp;
@@ -1007,29 +691,6 @@
 			putstrn0(buf, bp, 1);
 			a += bp;
 			l -= bp;
-		}
-		break;
-
-	case Qconsctl:
-		if(n >= sizeof(buf))
-			n = sizeof(buf)-1;
-		strncpy(buf, a, n);
-		buf[n] = 0;
-		for(a = buf; a;){
-			if(strncmp(a, "rawon", 5) == 0){
-				kbd.raw = 1;
-				/* clumsy hack - wake up reader */
-				ch = 0;
-				qwrite(kbdq, &ch, 1);			
-			} else if(strncmp(a, "rawoff", 6) == 0){
-				kbd.raw = 0;
-			} else if(strncmp(a, "ctlpon", 6) == 0){
-				kbd.ctlpoff = 0;
-			} else if(strncmp(a, "ctlpoff", 7) == 0){
-				kbd.ctlpoff = 1;
-			}
-			if(a = strchr(a, ' '))
-				a++;
 		}
 		break;
 
--- a/sys/src/9/port/devkbin.c
+++ /dev/null
@@ -1,120 +1,0 @@
-/*
- *  keyboard scan code input from outside the kernel.
- *  to avoid duplication of keyboard map processing for usb.
- */
-
-#include	"u.h"
-#include	"../port/lib.h"
-#include	"mem.h"
-#include	"dat.h"
-#include	"fns.h"
-#include	"../port/error.h"
-
-extern	void kbdputsc(int, int);
-
-enum {
-	Qdir,
-	Qkbd,
-};
-
-Dirtab kbintab[] = {
-	".",	{Qdir, 0, QTDIR},	0,	0555,
-	"kbin",	{Qkbd, 0},		0,	0200,
-};
-
-Lock	kbinlck;
-int	kbinbusy;
-
-static Chan *
-kbinattach(char *spec)
-{
-	return devattach(L'Ι', spec);
-}
-
-static Walkqid*
-kbinwalk(Chan *c, Chan *nc, char **name, int nname)
-{
-	return devwalk(c, nc, name, nname, kbintab, nelem(kbintab), devgen);
-}
-
-static int
-kbinstat(Chan *c, uchar *dp, int n)
-{
-	return devstat(c, dp, n, kbintab, nelem(kbintab), devgen);
-}
-
-static Chan*
-kbinopen(Chan *c, int omode)
-{
-	if(!iseve())
-		error(Eperm);
-	if(c->qid.path == Qkbd){
-		lock(&kbinlck);
-		if(kbinbusy){
-			unlock(&kbinlck);
-			error(Einuse);
-		}
-		kbinbusy++;
-		unlock(&kbinlck);
-	}
-	return devopen(c, omode, kbintab, nelem(kbintab), devgen);
-}
-
-static void
-kbinclose(Chan *c)
-{
-	if(c->aux){
-		free(c->aux);
-		c->aux = nil;
-	}
-	if(c->qid.path == Qkbd)
-		kbinbusy = 0;
-}
-
-static long
-kbinread(Chan *c, void *a, long n, vlong )
-{
-	if(c->qid.type == QTDIR)
-		return devdirread(c, a, n, kbintab, nelem(kbintab), devgen);
-	return 0;
-}
-
-static long
-kbinwrite(Chan *c, void *a, long n, vlong)
-{
-	int i;
-	uchar *p = a;
-
-	if(c->qid.type == QTDIR)
-		error(Eisdir);
-	switch((int)c->qid.path){
-	case Qkbd:
-		for(i = 0; i < n; i++)
-			kbdputsc(*p++, 1);	/* external source */
-		break;
-	default:
-		error(Egreg);
-	}
-	return n;
-}
-
-Dev kbindevtab = {
-	L'Ι',
-	"kbin",
-
-	devreset,
-	devinit,
-	devshutdown,
-	kbinattach,
-	kbinwalk,
-	kbinstat,
-	kbinopen,
-	devcreate,
-	kbinclose,
-	kbinread,
-	devbread,
-	kbinwrite,
-	devbwrite,
-	devremove,
-	devwstat,
-};
--- a/sys/src/9/port/devkbmap.c
+++ /dev/null
@@ -1,179 +1,0 @@
-/*
- *  keyboard map
- */
-
-#include	"u.h"
-#include	"../port/lib.h"
-#include	"mem.h"
-#include	"dat.h"
-#include	"fns.h"
-#include	"../port/error.h"
-
-enum{
-	Qdir,
-	Qdata,
-};
-Dirtab kbmaptab[]={
-	".",		{Qdir, 0, QTDIR},	0,	0555,
-	"kbmap",	{Qdata, 0},		0,	0600,
-};
-#define	NKBFILE	sizeof(kbmaptab)/sizeof(kbmaptab[0])
-
-#define	KBLINELEN	(3*NUMSIZE+1)	/* t code val\n */
-
-static Chan *
-kbmapattach(char *spec)
-{
-	return devattach(L'κ', spec);
-}
-
-static Walkqid*
-kbmapwalk(Chan *c, Chan *nc, char **name, int nname)
-{
-	return devwalk(c, nc, name, nname, kbmaptab, NKBFILE, devgen);
-}
-
-static int
-kbmapstat(Chan *c, uchar *dp, int n)
-{
-	return devstat(c, dp, n, kbmaptab, NKBFILE, devgen);
-}
-
-static Chan*
-kbmapopen(Chan *c, int omode)
-{
-	if(!iseve())
-		error(Eperm);
-	return devopen(c, omode, kbmaptab, NKBFILE, devgen);
-}
-
-static void
-kbmapclose(Chan *c)
-{
-	if(c->aux){
-		free(c->aux);
-		c->aux = nil;
-	}
-}
-
-static long
-kbmapread(Chan *c, void *a, long n, vlong offset)
-{
-	char *bp;
-	char tmp[KBLINELEN+1];
-	int t, sc;
-	Rune r;
-
-	if(c->qid.type == QTDIR)
-		return devdirread(c, a, n, kbmaptab, NKBFILE, devgen);
-
-	switch((int)(c->qid.path)){
-	case Qdata:
-		if(kbdgetmap(offset/KBLINELEN, &t, &sc, &r)) {
-			bp = tmp;
-			bp += readnum(0, bp, NUMSIZE, t, NUMSIZE);
-			bp += readnum(0, bp, NUMSIZE, sc, NUMSIZE);
-			bp += readnum(0, bp, NUMSIZE, r, NUMSIZE);
-			*bp++ = '\n';
-			*bp = 0;
-			n = readstr(offset%KBLINELEN, a, n, tmp);
-		} else
-			n = 0;
-		break;
-	default:
-		n=0;
-		break;
-	}
-	return n;
-}
-
-static long
-kbmapwrite(Chan *c, void *a, long n, vlong)
-{
-	char line[100], *lp, *b;
-	int key, m, l;
-	Rune r;
-
-	if(c->qid.type == QTDIR)
-		error(Eperm);
-
-	switch((int)(c->qid.path)){
-	case Qdata:
-		b = a;
-		l = n;
-		lp = line;
-		if(c->aux){
-			strcpy(line, c->aux);
-			lp = line+strlen(line);
-			free(c->aux);
-			c->aux = nil;
-		}
-		while(--l >= 0) {
-			*lp++  = *b++;
-			if(lp[-1] == '\n' || lp == &line[sizeof(line)-1]) {
-				*lp = 0;
-				if(*line == 0)
-					error(Ebadarg);
-				if(*line == '\n' || *line == '#'){
-					lp = line;
-					continue;
-				}
-				lp = line;
-				while(*lp == ' ' || *lp == '\t')
-					lp++;
-				m = strtoul(line, &lp, 0);
-				key = strtoul(lp, &lp, 0);
-				while(*lp == ' ' || *lp == '\t')
-					lp++;
-				r = 0;
-				if(*lp == '\'' && lp[1])
-					chartorune(&r, lp+1);
-				else if(*lp == '^' && lp[1]){
-					chartorune(&r, lp+1);
-					if(0x40 <= r && r < 0x60)
-						r -= 0x40;
-					else
-						error(Ebadarg);
-				}else if(*lp == 'M' && ('1' <= lp[1] && lp[1] <= '5'))
-					r = 0xF900+lp[1]-'0';
-				else if(*lp>='0' && *lp<='9') /* includes 0x... */
-					r = strtoul(lp, &lp, 0);
-				else
-					error(Ebadarg);
-				kbdputmap(m, key, r);
-				lp = line;
-			}
-		}
-		if(lp != line){
-			l = lp-line;
-			c->aux = lp = smalloc(l+1);
-			memmove(lp, line, l);
-			lp[l] = 0;
-		}
-		break;
-	default:
-		error(Ebadusefd);
-	}
-	return n;
-}
-
-Dev kbmapdevtab = {
-	L'κ',
-	"kbmap",
-
-	devreset,
-	devinit,
-	devshutdown,
-	kbmapattach,
-	kbmapwalk,
-	kbmapstat,
-	kbmapopen,
-	devcreate,
-	kbmapclose,
-	kbmapread,
-	devbread,
-	kbmapwrite,
-	devbwrite,
-	devremove,
-	devwstat,
-};
--- a/sys/src/9/port/devsrv.c
+++ b/sys/src/9/port/devsrv.c
@@ -352,3 +352,15 @@
 	srvremove,
 	srvwstat,
 };
+
+void
+srvrenameuser(char *old, char *new)
+{
+	Srv *sp;
+
+	qlock(&srvlk);
+	for(sp = srv; sp; sp = sp->link)
+		if(sp->owner!=nil && strcmp(old, sp->owner)==0)
+			kstrdup(&sp->owner, new);
+	qunlock(&srvlk);
+}
--- a/sys/src/9/port/devuart.c
+++ b/sys/src/9/port/devuart.c
@@ -223,9 +223,7 @@
 		if(p->console || p->special){
 			if(uartenable(p) != nil){
 				if(p->console){
-					kbdq = p->iq;
 					serialoq = p->oq;
-					p->putc = kbdcr2nl;
 				}
 				p->opens++;
 			}
--- a/sys/src/9/port/portfns.h
+++ b/sys/src/9/port/portfns.h
@@ -142,10 +142,6 @@
 int		ispages(void*);
 int		isphysseg(char*);
 void		ixsummary(void);
-int		kbdcr2nl(Queue*, int);
-int		kbdgetmap(uint, int*, int*, Rune*);
-int		kbdputc(Queue*, int);
-void		kbdputmap(ushort, ushort, Rune);
 void		kickpager(void);
 void		killbig(char*);
 void		kproc(char*, void(*)(void*), void*);
@@ -323,6 +319,7 @@
 void		splx(int);
 void		splxpc(int);
 char*		srvname(Chan*);
+void		srvrenameuser(char*, char*);
 int		swapcount(ulong);
 int		swapfull(void);
 void		swapinit(void);
--- /dev/null
+++ b/sys/src/cmd/aux/kbdfs.c
@@ -1,0 +1,1159 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <fcall.h>
+#include <thread.h>
+#include <9p.h>
+
+enum {
+	Spec=		0xF800,		/* Unicode private space */
+	PF=		Spec|0x20,	/* num pad function key */
+	View=		Spec|0x00,	/* view (shift window up) */
+	KF=		0xF000,		/* function key (begin Unicode private space) */
+	Shift=		Spec|0x60,
+	Break=		Spec|0x61,
+	Ctrl=		Spec|0x62,
+	Latin=		Spec|0x63,
+	Caps=		Spec|0x64,
+	Num=		Spec|0x65,
+	Middle=		Spec|0x66,
+	Altgr=		Spec|0x67,
+	Kmouse=		Spec|0x100,
+	No=		0x00,		/* peter */
+
+	Home=		KF|13,
+	Up=		KF|14,
+	Pgup=		KF|15,
+	Print=		KF|16,
+	Left=		KF|17,
+	Right=		KF|18,
+	End=		KF|24,
+	Down=		View,
+	Pgdown=		KF|19,
+	Ins=		KF|20,
+	Del=		0x7F,
+	Scroll=		KF|21,
+
+	Nscan=	128,
+
+	Int=	0,			/* scans indices */
+	Ext,
+	Nscans,
+
+	Qroot=	0,
+	Qkbd,
+	Qkbin,
+	Qkbmap,
+	Qcons,
+	Qconsctl,
+	Nqid,
+
+	Rawon=	0,
+	Rawoff,
+	Kbdflush,
+
+	STACKSZ = 8*1024,
+};
+
+typedef struct Key Key;
+typedef struct Scan Scan;
+
+struct Key {
+	int	down;
+	int	c;
+	Rune	r;
+};
+
+struct Scan {
+	int	esc1;
+	int	esc2;
+	int	caps;
+	int	num;
+	int	shift;
+	int	ctrl;
+	int	latin;
+	int	altgr;
+	int	leds;
+};
+
+struct Qtab {
+	char *name;
+	int mode;
+	int type;
+} qtab[Nqid] = {
+	"/",
+		DMDIR|0555,
+		QTDIR,
+
+	"kbd",
+		0666,
+		0,
+
+	"kbin",
+		0222,	
+		0,
+
+	"kbmap",
+		0666,	
+		0,
+
+	"cons",
+		0666,	
+		0,
+
+	"consctl",
+		0666,
+		0,
+};
+
+char Eshort[] = "read count too small";
+char Ebadarg[] = "invalid argument";
+char Eperm[] = "permission denied";
+char Enonexist[] = "file does not exist";
+char Ebadspec[] = "bad attach specifier";
+char Ewalk[] = "walk in non directory";
+char Efront[] = "the front fell off";
+
+int scanfd;
+int ledsfd;
+
+int consfd;
+int echofd;
+
+int kbdopen;
+int consopen;
+
+Channel *keychan;	/* Key */
+
+Channel *reqchan;	/* Req* */
+Channel *ctlchan;	/* int */
+
+Channel *rawchan;	/* Rune */
+Channel *linechan;	/* char * */
+Channel *kbdchan;	/* char* */
+
+/*
+ * The codes at 0x79 and 0x7b are produced by the PFU Happy Hacking keyboard.
+ * A 'standard' keyboard doesn't produce anything above 0x58.
+ */
+Rune kbtab[Nscan] = 
+{
+[0x00]	No,	0x1b,	'1',	'2',	'3',	'4',	'5',	'6',
+[0x08]	'7',	'8',	'9',	'0',	'-',	'=',	'\b',	'\t',
+[0x10]	'q',	'w',	'e',	'r',	't',	'y',	'u',	'i',
+[0x18]	'o',	'p',	'[',	']',	'\n',	Ctrl,	'a',	's',
+[0x20]	'd',	'f',	'g',	'h',	'j',	'k',	'l',	';',
+[0x28]	'\'',	'`',	Shift,	'\\',	'z',	'x',	'c',	'v',
+[0x30]	'b',	'n',	'm',	',',	'.',	'/',	Shift,	'*',
+[0x38]	Latin,	' ',	Ctrl,	KF|1,	KF|2,	KF|3,	KF|4,	KF|5,
+[0x40]	KF|6,	KF|7,	KF|8,	KF|9,	KF|10,	Num,	Scroll,	'7',
+[0x48]	'8',	'9',	'-',	'4',	'5',	'6',	'+',	'1',
+[0x50]	'2',	'3',	'0',	'.',	No,	No,	No,	KF|11,
+[0x58]	KF|12,	No,	No,	No,	No,	No,	No,	No,
+[0x60]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x68]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x70]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x78]	No,	View,	No,	Up,	No,	No,	No,	No,
+};
+
+Rune kbtabshift[Nscan] =
+{
+[0x00]	No,	0x1b,	'!',	'@',	'#',	'$',	'%',	'^',
+[0x08]	'&',	'*',	'(',	')',	'_',	'+',	'\b',	'\t',
+[0x10]	'Q',	'W',	'E',	'R',	'T',	'Y',	'U',	'I',
+[0x18]	'O',	'P',	'{',	'}',	'\n',	Ctrl,	'A',	'S',
+[0x20]	'D',	'F',	'G',	'H',	'J',	'K',	'L',	':',
+[0x28]	'"',	'~',	Shift,	'|',	'Z',	'X',	'C',	'V',
+[0x30]	'B',	'N',	'M',	'<',	'>',	'?',	Shift,	'*',
+[0x38]	Latin,	' ',	Ctrl,	KF|1,	KF|2,	KF|3,	KF|4,	KF|5,
+[0x40]	KF|6,	KF|7,	KF|8,	KF|9,	KF|10,	Num,	Scroll,	'7',
+[0x48]	'8',	'9',	'-',	'4',	'5',	'6',	'+',	'1',
+[0x50]	'2',	'3',	'0',	'.',	No,	No,	No,	KF|11,
+[0x58]	KF|12,	No,	No,	No,	No,	No,	No,	No,
+[0x60]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x68]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x70]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x78]	No,	Up,	No,	Up,	No,	No,	No,	No,
+};
+
+Rune kbtabesc1[Nscan] =
+{
+[0x00]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x08]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x10]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x18]	No,	No,	No,	No,	'\n',	Ctrl,	No,	No,
+[0x20]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x28]	No,	No,	Shift,	No,	No,	No,	No,	No,
+[0x30]	No,	No,	No,	No,	No,	'/',	No,	Print,
+[0x38]	Altgr,	No,	No,	No,	No,	No,	No,	No,
+[0x40]	No,	No,	No,	No,	No,	No,	Break,	Home,
+[0x48]	Up,	Pgup,	No,	Left,	No,	Right,	No,	End,
+[0x50]	Down,	Pgdown,	Ins,	Del,	No,	No,	No,	No,
+[0x58]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x60]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x68]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x70]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x78]	No,	Up,	No,	No,	No,	No,	No,	No,
+};
+
+Rune kbtabaltgr[Nscan] =
+{
+[0x00]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x08]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x10]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x18]	No,	No,	No,	No,	'\n',	Ctrl,	No,	No,
+[0x20]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x28]	No,	No,	Shift,	No,	No,	No,	No,	No,
+[0x30]	No,	No,	No,	No,	No,	'/',	No,	Print,
+[0x38]	Altgr,	No,	No,	No,	No,	No,	No,	No,
+[0x40]	No,	No,	No,	No,	No,	No,	Break,	Home,
+[0x48]	Up,	Pgup,	No,	Left,	No,	Right,	No,	End,
+[0x50]	Down,	Pgdown,	Ins,	Del,	No,	No,	No,	No,
+[0x58]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x60]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x68]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x70]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x78]	No,	Up,	No,	No,	No,	No,	No,	No,
+};
+
+Rune kbtabctrl[Nscan] =
+{
+[0x00]	No,	'', 	'', 	'', 	'', 	'', 	'', 	'', 
+[0x08]	'', 	'', 	'', 	'', 	'
', 	'', 	'\b',	'\t',
+[0x10]	'', 	'', 	'', 	'', 	'', 	'', 	'', 	'\t',
+[0x18]	'', 	'', 	'', 	'', 	'\n',	Ctrl,	'', 	'', 
+[0x20]	'', 	'', 	'', 	'\b',	'\n',	'', 	'', 	'', 
+[0x28]	'', 	No, 	Shift,	'', 	'', 	'', 	'', 	'', 
+[0x30]	'', 	'', 	'
', 	'', 	'', 	'', 	Shift,	'\n',
+[0x38]	Latin,	No, 	Ctrl,	'', 	'', 	'', 	'', 	'', 
+[0x40]	'', 	'', 	'', 	'
', 	'', 	'', 	'', 	'', 
+[0x48]	'', 	'', 	'
', 	'', 	'', 	'', 	'', 	'', 
+[0x50]	'', 	'', 	'', 	'', 	No,	No,	No,	'', 
+[0x58]	'', 	No,	No,	No,	No,	No,	No,	No,
+[0x60]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x68]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x70]	No,	No,	No,	No,	No,	No,	No,	No,
+[0x78]	No,	'', 	No,	'\b',	No,	No,	No,	No,
+};
+
+void reboot(void);
+
+/*
+ * Scan code processing
+ */
+void
+kbdputsc(Scan *scan, int c)
+{
+	Key key;
+
+	/*
+	 *  e0's is the first of a 2 character sequence, e1 the first
+	 *  of a 3 character sequence (on the safari)
+	 */
+	if(c == 0xe0){
+		scan->esc1 = 1;
+		return;
+	} else if(c == 0xe1){
+		scan->esc2 = 2;
+		return;
+	}
+
+	key.down = (c & 0x80) == 0;
+	key.c = c & 0x7f;
+
+	if(key.c >= Nscan)
+		return;
+
+	if(scan->esc1)
+		key.r = kbtabesc1[key.c];
+	else if(scan->shift)
+		key.r = kbtabshift[key.c];
+	else if(scan->altgr)
+		key.r = kbtabaltgr[key.c];
+	else if(scan->ctrl)
+		key.r = kbtabctrl[key.c];
+	else
+		key.r = kbtab[key.c];
+
+	if(scan->caps && key.r<='z' && key.r>='a')
+		key.r += 'A' - 'a';
+
+	if(scan->ctrl && scan->latin && key.r == Del)
+		reboot();
+
+	send(keychan, &key);
+
+	if(scan->esc1)
+		scan->esc1 = 0;
+	else if(scan->esc2)
+		scan->esc2--;
+
+	switch(key.r){
+	case Shift:
+		scan->shift = key.down;
+		break;
+	case Ctrl:
+		scan->ctrl = key.down;
+		break;
+	case Altgr:
+		scan->altgr = key.down;
+		break;
+	case Latin:
+		scan->latin = key.down;
+		break;
+	case Num:
+		scan->num ^= key.down;
+		break;
+	case Caps:
+		scan->caps ^= key.down;
+		break;
+	}
+}
+
+void
+setleds(Scan *scan, int leds)
+{
+	char buf[8];
+
+	if(ledsfd < 0 || scan->leds == leds)
+		return;
+	leds &= 7;
+	snprint(buf, sizeof(buf), "%d", leds);
+	pwrite(ledsfd, buf, strlen(buf), 0);
+	scan->leds = leds;
+}
+
+/*
+ * Read scan codes from scanfd
+ */ 
+void
+scanproc(void *)
+{
+	uchar buf[64];
+	Scan scan;
+	int i, n;
+
+	threadsetname("scanproc");
+
+	memset(&scan, 0, sizeof scan);
+	while((n = read(scanfd, buf, sizeof buf)) > 0){
+		for(i=0; i<n; i++)
+			kbdputsc(&scan, buf[i]);
+		setleds(&scan, (scan.num<<1) | (scan.caps<<2));
+	}
+}
+
+char*
+utfconv(Rune *r, int n)
+{
+	char *s, *p;
+	int l;
+
+	l = runenlen(r, n) + 1;
+	s = emalloc9p(l);
+	for(p = s; n > 0; r++, n--)
+		p += runetochar(p, r);
+	*p = 0;
+	return s;
+}
+
+/*
+ * Read key events from keychan and produce characters to
+ * rawchan and keystate in kbdchan. this way here is only
+ * one global keystate even if multiple keyboards are used.
+ */
+void
+keyproc(void *)
+{
+	int cb[Nscan];
+	Rune rb[Nscan];
+	Key key;
+	int i, nb;
+	char *s;
+
+	threadsetname("keyproc");
+
+	nb = 0;
+	while(recv(keychan, &key) > 0){
+		if(key.down){
+			switch(key.r){
+			case Caps:
+			case Num:
+			case Shift:
+			case Latin:
+			case Ctrl:
+			case Altgr:
+			case Kmouse|1:
+			case Kmouse|2:
+			case Kmouse|3:
+			case Kmouse|4:
+			case Kmouse|5:
+			case KF|11:
+			case KF|12:
+				break;
+			default:
+				nbsend(rawchan, &key.r);
+			}
+		}
+
+		s = nil;
+		for(i=0; i<nb && cb[i] != key.c; i++)
+			;
+		if(!key.down){
+			if(i < nb){
+				memmove(cb+i, cb+i+1, (nb-i+1) * sizeof(cb[0]));
+				memmove(rb+i, rb+i+1, (nb-i+1) * sizeof(rb[0]));
+				nb--;
+				s = utfconv(rb, nb);
+			}
+		} else if(i == nb && nb < nelem(cb) && key.r){
+			cb[nb] = key.c;
+			rb[nb] = key.r;
+			nb++;
+			s = utfconv(rb, nb);
+		}
+		if(s && nbsendp(kbdchan, s) <= 0)
+			free(s);
+	}
+}
+
+/*
+ * Read characters from consfd (serial console)
+ */ 
+void
+consproc(void *)
+{
+	char *p, *e, *x, buf[64];
+	Rune r;
+	int n;
+
+	threadsetname("consproc");
+
+	p = buf;
+	e = buf + sizeof(buf);
+	while((n = read(consfd, p, e - p)) > 0){
+		x = buf + n;
+		while(p < x && fullrune(p, x - p)){
+			p += chartorune(&r, p);
+			send(rawchan, &r);
+		}
+		n = x - p;
+		if(n > 0){
+			memmove(buf, p, n);
+			p = buf + n;
+		}
+	}
+}
+
+/*
+ * Cook lines for cons
+ */
+void
+lineproc(void *aux)
+{
+	char *l, *p, *e, *s;
+	Channel *cook;
+	int done;
+	Rune r;
+
+	cook = aux;
+
+	threadsetname("lineproc");
+
+	for(;;){
+		l = emalloc9p(1024);
+		p = s = l;
+		e = l + 1024-(UTFmax+1);
+		done = 0;
+		do {
+			recv(cook, &r);
+			switch(r){
+			case '\0':	/* flush */
+				p = l;
+				continue;
+			case '\b':	/* backspace */
+				while(p > l){
+					--p;
+					if(fullrune(p, s - p))
+						break;
+				}
+				write(echofd, "\b", 1);
+				continue;
+			case 0x04:	/* eof */
+				p = l;
+				done = 1;
+				break;
+			case '\r':
+				continue;
+			case '\n':
+				done = 1;
+			default:
+				p += runetochar(s = p, &r);
+				write(echofd, s, p - s);
+			}
+		} while(!done && p < e);
+		*p = 0;
+		sendp(linechan, l);
+	}
+}
+
+enum {
+	Areq,
+	Actl,
+	Araw,
+	Aline,
+	Akbd,
+	Aend,
+};
+
+/*
+ * Queue reads to cons and kbd, flushing and
+ * relay data between 9p and rawchan / kbdchan.
+ */
+void
+ctlproc(void *)
+{
+	struct {
+		Req *h;
+		Req **t;
+	} qcons, qkbd, *q;
+	Alt a[Aend+1];
+	Req *req;
+	Fid *fid;
+	Rune r;
+	char *s, *b, *p, *e;
+	int c, n, raw;
+	Channel *cook;
+
+	threadsetname("ctlproc");
+
+	cook = chancreate(sizeof(Rune), 0);
+
+	if(scanfd >= 0)
+		proccreate(scanproc, nil, STACKSZ);
+	if(consfd >= 0)
+		proccreate(consproc, nil, STACKSZ);
+
+	threadcreate(keyproc, nil, STACKSZ);
+	threadcreate(lineproc, cook, STACKSZ);
+
+	raw = 0;
+
+	b = p = e = nil;
+
+	qcons.h = nil;
+	qcons.t = &qcons.h;
+	qkbd.h = nil;
+	qkbd.t = &qkbd.h;
+
+	memset(a, 0, sizeof a);
+
+	a[Areq].c = reqchan;
+	a[Areq].v = &req;
+	a[Areq].op = CHANRCV;
+
+	a[Actl].c = ctlchan;
+	a[Actl].v = &c;
+	a[Actl].op = CHANRCV;
+
+	a[Araw].c = rawchan;
+	a[Araw].v = &r;
+	a[Araw].op = CHANRCV;
+
+	a[Aline].c = linechan;
+	a[Aline].v = &s;
+	a[Aline].op = CHANRCV;
+
+	a[Akbd].c = kbdchan;
+	a[Akbd].v = &s;
+	a[Akbd].op = CHANRCV;
+
+	a[Aend].op = CHANEND;
+
+	for(;;){
+		s = nil;
+
+		a[Araw].op = (b == nil) ? CHANRCV : CHANNOP;
+		a[Aline].op = (b == nil) ? CHANRCV : CHANNOP;
+		a[Akbd].op = qkbd.h || !kbdopen ? CHANRCV : CHANNOP;
+
+		switch(alt(a)){
+		case Areq:
+			fid = req->fid;
+			if(req->ifcall.type == Tflush){
+				Req **rr;
+
+				fid = req->oldreq->fid;
+				q = fid->qid.path == Qcons ? &qcons : &qkbd;
+				for(rr = &q->h; *rr && *rr != req->oldreq; rr = &((*rr)->aux))
+					;
+				if(*rr == req->oldreq){
+					if((*rr = req->oldreq->aux) == nil)
+						q->t = rr;
+					req->oldreq->aux = nil;
+					respond(req->oldreq, "interrupted");
+				}
+				respond(req, nil);
+			} else if(req->ifcall.type == Tread){
+				q = fid->qid.path == Qcons ? &qcons : &qkbd;
+				req->aux = nil;
+				*q->t = req;
+				q->t = &req->aux;
+				goto Havereq;
+			} else
+				respond(req, Efront);
+			break;
+
+		case Actl:
+			switch(c){
+			case Rawoff:
+			case Rawon:
+				raw = (c == Rawon);
+				if(raw){
+					while(s = nbrecvp(linechan))
+						free(s);
+					r = '\0';
+					send(cook, &r);
+					free(b);
+					b = nil;
+				}
+				break;
+			case Kbdflush:
+				while(s = nbrecvp(kbdchan))
+					free(s);
+				break;
+			}
+			break;
+
+		case Araw:
+			if(raw){
+				s = emalloc9p(UTFmax+1);
+				s[runetochar(s, &r)] = 0;
+			} else {
+				send(cook, &r);
+				break;
+			}
+			/* no break */
+
+		case Aline:
+			b = s;
+			p = s;
+			e = s + strlen(s);
+
+		Havereq:
+			while(b && (req = qcons.h)){
+				if((qcons.h = req->aux) == nil)
+					qcons.t = &qcons.h;
+				n = e - p;
+				if(req->ifcall.count < n)
+					n = req->ifcall.count;
+				req->ofcall.count = n;
+				memmove(req->ofcall.data, p, n);
+				respond(req, nil);
+				p += n;
+				if(p >= e){
+					free(b);
+					b = nil;
+				}
+			}
+			break;
+
+		case Akbd:
+			if(req = qkbd.h){
+				if((qkbd.h = req->aux) == nil)
+					qkbd.t = &qkbd.h;
+				n = strlen(s) + 1;
+				if(n > req->ifcall.count)
+					respond(req, Eshort);
+				else {
+					req->ofcall.count = n;
+					memmove(req->ofcall.data, s, n);
+					respond(req, nil);
+				}
+			}
+			break;
+		}
+	}
+}
+
+/*
+ * Keyboard layout maps
+ */
+
+Rune*
+kbmapent(int t, int sc)
+{
+	if(sc < 0 || sc >= Nscan)
+		return nil;
+	switch(t){
+	default:
+		return nil;
+	case 0:
+		return &kbtab[sc];
+	case 1:
+		return &kbtabshift[sc];
+	case 2:
+		return &kbtabesc1[sc];
+	case 3:
+		return &kbtabaltgr[sc];
+	case 4:
+		return &kbtabctrl[sc];
+	}
+}
+
+void
+kbmapread(Req *req)
+{
+	char tmp[3*12+1];
+	int t, sc, off, n;
+	Rune *rp;
+
+	off = req->ifcall.offset/(sizeof(tmp)-1);
+	t = off/Nscan;
+	sc = off%Nscan;
+	if(rp = kbmapent(t, sc))
+		sprint(tmp, "%11d %11d %11d\n", t, sc, *rp);
+	else
+		*tmp = 0;
+	n = strlen(tmp);
+	if(req->ifcall.count < n)
+		n = req->ifcall.count;
+	req->ofcall.count = n;
+	memmove(req->ofcall.data, tmp, n);
+	respond(req, nil);
+}
+
+void
+kbmapwrite(Req *req)
+{
+	char line[100], *lp, *b;
+	Rune r, *rp;
+	int sc, t, l;
+	Fid *f;
+
+	f = req->fid;
+	b = req->ifcall.data;
+	l = req->ifcall.count;
+	lp = line;
+	if(f->aux){
+		strcpy(line, f->aux);
+		lp = line+strlen(line);
+		free(f->aux);
+		f->aux = nil;
+	}
+	while(--l >= 0) {
+		*lp++  = *b++;
+		if(lp[-1] == '\n' || lp == &line[sizeof(line)-1]) {
+			*lp = 0;
+			if(*line == 0){
+			Badarg:
+				respond(req, Ebadarg);
+				return;
+			}
+			if(*line == '\n' || *line == '#'){
+				lp = line;
+				continue;
+			}
+			lp = line;
+			while(*lp == ' ' || *lp == '\t')
+				lp++;
+			t = strtoul(line, &lp, 0);
+			sc = strtoul(lp, &lp, 0);
+			while(*lp == ' ' || *lp == '\t')
+				lp++;
+			if((rp = kbmapent(t, sc)) == nil)
+				goto Badarg;
+			r = 0;
+			if(*lp == '\'' && lp[1])
+				chartorune(&r, lp+1);
+			else if(*lp == '^' && lp[1]){
+				chartorune(&r, lp+1);
+				if(0x40 <= r && r < 0x60)
+					r -= 0x40;
+				else
+					goto Badarg;
+			}else if(*lp == 'M' && ('1' <= lp[1] && lp[1] <= '5'))
+				r = 0xF900+lp[1]-'0';
+			else if(*lp>='0' && *lp<='9') /* includes 0x... */
+				r = strtoul(lp, &lp, 0);
+			else
+				goto Badarg;
+			*rp = r;
+			lp = line;
+		}
+	}
+	if(lp != line){
+		l = lp-line;
+		f->aux = lp = emalloc9p(l+1);
+		memmove(lp, line, l);
+		lp[l] = 0;
+	}
+	req->ofcall.count = req->ifcall.count;
+	respond(req, nil);
+}
+
+/*
+ * Filesystem
+ */
+
+static int
+fillstat(ulong qid, Dir *d)
+{
+	struct Qtab *t;
+
+	memset(d, 0, sizeof *d);
+	d->uid = "kbd";
+	d->gid = "kbd";
+	d->muid = "";
+	d->qid = (Qid){qid, 0, 0};
+	d->atime = time(0);
+	t = qtab + qid;
+	d->name = t->name;
+	d->qid.type = t->type;
+	d->mode = t->mode;
+	return 1;
+}
+
+static void
+fsattach(Req *r)
+{
+	char *spec;
+
+	spec = r->ifcall.aname;
+	if(spec && spec[0]){
+		respond(r, Ebadspec);
+		return;
+	}
+	r->fid->qid = (Qid){Qroot, 0, QTDIR};
+	r->ofcall.qid = r->fid->qid;
+	respond(r, nil);
+}
+
+static void
+fsstat(Req *r)
+{
+	fillstat((ulong)r->fid->qid.path, &r->d);
+	r->d.name = estrdup9p(r->d.name);
+	r->d.uid = estrdup9p(r->d.uid);
+	r->d.gid = estrdup9p(r->d.gid);
+	r->d.muid = estrdup9p(r->d.muid);
+	respond(r, nil);
+}
+
+static char*
+fswalk1(Fid *fid, char *name, Qid *qid)
+{
+	int i;
+	ulong path;
+
+	path = fid->qid.path;
+	switch(path){
+	case Qroot:
+		if (strcmp(name, "..") == 0) {
+			*qid = (Qid){Qroot, 0, QTDIR};
+			fid->qid = *qid;
+			return nil;
+		}
+		for(i = fid->qid.path; i<Nqid; i++){
+			if(strcmp(name, qtab[i].name) != 0)
+				continue;
+			*qid = (Qid){i, 0, 0};
+			fid->qid = *qid;
+			return nil;
+		}
+		return Enonexist;
+		
+	default:
+		return Ewalk;
+	}
+}
+
+static void
+fsopen(Req *r)
+{
+	Fid *f;
+	static int need[4] = { 4, 2, 6, 1 };
+	struct Qtab *t;
+	int n;
+
+	f = r->fid;
+	t = qtab + f->qid.path;
+	n = need[r->ifcall.mode & 3];
+	if((n & t->mode) != n)
+		respond(r, Eperm);
+	else{
+		f->aux = nil;
+		switch((ulong)f->qid.path){
+		case Qkbd:
+			if(kbdopen++ == 0)
+				sendul(ctlchan, Kbdflush);
+			break;
+		case Qcons:
+		case Qconsctl:
+			if(consopen++ == 0)
+				sendul(ctlchan, Rawoff);
+			break;
+		}
+		respond(r, nil);
+	}
+}
+
+static int
+readtopdir(Fid*, uchar *buf, long off, int cnt, int blen)
+{
+	int i, m, n;
+	long pos;
+	Dir d;
+
+	n = 0;
+	pos = 0;
+	for (i = 1; i < Nqid; i++){
+		fillstat(i, &d);
+		m = convD2M(&d, &buf[n], blen-n);
+		if(off <= pos){
+			if(m <= BIT16SZ || m > cnt)
+				break;
+			n += m;
+			cnt -= m;
+		}
+		pos += m;
+	}
+	return n;
+}
+
+static void
+fsread(Req *r)
+{
+	Fid *f;
+
+	f = r->fid;
+	switch((ulong)f->qid.path){
+	default:
+		respond(r, Efront);
+		return;
+
+	case Qroot:
+		r->ofcall.count = readtopdir(f, (void*)r->ofcall.data, r->ifcall.offset,
+			r->ifcall.count, r->ifcall.count);
+		break;
+
+	case Qkbd:
+	case Qcons:
+		sendp(reqchan, r);
+		return;
+
+	case Qkbmap:
+		kbmapread(r);
+		return;
+	}
+	respond(r, nil);
+}
+
+static void
+fswrite(Req *r)
+{
+	Fid *f;
+	char *p;
+	int n, i;
+
+	f = r->fid;
+	switch((ulong)f->qid.path){
+	default:
+		respond(r, Efront);
+		return;
+
+	case Qcons:
+		n = r->ifcall.count;
+		if(write(echofd, r->ifcall.data, n) != n){
+			responderror(r);
+			return;
+		}
+		r->ofcall.count = n;
+		break;
+
+	case Qconsctl:
+		p = r->ifcall.data;
+		n = r->ifcall.count;
+		if(n >= 5 && memcmp(p, "rawon", 5) == 0)
+			sendul(ctlchan, Rawon);
+		else if(n >= 6 && memcmp(p, "rawoff", 6) == 0)
+			sendul(ctlchan, Rawoff);
+		else {
+			respond(r, Ebadarg);
+			return;
+		}
+		r->ofcall.count = n;
+		break;
+
+	case Qkbin:
+		if(f->aux == nil){
+			f->aux = emalloc9p(sizeof(Scan));
+			memset(f->aux, 0, sizeof(Scan));
+		}
+		for(i=0; i<r->ifcall.count; i++)
+			kbdputsc((Scan*)f->aux, (uchar)r->ifcall.data[i]);
+		r->ofcall.count = i;
+		break;
+
+	case Qkbmap:
+		kbmapwrite(r);
+		return;
+
+	}
+	respond(r, nil);
+}
+
+static void
+fsflush(Req *r)
+{
+	switch((ulong)r->oldreq->fid->qid.path) {
+	case Qkbd:
+	case Qcons:
+		sendp(reqchan, r);
+		return;
+	}
+	respond(r, nil);
+}
+
+static void
+fsdestroyfid(Fid *f)
+{
+	void *p;
+
+	if(f->omode != -1)
+		switch((ulong)f->qid.path){
+		case Qkbin:
+		case Qkbmap:
+			if(p = f->aux){
+				f->aux = nil;
+				free(p);
+			}
+			break;
+		case Qkbd:
+			kbdopen--;
+			break;
+		case Qcons:
+		case Qconsctl:
+			consopen--;
+			break;
+		}
+}
+
+static void
+fsend(Srv*)
+{
+	threadexitsall(nil);
+}
+
+Srv fs = {
+	.attach=			fsattach,
+	.walk1=			fswalk1,
+	.open=			fsopen,
+	.read=			fsread,
+	.write=			fswrite,
+	.stat=			fsstat,
+	.flush=			fsflush,
+	.destroyfid=		fsdestroyfid,
+	.end=			fsend,
+};
+
+void
+reboot(void)
+{
+	int fd;
+
+	if((fd = open("/dev/reboot", OWRITE)) < 0){
+		fprint(2, "can't open /dev/reboot: %r\n");
+		return;
+	}
+	fprint(fd, "reboot\n");
+	close(fd);
+}
+
+void
+elevate(void)
+{
+	char buf[128];
+	Dir *d, nd;
+	int fd;
+
+	snprint(buf, sizeof(buf), "/proc/%d/ctl", getpid());
+	if((fd = open(buf, OWRITE)) < 0){
+		fprint(2, "can't open %s: %r\n", buf);
+		return;
+	}
+
+	/* get higher than normal priority */
+	fprint(fd, "pri 16");
+
+	/* dont let anybody kill us */
+	if(d = dirfstat(fd)){
+		nulldir(&nd);
+		nd.mode = d->mode & ~0222;
+		dirfwstat(fd, &nd);
+		free(d);
+	}
+
+	close(fd);
+	
+}
+
+void
+usage(void)
+{
+	fprint(2, "usage: kbdfs [-m mntpnt]\n");
+	exits("usage");
+}
+
+void
+threadmain(int argc, char** argv)
+{
+	char *mtpt = "/dev";
+	char *srv = nil;
+	char *cons = nil;
+
+	consfd = -1;
+	echofd = 1;
+
+	ARGBEGIN{
+	case 'D':
+		chatty9p++;
+		break;
+	case 'm':
+		mtpt = EARGF(usage());
+		break;
+	case 's':
+		srv = EARGF(usage());
+		break;
+	case 'c':
+		cons = EARGF(usage());
+		break;
+	default:
+		usage();
+	}ARGEND
+
+	if((scanfd = open("/dev/scancode", OREAD)) < 0)
+		fprint(2, "can't open /dev/scancode: %r\n");
+	if((ledsfd = open("/dev/leds", OWRITE)) < 0)
+		fprint(2, "can't open /dev/leds: %r\n");
+
+	if(cons){
+		if((consfd = open(cons, ORDWR)) < 0)
+			fprint(2, "can't open %s: %r\n", cons);
+		else
+			echofd = consfd;
+	}	
+	
+	keychan = chancreate(sizeof(Key), 8);
+	reqchan = chancreate(sizeof(Req*), 0);
+	ctlchan = chancreate(sizeof(int), 0);
+	rawchan = chancreate(sizeof(Rune), 32);
+	linechan = chancreate(sizeof(char*), 16);
+	kbdchan = chancreate(sizeof(char*), 16);
+
+	elevate();
+
+	procrfork(ctlproc, nil, STACKSZ, RFNAMEG|RFNOTEG);
+
+	threadpostmountsrv(&fs, srv, mtpt, MBEFORE);
+}
--- a/sys/src/cmd/aux/mkfile
+++ b/sys/src/cmd/aux/mkfile
@@ -16,6 +16,7 @@
 	depend\
 	disksim\
 	getflags\
+	kbdfs\
 	lines\
 	listen\
 	listen1\