diff mbox

kvm tools: Add support for PS/2 keyboard system

Message ID 1306940036-15540-1-git-send-email-levinsasha928@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Sasha Levin June 1, 2011, 2:53 p.m. UTC
From: John Floren <john@jfloren.net>

Add support for PS/2 keyboard system with AUX device (aka mouse).
The device works with vnc, the guest must be started with the
'--vnc' parameter for the device to be initialized.

Signed-off-by: John Floren <john@jfloren.net>
[ turn into patch and clean up code ]
Signed-off-by: Sasha Levin <levinsasha928@gmail.com>
---
 tools/kvm/Makefile            |    1 +
 tools/kvm/hw/pckbd.c          |  475 +++++++++++++++++++++++++++++++++++++++++
 tools/kvm/hw/vesa.c           |    3 +
 tools/kvm/include/kvm/pckbd.h |   19 ++
 tools/kvm/ioport.c            |    4 -
 tools/kvm/kvm-run.c           |    5 +-
 6 files changed, 502 insertions(+), 5 deletions(-)
 create mode 100644 tools/kvm/hw/pckbd.c
 create mode 100644 tools/kvm/include/kvm/pckbd.h

Comments

Ingo Molnar June 2, 2011, 7:21 a.m. UTC | #1
* Sasha Levin <levinsasha928@gmail.com> wrote:

> From: John Floren <john@jfloren.net>
> 
> Add support for PS/2 keyboard system with AUX device (aka mouse).
> The device works with vnc, the guest must be started with the
> '--vnc' parameter for the device to be initialized.
> 
> Signed-off-by: John Floren <john@jfloren.net>
> [ turn into patch and clean up code ]
> Signed-off-by: Sasha Levin <levinsasha928@gmail.com>
> ---
>  tools/kvm/Makefile            |    1 +
>  tools/kvm/hw/pckbd.c          |  475 +++++++++++++++++++++++++++++++++++++++++

Since this is a PS2 keyboard and mouse driver please name it 
i8042.[ch] like the kernel-side does.

that will also make it easier to find the guest-side <-> host-side 
pair of guest-driver/host-driver files :-)

>  tools/kvm/hw/vesa.c           |    3 +
>  tools/kvm/include/kvm/pckbd.h |   19 ++
>  tools/kvm/ioport.c            |    4 -
>  tools/kvm/kvm-run.c           |    5 +-
>  6 files changed, 502 insertions(+), 5 deletions(-)
>  create mode 100644 tools/kvm/hw/pckbd.c
>  create mode 100644 tools/kvm/include/kvm/pckbd.h

In general the code looks very clean to me!

Found two minor nits:

> +		ret = state.kq[state.kread++ % QUEUE_SIZE];
> +		ret = state.mq[state.mread++ % QUEUE_SIZE];

Please increment the index explicitly, on a separate line - the 
permanent side-effect to the keyboard state is not obvious at first 
sight.

Thanks,

	Ingo
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Pekka Enberg June 2, 2011, 8:52 a.m. UTC | #2
On Wed, 1 Jun 2011, Sasha Levin wrote:
> From: John Floren <john@jfloren.net>
>
> Add support for PS/2 keyboard system with AUX device (aka mouse).
> The device works with vnc, the guest must be started with the
> '--vnc' parameter for the device to be initialized.
>
> Signed-off-by: John Floren <john@jfloren.net>
> [ turn into patch and clean up code ]
> Signed-off-by: Sasha Levin <levinsasha928@gmail.com>

I am seeing this in my guest dmesg:

[   24.972609] i8042: PNP: No PS/2 controller found. Probing ports 
directly.
[   25.568982] i8042: Can't read CTR while initializing i8042
[   25.568996] i8042: probe of i8042 failed with error -5
[   25.569102] mousedev: PS/2 mouse device common for all mice

and nothing happens when I try to press some keys in vncviewer. There's a 
small blinking thingy following the mouse which I assume is our guest 
mouse cursor?

 			Pekka
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Pekka Enberg June 2, 2011, 8:57 a.m. UTC | #3
On Thu, 2 Jun 2011, Ingo Molnar wrote:
> Since this is a PS2 keyboard and mouse driver please name it
> i8042.[ch] like the kernel-side does.
>
> that will also make it easier to find the guest-side <-> host-side
> pair of guest-driver/host-driver files :-)

