xref: /kvmtool/hw/i8042.c (revision ca9326bbda95f3b0b694814d3bf4a015faaea175)
1e342083cSJohn Floren #include "kvm/read-write.h"
2e342083cSJohn Floren #include "kvm/ioport.h"
3e342083cSJohn Floren #include "kvm/mutex.h"
4e342083cSJohn Floren #include "kvm/util.h"
5e342083cSJohn Floren #include "kvm/term.h"
6e342083cSJohn Floren #include "kvm/kvm.h"
7e342083cSJohn Floren #include "kvm/i8042.h"
8e342083cSJohn Floren 
9e342083cSJohn Floren #include <rfb/keysym.h>
10e342083cSJohn Floren #include <rfb/rfb.h>
11e342083cSJohn Floren #include <stdint.h>
12e342083cSJohn Floren 
1367307cddSPekka Enberg /*
1467307cddSPekka Enberg  * IRQs
1567307cddSPekka Enberg  */
16e342083cSJohn Floren #define KBD_IRQ			1
17e342083cSJohn Floren #define AUX_IRQ			12
18e342083cSJohn Floren 
1967307cddSPekka Enberg /*
2067307cddSPekka Enberg  * Registers
2167307cddSPekka Enberg  */
2267307cddSPekka Enberg #define I8042_DATA_REG		0x60
2367307cddSPekka Enberg #define I8042_COMMAND_REG	0x64
2467307cddSPekka Enberg 
25b4a721d9SPekka Enberg /*
26b4a721d9SPekka Enberg  * Commands
27b4a721d9SPekka Enberg  */
28b4a721d9SPekka Enberg #define I8042_CMD_CTL_RCTR	0x20
29b4a721d9SPekka Enberg #define I8042_CMD_CTL_WCTR	0x60
30b4a721d9SPekka Enberg #define I8042_CMD_AUX_LOOP	0xD3
31b4a721d9SPekka Enberg #define I8042_CMD_AUX_SEND	0xD4
32b4a721d9SPekka Enberg #define I8042_CMD_AUX_TEST	0xA9
33b4a721d9SPekka Enberg #define I8042_CMD_AUX_DISABLE	0xA7
34b4a721d9SPekka Enberg #define I8042_CMD_AUX_ENABLE	0xA8
35e342083cSJohn Floren 
36e342083cSJohn Floren #define RESPONSE_ACK		0xFA
37e342083cSJohn Floren 
38e342083cSJohn Floren #define MODE_DISABLE_AUX	0x20
39e342083cSJohn Floren 
40e342083cSJohn Floren #define AUX_ENABLE_REPORTING	0x20
41e342083cSJohn Floren #define AUX_SCALING_FLAG	0x10
42e342083cSJohn Floren #define AUX_DEFAULT_RESOLUTION	0x2
43e342083cSJohn Floren #define AUX_DEFAULT_SAMPLE	100
44e342083cSJohn Floren 
4598cbe4d9SPekka Enberg /*
4698cbe4d9SPekka Enberg  * Status register bits
4798cbe4d9SPekka Enberg  */
48e9f4d662SPekka Enberg #define I8042_STR_AUXDATA	0x20
49e9f4d662SPekka Enberg #define I8042_STR_KEYLOCK	0x10
50e9f4d662SPekka Enberg #define I8042_STR_CMDDAT	0x08
51e9f4d662SPekka Enberg #define I8042_STR_MUXERR	0x04
52e9f4d662SPekka Enberg #define I8042_STR_OBF		0x01
53e342083cSJohn Floren 
54e342083cSJohn Floren #define KBD_MODE_KBD_INT	0x01
55e342083cSJohn Floren #define KBD_MODE_SYS		0x02
56e342083cSJohn Floren 
57e342083cSJohn Floren #define QUEUE_SIZE		128
58e342083cSJohn Floren 
59e342083cSJohn Floren /*
60e342083cSJohn Floren  * This represents the current state of the PS/2 keyboard system,
61e342083cSJohn Floren  * including the AUX device (the mouse)
62e342083cSJohn Floren  */
63e342083cSJohn Floren struct kbd_state {
64e342083cSJohn Floren 	struct kvm		*kvm;
65e342083cSJohn Floren 
66e342083cSJohn Floren 	char			kq[QUEUE_SIZE];	/* Keyboard queue */
67e342083cSJohn Floren 	int			kread, kwrite;	/* Indexes into the queue */
68e342083cSJohn Floren 	int			kcount;		/* number of elements in queue */
69e342083cSJohn Floren 
70e342083cSJohn Floren 	char			mq[QUEUE_SIZE];
71e342083cSJohn Floren 	int			mread, mwrite;
72e342083cSJohn Floren 	int			mcount;
73e342083cSJohn Floren 
74e342083cSJohn Floren 	u8			mstatus;	/* Mouse status byte */
75e342083cSJohn Floren 	u8			mres;		/* Current mouse resolution */
76e342083cSJohn Floren 	u8			msample;	/* Current mouse samples/second */
77e342083cSJohn Floren 
78e342083cSJohn Floren 	u8			mode;		/* i8042 mode register */
79e342083cSJohn Floren 	u8			status;		/* i8042 status register */
80e342083cSJohn Floren 	/*
81e342083cSJohn Floren 	 * Some commands (on port 0x64) have arguments;
82e342083cSJohn Floren 	 * we store the command here while we wait for the argument
83e342083cSJohn Floren 	 */
84e342083cSJohn Floren 	u32			write_cmd;
85e342083cSJohn Floren };
86e342083cSJohn Floren 
87e342083cSJohn Floren static struct kbd_state		state;
88e342083cSJohn Floren 
89e342083cSJohn Floren /*
90e342083cSJohn Floren  * If there are packets to be read, set the appropriate IRQs high
91e342083cSJohn Floren  */
92e342083cSJohn Floren static void kbd_update_irq(void)
93e342083cSJohn Floren {
94e342083cSJohn Floren 	u8 klevel = 0;
95e342083cSJohn Floren 	u8 mlevel = 0;
96e342083cSJohn Floren 
97e342083cSJohn Floren 	/* First, clear the kbd and aux output buffer full bits */
98e9f4d662SPekka Enberg 	state.status &= ~(I8042_STR_OBF | I8042_STR_AUXDATA);
99e342083cSJohn Floren 
100e342083cSJohn Floren 	if (state.kcount > 0) {
101e9f4d662SPekka Enberg 		state.status |= I8042_STR_OBF;
102e342083cSJohn Floren 		klevel = 1;
103e342083cSJohn Floren 	}
104e342083cSJohn Floren 
105e342083cSJohn Floren 	/* Keyboard has higher priority than mouse */
106e342083cSJohn Floren 	if (klevel == 0 && state.mcount != 0) {
107e9f4d662SPekka Enberg 		state.status |= I8042_STR_OBF | I8042_STR_AUXDATA;
108e342083cSJohn Floren 		mlevel = 1;
109e342083cSJohn Floren 	}
110e342083cSJohn Floren 
111e342083cSJohn Floren 	kvm__irq_line(state.kvm, KBD_IRQ, klevel);
112e342083cSJohn Floren 	kvm__irq_line(state.kvm, AUX_IRQ, mlevel);
113e342083cSJohn Floren }
114e342083cSJohn Floren 
115e342083cSJohn Floren /*
116e342083cSJohn Floren  * Add a byte to the mouse queue, then set IRQs
117e342083cSJohn Floren  */
118e342083cSJohn Floren static void mouse_queue(u8 c)
119e342083cSJohn Floren {
120e342083cSJohn Floren 	if (state.mcount >= QUEUE_SIZE)
121e342083cSJohn Floren 		return;
122e342083cSJohn Floren 
123e342083cSJohn Floren 	state.mq[state.mwrite++ % QUEUE_SIZE] = c;
124e342083cSJohn Floren 
125e342083cSJohn Floren 	state.mcount++;
126e342083cSJohn Floren 	kbd_update_irq();
127e342083cSJohn Floren }
128e342083cSJohn Floren 
129e342083cSJohn Floren /*
130e342083cSJohn Floren  * Add a byte to the keyboard queue, then set IRQs
131e342083cSJohn Floren  */
132e342083cSJohn Floren static void kbd_queue(u8 c)
133e342083cSJohn Floren {
134e342083cSJohn Floren 	if (state.kcount >= QUEUE_SIZE)
135e342083cSJohn Floren 		return;
136e342083cSJohn Floren 
137e342083cSJohn Floren 	state.kq[state.kwrite++ % QUEUE_SIZE] = c;
138e342083cSJohn Floren 
139e342083cSJohn Floren 	state.kcount++;
140e342083cSJohn Floren 	kbd_update_irq();
141e342083cSJohn Floren }
142e342083cSJohn Floren 
143*ca9326bbSPekka Enberg static void kbd_write_command(u8 val)
144e342083cSJohn Floren {
145e342083cSJohn Floren 	switch (val) {
146b4a721d9SPekka Enberg 	case I8042_CMD_CTL_RCTR:
147e342083cSJohn Floren 		kbd_queue(state.mode);
148e342083cSJohn Floren 		break;
149b4a721d9SPekka Enberg 	case I8042_CMD_CTL_WCTR:
150b4a721d9SPekka Enberg 	case I8042_CMD_AUX_SEND:
151b4a721d9SPekka Enberg 	case I8042_CMD_AUX_LOOP:
152e342083cSJohn Floren 		state.write_cmd = val;
153e342083cSJohn Floren 		break;
154b4a721d9SPekka Enberg 	case I8042_CMD_AUX_TEST:
155e342083cSJohn Floren 		/* 0 means we're a normal PS/2 mouse */
156e342083cSJohn Floren 		mouse_queue(0);
157e342083cSJohn Floren 		break;
158b4a721d9SPekka Enberg 	case I8042_CMD_AUX_DISABLE:
159e342083cSJohn Floren 		state.mode |= MODE_DISABLE_AUX;
160e342083cSJohn Floren 		break;
161b4a721d9SPekka Enberg 	case I8042_CMD_AUX_ENABLE:
162e342083cSJohn Floren 		state.mode &= ~MODE_DISABLE_AUX;
163e342083cSJohn Floren 		break;
164e342083cSJohn Floren 	default:
165e342083cSJohn Floren 		break;
166e342083cSJohn Floren 	}
167e342083cSJohn Floren }
168e342083cSJohn Floren 
169e342083cSJohn Floren /*
170e342083cSJohn Floren  * Called when the OS reads from port 0x60 (PS/2 data)
171e342083cSJohn Floren  */
172e342083cSJohn Floren static u32 kbd_read_data(void)
173e342083cSJohn Floren {
174e342083cSJohn Floren 	u32 ret;
175e342083cSJohn Floren 	int i;
176e342083cSJohn Floren 
177e342083cSJohn Floren 	if (state.kcount != 0) {
178e342083cSJohn Floren 		/* Keyboard data gets read first */
179e342083cSJohn Floren 		ret = state.kq[state.kread++ % QUEUE_SIZE];
180e342083cSJohn Floren 		state.kcount--;
181e342083cSJohn Floren 		kvm__irq_line(state.kvm, KBD_IRQ, 0);
182e342083cSJohn Floren 		kbd_update_irq();
183e342083cSJohn Floren 	} else if (state.mcount > 0) {
184e342083cSJohn Floren 		/* Followed by the mouse */
185e342083cSJohn Floren 		ret = state.mq[state.mread++ % QUEUE_SIZE];
186e342083cSJohn Floren 		state.mcount--;
187e342083cSJohn Floren 		kvm__irq_line(state.kvm, AUX_IRQ, 0);
188e342083cSJohn Floren 		kbd_update_irq();
189e342083cSJohn Floren 	} else if (state.kcount == 0) {
190e342083cSJohn Floren 		i = state.kread - 1;
191e342083cSJohn Floren 		if (i < 0)
192e342083cSJohn Floren 			i = QUEUE_SIZE;
193e342083cSJohn Floren 		ret = state.kq[i];
194e342083cSJohn Floren 	}
195e342083cSJohn Floren 	return ret;
196e342083cSJohn Floren }
197e342083cSJohn Floren 
198e342083cSJohn Floren /*
199e342083cSJohn Floren  * Called when the OS read from port 0x64, the command port
200e342083cSJohn Floren  */
201e342083cSJohn Floren static u32 kbd_read_status(void)
202e342083cSJohn Floren {
203e342083cSJohn Floren 	return (u32)state.status;
204e342083cSJohn Floren }
205e342083cSJohn Floren 
206e342083cSJohn Floren /*
207e342083cSJohn Floren  * Called when the OS writes to port 0x60 (data port)
208e342083cSJohn Floren  * Things written here are generally arguments to commands previously
209e342083cSJohn Floren  * written to port 0x64 and stored in state.write_cmd
210e342083cSJohn Floren  */
211e342083cSJohn Floren static void kbd_write_data(u32 val)
212e342083cSJohn Floren {
213e342083cSJohn Floren 	switch (state.write_cmd) {
214b4a721d9SPekka Enberg 	case I8042_CMD_CTL_WCTR:
215e342083cSJohn Floren 		state.mode = val;
216e342083cSJohn Floren 		kbd_update_irq();
217e342083cSJohn Floren 		break;
218b4a721d9SPekka Enberg 	case I8042_CMD_AUX_LOOP:
219e342083cSJohn Floren 		mouse_queue(val);
220e342083cSJohn Floren 		mouse_queue(RESPONSE_ACK);
221e342083cSJohn Floren 		break;
222b4a721d9SPekka Enberg 	case I8042_CMD_AUX_SEND:
223e342083cSJohn Floren 		/* The OS wants to send a command to the mouse */
224e342083cSJohn Floren 		mouse_queue(RESPONSE_ACK);
225e342083cSJohn Floren 		switch (val) {
226e342083cSJohn Floren 		case 0xe6:
227e342083cSJohn Floren 			/* set scaling = 1:1 */
228e342083cSJohn Floren 			state.mstatus &= ~AUX_SCALING_FLAG;
229e342083cSJohn Floren 			break;
230e342083cSJohn Floren 		case 0xe8:
231e342083cSJohn Floren 			/* set resolution */
232e342083cSJohn Floren 			state.mres = val;
233e342083cSJohn Floren 			break;
234e342083cSJohn Floren 		case 0xe9:
235e342083cSJohn Floren 			/* Report mouse status/config */
236e342083cSJohn Floren 			mouse_queue(state.mstatus);
237e342083cSJohn Floren 			mouse_queue(state.mres);
238e342083cSJohn Floren 			mouse_queue(state.msample);
239e342083cSJohn Floren 			break;
240e342083cSJohn Floren 		case 0xf2:
241e342083cSJohn Floren 			/* send ID */
242e342083cSJohn Floren 			mouse_queue(0); /* normal mouse */
243e342083cSJohn Floren 			break;
244e342083cSJohn Floren 		case 0xf3:
245e342083cSJohn Floren 			/* set sample rate */
246e342083cSJohn Floren 			state.msample = val;
247e342083cSJohn Floren 			break;
248e342083cSJohn Floren 		case 0xf4:
249e342083cSJohn Floren 			/* enable reporting */
250e342083cSJohn Floren 			state.mstatus |= AUX_ENABLE_REPORTING;
251e342083cSJohn Floren 			break;
252e342083cSJohn Floren 		case 0xf5:
253e342083cSJohn Floren 			state.mstatus &= ~AUX_ENABLE_REPORTING;
254e342083cSJohn Floren 			break;
255e342083cSJohn Floren 		case 0xf6:
256e342083cSJohn Floren 			/* set defaults, just fall through to reset */
257e342083cSJohn Floren 		case 0xff:
258e342083cSJohn Floren 			/* reset */
259e342083cSJohn Floren 			state.mstatus = 0x0;
260e342083cSJohn Floren 			state.mres = AUX_DEFAULT_RESOLUTION;
261e342083cSJohn Floren 			state.msample = AUX_DEFAULT_SAMPLE;
262e342083cSJohn Floren 			break;
263e342083cSJohn Floren 		default:
264e342083cSJohn Floren 			break;
265e342083cSJohn Floren 	}
266e342083cSJohn Floren 	break;
267e342083cSJohn Floren 	case 0:
268e342083cSJohn Floren 		/* Just send the ID */
269e342083cSJohn Floren 		kbd_queue(RESPONSE_ACK);
270e342083cSJohn Floren 		kbd_queue(0xab);
271e342083cSJohn Floren 		kbd_queue(0x41);
272e342083cSJohn Floren 		kbd_update_irq();
273e342083cSJohn Floren 		break;
274e342083cSJohn Floren 	default:
275e342083cSJohn Floren 		/* Yeah whatever */
276e342083cSJohn Floren 		break;
277e342083cSJohn Floren 	}
278e342083cSJohn Floren 	state.write_cmd = 0;
279e342083cSJohn Floren }
280e342083cSJohn Floren 
281e342083cSJohn Floren static void kbd_reset(void)
282e342083cSJohn Floren {
283e342083cSJohn Floren 	state = (struct kbd_state) {
284e9f4d662SPekka Enberg 		.status		= I8042_STR_MUXERR | I8042_STR_CMDDAT | I8042_STR_KEYLOCK, /* 0x1c */
285e342083cSJohn Floren 		.mode		= KBD_MODE_KBD_INT | KBD_MODE_SYS, /* 0x3 */
286e342083cSJohn Floren 		.mres		= AUX_DEFAULT_RESOLUTION,
287e342083cSJohn Floren 		.msample	= AUX_DEFAULT_SAMPLE,
288e342083cSJohn Floren 	};
289e342083cSJohn Floren }
290e342083cSJohn Floren 
291e342083cSJohn Floren /*
292e342083cSJohn Floren  * We can map the letters and numbers without a fuss,
293e342083cSJohn Floren  * but the other characters not so much.
294e342083cSJohn Floren  */
295e342083cSJohn Floren static char letters[26] = {
296e342083cSJohn Floren 	0x1c, 0x32, 0x21, 0x23, 0x24, /* a-e */
297e342083cSJohn Floren 	0x2b, 0x34, 0x33, 0x43, 0x3b, /* f-j */
298e342083cSJohn Floren 	0x42, 0x4b, 0x3a, 0x31, 0x44, /* k-o */
299e342083cSJohn Floren 	0x4d, 0x15, 0x2d, 0x1b, 0x2c, /* p-t */
300e342083cSJohn Floren 	0x3c, 0x2a, 0x1d, 0x22, 0x35, /* u-y */
301e342083cSJohn Floren 	0x1a,
302e342083cSJohn Floren };
303e342083cSJohn Floren 
304e342083cSJohn Floren static char num[10] = {
305e342083cSJohn Floren 	0x45, 0x16, 0x1e, 0x26, 0x2e, 0x23, 0x36, 0x3d, 0x3e, 0x46,
306e342083cSJohn Floren };
307e342083cSJohn Floren 
308e342083cSJohn Floren /*
309e342083cSJohn Floren  * This is called when the VNC server receives a key event
310e342083cSJohn Floren  * The reason this function is such a beast is that we have
311e342083cSJohn Floren  * to convert from ASCII characters (which is what VNC gets)
312e342083cSJohn Floren  * to PC keyboard scancodes, which is what Linux expects to
313e342083cSJohn Floren  * get from its keyboard. ASCII and the scancode set don't
314e342083cSJohn Floren  * really seem to mesh in any good way beyond some basics with
315e342083cSJohn Floren  * the letters and numbers.
316e342083cSJohn Floren  */
317e342083cSJohn Floren void kbd_handle_key(rfbBool down, rfbKeySym key, rfbClientPtr cl)
318e342083cSJohn Floren {
319e342083cSJohn Floren 	char tosend = 0;
320e342083cSJohn Floren 
321e342083cSJohn Floren 	if (key >= 0x41 && key <= 0x5a)
322e342083cSJohn Floren 		key += 0x20; /* convert to lowercase */
323e342083cSJohn Floren 
324e342083cSJohn Floren 	if (key >= 0x61 && key <= 0x7a) /* a-z */
325e342083cSJohn Floren 		tosend = letters[key - 0x61];
326e342083cSJohn Floren 
327e342083cSJohn Floren 	if (key >= 0x30 && key <= 0x39)
328e342083cSJohn Floren 		tosend = num[key - 0x30];
329e342083cSJohn Floren 
330e342083cSJohn Floren 	switch (key) {
331e342083cSJohn Floren 	case XK_Insert:		kbd_queue(0xe0);	tosend = 0x70;	break;
332e342083cSJohn Floren 	case XK_Delete:		kbd_queue(0xe0);	tosend = 0x71;	break;
333e342083cSJohn Floren 	case XK_Up:		kbd_queue(0xe0);	tosend = 0x75;	break;
334e342083cSJohn Floren 	case XK_Down:		kbd_queue(0xe0);	tosend = 0x72;	break;
335e342083cSJohn Floren 	case XK_Left:		kbd_queue(0xe0);	tosend = 0x6b;	break;
336e342083cSJohn Floren 	case XK_Right:		kbd_queue(0xe0);	tosend = 0x74;	break;
337e342083cSJohn Floren 	case XK_Page_Up:	kbd_queue(0xe0);	tosend = 0x7d;	break;
338e342083cSJohn Floren 	case XK_Page_Down:	kbd_queue(0xe0);	tosend = 0x7a;	break;
339e342083cSJohn Floren 	case XK_Home:		kbd_queue(0xe0);	tosend = 0x6c;	break;
340e342083cSJohn Floren 	case XK_BackSpace:	tosend = 0x66;		break;
341e342083cSJohn Floren 	case XK_Tab:		tosend = 0x0d;		break;
342e342083cSJohn Floren 	case XK_Return:		tosend = 0x5a;		break;
343e342083cSJohn Floren 	case XK_Escape:		tosend = 0x76;		break;
344e342083cSJohn Floren 	case XK_End:		tosend = 0x69;		break;
345e342083cSJohn Floren 	case XK_Shift_L:	tosend = 0x12;		break;
346e342083cSJohn Floren 	case XK_Shift_R:	tosend = 0x59;		break;
347e342083cSJohn Floren 	case XK_Control_R:	kbd_queue(0xe0);
348e342083cSJohn Floren 	case XK_Control_L:	tosend = 0x14;		break;
349e342083cSJohn Floren 	case XK_Alt_R:		kbd_queue(0xe0);
350e342083cSJohn Floren 	case XK_Alt_L:		tosend = 0x11;		break;
351e342083cSJohn Floren 	case XK_quoteleft:	tosend = 0x0e;		break;
352e342083cSJohn Floren 	case XK_minus:		tosend = 0x4e;		break;
353e342083cSJohn Floren 	case XK_equal:		tosend = 0x55;		break;
354e342083cSJohn Floren 	case XK_bracketleft:	tosend = 0x54;		break;
355e342083cSJohn Floren 	case XK_bracketright:	tosend = 0x5b;		break;
356e342083cSJohn Floren 	case XK_backslash:	tosend = 0x5d;		break;
357e342083cSJohn Floren 	case XK_Caps_Lock:	tosend = 0x58;		break;
358e342083cSJohn Floren 	case XK_semicolon:	tosend = 0x4c;		break;
359e342083cSJohn Floren 	case XK_quoteright:	tosend = 0x52;		break;
360e342083cSJohn Floren 	case XK_comma:		tosend = 0x41;		break;
361e342083cSJohn Floren 	case XK_period:		tosend = 0x49;		break;
362e342083cSJohn Floren 	case XK_slash:		tosend = 0x4a;		break;
363e342083cSJohn Floren 	case XK_space:		tosend = 0x29;		break;
364e342083cSJohn Floren 
365e342083cSJohn Floren 	/*
366e342083cSJohn Floren 	 * This is where I handle the shifted characters.
367e342083cSJohn Floren 	 * They don't really map nicely the way A-Z maps to a-z,
368e342083cSJohn Floren 	 * so I'm doing it manually
369e342083cSJohn Floren 	 */
370e342083cSJohn Floren 	case XK_exclam:		tosend = 0x16;		break;
371e342083cSJohn Floren 	case XK_quotedbl:	tosend = 0x52;		break;
372e342083cSJohn Floren 	case XK_numbersign:	tosend = 0x26;		break;
373e342083cSJohn Floren 	case XK_dollar:		tosend = 0x25;		break;
374e342083cSJohn Floren 	case XK_percent:	tosend = 0x2e;		break;
375e342083cSJohn Floren 	case XK_ampersand:	tosend = 0x3d;		break;
376e342083cSJohn Floren 	case XK_parenleft:	tosend = 0x46;		break;
377e342083cSJohn Floren 	case XK_parenright:	tosend = 0x45;		break;
378e342083cSJohn Floren 	case XK_asterisk:	tosend = 0x3e;		break;
379e342083cSJohn Floren 	case XK_plus:		tosend = 0x55;		break;
380e342083cSJohn Floren 	case XK_colon:		tosend = 0x4c;		break;
381e342083cSJohn Floren 	case XK_less:		tosend = 0x41;		break;
382e342083cSJohn Floren 	case XK_greater:	tosend = 0x49;		break;
383e342083cSJohn Floren 	case XK_question:	tosend = 0x4a;		break;
384e342083cSJohn Floren 	case XK_at:		tosend = 0x1e;		break;
385e342083cSJohn Floren 	case XK_asciicircum:	tosend = 0x36;		break;
386e342083cSJohn Floren 	case XK_underscore:	tosend = 0x4e;		break;
387e342083cSJohn Floren 	case XK_braceleft:	tosend = 0x54;		break;
388e342083cSJohn Floren 	case XK_braceright:	tosend = 0x5b;		break;
389e342083cSJohn Floren 	case XK_bar:		tosend = 0x5d;		break;
390e342083cSJohn Floren 	case XK_asciitilde:	tosend = 0x0e;		break;
391e342083cSJohn Floren 	default:		break;
392e342083cSJohn Floren 	}
393e342083cSJohn Floren 
394e342083cSJohn Floren 	/*
395e342083cSJohn Floren 	 * If this is a "key up" event (the user has released the key, we
396e342083cSJohn Floren 	 * need to send 0xf0 first.
397e342083cSJohn Floren 	 */
398e342083cSJohn Floren 	if (!down && tosend != 0x0)
399e342083cSJohn Floren 		kbd_queue(0xf0);
400e342083cSJohn Floren 
401e342083cSJohn Floren 	if (tosend)
402e342083cSJohn Floren 		kbd_queue(tosend);
403e342083cSJohn Floren }
404e342083cSJohn Floren 
405e342083cSJohn Floren /* The previous X and Y coordinates of the mouse */
406e342083cSJohn Floren static int xlast, ylast = -1;
407e342083cSJohn Floren 
408e342083cSJohn Floren /*
409e342083cSJohn Floren  * This function is called by the VNC server whenever a mouse event occurs.
410e342083cSJohn Floren  */
411e342083cSJohn Floren void kbd_handle_ptr(int buttonMask, int x, int y, rfbClientPtr cl)
412e342083cSJohn Floren {
413e342083cSJohn Floren 	int dx, dy;
414e342083cSJohn Floren 	char b1 = 0x8;
415e342083cSJohn Floren 
416e342083cSJohn Floren 	/* The VNC mask and the PS/2 button encoding are the same */
417e342083cSJohn Floren 	b1 |= buttonMask;
418e342083cSJohn Floren 
419e342083cSJohn Floren 	if (xlast >= 0 && ylast >= 0) {
420e342083cSJohn Floren 		/* The PS/2 mouse sends deltas, not absolutes */
421e342083cSJohn Floren 		dx = x - xlast;
422e342083cSJohn Floren 		dy = ylast - y;
423e342083cSJohn Floren 
424e342083cSJohn Floren 		/* Set overflow bits if needed */
425e342083cSJohn Floren 		if (dy > 255)
426e342083cSJohn Floren 			b1 |= 0x80;
427e342083cSJohn Floren 		if (dx > 255)
428e342083cSJohn Floren 			b1 |= 0x40;
429e342083cSJohn Floren 
430e342083cSJohn Floren 		/* Set negative bits if needed */
431e342083cSJohn Floren 		if (dy < 0)
432e342083cSJohn Floren 			b1 |= 0x20;
433e342083cSJohn Floren 		if (dx < 0)
434e342083cSJohn Floren 			b1 |= 0x10;
435e342083cSJohn Floren 
436e342083cSJohn Floren 		mouse_queue(b1);
437e342083cSJohn Floren 		mouse_queue(dx);
438e342083cSJohn Floren 		mouse_queue(dy);
439e342083cSJohn Floren 	}
440e342083cSJohn Floren 
441e342083cSJohn Floren 	xlast = x;
442e342083cSJohn Floren 	ylast = y;
443e342083cSJohn Floren 	rfbDefaultPtrAddEvent(buttonMask, x, y, cl);
444e342083cSJohn Floren }
445e342083cSJohn Floren 
446e342083cSJohn Floren /*
447e342083cSJohn Floren  * Called when the OS has written to one of the keyboard's ports (0x60 or 0x64)
448e342083cSJohn Floren  */
449e342083cSJohn Floren static bool kbd_in(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size, u32 count)
450e342083cSJohn Floren {
451*ca9326bbSPekka Enberg 	switch (port) {
452*ca9326bbSPekka Enberg 	case I8042_COMMAND_REG: {
453*ca9326bbSPekka Enberg 		u8 value = kbd_read_status();
454*ca9326bbSPekka Enberg 		ioport__write8(data, value);
455*ca9326bbSPekka Enberg 		break;
456e342083cSJohn Floren 	}
457*ca9326bbSPekka Enberg 	case I8042_DATA_REG: {
458*ca9326bbSPekka Enberg 		u32 value = kbd_read_data();
459*ca9326bbSPekka Enberg 		ioport__write32(data, value);
460*ca9326bbSPekka Enberg 		break;
461*ca9326bbSPekka Enberg 	}
462*ca9326bbSPekka Enberg 	default:
463*ca9326bbSPekka Enberg 		return false;
464*ca9326bbSPekka Enberg 	}
465*ca9326bbSPekka Enberg 
466e342083cSJohn Floren 	return true;
467e342083cSJohn Floren }
468e342083cSJohn Floren 
469e342083cSJohn Floren static bool kbd_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size, u32 count)
470e342083cSJohn Floren {
471*ca9326bbSPekka Enberg 	switch (port) {
472*ca9326bbSPekka Enberg 	case I8042_COMMAND_REG: {
473*ca9326bbSPekka Enberg 		u8 value = ioport__read8(data);
474*ca9326bbSPekka Enberg 		kbd_write_command(value);
475*ca9326bbSPekka Enberg 		break;
476*ca9326bbSPekka Enberg 	}
477*ca9326bbSPekka Enberg 	case I8042_DATA_REG: {
478*ca9326bbSPekka Enberg 		u32 value = ioport__read32(data);
479*ca9326bbSPekka Enberg 		kbd_write_data(value);
480*ca9326bbSPekka Enberg 		break;
481*ca9326bbSPekka Enberg 	}
482*ca9326bbSPekka Enberg 	default:
483*ca9326bbSPekka Enberg 		return false;
484*ca9326bbSPekka Enberg 	}
485e342083cSJohn Floren 
486e342083cSJohn Floren 	return true;
487e342083cSJohn Floren }
488e342083cSJohn Floren 
489e342083cSJohn Floren static struct ioport_operations kbd_ops = {
490e342083cSJohn Floren 	.io_in		= kbd_in,
491e342083cSJohn Floren 	.io_out		= kbd_out,
492e342083cSJohn Floren };
493e342083cSJohn Floren 
494e342083cSJohn Floren void kbd__init(struct kvm *kvm)
495e342083cSJohn Floren {
496e342083cSJohn Floren 	kbd_reset();
497e342083cSJohn Floren 	state.kvm = kvm;
49867307cddSPekka Enberg 	ioport__register(I8042_DATA_REG, &kbd_ops, 2, NULL);
49967307cddSPekka Enberg 	ioport__register(I8042_COMMAND_REG, &kbd_ops, 2, NULL);
500e342083cSJohn Floren }
501