xref: /kvmtool/hw/i8042.c (revision e9f4d662e6decb6abfbeeef2599fa0bd929d7495)
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 
25e342083cSJohn Floren #define CMD_READ_MODE		0x20
26e342083cSJohn Floren #define CMD_WRITE_MODE		0x60
27e342083cSJohn Floren #define CMD_WRITE_AUX_BUF	0xD3
28e342083cSJohn Floren #define CMD_WRITE_AUX		0xD4
29e342083cSJohn Floren #define CMD_TEST_AUX		0xA9
30e342083cSJohn Floren #define CMD_DISABLE_AUX		0xA7
31e342083cSJohn Floren #define CMD_ENABLE_AUX		0xA8
32e342083cSJohn Floren 
33e342083cSJohn Floren #define RESPONSE_ACK		0xFA
34e342083cSJohn Floren 
35e342083cSJohn Floren #define MODE_DISABLE_AUX	0x20
36e342083cSJohn Floren 
37e342083cSJohn Floren #define AUX_ENABLE_REPORTING	0x20
38e342083cSJohn Floren #define AUX_SCALING_FLAG	0x10
39e342083cSJohn Floren #define AUX_DEFAULT_RESOLUTION	0x2
40e342083cSJohn Floren #define AUX_DEFAULT_SAMPLE	100
41e342083cSJohn Floren 
4298cbe4d9SPekka Enberg /*
4398cbe4d9SPekka Enberg  * Status register bits
4498cbe4d9SPekka Enberg  */
45*e9f4d662SPekka Enberg #define I8042_STR_AUXDATA	0x20
46*e9f4d662SPekka Enberg #define I8042_STR_KEYLOCK	0x10
47*e9f4d662SPekka Enberg #define I8042_STR_CMDDAT	0x08
48*e9f4d662SPekka Enberg #define I8042_STR_MUXERR	0x04
49*e9f4d662SPekka Enberg #define I8042_STR_OBF		0x01
50e342083cSJohn Floren 
51e342083cSJohn Floren #define KBD_MODE_KBD_INT	0x01
52e342083cSJohn Floren #define KBD_MODE_SYS		0x02
53e342083cSJohn Floren 
54e342083cSJohn Floren #define QUEUE_SIZE		128
55e342083cSJohn Floren 
56e342083cSJohn Floren /*
57e342083cSJohn Floren  * This represents the current state of the PS/2 keyboard system,
58e342083cSJohn Floren  * including the AUX device (the mouse)
59e342083cSJohn Floren  */
60e342083cSJohn Floren struct kbd_state {
61e342083cSJohn Floren 	struct kvm		*kvm;
62e342083cSJohn Floren 
63e342083cSJohn Floren 	char			kq[QUEUE_SIZE];	/* Keyboard queue */
64e342083cSJohn Floren 	int			kread, kwrite;	/* Indexes into the queue */
65e342083cSJohn Floren 	int			kcount;		/* number of elements in queue */
66e342083cSJohn Floren 
67e342083cSJohn Floren 	char			mq[QUEUE_SIZE];
68e342083cSJohn Floren 	int			mread, mwrite;
69e342083cSJohn Floren 	int			mcount;
70e342083cSJohn Floren 
71e342083cSJohn Floren 	u8			mstatus;	/* Mouse status byte */
72e342083cSJohn Floren 	u8			mres;		/* Current mouse resolution */
73e342083cSJohn Floren 	u8			msample;	/* Current mouse samples/second */
74e342083cSJohn Floren 
75e342083cSJohn Floren 	u8			mode;		/* i8042 mode register */
76e342083cSJohn Floren 	u8			status;		/* i8042 status register */
77e342083cSJohn Floren 	/*
78e342083cSJohn Floren 	 * Some commands (on port 0x64) have arguments;
79e342083cSJohn Floren 	 * we store the command here while we wait for the argument
80e342083cSJohn Floren 	 */
81e342083cSJohn Floren 	u32			write_cmd;
82e342083cSJohn Floren };
83e342083cSJohn Floren 
84e342083cSJohn Floren static struct kbd_state		state;
85e342083cSJohn Floren 
86e342083cSJohn Floren /*
87e342083cSJohn Floren  * If there are packets to be read, set the appropriate IRQs high
88e342083cSJohn Floren  */
89e342083cSJohn Floren static void kbd_update_irq(void)
90e342083cSJohn Floren {
91e342083cSJohn Floren 	u8 klevel = 0;
92e342083cSJohn Floren 	u8 mlevel = 0;
93e342083cSJohn Floren 
94e342083cSJohn Floren 	/* First, clear the kbd and aux output buffer full bits */
95*e9f4d662SPekka Enberg 	state.status &= ~(I8042_STR_OBF | I8042_STR_AUXDATA);
96e342083cSJohn Floren 
97e342083cSJohn Floren 	if (state.kcount > 0) {
98*e9f4d662SPekka Enberg 		state.status |= I8042_STR_OBF;
99e342083cSJohn Floren 		klevel = 1;
100e342083cSJohn Floren 	}
101e342083cSJohn Floren 
102e342083cSJohn Floren 	/* Keyboard has higher priority than mouse */
103e342083cSJohn Floren 	if (klevel == 0 && state.mcount != 0) {
104*e9f4d662SPekka Enberg 		state.status |= I8042_STR_OBF | I8042_STR_AUXDATA;
105e342083cSJohn Floren 		mlevel = 1;
106e342083cSJohn Floren 	}
107e342083cSJohn Floren 
108e342083cSJohn Floren 	kvm__irq_line(state.kvm, KBD_IRQ, klevel);
109e342083cSJohn Floren 	kvm__irq_line(state.kvm, AUX_IRQ, mlevel);
110e342083cSJohn Floren }
111e342083cSJohn Floren 
112e342083cSJohn Floren /*
113e342083cSJohn Floren  * Add a byte to the mouse queue, then set IRQs
114e342083cSJohn Floren  */
115e342083cSJohn Floren static void mouse_queue(u8 c)
116e342083cSJohn Floren {
117e342083cSJohn Floren 	if (state.mcount >= QUEUE_SIZE)
118e342083cSJohn Floren 		return;
119e342083cSJohn Floren 
120e342083cSJohn Floren 	state.mq[state.mwrite++ % QUEUE_SIZE] = c;
121e342083cSJohn Floren 
122e342083cSJohn Floren 	state.mcount++;
123e342083cSJohn Floren 	kbd_update_irq();
124e342083cSJohn Floren }
125e342083cSJohn Floren 
126e342083cSJohn Floren /*
127e342083cSJohn Floren  * Add a byte to the keyboard queue, then set IRQs
128e342083cSJohn Floren  */
129e342083cSJohn Floren static void kbd_queue(u8 c)
130e342083cSJohn Floren {
131e342083cSJohn Floren 	if (state.kcount >= QUEUE_SIZE)
132e342083cSJohn Floren 		return;
133e342083cSJohn Floren 
134e342083cSJohn Floren 	state.kq[state.kwrite++ % QUEUE_SIZE] = c;
135e342083cSJohn Floren 
136e342083cSJohn Floren 	state.kcount++;
137e342083cSJohn Floren 	kbd_update_irq();
138e342083cSJohn Floren }
139e342083cSJohn Floren 
140e342083cSJohn Floren /*
141e342083cSJohn Floren  * This function is called when the OS issues a write to port 0x64
142e342083cSJohn Floren  */
143e342083cSJohn Floren static void kbd_write_command(u32 val)
144e342083cSJohn Floren {
145e342083cSJohn Floren 	switch (val) {
146e342083cSJohn Floren 	case CMD_READ_MODE:
147e342083cSJohn Floren 		kbd_queue(state.mode);
148e342083cSJohn Floren 		break;
149e342083cSJohn Floren 	case CMD_WRITE_MODE:
150e342083cSJohn Floren 	case CMD_WRITE_AUX:
151e342083cSJohn Floren 	case CMD_WRITE_AUX_BUF:
152e342083cSJohn Floren 		state.write_cmd = val;
153e342083cSJohn Floren 		break;
154e342083cSJohn Floren 	case CMD_TEST_AUX:
155e342083cSJohn Floren 		/* 0 means we're a normal PS/2 mouse */
156e342083cSJohn Floren 		mouse_queue(0);
157e342083cSJohn Floren 		break;
158e342083cSJohn Floren 	case CMD_DISABLE_AUX:
159e342083cSJohn Floren 		state.mode |= MODE_DISABLE_AUX;
160e342083cSJohn Floren 		break;
161e342083cSJohn Floren 	case CMD_ENABLE_AUX:
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) {
214e342083cSJohn Floren 	case CMD_WRITE_MODE:
215e342083cSJohn Floren 		state.mode = val;
216e342083cSJohn Floren 		kbd_update_irq();
217e342083cSJohn Floren 		break;
218e342083cSJohn Floren 	case CMD_WRITE_AUX_BUF:
219e342083cSJohn Floren 		mouse_queue(val);
220e342083cSJohn Floren 		mouse_queue(RESPONSE_ACK);
221e342083cSJohn Floren 		break;
222e342083cSJohn Floren 	case CMD_WRITE_AUX:
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) {
284*e9f4d662SPekka 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 {
451e342083cSJohn Floren 	u32 result;
452e342083cSJohn Floren 
45367307cddSPekka Enberg 	if (port == I8042_COMMAND_REG) {
454e342083cSJohn Floren 		result = kbd_read_status();
455e342083cSJohn Floren 		ioport__write8(data, (char)result);
456e342083cSJohn Floren 	} else {
457e342083cSJohn Floren 		result = kbd_read_data();
458e342083cSJohn Floren 		ioport__write32(data, result);
459e342083cSJohn Floren 	}
460e342083cSJohn Floren 	return true;
461e342083cSJohn Floren }
462e342083cSJohn Floren 
463e342083cSJohn Floren /*
464e342083cSJohn Floren  * Called when the OS attempts to read from a keyboard port (0x60 or 0x64)
465e342083cSJohn Floren  */
466e342083cSJohn Floren static bool kbd_out(struct ioport *ioport, struct kvm *kvm, u16 port, void *data, int size, u32 count)
467e342083cSJohn Floren {
46867307cddSPekka Enberg 	if (port == I8042_COMMAND_REG)
469e342083cSJohn Floren 		kbd_write_command(*((u32 *)data));
470e342083cSJohn Floren 	else
471e342083cSJohn Floren 		kbd_write_data(*((u32 *)data));
472e342083cSJohn Floren 
473e342083cSJohn Floren 	return true;
474e342083cSJohn Floren }
475e342083cSJohn Floren 
476e342083cSJohn Floren static struct ioport_operations kbd_ops = {
477e342083cSJohn Floren 	.io_in		= kbd_in,
478e342083cSJohn Floren 	.io_out		= kbd_out,
479e342083cSJohn Floren };
480e342083cSJohn Floren 
481e342083cSJohn Floren void kbd__init(struct kvm *kvm)
482e342083cSJohn Floren {
483e342083cSJohn Floren 	kbd_reset();
484e342083cSJohn Floren 	state.kvm = kvm;
48567307cddSPekka Enberg 	ioport__register(I8042_DATA_REG, &kbd_ops, 2, NULL);
48667307cddSPekka Enberg 	ioport__register(I8042_COMMAND_REG, &kbd_ops, 2, NULL);
487e342083cSJohn Floren }
488