These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / roms / seabios / src / hw / ps2port.c
1 // Support for handling the PS/2 mouse/keyboard ports.
2 //
3 // Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
4 // Several ideas taken from code Copyright (c) 1999-2004 Vojtech Pavlik
5 //
6 // This file may be distributed under the terms of the GNU LGPLv3 license.
7
8 #include "biosvar.h" // GET_LOW
9 #include "output.h" // dprintf
10 #include "pic.h" // pic_eoi1
11 #include "ps2port.h" // ps2_kbd_command
12 #include "romfile.h" // romfile_loadint
13 #include "stacks.h" // yield
14 #include "util.h" // udelay
15 #include "x86.h" // inb
16
17
18 /****************************************************************
19  * Low level i8042 commands.
20  ****************************************************************/
21
22 // Timeout value.
23 #define I8042_CTL_TIMEOUT       10000
24
25 #define I8042_BUFFER_SIZE       16
26
27 static int
28 i8042_wait_read(void)
29 {
30     dprintf(7, "i8042_wait_read\n");
31     int i;
32     for (i=0; i<I8042_CTL_TIMEOUT; i++) {
33         u8 status = inb(PORT_PS2_STATUS);
34         if (status & I8042_STR_OBF)
35             return 0;
36         udelay(50);
37     }
38     warn_timeout();
39     return -1;
40 }
41
42 static int
43 i8042_wait_write(void)
44 {
45     dprintf(7, "i8042_wait_write\n");
46     int i;
47     for (i=0; i<I8042_CTL_TIMEOUT; i++) {
48         u8 status = inb(PORT_PS2_STATUS);
49         if (! (status & I8042_STR_IBF))
50             return 0;
51         udelay(50);
52     }
53     warn_timeout();
54     return -1;
55 }
56
57 static int
58 i8042_flush(void)
59 {
60     dprintf(7, "i8042_flush\n");
61     int i;
62     for (i=0; i<I8042_BUFFER_SIZE; i++) {
63         u8 status = inb(PORT_PS2_STATUS);
64         if (! (status & I8042_STR_OBF))
65             return 0;
66         udelay(50);
67         u8 data = inb(PORT_PS2_DATA);
68         dprintf(7, "i8042 flushed %x (status=%x)\n", data, status);
69     }
70
71     warn_timeout();
72     return -1;
73 }
74
75 static int
76 __i8042_command(int command, u8 *param)
77 {
78     int receive = (command >> 8) & 0xf;
79     int send = (command >> 12) & 0xf;
80
81     // Send the command.
82     int ret = i8042_wait_write();
83     if (ret)
84         return ret;
85     outb(command, PORT_PS2_STATUS);
86
87     // Send parameters (if any).
88     int i;
89     for (i = 0; i < send; i++) {
90         ret = i8042_wait_write();
91         if (ret)
92             return ret;
93         outb(param[i], PORT_PS2_DATA);
94     }
95
96     // Receive parameters (if any).
97     for (i = 0; i < receive; i++) {
98         ret = i8042_wait_read();
99         if (ret)
100             return ret;
101         param[i] = inb(PORT_PS2_DATA);
102         dprintf(7, "i8042 param=%x\n", param[i]);
103     }
104
105     return 0;
106 }
107
108 static int
109 i8042_command(int command, u8 *param)
110 {
111     dprintf(7, "i8042_command cmd=%x\n", command);
112     int ret = __i8042_command(command, param);
113     if (ret)
114         dprintf(2, "i8042 command %x failed\n", command);
115     return ret;
116 }
117
118 static int
119 i8042_kbd_write(u8 c)
120 {
121     dprintf(7, "i8042_kbd_write c=%d\n", c);
122     int ret = i8042_wait_write();
123     if (! ret)
124         outb(c, PORT_PS2_DATA);
125     return ret;
126 }
127
128 static int
129 i8042_aux_write(u8 c)
130 {
131     return i8042_command(I8042_CMD_AUX_SEND, &c);
132 }
133
134 void
135 i8042_reboot(void)
136 {
137     if (! CONFIG_PS2PORT)
138        return;
139     int i;
140     for (i=0; i<10; i++) {
141         i8042_wait_write();
142         udelay(50);
143         outb(0xfe, PORT_PS2_STATUS); /* pulse reset low */
144         udelay(50);
145     }
146 }
147
148
149 /****************************************************************
150  * Device commands.
151  ****************************************************************/
152
153 #define PS2_RET_ACK             0xfa
154 #define PS2_RET_NAK             0xfe
155
156 static int
157 ps2_recvbyte(int aux, int needack, int timeout)
158 {
159     u32 end = timer_calc(timeout);
160     for (;;) {
161         u8 status = inb(PORT_PS2_STATUS);
162         if (status & I8042_STR_OBF) {
163             u8 data = inb(PORT_PS2_DATA);
164             dprintf(7, "ps2 read %x\n", data);
165
166             if (!!(status & I8042_STR_AUXDATA) == aux) {
167                 if (!needack)
168                     return data;
169                 if (data == PS2_RET_ACK)
170                     return data;
171                 if (data == PS2_RET_NAK) {
172                     dprintf(1, "Got ps2 nak (status=%x)\n", status);
173                     return data;
174                 }
175             }
176
177             // This data not part of command - just discard it.
178             dprintf(1, "Discarding ps2 data %02x (status=%02x)\n", data, status);
179         }
180
181         if (timer_check(end)) {
182             // Don't warn on second byte of a reset
183             if (timeout > 100)
184                 warn_timeout();
185             return -1;
186         }
187         yield();
188     }
189 }
190
191 static int
192 ps2_sendbyte(int aux, u8 command, int timeout)
193 {
194     dprintf(7, "ps2_sendbyte aux=%d cmd=%x\n", aux, command);
195     int ret;
196     if (aux)
197         ret = i8042_aux_write(command);
198     else
199         ret = i8042_kbd_write(command);
200     if (ret)
201         return ret;
202
203     // Read ack.
204     ret = ps2_recvbyte(aux, 1, timeout);
205     if (ret < 0)
206         return ret;
207     if (ret != PS2_RET_ACK)
208         return -1;
209
210     return 0;
211 }
212
213 u8 Ps2ctr VARLOW = I8042_CTR_KBDDIS | I8042_CTR_AUXDIS;
214
215 static int
216 __ps2_command(int aux, int command, u8 *param)
217 {
218     int ret2;
219     int receive = (command >> 8) & 0xf;
220     int send = (command >> 12) & 0xf;
221
222     // Disable interrupts and keyboard/mouse.
223     u8 ps2ctr = GET_LOW(Ps2ctr);
224     u8 newctr = ((ps2ctr | I8042_CTR_AUXDIS | I8042_CTR_KBDDIS)
225                  & ~(I8042_CTR_KBDINT|I8042_CTR_AUXINT));
226     dprintf(6, "i8042 ctr old=%x new=%x\n", ps2ctr, newctr);
227     int ret = i8042_command(I8042_CMD_CTL_WCTR, &newctr);
228     if (ret)
229         return ret;
230
231     // Flush any interrupts already pending.
232     yield();
233
234     // Enable port command is being sent to.
235     SET_LOW(Ps2ctr, newctr);
236     if (aux)
237         newctr &= ~I8042_CTR_AUXDIS;
238     else
239         newctr &= ~I8042_CTR_KBDDIS;
240     ret = i8042_command(I8042_CMD_CTL_WCTR, &newctr);
241     if (ret)
242         goto fail;
243
244     if ((u8)command == (u8)ATKBD_CMD_RESET_BAT) {
245         // Reset is special wrt timeouts.
246
247         // Send command.
248         ret = ps2_sendbyte(aux, command, 1000);
249         if (ret)
250             goto fail;
251
252         // Receive parameters.
253         ret = ps2_recvbyte(aux, 0, 4000);
254         if (ret < 0)
255             goto fail;
256         param[0] = ret;
257         if (receive > 1) {
258             ret = ps2_recvbyte(aux, 0, 500);
259             if (ret < 0)
260                 goto fail;
261             param[1] = ret;
262         }
263     } else if (command == ATKBD_CMD_GETID) {
264         // Getid is special wrt bytes received.
265
266         // Send command.
267         ret = ps2_sendbyte(aux, command, 200);
268         if (ret)
269             goto fail;
270
271         // Receive parameters.
272         ret = ps2_recvbyte(aux, 0, 500);
273         if (ret < 0)
274             goto fail;
275         param[0] = ret;
276         if (ret == 0xab || ret == 0xac || ret == 0x2b || ret == 0x5d
277             || ret == 0x60 || ret == 0x47) {
278             // These ids (keyboards) return two bytes.
279             ret = ps2_recvbyte(aux, 0, 500);
280             if (ret < 0)
281                 goto fail;
282             param[1] = ret;
283         } else {
284             param[1] = 0;
285         }
286     } else {
287         // Send command.
288         ret = ps2_sendbyte(aux, command, 200);
289         if (ret)
290             goto fail;
291
292         // Send parameters (if any).
293         int i;
294         for (i = 0; i < send; i++) {
295             ret = ps2_sendbyte(aux, param[i], 200);
296             if (ret)
297                 goto fail;
298         }
299
300         // Receive parameters (if any).
301         for (i = 0; i < receive; i++) {
302             ret = ps2_recvbyte(aux, 0, 500);
303             if (ret < 0)
304                 goto fail;
305             param[i] = ret;
306         }
307     }
308
309     ret = 0;
310
311 fail:
312     // Restore interrupts and keyboard/mouse.
313     SET_LOW(Ps2ctr, ps2ctr);
314     ret2 = i8042_command(I8042_CMD_CTL_WCTR, &ps2ctr);
315     if (ret2)
316         return ret2;
317
318     return ret;
319 }
320
321 static int
322 ps2_command(int aux, int command, u8 *param)
323 {
324     dprintf(7, "ps2_command aux=%d cmd=%x\n", aux, command);
325     int ret = __ps2_command(aux, command, param);
326     if (ret)
327         dprintf(2, "ps2 command %x failed (aux=%d)\n", command, aux);
328     return ret;
329 }
330
331 int
332 ps2_kbd_command(int command, u8 *param)
333 {
334     if (! CONFIG_PS2PORT)
335         return -1;
336     return ps2_command(0, command, param);
337 }
338
339 int
340 ps2_mouse_command(int command, u8 *param)
341 {
342     if (! CONFIG_PS2PORT)
343         return -1;
344
345     // Update ps2ctr for mouse enable/disable.
346     if (command == PSMOUSE_CMD_ENABLE || command == PSMOUSE_CMD_DISABLE) {
347         u8 ps2ctr = GET_LOW(Ps2ctr);
348         if (command == PSMOUSE_CMD_ENABLE)
349             ps2ctr = ((ps2ctr | (CONFIG_HARDWARE_IRQ ? I8042_CTR_AUXINT : 0))
350                       & ~I8042_CTR_AUXDIS);
351         else
352             ps2ctr = (ps2ctr | I8042_CTR_AUXDIS) & ~I8042_CTR_AUXINT;
353         SET_LOW(Ps2ctr, ps2ctr);
354     }
355
356     return ps2_command(1, command, param);
357 }
358
359
360 /****************************************************************
361  * IRQ handlers
362  ****************************************************************/
363
364 // INT74h : PS/2 mouse hardware interrupt
365 void VISIBLE16
366 handle_74(void)
367 {
368     if (! CONFIG_PS2PORT)
369         return;
370
371     debug_isr(DEBUG_ISR_74);
372
373     u8 v = inb(PORT_PS2_STATUS);
374     if ((v & (I8042_STR_OBF|I8042_STR_AUXDATA))
375         != (I8042_STR_OBF|I8042_STR_AUXDATA)) {
376         dprintf(1, "ps2 mouse irq but no mouse data.\n");
377         goto done;
378     }
379     v = inb(PORT_PS2_DATA);
380
381     if (!(GET_LOW(Ps2ctr) & I8042_CTR_AUXINT))
382         // Interrupts not enabled.
383         goto done;
384
385     process_mouse(v);
386
387 done:
388     pic_eoi2();
389 }
390
391 // INT09h : Keyboard Hardware Service Entry Point
392 void VISIBLE16
393 handle_09(void)
394 {
395     if (! CONFIG_PS2PORT)
396         return;
397
398     debug_isr(DEBUG_ISR_09);
399
400     // read key from keyboard controller
401     u8 v = inb(PORT_PS2_STATUS);
402     if (v & I8042_STR_AUXDATA) {
403         dprintf(1, "ps2 keyboard irq but found mouse data?!\n");
404         goto done;
405     }
406     v = inb(PORT_PS2_DATA);
407
408     if (!(GET_LOW(Ps2ctr) & I8042_CTR_KBDINT))
409         // Interrupts not enabled.
410         goto done;
411
412     process_key(v);
413
414     // Some old programs expect ISR to turn keyboard back on.
415     i8042_command(I8042_CMD_KBD_ENABLE, NULL);
416
417 done:
418     pic_eoi1();
419 }
420
421 // Check for ps2 activity on machines without hardware irqs
422 void
423 ps2_check_event(void)
424 {
425     if (! CONFIG_PS2PORT || CONFIG_HARDWARE_IRQ)
426         return;
427     u8 ps2ctr = GET_LOW(Ps2ctr);
428     if ((ps2ctr & (I8042_CTR_KBDDIS|I8042_CTR_AUXDIS))
429         == (I8042_CTR_KBDDIS|I8042_CTR_AUXDIS))
430         return;
431     for (;;) {
432         u8 status = inb(PORT_PS2_STATUS);
433         if (!(status & I8042_STR_OBF))
434             break;
435         u8 data = inb(PORT_PS2_DATA);
436         if (status & I8042_STR_AUXDATA) {
437             if (!(ps2ctr & I8042_CTR_AUXDIS))
438                 process_mouse(data);
439         } else {
440             if (!(ps2ctr & I8042_CTR_KBDDIS))
441                 process_key(data);
442         }
443     }
444 }
445
446
447 /****************************************************************
448  * Setup
449  ****************************************************************/
450
451 static void
452 ps2_keyboard_setup(void *data)
453 {
454     /* flush incoming keys */
455     int ret = i8042_flush();
456     if (ret)
457         return;
458
459     // Controller self-test.
460     u8 param[2];
461     ret = i8042_command(I8042_CMD_CTL_TEST, param);
462     if (ret)
463         return;
464     if (param[0] != 0x55) {
465         dprintf(1, "i8042 self test failed (got %x not 0x55)\n", param[0]);
466         return;
467     }
468
469     // Controller keyboard test.
470     ret = i8042_command(I8042_CMD_KBD_TEST, param);
471     if (ret)
472         return;
473     if (param[0] != 0x00) {
474         dprintf(1, "i8042 keyboard test failed (got %x not 0x00)\n", param[0]);
475         return;
476     }
477
478
479     /* ------------------- keyboard side ------------------------*/
480     /* reset keyboard and self test  (keyboard side) */
481     int spinupdelay = romfile_loadint("etc/ps2-keyboard-spinup", 0);
482     u32 end = timer_calc(spinupdelay);
483     for (;;) {
484         ret = ps2_kbd_command(ATKBD_CMD_RESET_BAT, param);
485         if (!ret)
486             break;
487         if (timer_check(end)) {
488             if (spinupdelay)
489                 warn_timeout();
490             return;
491         }
492         yield();
493     }
494     if (param[0] != 0xaa) {
495         dprintf(1, "keyboard self test failed (got %x not 0xaa)\n", param[0]);
496         return;
497     }
498
499     /* Disable keyboard */
500     ret = ps2_kbd_command(ATKBD_CMD_RESET_DIS, NULL);
501     if (ret)
502         return;
503
504     // Set scancode command (mode 2)
505     param[0] = 0x02;
506     ret = ps2_kbd_command(ATKBD_CMD_SSCANSET, param);
507     if (ret)
508         return;
509
510     // Keyboard Mode: disable mouse, scan code convert, enable kbd IRQ
511     Ps2ctr = (I8042_CTR_AUXDIS | I8042_CTR_XLATE
512               | (CONFIG_HARDWARE_IRQ ? I8042_CTR_KBDINT : 0));
513
514     /* Enable keyboard */
515     ret = ps2_kbd_command(ATKBD_CMD_ENABLE, NULL);
516     if (ret)
517         return;
518
519     dprintf(1, "PS2 keyboard initialized\n");
520 }
521
522 void
523 ps2port_setup(void)
524 {
525     ASSERT32FLAT();
526     if (! CONFIG_PS2PORT)
527         return;
528     dprintf(3, "init ps2port\n");
529
530     enable_hwirq(1, FUNC16(entry_09));
531     enable_hwirq(12, FUNC16(entry_74));
532
533     run_thread(ps2_keyboard_setup, NULL);
534 }