Fixed, thanks!
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Sasha Levin June 2, 2011, 9:10 a.m. UTC | #4
On Thu, 2011-06-02 at 11:52 +0300, Pekka Enberg wrote:
> On Wed, 1 Jun 2011, Sasha Levin wrote:
> > From: John Floren <john@jfloren.net>
> >
> > Add support for PS/2 keyboard system with AUX device (aka mouse).
> > The device works with vnc, the guest must be started with the
> > '--vnc' parameter for the device to be initialized.
> >
> > Signed-off-by: John Floren <john@jfloren.net>
> > [ turn into patch and clean up code ]
> > Signed-off-by: Sasha Levin <levinsasha928@gmail.com>
> 
> I am seeing this in my guest dmesg:
> 
> [   24.972609] i8042: PNP: No PS/2 controller found. Probing ports 
> directly.
> [   25.568982] i8042: Can't read CTR while initializing i8042
> [   25.568996] i8042: probe of i8042 failed with error -5
> [   25.569102] mousedev: PS/2 mouse device common for all mice
> 
> and nothing happens when I try to press some keys in vncviewer. There's a 
> small blinking thingy following the mouse which I assume is our guest 
> mouse cursor?

Strange, those are the same errors you're supposed to get when theres no
i8042 device at all.

Could you verify that hw/pckbd.c is being built? Did the makefile get
updated?

What I'm seeing here is:

[   49.326316] i8042: PNP: No PS/2 controller found. Probing ports
directly.
[   50.410492] serio: i8042 KBD port at 0x60,0x64 irq 1
[   51.518829] serio: i8042 AUX port at 0x60,0x64 irq 12
[   51.519104] mousedev: PS/2 mouse device common for all mice
Pekka Enberg June 2, 2011, 9:15 a.m. UTC | #5
On Thu, 2 Jun 2011, Sasha Levin wrote:
> Strange, those are the same errors you're supposed to get when theres no
> i8042 device at all.
>
> Could you verify that hw/pckbd.c is being built? Did the makefile get
> updated?
>
> What I'm seeing here is:
>
> [   49.326316] i8042: PNP: No PS/2 controller found. Probing ports
> directly.
> [   50.410492] serio: i8042 KBD port at 0x60,0x64 irq 1
> [   51.518829] serio: i8042 AUX port at 0x60,0x64 irq 12
> [   51.519104] mousedev: PS/2 mouse device common for all mice

kbd_init() is called if that's what you wanted to know.

 			Pekka
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Ingo Molnar June 2, 2011, 9:18 a.m. UTC | #6
* Pekka Enberg <penberg@kernel.org> wrote:

> On Thu, 2 Jun 2011, Sasha Levin wrote:
> >Strange, those are the same errors you're supposed to get when theres no
> >i8042 device at all.
> >
> >Could you verify that hw/pckbd.c is being built? Did the makefile get
> >updated?
> >
> >What I'm seeing here is:
> >
> >[   49.326316] i8042: PNP: No PS/2 controller found. Probing ports
> >directly.
> >[   50.410492] serio: i8042 KBD port at 0x60,0x64 irq 1
> >[   51.518829] serio: i8042 AUX port at 0x60,0x64 irq 12
> >[   51.519104] mousedev: PS/2 mouse device common for all mice
> 
> kbd_init() is called if that's what you wanted to know.

btw., i suspect port IO printf()s in i8042.c would be rather 
informative now: they would nicely and synchronously interlace with 
guest serial console output, showing exactly what is going on in the 
probing sequence.

Thanks,

	Ingo
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Pekka Enberg June 2, 2011, 9:23 a.m. UTC | #7
On Thu, 2011-06-02 at 11:18 +0200, Ingo Molnar wrote:
> * Pekka Enberg <penberg@kernel.org> wrote:
> 
> > On Thu, 2 Jun 2011, Sasha Levin wrote:
> > >Strange, those are the same errors you're supposed to get when theres no
> > >i8042 device at all.
> > >
> > >Could you verify that hw/pckbd.c is being built? Did the makefile get
> > >updated?
> > >
> > >What I'm seeing here is:
> > >
> > >[   49.326316] i8042: PNP: No PS/2 controller found. Probing ports
> > >directly.
> > >[   50.410492] serio: i8042 KBD port at 0x60,0x64 irq 1
> > >[   51.518829] serio: i8042 AUX port at 0x60,0x64 irq 12
> > >[   51.519104] mousedev: PS/2 mouse device common for all mice
> > 
> > kbd_init() is called if that's what you wanted to know.
> 
> btw., i suspect port IO printf()s in i8042.c would be rather 
> informative now: they would nicely and synchronously interlace with 
> guest serial console output, showing exactly what is going on in the 
> probing sequence.

Do you mean a generic ioport tracing thing?

  ./kvm run --trace=ioport

