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