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