or something?

			Pekka


--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Sasha Levin June 2, 2011, 9:34 a.m. UTC | #8
On Thu, 2011-06-02 at 12:15 +0300, Pekka Enberg wrote:
> On Thu, 2 Jun 2011, Sasha Levin wrote:
> > Strange, those are the same errors you're supposed to get when theres no
> > i8042 device at all.
> >
> > Could you verify that hw/pckbd.c is being built? Did the makefile get
> > updated?
> >
> > What I'm seeing here is:
> >
> > [   49.326316] i8042: PNP: No PS/2 controller found. Probing ports
> > directly.
> > [   50.410492] serio: i8042 KBD port at 0x60,0x64 irq 1
> > [   51.518829] serio: i8042 AUX port at 0x60,0x64 irq 12
> > [   51.519104] mousedev: PS/2 mouse device common for all mice
> 
> kbd_init() is called if that's what you wanted to know.

I've pulled master, and tried running keyboard on it. What I see now is:

[   33.114810] i8042: PNP: No PS/2 controller found. Probing ports
directly.
[   33.365370] serio: i8042 KBD port at 0x60,0x64 irq 1
[   33.365599] mousedev: PS/2 mouse device common for all mice

Which means that the mouse is gone. Might be related to what you're
seeing.
Ingo Molnar June 2, 2011, 9:34 a.m. UTC | #9
* Pekka Enberg <penberg@kernel.org> wrote:

> On Thu, 2011-06-02 at 11:18 +0200, Ingo Molnar wrote:
> > * Pekka Enberg <penberg@kernel.org> wrote:
> > 
> > > On Thu, 2 Jun 2011, Sasha Levin wrote:
> > > >Strange, those are the same errors you're supposed to get when theres no
> > > >i8042 device at all.
> > > >
> > > >Could you verify that hw/pckbd.c is being built? Did the makefile get
> > > >updated?
> > > >
> > > >What I'm seeing here is:
> > > >
> > > >[   49.326316] i8042: PNP: No PS/2 controller found. Probing ports
> > > >directly.
> > > >[   50.410492] serio: i8042 KBD port at 0x60,0x64 irq 1
> > > >[   51.518829] serio: i8042 AUX port at 0x60,0x64 irq 12
> > > >[   51.519104] mousedev: PS/2 mouse device common for all mice
> > > 
> > > kbd_init() is called if that's what you wanted to know.
> > 
> > btw., i suspect port IO printf()s in i8042.c would be rather 
> > informative now: they would nicely and synchronously interlace with 
> > guest serial console output, showing exactly what is going on in the 
> > probing sequence.
> 
> Do you mean a generic ioport tracing thing?
> 
>   ./kvm run --trace=ioport
> 
> or something?

heh, yeah :-)

btw., i'm getting the same message here:

[    1.458000] usbcore: registered new interface driver libusual
[    1.460000] i8042: PNP: No PS/2 controller found. Probing ports directly.
[    1.463000] i8042: Can't read CTR while initializing i8042
[    1.464000] i8042: probe of i8042 failed with error -5
[    1.465000] mousedev: PS/2 mouse device common for all mice
[    1.467000] rtc_cmos rtc_cmos: rtc core: registered rtc_cmos as rtc0

Thanks,

	Ingo
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Ingo Molnar June 2, 2011, 9:42 a.m. UTC | #10
btw., i tried to match up the kernel's i8042.c with kvm's and it was 
pretty hard due to arbitrary differences. The kernel knows about:

------------------->
/*
 * Status register bits.
 */

#define I8042_STR_PARITY        0x80
#define I8042_STR_TIMEOUT       0x40
#define I8042_STR_AUXDATA       0x20
#define I8042_STR_KEYLOCK       0x10
#define I8042_STR_CMDDAT        0x08
#define I8042_STR_MUXERR        0x04
#define I8042_STR_IBF           0x02
#define I8042_STR_OBF           0x01

/*
 * Control register bits.
 */

#define I8042_CTR_KBDINT        0x01
#define I8042_CTR_AUXINT        0x02
#define I8042_CTR_IGNKEYLOCK    0x08
#define I8042_CTR_KBDDIS        0x10
#define I8042_CTR_AUXDIS        0x20
#define I8042_CTR_XLATE         0x40

/*
 * Return codes.
 */

#define I8042_RET_CTL_TEST      0x55
<-------------------


While hw/i8042.c knows about similar things, just under different 
names:

