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