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
31static inline uint8_t i8042_read_data(void) {
32    return inp(I8042_DATA_REG);
33}
34
35static inline uint8_t i8042_read_status(void) {
36    return inp(I8042_STATUS_REG);
37}
38
39static inline void i8042_write_data(uint8_t val) {
40    outp(I8042_DATA_REG, val);
41}
42
43static 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 */
93enum 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
162static_assert(_KEY_LAST < 0x100, "");
163
164/* scancode translation tables */
165const 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
176const 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
187const 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 */
198static bool key_lshift;
199static bool key_rshift;
200static int last_code;
201
202static cbuf_t* key_buf;
203
204static 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
289static 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
298static 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
307static 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
319static 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
354static 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
389static void 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}
410
411int platform_read_key(char* c) {
412    ssize_t len = cbuf_read_char(key_buf, c, true);
413    return static_cast<int>(len);
414}
415
416void platform_init_keyboard(cbuf_t* buffer) {
417    uint8_t ctr;
418
419    key_buf = buffer;
420
421    i8042_flush();
422
423    if (i8042_command(&ctr, I8042_CMD_CTL_RCTR)) {
424        dprintf(SPEW, "Failed to read CTR while initializing i8042\n");
425        return;
426    }
427
428    // turn on translation
429    ctr |= I8042_CTR_XLATE;
430
431    // enable keyboard and keyboard irq
432    ctr &= static_cast<uint8_t>(~I8042_CTR_KBDDIS);
433    ctr |= I8042_CTR_KBDINT;
434
435    if (i8042_command(&ctr, I8042_CMD_CTL_WCTR)) {
436        dprintf(SPEW, "Failed to write CTR while initializing i8042\n");
437        return;
438    }
439
440    /* enable PS/2 port */
441    i8042_command(NULL, I8042_CMD_KBD_EN);
442
443    /* send a enable scan command to the keyboard */
444    keyboard_command(&ctr, 0x1f4);
445
446    uint32_t irq = apic_io_isa_to_global(ISA_IRQ_KEYBOARD);
447    zx_status_t status = register_int_handler(irq, &i8042_interrupt, NULL);
448    DEBUG_ASSERT(status == ZX_OK);
449    unmask_interrupt(irq);
450
451    i8042_interrupt(NULL);
452}
453
454void pc_keyboard_reboot(void) {
455    if (i8042_wait_write() != 0) {
456        return;
457    }
458
459    i8042_write_command(I8042_CMD_PULSE_RESET);
460    // Wait a second for the command to process before declaring failure
461    spin(1000000);
462}
463