------------------->
#define CMD_READ_MODE           0x20
#define CMD_WRITE_MODE          0x60
#define CMD_WRITE_AUX_BUF       0xD3
#define CMD_WRITE_AUX           0xD4
#define CMD_TEST_AUX            0xA9
#define CMD_DISABLE_AUX         0xA7
#define CMD_ENABLE_AUX          0xA8

#define RESPONSE_ACK            0xFA

#define MODE_DISABLE_AUX        0x20

#define AUX_ENABLE_REPORTING    0x20
#define AUX_SCALING_FLAG        0x10
#define AUX_DEFAULT_RESOLUTION  0x2
#define AUX_DEFAULT_SAMPLE      100

#define KBD_STATUS_SYS          0x4
#define KBD_STATUS_A2           0x8
#define KBD_STATUS_INH          0x10
#define KBD_STATUS_OBF          0x01
#define KBD_STATUS_AUX_OBF      0x20

#define KBD_MODE_KBD_INT        0x01
#define KBD_MODE_SYS            0x02
<-------------------

Would it be possible for hw/i8042.c to use the kernel names, to make 
it easier to review and debug this code?

(also, 0x2 should be 0x02 to make it easier to review as well.)

Thanks,

	Ingo
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/tools/kvm/Makefile b/tools/kvm/Makefile
index e7ceb5c..c0feae0 100644
--- a/tools/kvm/Makefile
+++ b/tools/kvm/Makefile
@@ -64,6 +64,7 @@  has_vncserver := $(call try-cc,$(SOURCE_VNCSERVER),$(FLAGS_VNCSERVER))
 ifeq ($(has_vncserver),y)
 	CFLAGS	+= -DCONFIG_HAS_VNCSERVER
 	OBJS	+= hw/vesa.o
+	OBJS    += hw/pckbd.o
 	LIBS	+= -lvncserver
 endif
 
