1// Copyright 2016 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <ddk/binding.h> 6#include <ddk/device.h> 7#include <ddk/driver.h> 8#include <ddk/protocol/hidbus.h> 9#include <zircon/device/input.h> 10#include <hw/inout.h> 11 12#include <zircon/syscalls.h> 13#include <zircon/types.h> 14 15#include <hid/usages.h> 16 17#include <stdio.h> 18#include <stdlib.h> 19#include <string.h> 20#include <threads.h> 21#include <unistd.h> 22 23#define xprintf(fmt...) do {} while (0) 24 25typedef struct i8042_device { 26 mtx_t lock; 27 hidbus_ifc_t* ifc; 28 void* cookie; 29 30 zx_handle_t irq; 31 thrd_t irq_thread; 32 33 int last_code; 34 35 int type; 36 union { 37 boot_kbd_report_t kbd; 38 boot_mouse_report_t mouse; 39 } report; 40} i8042_device_t; 41 42static inline bool is_kbd_modifier(uint8_t usage) { 43 return (usage >= HID_USAGE_KEY_LEFT_CTRL && usage <= HID_USAGE_KEY_RIGHT_GUI); 44} 45 46#define MOD_SET 1 47#define MOD_EXISTS 2 48#define MOD_ROLLOVER 3 49static int i8042_modifier_key(i8042_device_t* dev, uint8_t mod, bool down) { 50 int bit = mod - HID_USAGE_KEY_LEFT_CTRL; 51 if (bit < 0 || bit > 7) return MOD_ROLLOVER; 52 if (down) { 53 if (dev->report.kbd.modifier & 1 << bit) { 54 return MOD_EXISTS; 55 } else { 56 dev->report.kbd.modifier |= 1 << bit; 57 } 58 } else { 59 dev->report.kbd.modifier &= ~(1 << bit); 60 } 61 return MOD_SET; 62} 63 64#define KEY_ADDED 1 65#define KEY_EXISTS 2 66#define KEY_ROLLOVER 3 67static int i8042_add_key(i8042_device_t* dev, uint8_t usage) { 68 for (int i = 0; i < 6; i++) { 69 if (dev->report.kbd.usage[i] == usage) return KEY_EXISTS; 70 if (dev->report.kbd.usage[i] == 0) { 71 dev->report.kbd.usage[i] = usage; 72 return KEY_ADDED; 73 } 74 } 75 return KEY_ROLLOVER; 76} 77 78#define KEY_REMOVED 1 79#define KEY_NOT_FOUND 2 80static int i8042_rm_key(i8042_device_t* dev, uint8_t usage) { 81 int idx = -1; 82 for (int i = 0; i < 6; i++) { 83 if (dev->report.kbd.usage[i] == usage) { 84 idx = i; 85 break; 86 } 87 } 88 if (idx == -1) return KEY_NOT_FOUND; 89 for (int i = idx; i < 5; i++) { 90 dev->report.kbd.usage[i] = dev->report.kbd.usage[i+1]; 91 } 92 dev->report.kbd.usage[5] = 0; 93 return KEY_REMOVED; 94} 95 96#define I8042_COMMAND_REG 0x64 97#define I8042_STATUS_REG 0x64 98#define I8042_DATA_REG 0x60 99 100#define ISA_IRQ_KEYBOARD 0x1 101#define ISA_IRQ_MOUSE 0x0c 102 103static inline int i8042_read_data(void) { 104 return inp(I8042_DATA_REG); 105} 106 107static inline int i8042_read_status(void) { 108 return inp(I8042_STATUS_REG); 109} 110 111static inline void i8042_write_data(int val) { 112 outp(I8042_DATA_REG, val); 113} 114 115static inline void i8042_write_command(int val) { 116 outp(I8042_COMMAND_REG, val); 117} 118 119/* 120 * timeout in milliseconds 121 */ 122#define I8042_CTL_TIMEOUT 500 123 124/* 125 * status register bits 126 */ 127#define I8042_STR_PARITY 0x80 128#define I8042_STR_TIMEOUT 0x40 129#define I8042_STR_AUXDATA 0x20 130#define I8042_STR_KEYLOCK 0x10 131#define I8042_STR_CMDDAT 0x08 132#define I8042_STR_MUXERR 0x04 133#define I8042_STR_IBF 0x02 134#define I8042_STR_OBF 0x01 135 136/* 137 * control register bits 138 */ 139#define I8042_CTR_KBDINT 0x01 140#define I8042_CTR_AUXINT 0x02 141#define I8042_CTR_IGNKEYLK 0x08 142#define I8042_CTR_KBDDIS 0x10 143#define I8042_CTR_AUXDIS 0x20 144#define I8042_CTR_XLATE 0x40 145 146/* 147 * commands 148 */ 149#define I8042_CMD_CTL_RCTR 0x0120 150#define I8042_CMD_CTL_WCTR 0x1060 151#define I8042_CMD_CTL_TEST 0x01aa 152#define I8042_CMD_CTL_AUX 0x00d4 153 154// Identity response will be ACK + 0, 1, or 2 bytes 155#define I8042_CMD_IDENTIFY 0x03f2 156#define I8042_CMD_SCAN_DIS 0x01f5 157#define I8042_CMD_SCAN_EN 0x01f4 158 159#define I8042_CMD_CTL_KBD_DIS 0x00ad 160#define I8042_CMD_CTL_KBD_EN 0x00ae 161#define I8042_CMD_CTL_KBD_TEST 0x01ab 162#define I8042_CMD_KBD_MODE 0x01f0 163 164#define I8042_CMD_CTL_MOUSE_DIS 0x00a7 165#define I8042_CMD_CTL_MOUSE_EN 0x00a8 166#define I8042_CMD_CTL_MOUSE_TEST 0x01a9 167 168/* 169 * used for flushing buffers. the i8042 internal buffer shoudn't exceed this. 170 */ 171#define I8042_BUFFER_LENGTH 32 172 173static const uint8_t kbd_hid_report_desc[] = { 174 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 175 0x09, 0x06, // Usage (Keyboard) 176 0xA1, 0x01, // Collection (Application) 177 0x05, 0x07, // Usage Page (Kbrd/Keypad) 178 0x19, 0xE0, // Usage Minimum (0xE0) 179 0x29, 0xE7, // Usage Maximum (0xE7) 180 0x15, 0x00, // Logical Minimum (0) 181 0x25, 0x01, // Logical Maximum (1) 182 0x75, 0x01, // Report Size (1) 183 0x95, 0x08, // Report Count (8) 184 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 185 0x95, 0x01, // Report Count (1) 186 0x75, 0x08, // Report Size (8) 187 0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) 188 0x95, 0x05, // Report Count (5) 189 0x75, 0x01, // Report Size (1) 190 0x05, 0x08, // Usage Page (LEDs) 191 0x19, 0x01, // Usage Minimum (Num Lock) 192 0x29, 0x05, // Usage Maximum (Kana) 193 0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 194 0x95, 0x01, // Report Count (1) 195 0x75, 0x03, // Report Size (3) 196 0x91, 0x01, // Output (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile) 197 0x95, 0x06, // Report Count (6) 198 0x75, 0x08, // Report Size (8) 199 0x15, 0x00, // Logical Minimum (0) 200 0x25, 0x65, // Logical Maximum (101) 201 0x05, 0x07, // Usage Page (Kbrd/Keypad) 202 0x19, 0x00, // Usage Minimum (0x00) 203 0x29, 0x65, // Usage Maximum (0x65) 204 0x81, 0x00, // Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) 205 0xC0, // End Collection 206}; 207 208static const uint8_t pc_set1_usage_map[128] = { 209 /* 0x00 */ 0, HID_USAGE_KEY_ESC, HID_USAGE_KEY_1, HID_USAGE_KEY_2, 210 /* 0x04 */ HID_USAGE_KEY_3, HID_USAGE_KEY_4, HID_USAGE_KEY_5, HID_USAGE_KEY_6, 211 /* 0x08 */ HID_USAGE_KEY_7, HID_USAGE_KEY_8, HID_USAGE_KEY_9, HID_USAGE_KEY_0, 212 /* 0x0c */ HID_USAGE_KEY_MINUS, HID_USAGE_KEY_EQUAL, HID_USAGE_KEY_BACKSPACE, HID_USAGE_KEY_TAB, 213 /* 0x10 */ HID_USAGE_KEY_Q, HID_USAGE_KEY_W, HID_USAGE_KEY_E, HID_USAGE_KEY_R, 214 /* 0x14 */ HID_USAGE_KEY_T, HID_USAGE_KEY_Y, HID_USAGE_KEY_U, HID_USAGE_KEY_I, 215 /* 0x18 */ HID_USAGE_KEY_O, HID_USAGE_KEY_P, HID_USAGE_KEY_LEFTBRACE, HID_USAGE_KEY_RIGHTBRACE, 216 /* 0x1c */ HID_USAGE_KEY_ENTER, HID_USAGE_KEY_LEFT_CTRL, HID_USAGE_KEY_A, HID_USAGE_KEY_S, 217 /* 0x20 */ HID_USAGE_KEY_D, HID_USAGE_KEY_F, HID_USAGE_KEY_G, HID_USAGE_KEY_H, 218 /* 0x24 */ HID_USAGE_KEY_J, HID_USAGE_KEY_K, HID_USAGE_KEY_L, HID_USAGE_KEY_SEMICOLON, 219 /* 0x28 */ HID_USAGE_KEY_APOSTROPHE, HID_USAGE_KEY_GRAVE, HID_USAGE_KEY_LEFT_SHIFT, HID_USAGE_KEY_BACKSLASH, 220 /* 0x2c */ HID_USAGE_KEY_Z, HID_USAGE_KEY_X, HID_USAGE_KEY_C, HID_USAGE_KEY_V, 221 /* 0x30 */ HID_USAGE_KEY_B, HID_USAGE_KEY_N, HID_USAGE_KEY_M, HID_USAGE_KEY_COMMA, 222 /* 0x34 */ HID_USAGE_KEY_DOT, HID_USAGE_KEY_SLASH, HID_USAGE_KEY_RIGHT_SHIFT, HID_USAGE_KEY_KP_ASTERISK, 223 /* 0x38 */ HID_USAGE_KEY_LEFT_ALT, HID_USAGE_KEY_SPACE, HID_USAGE_KEY_CAPSLOCK, HID_USAGE_KEY_F1, 224 /* 0x3c */ HID_USAGE_KEY_F2, HID_USAGE_KEY_F3, HID_USAGE_KEY_F4, HID_USAGE_KEY_F5, 225 /* 0x40 */ HID_USAGE_KEY_F6, HID_USAGE_KEY_F7, HID_USAGE_KEY_F8, HID_USAGE_KEY_F9, 226 /* 0x44 */ HID_USAGE_KEY_F10, HID_USAGE_KEY_NUMLOCK, HID_USAGE_KEY_SCROLLLOCK, HID_USAGE_KEY_KP_7, 227 /* 0x48 */ HID_USAGE_KEY_KP_8, HID_USAGE_KEY_KP_9, HID_USAGE_KEY_KP_MINUS, HID_USAGE_KEY_KP_4, 228 /* 0x4c */ HID_USAGE_KEY_KP_5, HID_USAGE_KEY_KP_6, HID_USAGE_KEY_KP_PLUS, HID_USAGE_KEY_KP_1, 229 /* 0x50 */ HID_USAGE_KEY_KP_2, HID_USAGE_KEY_KP_3, HID_USAGE_KEY_KP_0, HID_USAGE_KEY_KP_DOT, 230 /* 0x54 */ 0, 0, 0, HID_USAGE_KEY_F11, 231 /* 0x58 */ HID_USAGE_KEY_F12, 0, 0, 0, 232}; 233 234static const uint8_t pc_set1_usage_map_e0[128] = { 235 /* 0x00 */ 0, 0, 0, 0, 0, 0, 0, 0, 236 /* 0x08 */ 0, 0, 0, 0, 0, 0, 0, 0, 237 /* 0x10 */ 0, 0, 0, 0, 0, 0, 0, 0, 238 /* 0x18 */ 0, 0, 0, 0, HID_USAGE_KEY_KP_ENTER, HID_USAGE_KEY_RIGHT_CTRL, 0, 0, 239 /* 0x20 */ 0, 0, 0, 0, 0, 0, 0, 0, 240 /* 0x28 */ 0, 0, 0, 0, 0, 0, HID_USAGE_KEY_VOL_DOWN, 0, 241 /* 0x30 */ HID_USAGE_KEY_VOL_UP, 0, 0, 0, 0, HID_USAGE_KEY_KP_SLASH, 0, HID_USAGE_KEY_PRINTSCREEN, 242 /* 0x38 */ HID_USAGE_KEY_RIGHT_ALT, 0, 0, 0, 0, 0, 0, 0, 243 /* 0x40 */ 0, 0, 0, 0, 0, 0, 0, HID_USAGE_KEY_HOME, 244 /* 0x48 */ HID_USAGE_KEY_UP, HID_USAGE_KEY_PAGEUP, 0, HID_USAGE_KEY_LEFT, 0, HID_USAGE_KEY_RIGHT, 0, HID_USAGE_KEY_END, 245 /* 0x50 */ HID_USAGE_KEY_DOWN, HID_USAGE_KEY_PAGEDOWN, HID_USAGE_KEY_INSERT, HID_USAGE_KEY_DELETE, 0, 0, 0, 0, 246 /* 0x58 */ 0, 0, 0, HID_USAGE_KEY_LEFT_GUI, HID_USAGE_KEY_RIGHT_GUI, 0 /* MENU */, 0, 0, 247}; 248 249static const uint8_t mouse_hid_report_desc[] = { 250 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 251 0x09, 0x02, // Usage (Mouse) 252 0xA1, 0x01, // Collection (Application) 253 0x09, 0x01, // Usage (Pointer) 254 0xA1, 0x00, // Collection (Physical) 255 0x05, 0x09, // Usage Page (Button) 256 0x19, 0x01, // Usage Minimum (0x01) 257 0x29, 0x03, // Usage Maximum (0x03) 258 0x15, 0x00, // Logical Minimum (0) 259 0x25, 0x01, // Logical Maximum (1) 260 0x95, 0x03, // Report Count (3) 261 0x75, 0x01, // Report Size (1) 262 0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position) 263 0x95, 0x01, // Report Count (1) 264 0x75, 0x05, // Report Size (5) 265 0x81, 0x01, // Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position) 266 0x05, 0x01, // Usage Page (Generic Desktop Ctrls) 267 0x09, 0x30, // Usage (X) 268 0x09, 0x31, // Usage (Y) 269 0x15, 0x81, // Logical Minimum (129) 270 0x25, 0x7F, // Logical Maximum (127) 271 0x75, 0x08, // Report Size (8) 272 0x95, 0x02, // Report Count (2) 273 0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position) 274 0xC0, // End Collection 275 0xC0, // End Collection 276}; 277 278static const boot_kbd_report_t report_err_rollover = { 279 .modifier = 1, 280 .usage = {1, 1, 1, 1, 1, 1 } 281}; 282 283static int i8042_wait_read(void) { 284 int i = 0; 285 while ((~i8042_read_status() & I8042_STR_OBF) && (i < I8042_CTL_TIMEOUT)) { 286 usleep(10); 287 i++; 288 } 289 return -(i == I8042_CTL_TIMEOUT); 290} 291 292static int i8042_wait_write(void) { 293 int i = 0; 294 while ((i8042_read_status() & I8042_STR_IBF) && (i < I8042_CTL_TIMEOUT)) { 295 usleep(10); 296 i++; 297 } 298 return -(i == I8042_CTL_TIMEOUT); 299} 300 301static int i8042_flush(void) { 302 unsigned char data __UNUSED; 303 int i = 0; 304 305 while ((i8042_read_status() & I8042_STR_OBF) && (i++ < I8042_BUFFER_LENGTH)) { 306 usleep(10); 307 data = i8042_read_data(); 308 } 309 310 return i; 311} 312 313static int i8042_command_data(uint8_t* param, int command) { 314 int retval = 0, i = 0; 315 316 for (i = 0; i < ((command >> 12) & 0xf); i++) { 317 if ((retval = i8042_wait_write())) { 318 break; 319 } 320 321 i8042_write_data(param[i]); 322 } 323 324 int expected = (command >> 8) & 0xf; 325 if (!retval) { 326 for (i = 0; i < expected; i++) { 327 if ((retval = i8042_wait_read())) { 328 xprintf("i8042: timeout reading; got %d bytes\n", i); 329 return i; 330 } 331 332 // TODO: do we need to distinguish keyboard and aux data? 333 param[i] = i8042_read_data(); 334 } 335 } 336 337 return retval ? retval : expected; 338} 339 340static int i8042_command(uint8_t* param, int command) { 341 xprintf("i8042 ctl command 0x%04x\n", command & 0xffff); 342 int retval = 0; 343 344 retval = i8042_wait_write(); 345 if (!retval) { 346 i8042_write_command(command & 0xff); 347 } 348 349 if (!retval) { 350 retval = i8042_command_data(param, command); 351 } 352 353 return retval; 354} 355 356static int i8042_selftest(void) { 357 uint8_t param; 358 int i = 0; 359 do { 360 if (i8042_command(¶m, I8042_CMD_CTL_TEST) < 0) { 361 return -1; 362 } 363 if (param == 0x55) 364 return 0; 365 usleep(50 * 1000); 366 } while (i++ < 5); 367 return -1; 368} 369 370static int i8042_dev_command(uint8_t* param, int command) { 371 xprintf("i8042 dev command 0x%04x\n", command & 0xffff); 372 int retval = 0; 373 374 retval = i8042_wait_write(); 375 if (!retval) { 376 i8042_write_data(command & 0xff); 377 } 378 379 if (!retval) { 380 retval = i8042_command_data(param, command); 381 } 382 383 return retval; 384} 385 386static int i8042_aux_command(uint8_t* param, int command) { 387 xprintf("i8042 aux command\n"); 388 int retval = 0; 389 390 retval = i8042_wait_write(); 391 if (!retval) { 392 i8042_write_command(I8042_CMD_CTL_AUX); 393 } 394 395 if (!retval) { 396 return i8042_dev_command(param, command); 397 } 398 399 return retval; 400} 401 402static void i8042_process_scode(i8042_device_t* dev, uint8_t scode, unsigned int flags) { 403 // is this a multi code sequence? 404 bool multi = (dev->last_code == 0xe0); 405 406 // update the last received code 407 dev->last_code = scode; 408 409 // save the key up event bit 410 bool key_up = !!(scode & 0x80); 411 scode &= 0x7f; 412 413 // translate the key based on our translation table 414 uint8_t usage; 415 if (multi) { 416 usage = pc_set1_usage_map_e0[scode]; 417 } else { 418 usage = pc_set1_usage_map[scode]; 419 } 420 if (!usage) return; 421 422 bool rollover = false; 423 if (is_kbd_modifier(usage)) { 424 switch (i8042_modifier_key(dev, usage, !key_up)) { 425 case MOD_EXISTS: 426 return; 427 case MOD_ROLLOVER: 428 rollover = true; 429 break; 430 case MOD_SET: 431 default: 432 break; 433 } 434 } else if (key_up) { 435 if (i8042_rm_key(dev, usage) != KEY_REMOVED) { 436 rollover = true; 437 } 438 } else { 439 switch (i8042_add_key(dev, usage)) { 440 case KEY_EXISTS: 441 return; 442 case KEY_ROLLOVER: 443 rollover = true; 444 break; 445 case KEY_ADDED: 446 default: 447 break; 448 } 449 } 450 451 //cprintf("i8042: scancode=0x%x, keyup=%u, multi=%u: usage=0x%x\n", scode, !!key_up, multi, usage); 452 453 const boot_kbd_report_t* report = rollover ? &report_err_rollover : &dev->report.kbd; 454 mtx_lock(&dev->lock); 455 if (dev->ifc) { 456 dev->ifc->io_queue(dev->cookie, (const uint8_t*)report, sizeof(*report)); 457 } 458 mtx_unlock(&dev->lock); 459} 460 461static void i8042_process_mouse(i8042_device_t* dev, uint8_t data, unsigned int flags) { 462 switch (dev->last_code) { 463 case 0: 464 if (!(data & 0x08)) { 465 // The first byte always has bit 3 set, so skip this packet. 466 return; 467 } 468 dev->report.mouse.buttons = data; 469 break; 470 case 1: { 471 int state = dev->report.mouse.buttons; 472 int d = data; 473 dev->report.mouse.rel_x = d - ((state << 4) & 0x100); 474 break; 475 } 476 case 2: { 477 int state = dev->report.mouse.buttons; 478 int d = data; 479 // PS/2 maps the y-axis backwards so invert the rel_y value 480 dev->report.mouse.rel_y = ((state << 3) & 0x100) - d; 481 dev->report.mouse.buttons &= 0x7; 482 483 mtx_lock(&dev->lock); 484 if (dev->ifc) { 485 dev->ifc->io_queue(dev->cookie, (const uint8_t*)&dev->report.mouse, 486 sizeof(dev->report.mouse)); 487 } 488 mtx_unlock(&dev->lock); 489 memset(&dev->report.mouse, 0, sizeof(dev->report.mouse)); 490 break; 491 } 492 } 493 dev->last_code = (dev->last_code + 1) % 3; 494} 495 496static int i8042_irq_thread(void* arg) { 497 i8042_device_t* device = (i8042_device_t*)arg; 498 499 // enable I/O port access 500 // TODO 501 zx_status_t status; 502 status = zx_ioports_request(get_root_resource(), I8042_COMMAND_REG, 1); 503 if (status) 504 return 0; 505 status = zx_ioports_request(get_root_resource(), I8042_DATA_REG, 1); 506 if (status) 507 return 0; 508 509 for (;;) { 510 status = zx_interrupt_wait(device->irq, NULL); 511 if (status != ZX_OK) { 512 break; 513 } 514 // keep handling status on the controller until no bits are set we care about 515 bool retry; 516 do { 517 retry = false; 518 519 uint8_t str = i8042_read_status(); 520 521 // check for incoming data from the controller 522 // TODO: deal with potential race between IRQ1 and IRQ12 523 if (str & I8042_STR_OBF) { 524 uint8_t data = i8042_read_data(); 525 // TODO: should we check (str & I8042_STR_AUXDATA) before 526 // handling this byte? 527 if (device->type == INPUT_PROTO_KBD) { 528 i8042_process_scode(device, data, 529 ((str & I8042_STR_PARITY) ? I8042_STR_PARITY : 0) | 530 ((str & I8042_STR_TIMEOUT) ? I8042_STR_TIMEOUT : 0)); 531 } else if (device->type == INPUT_PROTO_MOUSE) { 532 i8042_process_mouse(device, data, 0); 533 } 534 retry = true; 535 } 536 // TODO check other status bits here 537 } while (retry); 538 } 539 return 0; 540} 541 542static zx_status_t i8042_setup(uint8_t* ctr) { 543 // enable I/O port access 544 zx_status_t status = zx_ioports_request(get_root_resource(), I8042_COMMAND_REG, 1); 545 if (status) 546 return status; 547 status = zx_ioports_request(get_root_resource(), I8042_DATA_REG, 1); 548 if (status) 549 return status; 550 551 // initialize hardware 552 i8042_command(NULL, I8042_CMD_CTL_KBD_DIS); 553 i8042_command(NULL, I8042_CMD_CTL_MOUSE_DIS); 554 i8042_flush(); 555 556 if (i8042_command(ctr, I8042_CMD_CTL_RCTR) < 0) 557 return -1; 558 559 xprintf("i8042 controller register: 0x%02x\n", *ctr); 560 bool have_mouse = !!(*ctr & I8042_CTR_AUXDIS); 561 // disable IRQs and translation 562 *ctr &= ~(I8042_CTR_KBDINT | I8042_CTR_AUXINT | I8042_CTR_XLATE); 563 if (i8042_command(ctr, I8042_CMD_CTL_WCTR) < 0) 564 return -1; 565 566 if (i8042_selftest() < 0) { 567 printf("i8042 self-test failed\n"); 568 return -1; 569 } 570 571 uint8_t resp = 0; 572 if (i8042_command(&resp, I8042_CMD_CTL_KBD_TEST) < 0) 573 return -1; 574 if (resp != 0x00) { 575 printf("i8042 kbd test failed: 0x%02x\n", resp); 576 return -1; 577 } 578 if (have_mouse) { 579 resp = 0; 580 if (i8042_command(&resp, I8042_CMD_CTL_MOUSE_TEST) < 0) 581 return -1; 582 if (resp != 0x00) { 583 printf("i8042 mouse test failed: 0x%02x\n", resp); 584 return -1; 585 } 586 } 587 return ZX_OK; 588} 589 590static void i8042_identify(int (*cmd)(uint8_t* param, int command)) { 591 uint8_t resp[3]; 592 if (cmd(resp, I8042_CMD_SCAN_DIS) < 0) return; 593 resp[0] = 0; 594 int ident_sz = cmd(resp, I8042_CMD_IDENTIFY); 595 if (ident_sz < 0) return; 596 printf("i8042 device "); 597 switch (ident_sz) { 598 case 1: 599 printf("(unknown)"); 600 break; 601 case 2: 602 printf("0x%02x", resp[1]); 603 break; 604 case 3: 605 printf("0x%02x 0x%02x", resp[1], resp[2]); 606 break; 607 default: 608 printf("failed to respond to IDENTIFY"); 609 } 610 printf("\n"); 611 cmd(resp, I8042_CMD_SCAN_EN); 612} 613 614static zx_status_t i8042_query(void* ctx, uint32_t options, hid_info_t* info) { 615 i8042_device_t* i8042 = ctx; 616 info->dev_num = i8042->type; // use the type for the device number for now 617 info->dev_class = i8042->type; 618 info->boot_device = true; 619 return ZX_OK; 620} 621 622static zx_status_t i8042_start(void* ctx, hidbus_ifc_t* ifc, void* cookie) { 623 i8042_device_t* i8042 = ctx; 624 mtx_lock(&i8042->lock); 625 if (i8042->ifc != NULL) { 626 mtx_unlock(&i8042->lock); 627 return ZX_ERR_ALREADY_BOUND; 628 } 629 i8042->ifc = ifc; 630 i8042->cookie = cookie; 631 mtx_unlock(&i8042->lock); 632 return ZX_OK; 633} 634 635static void i8042_stop(void* ctx) { 636 i8042_device_t* i8042 = ctx; 637 mtx_lock(&i8042->lock); 638 i8042->ifc = NULL; 639 i8042->cookie = NULL; 640 mtx_unlock(&i8042->lock); 641} 642 643static zx_status_t i8042_get_descriptor(void* ctx, uint8_t desc_type, 644 void** data, size_t* len) { 645 if (data == NULL || len == NULL) { 646 return ZX_ERR_INVALID_ARGS; 647 } 648 649 if (desc_type != HID_DESC_TYPE_REPORT) { 650 return ZX_ERR_NOT_FOUND; 651 } 652 653 i8042_device_t* device = ctx; 654 const uint8_t* buf = NULL; 655 size_t buflen = 0; 656 if (device->type == INPUT_PROTO_KBD) { 657 buf = (void*)&kbd_hid_report_desc; 658 buflen = sizeof(kbd_hid_report_desc); 659 } else if (device->type == INPUT_PROTO_MOUSE) { 660 buf = (void*)&mouse_hid_report_desc; 661 buflen = sizeof(mouse_hid_report_desc); 662 } else { 663 return ZX_ERR_NOT_SUPPORTED; 664 } 665 666 *data = malloc(buflen); 667 *len = buflen; 668 memcpy(*data, buf, buflen); 669 return ZX_OK; 670} 671 672static zx_status_t i8042_get_report(void* ctx, uint8_t rpt_type, uint8_t rpt_id, 673 void* data, size_t len, size_t* out_len) { 674 return ZX_ERR_NOT_SUPPORTED; 675} 676 677static zx_status_t i8042_set_report(void* ctx, uint8_t rpt_type, uint8_t rpt_id, 678 void* data, size_t len) { 679 return ZX_ERR_NOT_SUPPORTED; 680} 681 682static zx_status_t i8042_get_idle(void* ctx, uint8_t rpt_type, uint8_t* duration) { 683 return ZX_ERR_NOT_SUPPORTED; 684} 685 686static zx_status_t i8042_set_idle(void* ctx, uint8_t rpt_type, uint8_t duration) { 687 return ZX_OK; 688} 689 690static zx_status_t i8042_get_protocol(void* ctx, uint8_t* protocol) { 691 return ZX_ERR_NOT_SUPPORTED; 692} 693 694static zx_status_t i8042_set_protocol(void* ctx, uint8_t protocol) { 695 return ZX_OK; 696} 697 698static hidbus_protocol_ops_t hidbus_ops = { 699 .query = i8042_query, 700 .start = i8042_start, 701 .stop = i8042_stop, 702 .get_descriptor = i8042_get_descriptor, 703 .get_report = i8042_get_report, 704 .set_report = i8042_set_report, 705 .get_idle = i8042_get_idle, 706 .set_idle = i8042_set_idle, 707 .get_protocol = i8042_get_protocol, 708 .set_protocol = i8042_set_protocol, 709}; 710 711static void i8042_cleanup_irq_thread(i8042_device_t* dev) { 712 zx_interrupt_destroy(dev->irq); 713 thrd_join(dev->irq_thread, NULL); 714 zx_handle_close(dev->irq); 715} 716 717static void i8042_release(void* ctx) { 718 i8042_device_t* i8042 = ctx; 719 i8042_cleanup_irq_thread(i8042); 720 free(i8042); 721} 722 723static zx_protocol_device_t i8042_dev_proto = { 724 .version = DEVICE_OPS_VERSION, 725 .release = i8042_release, 726}; 727 728static zx_status_t i8042_dev_init(i8042_device_t* dev, const char* name, zx_device_t* parent) { 729 // enable device port 730 int cmd = dev->type == INPUT_PROTO_KBD ? 731 I8042_CMD_CTL_KBD_DIS : I8042_CMD_CTL_MOUSE_DIS; 732 i8042_command(NULL, cmd); 733 734 // TODO: use identity to determine device type, rather than assuming aux == 735 // mouse 736 i8042_identify(dev->type == INPUT_PROTO_KBD ? 737 i8042_dev_command : i8042_aux_command); 738 739 cmd = dev->type == INPUT_PROTO_KBD ? 740 I8042_CMD_CTL_KBD_EN : I8042_CMD_CTL_MOUSE_EN; 741 i8042_command(NULL, cmd); 742 743 uint32_t interrupt = dev->type == INPUT_PROTO_KBD ? 744 ISA_IRQ_KEYBOARD : ISA_IRQ_MOUSE; 745 746 zx_status_t status = zx_interrupt_create(get_root_resource(), interrupt, 747 ZX_INTERRUPT_REMAP_IRQ, &dev->irq); 748 if (status != ZX_OK) { 749 return status; 750 } 751 752 // create irq thread 753 const char* tname = dev->type == INPUT_PROTO_KBD ? 754 "i8042-kbd-irq" : "i8042-mouse-irq"; 755 int ret = thrd_create_with_name(&dev->irq_thread, i8042_irq_thread, dev, tname); 756 if (ret != thrd_success) { 757 zx_handle_close(dev->irq); 758 return ZX_ERR_BAD_STATE; 759 } 760 761 device_add_args_t args = { 762 .version = DEVICE_ADD_ARGS_VERSION, 763 .name = name, 764 .ctx = dev, 765 .ops = &i8042_dev_proto, 766 .proto_id = ZX_PROTOCOL_HIDBUS, 767 .proto_ops = &hidbus_ops, 768 }; 769 770 status = device_add(parent, &args, NULL); 771 if (status != ZX_OK) { 772 i8042_cleanup_irq_thread(dev); 773 } 774 return status; 775} 776 777static int i8042_init_thread(void* arg) { 778 zx_device_t* parent = arg; 779 uint8_t ctr = 0; 780 zx_status_t status = i8042_setup(&ctr); 781 if (status != ZX_OK) { 782 return status; 783 } 784 785 bool have_mouse = !!(ctr & I8042_CTR_AUXDIS); 786 787 // turn on translation 788 ctr |= I8042_CTR_XLATE; 789 790 // enable devices and irqs 791 ctr &= ~I8042_CTR_KBDDIS; 792 ctr |= I8042_CTR_KBDINT; 793 if (have_mouse) { 794 ctr &= ~I8042_CTR_AUXDIS; 795 ctr |= I8042_CTR_AUXINT; 796 } 797 798 if (i8042_command(&ctr, I8042_CMD_CTL_WCTR) < 0) 799 return -1; 800 801 // create keyboard device 802 i8042_device_t* kbd_device = calloc(1, sizeof(i8042_device_t)); 803 if (!kbd_device) 804 return ZX_ERR_NO_MEMORY; 805 806 mtx_init(&kbd_device->lock, mtx_plain); 807 kbd_device->type = INPUT_PROTO_KBD; 808 status = i8042_dev_init(kbd_device, "i8042-keyboard", parent); 809 if (status != ZX_OK) { 810 free(kbd_device); 811 } 812 813 // Mouse 814 if (have_mouse) { 815 i8042_device_t* mouse_device = NULL; 816 mouse_device = calloc(1, sizeof(i8042_device_t)); 817 if (mouse_device) { 818 mtx_init(&mouse_device->lock, mtx_plain); 819 mouse_device->type = INPUT_PROTO_MOUSE; 820 status = i8042_dev_init(mouse_device, "i8042-mouse", parent); 821 if (status != ZX_OK) { 822 free(mouse_device); 823 } 824 } 825 } 826 827 xprintf("initialized i8042 driver\n"); 828 829 return ZX_OK; 830} 831 832static zx_status_t i8042_bind(void* ctx, zx_device_t* parent) { 833 thrd_t t; 834 int rc = thrd_create_with_name(&t, i8042_init_thread, parent, "i8042-init"); 835 return rc; 836} 837 838static zx_driver_ops_t i8042_driver_ops = { 839 .version = DRIVER_OPS_VERSION, 840 .bind = i8042_bind, 841}; 842 843ZIRCON_DRIVER_BEGIN(i8042, i8042_driver_ops, "zircon", "0.1", 6) 844 BI_ABORT_IF(NE, BIND_PROTOCOL, ZX_PROTOCOL_ACPI), 845 BI_GOTO_IF(NE, BIND_ACPI_HID_0_3, 0x504e5030, 0), // PNP0303\0 846 BI_MATCH_IF(EQ, BIND_ACPI_HID_4_7, 0x33303300), 847 BI_LABEL(0), 848 BI_ABORT_IF(NE, BIND_ACPI_CID_0_3, 0x504e5030), // PNP0303\0 849 BI_MATCH_IF(EQ, BIND_ACPI_CID_4_7, 0x33303300), 850ZIRCON_DRIVER_END(i8042) 851