1 // Copyright 2016 The Fuchsia Authors
2 // Copyright (c) 2009 Corey Tabaka
3 // Copyright (c) 2016 Travis Geiselbrecht
4 //
5 // Use of this source code is governed by a MIT-style
6 // license that can be found in the LICENSE file or at
7 // https://opensource.org/licenses/MIT
8 
9 #include <platform/keyboard.h>
10 
11 #include "platform_p.h"
12 #include <arch/x86.h>
13 #include <arch/x86/apic.h>
14 #include <assert.h>
15 #include <ctype.h>
16 #include <debug.h>
17 #include <dev/interrupt.h>
18 #include <err.h>
19 #include <kernel/thread.h>
20 #include <lib/cbuf.h>
21 #include <platform.h>
22 #include <platform/console.h>
23 #include <platform/pc.h>
24 #include <platform/timer.h>
25 #include <reg.h>
26 #include <sys/types.h>
27 #include <trace.h>
28 
29 #define LOCAL_TRACE 0
30 
i8042_read_data(void)31 static inline uint8_t i8042_read_data(void) {
32     return inp(I8042_DATA_REG);
33 }
34 
i8042_read_status(void)35 static inline uint8_t i8042_read_status(void) {
36     return inp(I8042_STATUS_REG);
37 }
38 
i8042_write_data(uint8_t val)39 static inline void i8042_write_data(uint8_t val) {
40     outp(I8042_DATA_REG, val);
41 }
42 
i8042_write_command(uint8_t val)43 static inline void i8042_write_command(uint8_t val) {
44     outp(I8042_COMMAND_REG, val);
45 }
46 
47 /*
48  * timeout in milliseconds
49  */
50 #define I8042_CTL_TIMEOUT 500
51 
52 /*
53  * status register bits
54  */
55 #define I8042_STR_PARITY 0x80
56 #define I8042_STR_TIMEOUT 0x40
57 #define I8042_STR_AUXDATA 0x20
58 #define I8042_STR_KEYLOCK 0x10
59 #define I8042_STR_CMDDAT 0x08
60 #define I8042_STR_MUXERR 0x04
61 #define I8042_STR_IBF 0x02
62 #define I8042_STR_OBF 0x01
63 
64 /*
65  * control register bits
66  */
67 #define I8042_CTR_KBDINT 0x01
68 #define I8042_CTR_AUXINT 0x02
69 #define I8042_CTR_IGNKEYLK 0x08
70 #define I8042_CTR_KBDDIS 0x10
71 #define I8042_CTR_AUXDIS 0x20
72 #define I8042_CTR_XLATE 0x40
73 
74 /*
75  * commands
76  */
77 #define I8042_CMD_CTL_RCTR 0x0120
78 #define I8042_CMD_CTL_WCTR 0x1060
79 #define I8042_CMD_CTL_TEST 0x01aa
80 
81 #define I8042_CMD_KBD_DIS 0x00ad
82 #define I8042_CMD_KBD_EN 0x00ae
83 #define I8042_CMD_PULSE_RESET 0x00fe
84 #define I8042_CMD_KBD_TEST 0x01ab
85 #define I8042_CMD_KBD_MODE 0x01f0
86 
87 /*
88  * used for flushing buffers. the i8042 internal buffer shoudn't exceed this.
89  */
90 #define I8042_BUFFER_LENGTH 32
91 
92 /* extended keys that aren't pure ascii */
93 enum extended_keys {
94     KEY_RETURN = 0x80,
95     KEY_ESC,
96     KEY_LSHIFT,
97     KEY_RSHIFT,
98     KEY_LCTRL,
99     KEY_RCTRL,
100     KEY_LALT,
101     KEY_RALT,
102     KEY_CAPSLOCK,
103     KEY_LWIN,
104     KEY_RWIN,
105     KEY_MENU,
106     KEY_F1,
107     KEY_F2,
108     KEY_F3,
109     KEY_F4,
110     KEY_F5,
111     KEY_F6,
112     KEY_F7,
113     KEY_F8,
114     KEY_F9,
115     KEY_F10,
116     KEY_F11,
117     KEY_F12,
118     KEY_F13,
119     KEY_F14,
120     KEY_F15,
121     KEY_F16,
122     KEY_F17,
123     KEY_F18,
124     KEY_F19,
125     KEY_F20,
126     KEY_PRTSCRN,
127     KEY_SCRLOCK,
128     KEY_PAUSE,
129     KEY_TAB,
130     KEY_BACKSPACE,
131     KEY_INS,
132     KEY_DEL,
133     KEY_HOME,
134     KEY_END,
135     KEY_PGUP,
136     KEY_PGDN,
137     KEY_ARROW_UP,
138     KEY_ARROW_DOWN,
139     KEY_ARROW_LEFT,
140     KEY_ARROW_RIGHT,
141     KEY_PAD_NUMLOCK,
142     KEY_PAD_DIVIDE,
143     KEY_PAD_MULTIPLY,
144     KEY_PAD_MINUS,
145     KEY_PAD_PLUS,
146     KEY_PAD_ENTER,
147     KEY_PAD_PERIOD,
148     KEY_PAD_0,
149     KEY_PAD_1,
150     KEY_PAD_2,
151     KEY_PAD_3,
152     KEY_PAD_4,
153     KEY_PAD_5,
154     KEY_PAD_6,
155     KEY_PAD_7,
156     KEY_PAD_8,
157     KEY_PAD_9,
158 
159     _KEY_LAST,
160 };
161 
162 static_assert(_KEY_LAST < 0x100, "");
163 
164 /* scancode translation tables */
165 const uint8_t pc_keymap_set1_lower[128] = {
166     /* 0x00 */ 0, KEY_ESC, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', KEY_BACKSPACE, KEY_TAB,
167     /* 0x10 */ 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', KEY_RETURN, KEY_LCTRL, 'a', 's',
168     /* 0x20 */ 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', KEY_LSHIFT, '\\', 'z', 'x', 'c', 'v',
169     /* 0x30 */ 'b', 'n', 'm', ',', '.', '/', KEY_RSHIFT, '*', KEY_LALT, ' ', KEY_CAPSLOCK, KEY_F1, KEY_F2,
170     KEY_F3, KEY_F4, KEY_F5,
171     /* 0x40 */ KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_PAD_NUMLOCK, KEY_SCRLOCK, KEY_PAD_7, KEY_PAD_8,
172     KEY_PAD_9, KEY_PAD_MINUS, KEY_PAD_4, KEY_PAD_5, KEY_PAD_6, KEY_PAD_PLUS, KEY_PAD_1,
173     /* 0x50 */ KEY_PAD_2, KEY_PAD_3, KEY_PAD_0, KEY_PAD_PERIOD, 0, 0, 0, KEY_F11, KEY_F12, 0, 0, 0, 0, 0, 0, 0,
174 };
175 
176 const uint8_t pc_keymap_set1_upper[128] = {
177     /* 0x00 */ 0, KEY_ESC, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', KEY_BACKSPACE, KEY_TAB,
178     /* 0x10 */ 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', KEY_RETURN, KEY_LCTRL, 'A', 'S',
179     /* 0x20 */ 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', KEY_LSHIFT, '|', 'Z', 'X', 'C', 'V',
180     /* 0x30 */ 'B', 'N', 'M', '<', '>', '?', KEY_RSHIFT, '*', KEY_LALT, ' ', KEY_CAPSLOCK, KEY_F1, KEY_F2,
181     KEY_F3, KEY_F4, KEY_F5,
182     /* 0x40 */ KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_PAD_NUMLOCK, KEY_SCRLOCK, KEY_PAD_7, KEY_PAD_8,
183     KEY_PAD_9, KEY_PAD_MINUS, KEY_PAD_4, KEY_PAD_5, KEY_PAD_6, KEY_PAD_PLUS, KEY_PAD_1,
184     /* 0x50 */ KEY_PAD_2, KEY_PAD_3, KEY_PAD_0, KEY_PAD_PERIOD, 0, 0, 0, KEY_F11, KEY_F12, 0, 0, 0, 0, 0, 0, 0,
185 };
186 
187 const uint8_t pc_keymap_set1_e0[128] = {
188     /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
189     /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_PAD_ENTER, KEY_RCTRL, 0, 0,
190     /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
191     /* 0x30 */ 0, 0, 0, 0, 0, KEY_PAD_DIVIDE, 0, KEY_PRTSCRN, KEY_RALT, 0, 0, 0, 0, 0, 0, 0,
192     /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, KEY_HOME, KEY_ARROW_UP, KEY_PGUP, 0, KEY_ARROW_LEFT, 0, KEY_ARROW_RIGHT, 0, KEY_END,
193     /* 0x50 */ KEY_ARROW_DOWN, KEY_PGDN, KEY_INS, 0, 0, 0, 0, 0, 0, 0, 0, KEY_LWIN, KEY_RWIN, KEY_MENU, 0, 0};
194 
195 /*
196  * state key flags
197  */
198 static bool key_lshift;
199 static bool key_rshift;
200 static int last_code;
201 
202 static cbuf_t* key_buf;
203 
i8042_process_scode(uint8_t scode,unsigned int flags)204 static void i8042_process_scode(uint8_t scode, unsigned int flags) {
205     // is this a multi code sequence?
206     bool multi = (last_code == 0xe0);
207 
208     // update the last received code
209     last_code = scode;
210 
211     // save the key up event bit
212     bool key_up = !!(scode & 0x80);
213     scode &= 0x7f;
214 
215     // translate the key based on our translation table
216     uint8_t key_code;
217     if (multi) {
218         key_code = pc_keymap_set1_e0[scode];
219     } else if (key_lshift || key_rshift) {
220         key_code = pc_keymap_set1_upper[scode];
221     } else {
222         key_code = pc_keymap_set1_lower[scode];
223     }
224 
225     LTRACEF("scancode 0x%x, keyup %d, multi %d: keycode 0x%x\n", scode, !!key_up, multi, key_code);
226 
227     // generate a character string to feed into the queue
228     char str[4] = {0};
229     switch (key_code) {
230     // for all the usual ascii strings, generate the target string directly
231     case 1 ... 0x7f:
232         str[0] = key_code;
233         break;
234 
235     // a few special keys we can generate stuff for directly
236     case KEY_RETURN:
237     case KEY_PAD_ENTER:
238         str[0] = '\n';
239         break;
240     case KEY_BACKSPACE:
241         str[0] = '\b';
242         break;
243     case KEY_TAB:
244         str[0] = '\t';
245         break;
246 
247     // generate vt100 key codes for arrows
248     case KEY_ARROW_UP:
249         str[0] = 0x1b;
250         str[1] = '[';
251         str[2] = 65;
252         break;
253     case KEY_ARROW_DOWN:
254         str[0] = 0x1b;
255         str[1] = '[';
256         str[2] = 66;
257         break;
258     case KEY_ARROW_RIGHT:
259         str[0] = 0x1b;
260         str[1] = '[';
261         str[2] = 67;
262         break;
263     case KEY_ARROW_LEFT:
264         str[0] = 0x1b;
265         str[1] = '[';
266         str[2] = 68;
267         break;
268 
269     // left and right shift are special
270     case KEY_LSHIFT:
271         key_lshift = !key_up;
272         break;
273     case KEY_RSHIFT:
274         key_rshift = !key_up;
275         break;
276 
277     // everything else we just eat
278     default:; // nothing
279     }
280 
281     if (!key_up) {
282         for (uint i = 0; str[i] != '\0'; i++) {
283             LTRACEF("char 0x%hhx (%c)\n", str[i], isprint(str[i]) ? (str[i]) : ' ');
284             cbuf_write_char(key_buf, str[i]);
285         }
286     }
287 }
288 
i8042_wait_read(void)289 static int i8042_wait_read(void) {
290     int i = 0;
291     while ((~i8042_read_status() & I8042_STR_OBF) && (i < I8042_CTL_TIMEOUT)) {
292         spin(10);
293         i++;
294     }
295     return -(i == I8042_CTL_TIMEOUT);
296 }
297 
i8042_wait_write(void)298 static int i8042_wait_write(void) {
299     int i = 0;
300     while ((i8042_read_status() & I8042_STR_IBF) && (i < I8042_CTL_TIMEOUT)) {
301         spin(10);
302         i++;
303     }
304     return -(i == I8042_CTL_TIMEOUT);
305 }
306 
i8042_flush(void)307 static int i8042_flush(void) {
308     unsigned char data __UNUSED;
309     int i = 0;
310 
311     while ((i8042_read_status() & I8042_STR_OBF) && (i++ < I8042_BUFFER_LENGTH)) {
312         spin(10);
313         data = i8042_read_data();
314     }
315 
316     return i;
317 }
318 
i8042_command(uint8_t * param,uint16_t command)319 static int i8042_command(uint8_t* param, uint16_t command) {
320     int retval = 0, i = 0;
321 
322     retval = i8042_wait_write();
323     if (!retval) {
324         i8042_write_command(static_cast<uint8_t>(command));
325     }
326 
327     if (!retval) {
328         for (i = 0; i < ((command >> 12) & 0xf); i++) {
329             if ((retval = i8042_wait_write())) {
330                 break;
331             }
332 
333             i8042_write_data(param[i]);
334         }
335     }
336 
337     if (!retval) {
338         for (i = 0; i < ((command >> 8) & 0xf); i++) {
339             if ((retval = i8042_wait_read())) {
340                 break;
341             }
342 
343             if (i8042_read_status() & I8042_STR_AUXDATA) {
344                 param[i] = static_cast<uint8_t>(~i8042_read_data());
345             } else {
346                 param[i] = i8042_read_data();
347             }
348         }
349     }
350 
351     return retval;
352 }
353 
keyboard_command(uint8_t * param,int command)354 static int keyboard_command(uint8_t* param, int command) {
355     int retval = 0, i = 0;
356 
357     retval = i8042_wait_write();
358     if (!retval) {
359         i8042_write_data(static_cast<uint8_t>(command));
360     }
361 
362     if (!retval) {
363         for (i = 0; i < ((command >> 12) & 0xf); i++) {
364             if ((retval = i8042_wait_write())) {
365                 break;
366             }
367 
368             i8042_write_data(param[i]);
369         }
370     }
371 
372     if (!retval) {
373         for (i = 0; i < ((command >> 8) & 0xf); i++) {
374             if ((retval = i8042_wait_read())) {
375                 break;
376             }
377 
378             if (i8042_read_status() & I8042_STR_AUXDATA) {
379                 param[i] = static_cast<uint8_t>(~i8042_read_data());
380             } else {
381                 param[i] = i8042_read_data();
382             }
383         }
384     }
385 
386     return retval;
387 }
388 
i8042_interrupt(void * arg)389 static interrupt_eoi i8042_interrupt(void* arg) {
390     // keep handling status on the keyboard controller until no bits are set we care about
391     bool retry;
392     do {
393         retry = false;
394 
395         uint8_t str = i8042_read_status();
396 
397         // check for incoming data from the controller
398         if (str & I8042_STR_OBF) {
399             uint8_t data = i8042_read_data();
400             i8042_process_scode(data,
401                                 ((str & I8042_STR_PARITY) ? I8042_STR_PARITY : 0) |
402                                 ((str & I8042_STR_TIMEOUT) ? I8042_STR_TIMEOUT : 0));
403 
404             retry = true;
405         }
406 
407         // TODO: check other status bits here
408     } while (retry);
409     return IRQ_EOI_DEACTIVATE;
410 }
411 
platform_read_key(char * c)412 int platform_read_key(char* c) {
413     ssize_t len = cbuf_read_char(key_buf, c, true);
414     return static_cast<int>(len);
415 }
416 
platform_init_keyboard(cbuf_t * buffer)417 void platform_init_keyboard(cbuf_t* buffer) {
418     uint8_t ctr;
419 
420     key_buf = buffer;
421 
422     i8042_flush();
423 
424     if (i8042_command(&ctr, I8042_CMD_CTL_RCTR)) {
425         dprintf(SPEW, "Failed to read CTR while initializing i8042\n");
426         return;
427     }
428 
429     // turn on translation
430     ctr |= I8042_CTR_XLATE;
431 
432     // enable keyboard and keyboard irq
433     ctr &= static_cast<uint8_t>(~I8042_CTR_KBDDIS);
434     ctr |= I8042_CTR_KBDINT;
435 
436     if (i8042_command(&ctr, I8042_CMD_CTL_WCTR)) {
437         dprintf(SPEW, "Failed to write CTR while initializing i8042\n");
438         return;
439     }
440 
441     /* enable PS/2 port */
442     i8042_command(NULL, I8042_CMD_KBD_EN);
443 
444     /* send a enable scan command to the keyboard */
445     keyboard_command(&ctr, 0x1f4);
446 
447     uint32_t irq = apic_io_isa_to_global(ISA_IRQ_KEYBOARD);
448     zx_status_t status = register_int_handler(irq, &i8042_interrupt, NULL);
449     DEBUG_ASSERT(status == ZX_OK);
450     unmask_interrupt(irq);
451 
452     i8042_interrupt(NULL);
453 }
454 
pc_keyboard_reboot(void)455 void pc_keyboard_reboot(void) {
456     if (i8042_wait_write() != 0) {
457         return;
458     }
459 
460     i8042_write_command(I8042_CMD_PULSE_RESET);
461     // Wait a second for the command to process before declaring failure
462     spin(1000000);
463 }
464