diff --git a/tools/kvm/hw/pckbd.c b/tools/kvm/hw/pckbd.c
new file mode 100644
index 0000000..14d074d
--- /dev/null
+++ b/tools/kvm/hw/pckbd.c
@@ -0,0 +1,475 @@ 
+#include "kvm/read-write.h"
+#include "kvm/ioport.h"
+#include "kvm/mutex.h"
+#include "kvm/util.h"
+#include "kvm/term.h"
+#include "kvm/kvm.h"
+#include "kvm/pckbd.h"
+
+#include <rfb/keysym.h>
+#include <rfb/rfb.h>
+#include <stdint.h>
+
+#define KBD_IRQ			1
+#define AUX_IRQ			12
+
+#define CMD_READ_MODE		0x20
+#define CMD_WRITE_MODE		0x60
+#define CMD_WRITE_AUX_BUF	0xD3
+#define CMD_WRITE_AUX		0xD4
+#define CMD_TEST_AUX		0xA9
+#define CMD_DISABLE_AUX		0xA7
+#define CMD_ENABLE_AUX		0xA8
+
+#define RESPONSE_ACK		0xFA
+
+#define MODE_DISABLE_AUX	0x20
+
+#define AUX_ENABLE_REPORTING	0x20
+#define AUX_SCALING_FLAG	0x10
+#define AUX_DEFAULT_RESOLUTION	0x2
+#define AUX_DEFAULT_SAMPLE	100
+
+#define KBD_STATUS_SYS		0x4
+#define KBD_STATUS_A2		0x8
+#define KBD_STATUS_INH		0x10
+#define KBD_STATUS_OBF		0x01
+#define KBD_STATUS_AUX_OBF	0x20
+
+#define KBD_MODE_KBD_INT	0x01
+#define KBD_MODE_SYS		0x02
+
+#define QUEUE_SIZE		128
+
+/*
+ * This represents the current state of the PS/2 keyboard system,
+ * including the AUX device (the mouse)
+ */
+struct kbd_state {
+	struct kvm		*kvm;
+
+	char			kq[QUEUE_SIZE];	/* Keyboard queue */
+	int			kread, kwrite;	/* Indexes into the queue */
+	int			kcount;		/* number of elements in queue */
+
+	char			mq[QUEUE_SIZE];
+	int			mread, mwrite;
+	int			mcount;
+
+	u8			mstatus;	/* Mouse status byte */
+	u8			mres;		/* Current mouse resolution */
+	u8			msample;	/* Current mouse samples/second */
+
+	u8			mode;		/* i8042 mode register */
+	u8			status;		/* i8042 status register */
+	/*
+	 * Some commands (on port 0x64) have arguments;
+	 * we store the command here while we wait for the argument
+	 */
+	u32			write_cmd;
+};
+
+static struct kbd_state		state;
+
+/*
+ * If there are packets to be read, set the appropriate IRQs high
+ */
+static void kbd_update_irq(void)
+{
+	u8 klevel = 0;
+	u8 mlevel = 0;
+
+	/* First, clear the kbd and aux output buffer full bits */
+	state.status &= ~(KBD_STATUS_OBF | KBD_STATUS_AUX_OBF);
+
+	if (state.kcount > 0) {
+		state.status |= KBD_STATUS_OBF;
+		klevel = 1;
+	}
+
+	/* Keyboard has higher priority than mouse */
+	if (klevel == 0 && state.mcount != 0) {
+		state.status |= KBD_STATUS_OBF | KBD_STATUS_AUX_OBF;
+		mlevel = 1;
+	}
+
+	kvm__irq_line(state.kvm, KBD_IRQ, klevel);
+	kvm__irq_line(state.kvm, AUX_IRQ, mlevel);
+}
+
+/*
+ * Add a byte to the mouse queue, then set IRQs
+ */
+static void mouse_queue(u8 c)
+{
+	if (state.mcount >= QUEUE_SIZE)
+		return;
+
+	state.mq[state.mwrite++ % QUEUE_SIZE] = c;
+
+	state.mcount++;
+	kbd_update_irq();
+}
+
+/*
+ * Add a byte to the keyboard queue, then set IRQs
+ */
+static void kbd_queue(u8 c)
+{
+	if (state.kcount >= QUEUE_SIZE)
+		return;
+
+	state.kq[state.kwrite++ % QUEUE_SIZE] = c;
+
+	state.kcount++;
+	kbd_update_irq();
+}
+
+/*
+ * This function is called when the OS issues a write to port 0x64
+ */
+static void kbd_write_command(u32 val)
+{
+	switch (val) {
+	case CMD_READ_MODE:
+		kbd_queue(state.mode);
+		break;
+	case CMD_WRITE_MODE:
+	case CMD_WRITE_AUX:
+	case CMD_WRITE_AUX_BUF:
+		state.write_cmd = val;
+		break;
+	case CMD_TEST_AUX:
+		/* 0 means we're a normal PS/2 mouse */
+		mouse_queue(0);
+		break;
+	case CMD_DISABLE_AUX:
+		state.mode |= MODE_DISABLE_AUX;
+		break;
+	case CMD_ENABLE_AUX:
+		state.mode &= ~MODE_DISABLE_AUX;
+		break;
+	default:
+		break;
+	}
+}
+
+/*
+ * Called when the OS reads from port 0x60 (PS/2 data)
+ */
+static u32 kbd_read_data(void)
+{
+	u32 ret;
+	int i;
+
+	if (state.kcount != 0) {
+		/* Keyboard data gets read first */
+		ret = state.kq[state.kread++ % QUEUE_SIZE];
+		state.kcount--;
+		kvm__irq_line(state.kvm, KBD_IRQ, 0);
+		kbd_update_irq();
+	} else if (state.mcount > 0) {
+		/* Followed by the mouse */
+		ret = state.mq[state.mread++ % QUEUE_SIZE];
+		state.mcount--;
+		kvm__irq_line(state.kvm, AUX_IRQ, 0);
+		kbd_update_irq();
+	} else if (state.kcount == 0) {
+		i = state.kread - 1;
+		if (i < 0)
+			i = QUEUE_SIZE;
+		ret = state.kq[i];
+	}
+	return ret;
+}
+
+/*
+ * Called when the OS read from port 0x64, the command port
+ */
+static u32 kbd_read_status(void)
+{
+	return (u32)state.status;
+}
+
+/*
+ * Called when the OS writes to port 0x60 (data port)
+ * Things written here are generally arguments to commands previously
+ * written to port 0x64 and stored in state.write_cmd
+ */
+static void kbd_write_data(u32 val)
+{
+	switch (state.write_cmd) {
+	case CMD_WRITE_MODE:
+		state.mode = val;
+		kbd_update_irq();
+		break;
+	case CMD_WRITE_AUX_BUF:
+		mouse_queue(val);
+		mouse_queue(RESPONSE_ACK);
+		break;
+	case CMD_WRITE_AUX:
+		/* The OS wants to send a command to the mouse */
+		mouse_queue(RESPONSE_ACK);
+		switch (val) {
+		case 0xe6:
+			/* set scaling = 1:1 */
+			state.mstatus &= ~AUX_SCALING_FLAG;
+			break;
+		case 0xe8:
+			/* set resolution */
+			state.mres = val;
+			break;
+		case 0xe9:
+			/* Report mouse status/config */
+			mouse_queue(state.mstatus);
+			mouse_queue(state.mres);
+			mouse_queue(state.msample);
+			break;
+		case 0xf2:
+			/* send ID */
+			mouse_queue(0); /* normal mouse */
+			break;
+		case 0xf3:
+			/* set sample rate */
+			state.msample = val;
+			break;
+		case 0xf4:
+			/* enable reporting */
+			state.mstatus |= AUX_ENABLE_REPORTING;
+			break;
+		case 0xf5:
+			state.mstatus &= ~AUX_ENABLE_REPORTING;
+			break;
+		case 0xf6:
+			/* set defaults, just fall through to reset */
+		case 0xff:
+			/* reset */
+			state.mstatus = 0x0;
+			state.mres = AUX_DEFAULT_RESOLUTION;
+			state.msample = AUX_DEFAULT_SAMPLE;
+			break;
+		default:
+			break;
+	}
+	break;
+	case 0:
+		/* Just send the ID */
+		kbd_queue(RESPONSE_ACK);
+		kbd_queue(0xab);
+		kbd_queue(0x41);
+		kbd_update_irq();
+		break;
+	default:
+		/* Yeah whatever */
+		break;
+	}
+	state.write_cmd = 0;
+}
+
+static void kbd_reset(void)
+{
+	state = (struct kbd_state) {
+		.status		= KBD_STATUS_SYS | KBD_STATUS_A2 | KBD_STATUS_INH, /* 0x1c */
+		.mode		= KBD_MODE_KBD_INT | KBD_MODE_SYS, /* 0x3 */
+		.mres		= AUX_DEFAULT_RESOLUTION,
+		.msample	= AUX_DEFAULT_SAMPLE,
+	};
+}
+
+/*
+ * We can map the letters and numbers without a fuss,
+ * but the other characters not so much.
+ */
+static char letters[26] = {
+	0x1c, 0x32, 0x21, 0x23, 0x24, /* a-e */
+	0x2b, 0x34, 0x33, 0x43, 0x3b, /* f-j */
+	0x42, 0x4b, 0x3a, 0x31, 0x44, /* k-o */
+	0x4d, 0x15, 0x2d, 0x1b, 0x2c, /* p-t */
+	0x3c, 0x2a, 0x1d, 0x22, 0x35, /* u-y */
+	0x1a,
+};
+
+static char num[10] = {
+	0x45, 0x16, 0x1e, 0x26, 0x2e, 0x23, 0x36, 0x3d, 0x3e, 0x46,
+};
+
+/*
+ * This is called when the VNC server receives a key event
+ * The reason this function is such a beast is that we have
+ * to convert from ASCII characters (which is what VNC gets)
+ * to PC keyboard scancodes, which is what Linux expects to
+ * get from its keyboard. ASCII and the scancode set don't
+ * really seem to mesh in any good way beyond some basics with
+ * the letters and numbers.
+ */
+void kbd_handle_key(rfbBool down, rfbKeySym key, rfbClientPtr cl)
+{
+	char tosend = 0;
+
+	if (key >= 0x41 && key <= 0x5a)
+		key += 0x20; /* convert to lowercase */
+
+	if (key >= 0x61 && key <= 0x7a) /* a-z */
+		tosend = letters[key - 0x61];
+
+	if (key >= 0x30 && key <= 0x39)
+		tosend = num[key - 0x30];
+
+	switch (key) {
+	case XK_Insert:		kbd_queue(0xe0);	tosend = 0x70;	break;
+	case XK_Delete:		kbd_queue(0xe0);	tosend = 0x71;	break;
+	case XK_Up:		kbd_queue(0xe0);	tosend = 0x75;	break;
+	case XK_Down:		kbd_queue(0xe0);	tosend = 0x72;	break;
+	case XK_Left:		kbd_queue(0xe0);	tosend = 0x6b;	break;
+	case XK_Right:		kbd_queue(0xe0);	tosend = 0x74;	break;
+	case XK_Page_Up:	kbd_queue(0xe0);	tosend = 0x7d;	break;
+	case XK_Page_Down:	kbd_queue(0xe0);	tosend = 0x7a;	break;
+	case XK_Home:		kbd_queue(0xe0);	tosend = 0x6c;	break;
+	case XK_BackSpace:	tosend = 0x66;		break;
+	case XK_Tab:		tosend = 0x0d;		break;
+	case XK_Return:		tosend = 0x5a;		break;
+	case XK_Escape:		tosend = 0x76;		break;
+	case XK_End:		tosend = 0x69;		break;
+	case XK_Shift_L:	tosend = 0x12;		break;
+	case XK_Shift_R:	tosend = 0x59;		break;
+	case XK_Control_R:	kbd_queue(0xe0);
+	case XK_Control_L:	tosend = 0x14;		break;
+	case XK_Alt_R:		kbd_queue(0xe0);
+	case XK_Alt_L:		tosend = 0x11;		break;
+	case XK_quoteleft:	tosend = 0x0e;		break;
+	case XK_minus:		tosend = 0x4e;		break;
+	case XK_equal:		tosend = 0x55;		break;
+	case XK_bracketleft:	tosend = 0x54;		break;
+	case XK_bracketright:	tosend = 0x5b;		break;
+	case XK_backslash:	tosend = 0x5d;		break;
+	case XK_Caps_Lock:	tosend = 0x58;		break;
+	case XK_semicolon:	tosend = 0x4c;		break;
+	case XK_quoteright:	tosend = 0x52;		break;
+	case XK_comma:		tosend = 0x41;		break;
+	case XK_period:		tosend = 0x49;		break;
+	case XK_slash:		tosend = 0x4a;		break;
+	case XK_space:		tosend = 0x29;		break;
+
+	/*
+	 * This is where I handle the shifted characters.
+	 * They don't really map nicely the way A-Z maps to a-z,
+	 * so I'm doing it manually
+	 */
+	case XK_exclam:		tosend = 0x16;		break;
+	case XK_quotedbl:	tosend = 0x52;		break;
+	case XK_numbersign:	tosend = 0x26;		break;
+	case XK_dollar:		tosend = 0x25;		break;
+	case XK_percent:	tosend = 0x2e;		break;
+	case XK_ampersand:	tosend = 0x3d;		break;
+	case XK_parenleft:	tosend = 0x46;		break;
+	case XK_parenright:	tosend = 0x45;		break;
+	case XK_asterisk:	tosend = 0x3e;		break;
+	case XK_plus:		tosend = 0x55;		break;
+	case XK_colon:		tosend = 0x4c;		break;
+	case XK_less:		tosend = 0x41;		break;
+	case XK_greater:	tosend = 0x49;		break;
+	case XK_question:	tosend = 0x4a;		break;
+	case XK_at:		tosend = 0x1e;		break;
+	case XK_asciicircum:	tosend = 0x36;		break;
+	case XK_underscore:	tosend = 0x4e;		break;
+	case XK_braceleft:	tosend = 0x54;		break;
+	case XK_braceright:	tosend = 0x5b;		break;
+	case XK_bar:		tosend = 0x5d;		break;
+	case XK_asciitilde:	tosend = 0x0e;		break;
+	default:		break;
+	}
+
+	/*
+	 * If this is a "key up" event (the user has released the key, we
+	 * need to send 0xf0 first.
+	 */
+	if (!down && tosend != 0x0)
+		kbd_queue(0xf0);
+
+	if (tosend)
+		kbd_queue(tosend);
+}
+
+/* The previous X and Y coordinates of the mouse */
+static int xlast, ylast = -1;
+
+/*
+ * This function is called by the VNC server whenever a mouse event occurs.
+ */
+void kbd_handle_ptr(int buttonMask, int x, int y, rfbClientPtr cl)
+{
+	int dx, dy;
+	char b1 = 0x8;
+
+	/* The VNC mask and the PS/2 button encoding are the same */
+	b1 |= buttonMask;
+
+	if (xlast >= 0 && ylast >= 0) {
+		/* The PS/2 mouse sends deltas, not absolutes */
+		dx = x - xlast;
+		dy = ylast - y;
+
+		/* Set overflow bits if needed */
+		if (dy > 255)
+			b1 |= 0x80;
+		if (dx > 255)
+			b1 |= 0x40;
+
+		/* Set negative bits if needed */
+		if (dy < 0)
+			b1 |= 0x20;
+		if (dx < 0)
+			b1 |= 0x10;
+
+		mouse_queue(b1);
+		mouse_queue(dx);
+		mouse_queue(dy);
+	}
+
+	xlast = x;
+	ylast = y;
+	rfbDefaultPtrAddEvent(buttonMask, x, y, cl);
+}
+
+/*
+ * Called when the OS has written to one of the keyboard's ports (0x60 or 0x64)
+ */
+static bool kbd_in(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size, u32 count)
+{
+	u32 result;
+
+	if (port == 0x64) {
+		result = kbd_read_status();
+		ioport__write8(data, (char)result);
+	} else {
+		result = kbd_read_data();
+		ioport__write32(data, result);
+	}
+	return true;
+}
+
+/*
+ * Called when the OS attempts to read from a keyboard port (0x60 or 0x64)
+ */
+static bool kbd_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size, u32 count)
+{
+	if (port == 0x64)
+		kbd_write_command(*((u32 *)data));
+	else
+		kbd_write_data(*((u32 *)data));
+
+	return true;
+}
+
+static struct ioport_operations kbd_ops = {
+	.io_in		= kbd_in,
+	.io_out		= kbd_out,
+};
+
+void kbd__init(struct kvm *kvm)
+{
+	kbd_reset();
+	state.kvm = kvm;
+	ioport__register(0x60, &kbd_ops, 2, NULL);
+	ioport__register(0x64, &kbd_ops, 2, NULL);
+}
diff --git a/tools/kvm/hw/vesa.c b/tools/kvm/hw/vesa.c
index 85fe1a9..07d622b 100644
--- a/tools/kvm/hw/vesa.c
+++ b/tools/kvm/hw/vesa.c
@@ -7,6 +7,7 @@ 
 #include "kvm/irq.h"
 #include "kvm/kvm.h"
 #include "kvm/pci.h"
+#include "kvm/pckbd.h"
 
 #include <sys/types.h>
 #include <sys/ioctl.h>
@@ -99,6 +100,8 @@  void *vesa__dovnc(void *v)
 	server = rfbGetScreen(&argc, (char **) argv, VESA_WIDTH, VESA_HEIGHT, 8, 3, 4);
 	server->frameBuffer		= videomem;
 	server->alwaysShared		= TRUE;
+	server->kbdAddEvent		= kbd_handle_key;
+	server->ptrAddEvent		= kbd_handle_ptr;
 	rfbInitServer(server);
 
 	while (rfbIsActive(server)) {
diff --git a/tools/kvm/include/kvm/pckbd.h b/tools/kvm/include/kvm/pckbd.h
new file mode 100644
index 0000000..3416b64
--- /dev/null
+++ b/tools/kvm/include/kvm/pckbd.h
@@ -0,0 +1,19 @@ 
+#ifndef KVM__PCKBD_H
+#define KVM__PCKBD_H
+
+void kbd__init(struct kvm *kvm);
+
+#ifdef CONFIG_HAS_VNCSERVER
+#include <rfb/keysym.h>
+#include <rfb/rfb.h>
+
+void kbd_handle_key(rfbBool, rfbKeySym, rfbClientPtr);
+void kbd_handle_ptr(int, int, int, rfbClientPtr);
+
+#else
+
+void kbd__init(struct kvm *kvm) { }
+
+#endif
+
+#endif
diff --git a/tools/kvm/ioport.c b/tools/kvm/ioport.c
index d0a1aa8..706d635 100644
--- a/tools/kvm/ioport.c
+++ b/tools/kvm/ioport.c
@@ -160,10 +160,6 @@  void ioport__setup_legacy(void)
 	/* PORT 0040-005F - PIT - PROGRAMMABLE INTERVAL TIMER (8253, 8254) */
 	ioport__register(0x0040, &dummy_read_write_ioport_ops, 4, NULL);
 
-	/* PORT 0060-006F - KEYBOARD CONTROLLER 804x (8041, 8042) (or PPI (8255) on PC,XT) */
-	ioport__register(0x0060, &dummy_read_write_ioport_ops, 2, NULL);
-	ioport__register(0x0064, &dummy_read_write_ioport_ops, 1, NULL);
-
 	/* 0x00A0 - 0x00AF - 8259A PIC 2 */
 	ioport__register(0x00A0, &dummy_read_write_ioport_ops, 2, NULL);
 
diff --git a/tools/kvm/kvm-run.c b/tools/kvm/kvm-run.c
index 48b8e70..60204a1 100644
--- a/tools/kvm/kvm-run.c
+++ b/tools/kvm/kvm-run.c
@@ -30,6 +30,7 @@ 
 #include <kvm/virtio-9p.h>
 #include <kvm/vesa.h>
 #include <kvm/ioeventfd.h>
+#include <kvm/pckbd.h>
 
 /* header files for gitish interface  */
 #include <kvm/kvm-run.h>
@@ -611,8 +612,10 @@  int kvm_cmd_run(int argc, const char **argv, const char *prefix)
 
 	kvm__init_ram(kvm);
 
-	if (vnc)
+	if (vnc) {
+		kbd__init(kvm);
 		vesa__init(kvm);
+	}
 
 	thread_pool__init(nr_online_cpus);
 	ioeventfd__start();