diff options
Diffstat (limited to 'common/keyboard_8042.c')
-rw-r--r-- | common/keyboard_8042.c | 1328 |
1 files changed, 0 insertions, 1328 deletions
diff --git a/common/keyboard_8042.c b/common/keyboard_8042.c deleted file mode 100644 index 699eaa6687..0000000000 --- a/common/keyboard_8042.c +++ /dev/null @@ -1,1328 +0,0 @@ -/* Copyright 2013 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - * - * 8042 keyboard protocol - */ - -#include "chipset.h" -#include "button.h" -#include "common.h" -#include "console.h" -#include "device_event.h" -#include "hooks.h" -#include "host_command.h" -#include "i8042_protocol.h" -#include "keyboard_8042_sharedlib.h" -#include "keyboard_config.h" -#include "keyboard_protocol.h" -#include "lightbar.h" -#include "lpc.h" -#include "power_button.h" -#include "queue.h" -#include "shared_mem.h" -#include "system.h" -#include "task.h" -#include "timer.h" -#include "util.h" - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_KEYBOARD, outstr) -#define CPRINTS(format, args...) cprints(CC_KEYBOARD, format, ## args) - -#ifdef CONFIG_KEYBOARD_DEBUG -#define CPUTS5(outstr) cputs(CC_KEYBOARD, outstr) -#define CPRINTS5(format, args...) cprints(CC_KEYBOARD, format, ## args) -#else -#define CPUTS5(outstr) -#define CPRINTS5(format, args...) -#endif - -/* - * This command needs malloc to work. Could we use this instead? - * - * #define CMD_KEYBOARD_LOG IS_ENABLED(CONFIG_MALLOC) - */ -#ifdef CONFIG_MALLOC -#define CMD_KEYBOARD_LOG 1 -#else -#define CMD_KEYBOARD_LOG 0 -#endif - -static enum { - STATE_NORMAL = 0, - STATE_SCANCODE, - STATE_SETLEDS, - STATE_EX_SETLEDS_1, /* Expect 2-byte parameter */ - STATE_EX_SETLEDS_2, - STATE_WRITE_CMD_BYTE, - STATE_WRITE_OUTPUT_PORT, - STATE_ECHO_MOUSE, - STATE_SETREP, - STATE_SEND_TO_MOUSE, -} data_port_state = STATE_NORMAL; - -enum scancode_set_list { - SCANCODE_GET_SET = 0, - SCANCODE_SET_1, - SCANCODE_SET_2, - SCANCODE_SET_3, - SCANCODE_MAX = SCANCODE_SET_3, -}; - -#define MAX_SCAN_CODE_LEN 4 - -/* Number of bytes host can get behind before we start generating extra IRQs */ -#define KB_TO_HOST_RETRIES 3 - -/* - * Mutex to control write access to the to-host buffer head. Don't need to - * mutex the tail because reads are only done in one place. - */ -static mutex_t to_host_mutex; - -/* Queue command/data to the host */ -enum { - CHAN_KBD = 0, - CHAN_AUX, -}; -struct data_byte { - uint8_t chan; - uint8_t byte; -}; - -static struct queue const to_host = QUEUE_NULL(16, struct data_byte); - -/* Queue command/data from the host */ -enum { - HOST_COMMAND = 0, - HOST_DATA, -}; -struct host_byte { - uint8_t type; - uint8_t byte; -}; - -/* - * The buffer for i8042 command from host. So far the largest command - * we see from kernel is: - * - * d1 -> i8042 (command) # enable A20 in i8042_platform_init() of - * df -> i8042 (parameter) # serio/i8042-x86ia64io.h file. - * ff -> i8042 (command) - * 20 -> i8042 (command) # read CTR - * - * Hence, 5 (actually 4 plus one spare) is large enough, but use 8 for safety. - */ -static struct queue const from_host = QUEUE_NULL(8, struct host_byte); - -/* Queue aux data to the host from interrupt context. */ -static struct queue const aux_to_host_queue = QUEUE_NULL(16, uint8_t); - -static int i8042_keyboard_irq_enabled; -static int i8042_aux_irq_enabled; - -/* i8042 global settings */ -static int keyboard_enabled; /* default the keyboard is disabled. */ -static int aux_chan_enabled; /* default the mouse is disabled. */ -static int keystroke_enabled; /* output keystrokes */ -static uint8_t resend_command[MAX_SCAN_CODE_LEN]; -static uint8_t resend_command_len; -static uint8_t controller_ram_address; -static uint8_t controller_ram[0x20] = { - /* the so called "command byte" */ - I8042_XLATE | I8042_AUX_DIS | I8042_KBD_DIS, - /* 0x01 - 0x1f are controller RAM */ -}; -static uint8_t A20_status; - -/* - * Scancode settings - */ -static enum scancode_set_list scancode_set = SCANCODE_SET_2; - -/* - * Typematic delay, rate and counter variables. - * - * 7 6 5 4 3 2 1 0 - * +-----+-----+-----+-----+-----+-----+-----+-----+ - * |un- | delay | B | D | - * | used| 0 1 | 0 1 | 0 1 1 | - * +-----+-----+-----+-----+-----+-----+-----+-----+ - * Formula: - * the inter-char delay = (2 ** B) * (D + 8) / 240 (sec) - * Default: 500ms delay, 10.9 chars/sec. - */ -#define DEFAULT_TYPEMATIC_VALUE (BIT(5) | BIT(3) | (3 << 0)) -static uint8_t typematic_value_from_host; -static int typematic_first_delay; -static int typematic_inter_delay; -static int typematic_len; /* length of typematic_scan_code */ -static uint8_t typematic_scan_code[MAX_SCAN_CODE_LEN]; -static timestamp_t typematic_deadline; - -#define KB_SYSJUMP_TAG 0x4b42 /* "KB" */ -#define KB_HOOK_VERSION 2 -/* the previous keyboard state before reboot_ec. */ -struct kb_state { - uint8_t codeset; - uint8_t ctlram; - uint8_t keystroke_enabled; -}; - -/*****************************************************************************/ -/* Keyboard event log */ - -/* Log the traffic between EC and host -- for debug only */ -#define MAX_KBLOG 512 /* Max events in keyboard log */ - -struct kblog_t { - /* - * Type: - * - * s = byte enqueued to send to host - * a = aux byte enqueued to send to host - * t = to-host queue tail pointer before type='s' bytes enqueued - * - * d = data byte from host - * c = command byte from host - * - * k = to-host queue head pointer before byte dequeued - * K = byte actually sent to host via LPC - * A = byte actually sent to host via LPC as AUX - * - * x = to_host queue was cleared - * - * The to-host head and tail pointers are logged pre-wrapping to the - * queue size. This means that they continually increment as units - * are dequeued and enqueued respectively. Since only the bottom - * byte of the value is logged they will wrap every 256 units. - */ - uint8_t type; - uint8_t byte; -}; - -static struct kblog_t *kblog_buf; /* Log buffer; NULL if not logging */ -static int kblog_len; /* Current log length */ - -/** - * Add event to keyboard log. - */ -static void kblog_put(char type, uint8_t byte) -{ - if (kblog_buf && kblog_len < MAX_KBLOG) { - kblog_buf[kblog_len].type = type; - kblog_buf[kblog_len].byte = byte; - kblog_len++; - } -} - -/*****************************************************************************/ - -void keyboard_host_write(int data, int is_cmd) -{ - struct host_byte h; - - h.type = is_cmd ? HOST_COMMAND : HOST_DATA; - h.byte = data; - queue_add_unit(&from_host, &h); - task_wake(TASK_ID_KEYPROTO); -} - -/** - * Enable keyboard IRQ generation. - * - * @param enable Enable (!=0) or disable (0) IRQ generation. - */ -static void keyboard_enable_irq(int enable) -{ - CPRINTS("KB IRQ %s", enable ? "enable" : "disable"); - - i8042_keyboard_irq_enabled = enable; - if (enable) - lpc_keyboard_resume_irq(); -} - -/** - * Enable mouse IRQ generation. - * - * @param enable Enable (!=0) or disable (0) IRQ generation. - */ -static void aux_enable_irq(int enable) -{ - CPRINTS("AUX IRQ %s", enable ? "enable" : "disable"); - - i8042_aux_irq_enabled = enable; -} - -/** - * Send a scan code to the host. - * - * The EC lib will push the scan code bytes to host via port 0x60 and assert - * the IBF flag to trigger an interrupt. The EC lib must queue them if the - * host cannot read the previous byte away in time. - * - * @param len Number of bytes to send to the host - * @param to_host Data to send - * @param chan Channel to send data on - */ -static void i8042_send_to_host(int len, const uint8_t *bytes, - uint8_t chan) -{ - int i; - struct data_byte data; - - /* Enqueue output data if there's space */ - mutex_lock(&to_host_mutex); - - for (i = 0; i < len; i++) - kblog_put(chan == CHAN_AUX ? 'a' : 's', bytes[i]); - - if (queue_space(&to_host) >= len) { - kblog_put('t', to_host.state->tail); - for (i = 0; i < len; i++) { - data.chan = chan; - data.byte = bytes[i]; - queue_add_unit(&to_host, &data); - } - } - mutex_unlock(&to_host_mutex); - - /* Wake up the task to move from queue to host */ - task_wake(TASK_ID_KEYPROTO); -} - -/* Change to set 1 if the I8042_XLATE flag is set. */ -static enum scancode_set_list acting_code_set(enum scancode_set_list set) -{ - /* Always generate set 1 if keyboard translation is enabled */ - if (controller_ram[0] & I8042_XLATE) - return SCANCODE_SET_1; - - return set; -} - -static int is_supported_code_set(enum scancode_set_list set) -{ - return (set == SCANCODE_SET_1 || set == SCANCODE_SET_2); -} - -/** - * Return the make or break code bytes for the active scancode set. - * - * @param make_code The make code to generate the make or break code from - * @param pressed Whether the key or button was pressed - * @param code_set The scancode set being used - * @param scan_code An array of bytes to store the make or break code in - * @param len The number of valid bytes to send in scan_code - */ -static void scancode_bytes(uint16_t make_code, int8_t pressed, - enum scancode_set_list code_set, uint8_t *scan_code, - int32_t *len) -{ - *len = 0; - - /* Output the make code (from table) */ - if (make_code >= 0x0100) { - scan_code[(*len)++] = make_code >> 8; - make_code &= 0xff; - } - - switch (code_set) { - case SCANCODE_SET_1: - make_code = scancode_translate_set2_to_1(make_code); - scan_code[(*len)++] = pressed ? make_code : (make_code | 0x80); - break; - - case SCANCODE_SET_2: - if (pressed) { - scan_code[(*len)++] = make_code; - } else { - scan_code[(*len)++] = 0xf0; - scan_code[(*len)++] = make_code; - } - break; - default: - break; - } -} - -static enum ec_error_list matrix_callback(int8_t row, int8_t col, - int8_t pressed, - enum scancode_set_list code_set, - uint8_t *scan_code, int32_t *len) -{ - uint16_t make_code; - - ASSERT(scan_code); - ASSERT(len); - - if (row >= KEYBOARD_ROWS || col >= keyboard_cols) - return EC_ERROR_INVAL; - - make_code = get_scancode_set2(row, col); - -#ifdef CONFIG_KEYBOARD_SCANCODE_CALLBACK - { - enum ec_error_list r = keyboard_scancode_callback( - &make_code, pressed); - if (r != EC_SUCCESS) - return r; - } -#endif - - code_set = acting_code_set(code_set); - if (!is_supported_code_set(code_set)) { - CPRINTS("KB scancode set %d unsupported", code_set); - return EC_ERROR_UNIMPLEMENTED; - } - - if (!make_code) { - CPRINTS("KB scancode %d:%d missing", row, col); - return EC_ERROR_UNIMPLEMENTED; - } - - scancode_bytes(make_code, pressed, code_set, scan_code, len); - return EC_SUCCESS; -} - -/** - * Set typematic delays based on host data byte. - */ -static void set_typematic_delays(uint8_t data) -{ - typematic_value_from_host = data; - typematic_first_delay = MSEC * - (((typematic_value_from_host & 0x60) >> 5) + 1) * 250; - typematic_inter_delay = SECOND * - (1 << ((typematic_value_from_host & 0x18) >> 3)) * - ((typematic_value_from_host & 0x7) + 8) / 240; -} - -static void reset_rate_and_delay(void) -{ - set_typematic_delays(DEFAULT_TYPEMATIC_VALUE); -} - -void keyboard_clear_buffer(void) -{ - CPRINTS("KB Clear Buffer"); - mutex_lock(&to_host_mutex); - kblog_put('x', queue_count(&to_host)); - queue_init(&to_host); - mutex_unlock(&to_host_mutex); - lpc_keyboard_clear_buffer(); -} - -static void keyboard_wakeup(void) -{ - host_set_single_event(EC_HOST_EVENT_KEY_PRESSED); -} - -static void set_typematic_key(const uint8_t *scan_code, int32_t len) -{ - typematic_deadline.val = get_time().val + typematic_first_delay; - memcpy(typematic_scan_code, scan_code, len); - typematic_len = len; -} - -void clear_typematic_key(void) -{ - typematic_len = 0; -} - -void keyboard_state_changed(int row, int col, int is_pressed) -{ - uint8_t scan_code[MAX_SCAN_CODE_LEN]; - int32_t len = 0; - enum ec_error_list ret; - -#ifdef CONFIG_KEYBOARD_DEBUG - char mylabel = get_keycap_label(row, col); - - if (mylabel & KEYCAP_LONG_LABEL_BIT) - CPRINTS("KB (%d,%d)=%d %s", row, col, is_pressed, - get_keycap_long_label(mylabel & KEYCAP_LONG_LABEL_INDEX_BITMASK)); - else - CPRINTS("KB (%d,%d)=%d %c", row, col, is_pressed, mylabel); -#endif - - ret = matrix_callback(row, col, is_pressed, scancode_set, scan_code, - &len); - if (ret == EC_SUCCESS) { - ASSERT(len > 0); - if (keystroke_enabled) - i8042_send_to_host(len, scan_code, CHAN_KBD); - } - - if (is_pressed) { - keyboard_wakeup(); - set_typematic_key(scan_code, len); - task_wake(TASK_ID_KEYPROTO); - } else { - clear_typematic_key(); - } -} - -static void keystroke_enable(int enable) -{ - if (!keystroke_enabled && enable) - CPRINTS("KS enable"); - else if (keystroke_enabled && !enable) - CPRINTS("KS disable"); - - keystroke_enabled = enable; -} - -static void keyboard_enable(int enable) -{ - if (!keyboard_enabled && enable) - CPRINTS("KB enable"); - else if (keyboard_enabled && !enable) - CPRINTS("KB disable"); - - keyboard_enabled = enable; -} - -static void aux_enable(int enable) -{ - if (!aux_chan_enabled && enable) - CPRINTS("AUX enabled"); - else if (aux_chan_enabled && !enable) - CPRINTS("AUX disabled"); - - aux_chan_enabled = enable; -} - -static uint8_t read_ctl_ram(uint8_t addr) -{ - if (addr < ARRAY_SIZE(controller_ram)) - return controller_ram[addr]; - else - return 0; -} - -/** - * Manipulate the controller_ram[]. - * - * Some bits change may trigger internal state change. - */ -static void update_ctl_ram(uint8_t addr, uint8_t data) -{ - uint8_t orig; - - if (addr >= ARRAY_SIZE(controller_ram)) - return; - - orig = controller_ram[addr]; - controller_ram[addr] = data; - CPRINTS5("KB set CTR_RAM(0x%02x)=0x%02x (old:0x%02x)", - addr, data, orig); - - if (addr == 0x00) { - /* Keyboard enable/disable */ - - /* Enable IRQ before enable keyboard (queue chars to host) */ - if (!(orig & I8042_ENIRQ1) && (data & I8042_ENIRQ1)) - keyboard_enable_irq(1); - if (!(orig & I8042_ENIRQ12) && (data & I8042_ENIRQ12)) - aux_enable_irq(1); - - /* Handle the I8042_KBD_DIS bit */ - keyboard_enable(!(data & I8042_KBD_DIS)); - - /* Handle the I8042_AUX_DIS bit */ - aux_enable(!(data & I8042_AUX_DIS)); - - /* - * Disable IRQ after disable keyboard so that every char must - * have informed the host. - */ - if ((orig & I8042_ENIRQ1) && !(data & I8042_ENIRQ1)) - keyboard_enable_irq(0); - if ((orig & I8042_ENIRQ12) && !(data & I8042_ENIRQ12)) - aux_enable_irq(0); - } -} - -/** - * Handle the port 0x60 writes from host. - * - * Returns 1 if the event was handled. - */ -static int handle_mouse_data(uint8_t data, uint8_t *output, int *count) -{ - int out_len = 0; - - switch (data_port_state) { - case STATE_ECHO_MOUSE: - CPRINTS5("STATE_ECHO_MOUSE: 0x%02x", data); - output[out_len++] = data; - data_port_state = STATE_NORMAL; - break; - - case STATE_SEND_TO_MOUSE: - CPRINTS5("STATE_SEND_TO_MOUSE: 0x%02x", data); - send_aux_data_to_device(data); - data_port_state = STATE_NORMAL; - break; - - default: /* STATE_NORMAL */ - return 0; - } - - ASSERT(out_len <= MAX_SCAN_CODE_LEN); - - *count = out_len; - - return 1; -} - -/** - * Handle the port 0x60 writes from host. - * - * This functions returns the number of bytes stored in *output buffer. - */ -static int handle_keyboard_data(uint8_t data, uint8_t *output) -{ - int out_len = 0; - int save_for_resend = 1; - int i; - - switch (data_port_state) { - case STATE_SCANCODE: - CPRINTS5("KB eaten by STATE_SCANCODE: 0x%02x", data); - if (data == SCANCODE_GET_SET) { - output[out_len++] = I8042_RET_ACK; - output[out_len++] = scancode_set; - } else { - scancode_set = data; - CPRINTS("KB scancode set to %d", scancode_set); - output[out_len++] = I8042_RET_ACK; - } - data_port_state = STATE_NORMAL; - break; - - case STATE_SETLEDS: - CPRINTS5("KB eaten by STATE_SETLEDS"); - output[out_len++] = I8042_RET_ACK; - data_port_state = STATE_NORMAL; - break; - - case STATE_EX_SETLEDS_1: - CPRINTS5("KB eaten by STATE_EX_SETLEDS_1"); - output[out_len++] = I8042_RET_ACK; - data_port_state = STATE_EX_SETLEDS_2; - break; - - case STATE_EX_SETLEDS_2: - CPRINTS5("KB eaten by STATE_EX_SETLEDS_2"); - output[out_len++] = I8042_RET_ACK; - data_port_state = STATE_NORMAL; - break; - - case STATE_WRITE_CMD_BYTE: - CPRINTS5("KB eaten by STATE_WRITE_CMD_BYTE: 0x%02x", - data); - update_ctl_ram(controller_ram_address, data); - data_port_state = STATE_NORMAL; - break; - - case STATE_WRITE_OUTPUT_PORT: - CPRINTS5("KB eaten by STATE_WRITE_OUTPUT_PORT: 0x%02x", - data); - A20_status = (data & BIT(1)) ? 1 : 0; - data_port_state = STATE_NORMAL; - break; - - case STATE_SETREP: - CPRINTS5("KB eaten by STATE_SETREP: 0x%02x", data); - set_typematic_delays(data); - - output[out_len++] = I8042_RET_ACK; - data_port_state = STATE_NORMAL; - break; - - default: /* STATE_NORMAL */ - switch (data) { - case I8042_CMD_GSCANSET: /* also I8042_CMD_SSCANSET */ - output[out_len++] = I8042_RET_ACK; - data_port_state = STATE_SCANCODE; - break; - - case I8042_CMD_SETLEDS: - /* Chrome OS doesn't have keyboard LEDs, so ignore */ - output[out_len++] = I8042_RET_ACK; - data_port_state = STATE_SETLEDS; - break; - - case I8042_CMD_EX_SETLEDS: - output[out_len++] = I8042_RET_ACK; - data_port_state = STATE_EX_SETLEDS_1; - break; - - case I8042_CMD_DIAG_ECHO: - output[out_len++] = I8042_RET_ACK; - output[out_len++] = I8042_CMD_DIAG_ECHO; - break; - - case I8042_CMD_GETID: /* fall-thru */ - case I8042_CMD_OK_GETID: - output[out_len++] = I8042_RET_ACK; - output[out_len++] = 0xab; /* Regular keyboards */ - output[out_len++] = 0x83; - break; - - case I8042_CMD_SETREP: - output[out_len++] = I8042_RET_ACK; - data_port_state = STATE_SETREP; - break; - - case I8042_CMD_ENABLE: - output[out_len++] = I8042_RET_ACK; - keystroke_enable(1); - keyboard_clear_buffer(); - break; - - case I8042_CMD_RESET_DIS: - output[out_len++] = I8042_RET_ACK; - keystroke_enable(0); - reset_rate_and_delay(); - keyboard_clear_buffer(); - break; - - case I8042_CMD_RESET_DEF: - output[out_len++] = I8042_RET_ACK; - reset_rate_and_delay(); - keyboard_clear_buffer(); - break; - - case I8042_CMD_RESET: - reset_rate_and_delay(); - keyboard_clear_buffer(); - output[out_len++] = I8042_RET_ACK; - break; - - case I8042_CMD_RESEND: - save_for_resend = 0; - for (i = 0; i < resend_command_len; ++i) - output[out_len++] = resend_command[i]; - break; - - case 0x60: /* fall-thru */ - case 0x45: - /* U-boot hack. Just ignore; don't reply. */ - break; - - case I8042_CMD_SETALL_MB: /* fall-thru */ - case I8042_CMD_SETALL_MBR: - case I8042_CMD_EX_ENABLE: - default: - output[out_len++] = I8042_RET_NAK; - CPRINTS("KB Unsupported i8042 data 0x%02x", - data); - break; - } - } - - /* For resend, keep output before leaving. */ - if (out_len && save_for_resend) { - ASSERT(out_len <= MAX_SCAN_CODE_LEN); - for (i = 0; i < out_len; ++i) - resend_command[i] = output[i]; - resend_command_len = out_len; - } - - ASSERT(out_len <= MAX_SCAN_CODE_LEN); - return out_len; -} - -/** - * Handle the port 0x64 writes from host. - * - * This functions returns the number of bytes stored in *output buffer. - * BUT those bytes will appear at port 0x60. - */ -static int handle_keyboard_command(uint8_t command, uint8_t *output) -{ - int out_len = 0; - - CPRINTS5("KB recv cmd: 0x%02x", command); - kblog_put('c', command); - - switch (command) { - case I8042_READ_CMD_BYTE: - /* - * Ensure that the keyboard buffer is cleared before adding - * command byte to it. Since the host is asking for command - * byte, sending it buffered key press data can confuse the - * host and result in it taking incorrect action. - */ - keyboard_clear_buffer(); - output[out_len++] = read_ctl_ram(0); - break; - - case I8042_WRITE_CMD_BYTE: - data_port_state = STATE_WRITE_CMD_BYTE; - controller_ram_address = command - 0x60; - break; - - case I8042_DIS_KB: - update_ctl_ram(0, read_ctl_ram(0) | I8042_KBD_DIS); - reset_rate_and_delay(); - typematic_len = 0; /* stop typematic */ - keyboard_clear_buffer(); - break; - - case I8042_ENA_KB: - update_ctl_ram(0, read_ctl_ram(0) & ~I8042_KBD_DIS); - keystroke_enable(1); - keyboard_clear_buffer(); - break; - - case I8042_READ_OUTPUT_PORT: - output[out_len++] = - (lpc_keyboard_input_pending() ? BIT(5) : 0) | - (lpc_keyboard_has_char() ? BIT(4) : 0) | - (A20_status ? BIT(1) : 0) | - 1; /* Main processor in normal mode */ - break; - - case I8042_WRITE_OUTPUT_PORT: - data_port_state = STATE_WRITE_OUTPUT_PORT; - break; - - case I8042_RESET_SELF_TEST: - output[out_len++] = 0x55; /* Self test success */ - break; - - case I8042_TEST_KB_PORT: - output[out_len++] = 0x00; - break; - - case I8042_DIS_MOUSE: - update_ctl_ram(0, read_ctl_ram(0) | I8042_AUX_DIS); - break; - - case I8042_ENA_MOUSE: - update_ctl_ram(0, read_ctl_ram(0) & ~I8042_AUX_DIS); - break; - - case I8042_TEST_MOUSE: - output[out_len++] = 0; /* No error detected */ - break; - - case I8042_ECHO_MOUSE: - data_port_state = STATE_ECHO_MOUSE; - break; - - case I8042_SEND_TO_MOUSE: - data_port_state = STATE_SEND_TO_MOUSE; - break; - - case I8042_SYSTEM_RESET: - chipset_reset(CHIPSET_RESET_KB_SYSRESET); - break; - - default: - if (command >= I8042_READ_CTL_RAM && - command <= I8042_READ_CTL_RAM_END) { - output[out_len++] = read_ctl_ram(command - 0x20); - } else if (command >= I8042_WRITE_CTL_RAM && - command <= I8042_WRITE_CTL_RAM_END) { - data_port_state = STATE_WRITE_CMD_BYTE; - controller_ram_address = command - 0x60; - } else if (command == I8042_DISABLE_A20) { - A20_status = 0; - } else if (command == I8042_ENABLE_A20) { - A20_status = 1; - } else if (command >= I8042_PULSE_START && - command <= I8042_PULSE_END) { - /* Pulse Output Bits, - * b0=0 to reset CPU, see I8042_SYSTEM_RESET above - * b1=0 to disable A20 line - */ - A20_status = command & BIT(1) ? 1 : 0; - } else { - CPRINTS("KB unsupported cmd: 0x%02x", command); - reset_rate_and_delay(); - keyboard_clear_buffer(); - output[out_len++] = I8042_RET_NAK; - data_port_state = STATE_NORMAL; - } - break; - } - - return out_len; -} - -static void i8042_handle_from_host(void) -{ - struct host_byte h; - int ret_len; - uint8_t output[MAX_SCAN_CODE_LEN]; - uint8_t chan = CHAN_KBD; - - while (queue_remove_unit(&from_host, &h)) { - if (h.type == HOST_COMMAND) { - ret_len = handle_keyboard_command(h.byte, output); - } else { - CPRINTS5("KB recv data: 0x%02x", h.byte); - kblog_put('d', h.byte); - - if (IS_ENABLED(CONFIG_8042_AUX) && - handle_mouse_data(h.byte, output, &ret_len)) - chan = CHAN_AUX; - else - ret_len = handle_keyboard_data(h.byte, output); - } - - i8042_send_to_host(ret_len, output, chan); - } -} - -void keyboard_protocol_task(void *u) -{ - int wait = -1; - int retries = 0; - - reset_rate_and_delay(); - - while (1) { - /* Wait for next host read/write */ - task_wait_event(wait); - - while (1) { - timestamp_t t = get_time(); - struct data_byte entry; - - /* Handle typematic */ - if (!typematic_len) { - /* Typematic disabled; wait for enable */ - wait = -1; - } else if (timestamp_expired(typematic_deadline, &t)) { - /* Ready for next typematic keystroke */ - if (keystroke_enabled) - i8042_send_to_host(typematic_len, - typematic_scan_code, - CHAN_KBD); - typematic_deadline.val = t.val + - typematic_inter_delay; - wait = typematic_inter_delay; - } else { - /* Wait for remaining interval */ - wait = typematic_deadline.val - t.val; - } - - /* Handle command/data write from host */ - i8042_handle_from_host(); - - /* Check if we have data to send to host */ - if (queue_is_empty(&to_host)) - break; - - /* Handle data waiting for host */ - if (lpc_keyboard_has_char()) { - /* If interrupts disabled, nothing we can do */ - if (!i8042_keyboard_irq_enabled && - !i8042_aux_irq_enabled) - break; - - /* Give the host a little longer to respond */ - if (++retries < KB_TO_HOST_RETRIES) - break; - - /* - * We keep getting data, but the host keeps - * ignoring us. Fine, we're done waiting. - * Hey, host, are you ever gonna get to this - * data? Send it another interrupt in case it - * somehow missed the first one. - */ - CPRINTS("KB extra IRQ"); - lpc_keyboard_resume_irq(); - retries = 0; - break; - } - - /* Get a char from buffer. */ - kblog_put('k', to_host.state->head); - queue_remove_unit(&to_host, &entry); - - /* Write to host. */ - if (entry.chan == CHAN_AUX && - IS_ENABLED(CONFIG_8042_AUX)) { - kblog_put('A', entry.byte); - lpc_aux_put_char(entry.byte, - i8042_aux_irq_enabled); - } else { - kblog_put('K', entry.byte); - lpc_keyboard_put_char( - entry.byte, i8042_keyboard_irq_enabled); - } - retries = 0; - } - } -} - -static void send_aux_data_to_host_deferred(void) -{ - uint8_t data; - - if (IS_ENABLED(CONFIG_DEVICE_EVENT) && - chipset_in_state(CHIPSET_STATE_ANY_SUSPEND)) - device_set_single_event(EC_DEVICE_EVENT_TRACKPAD); - - while (!queue_is_empty(&aux_to_host_queue)) { - queue_remove_unit(&aux_to_host_queue, &data); - if (aux_chan_enabled && IS_ENABLED(CONFIG_8042_AUX)) - i8042_send_to_host(1, &data, CHAN_AUX); - else - CPRINTS("AUX Callback ignored"); - } -} -DECLARE_DEFERRED(send_aux_data_to_host_deferred); - -/** - * Send aux data to host from interrupt context. - * - * @param data Aux response to send to host. - */ -void send_aux_data_to_host_interrupt(uint8_t data) -{ - queue_add_unit(&aux_to_host_queue, &data); - hook_call_deferred(&send_aux_data_to_host_deferred_data, 0); -} - -/** - * Handle button changing state. - * - * @param button Type of button that changed - * @param is_pressed Whether the button was pressed or released - */ -test_mockable void keyboard_update_button(enum keyboard_button_type button, - int is_pressed) -{ - uint8_t scan_code[MAX_SCAN_CODE_LEN]; - uint32_t len; - struct button_8042_t button_8042; - enum scancode_set_list code_set; - - /* - * Only send the scan code if main chipset is fully awake and - * keystrokes are enabled. - */ - if (!chipset_in_state(CHIPSET_STATE_ON) || !keystroke_enabled) - return; - - code_set = acting_code_set(scancode_set); - if (!is_supported_code_set(code_set)) - return; - - button_8042 = buttons_8042[button]; - scancode_bytes(button_8042.scancode, is_pressed, code_set, scan_code, - &len); - ASSERT(len > 0); - - if (button_8042.repeat) { - if (is_pressed) - set_typematic_key(scan_code, len); - else - clear_typematic_key(); - } - - if (keystroke_enabled) { - i8042_send_to_host(len, scan_code, CHAN_KBD); - task_wake(TASK_ID_KEYPROTO); - } -} - -/*****************************************************************************/ -/* Console commands */ -#ifdef CONFIG_CMD_KEYBOARD -static int command_typematic(int argc, char **argv) -{ - int i; - - if (argc == 3) { - typematic_first_delay = strtoi(argv[1], NULL, 0) * MSEC; - typematic_inter_delay = strtoi(argv[2], NULL, 0) * MSEC; - } - - ccprintf("From host: 0x%02x\n", typematic_value_from_host); - ccprintf("First delay: %3d ms\n", typematic_first_delay / 1000); - ccprintf("Inter delay: %3d ms\n", typematic_inter_delay / 1000); - ccprintf("Now: %.6" PRId64 "\n", get_time().val); - ccprintf("Deadline: %.6" PRId64 "\n", typematic_deadline.val); - - ccputs("Repeat scan code: {"); - for (i = 0; i < typematic_len; ++i) - ccprintf("0x%02x, ", typematic_scan_code[i]); - ccputs("}\n"); - return EC_SUCCESS; -} - -static int command_codeset(int argc, char **argv) -{ - if (argc == 2) { - int set = strtoi(argv[1], NULL, 0); - switch (set) { - case SCANCODE_SET_1: /* fall-thru */ - case SCANCODE_SET_2: /* fall-thru */ - scancode_set = set; - break; - default: - return EC_ERROR_PARAM1; - } - } - - ccprintf("Set: %d\n", scancode_set); - ccprintf("I8042_XLATE: %d\n", controller_ram[0] & I8042_XLATE ? 1 : 0); - return EC_SUCCESS; -} - -static int command_controller_ram(int argc, char **argv) -{ - int index; - - if (argc < 2) - return EC_ERROR_PARAM_COUNT; - - index = strtoi(argv[1], NULL, 0); - if (index >= ARRAY_SIZE(controller_ram)) - return EC_ERROR_PARAM1; - - if (argc >= 3) - update_ctl_ram(index, strtoi(argv[2], NULL, 0)); - - ccprintf("%d = 0x%02x\n", index, controller_ram[index]); - return EC_SUCCESS; -} - -static int command_keyboard_log(int argc, char **argv) -{ - int i; - - /* If no args, print log */ - if (argc == 1) { - ccprintf("KBC log (len=%d):\n", kblog_len); - for (i = 0; kblog_buf && i < kblog_len; ++i) { - ccprintf("%c.%02x ", - kblog_buf[i].type, kblog_buf[i].byte); - if ((i & 15) == 15) { - ccputs("\n"); - cflush(); - } - } - ccputs("\n"); - return EC_SUCCESS; - } - - /* Otherwise, enable/disable */ - if (!parse_bool(argv[1], &i)) - return EC_ERROR_PARAM1; - - if (i) { - if (!kblog_buf) { - int rv = SHARED_MEM_ACQUIRE_CHECK( - sizeof(*kblog_buf) * MAX_KBLOG, - (char **)&kblog_buf); - if (rv != EC_SUCCESS) - kblog_buf = NULL; - kblog_len = 0; - return rv; - } - } else { - kblog_len = 0; - if (kblog_buf) - shared_mem_release(kblog_buf); - kblog_buf = NULL; - } - - return EC_SUCCESS; -} - -static int command_keyboard(int argc, char **argv) -{ - int ena; - - if (argc > 1) { - if (!parse_bool(argv[1], &ena)) - return EC_ERROR_PARAM1; - - keyboard_enable(ena); - } - - ccprintf("Enabled: %d\n", keyboard_enabled); - return EC_SUCCESS; -} - -static int command_8042_internal(int argc, char **argv) -{ - int i; - - ccprintf("data_port_state=%d\n", data_port_state); - ccprintf("i8042_keyboard_irq_enabled=%d\n", i8042_keyboard_irq_enabled); - ccprintf("i8042_aux_irq_enabled=%d\n", i8042_aux_irq_enabled); - ccprintf("keyboard_enabled=%d\n", keyboard_enabled); - ccprintf("keystroke_enabled=%d\n", keystroke_enabled); - ccprintf("aux_chan_enabled=%d\n", aux_chan_enabled); - - ccprintf("resend_command[]={"); - for (i = 0; i < resend_command_len; i++) - ccprintf("0x%02x, ", resend_command[i]); - ccprintf("}\n"); - - ccprintf("controller_ram_address=0x%02x\n", controller_ram_address); - ccprintf("A20_status=%d\n", A20_status); - - ccprintf("from_host[]={"); - for (i = 0; i < queue_count(&from_host); ++i) { - struct host_byte entry; - - queue_peek_units(&from_host, &entry, i, 1); - - ccprintf("0x%02x, 0x%02x, ", entry.type, entry.byte); - } - ccprintf("}\n"); - - ccprintf("to_host[]={"); - for (i = 0; i < queue_count(&to_host); ++i) { - struct data_byte entry; - - queue_peek_units(&to_host, &entry, i, 1); - - ccprintf("0x%02x%s, ", entry.byte, - entry.chan == CHAN_AUX ? " aux" : ""); - } - ccprintf("}\n"); - - return EC_SUCCESS; -} - -/* Zephyr only provides these as subcommands*/ -#ifndef CONFIG_ZEPHYR -DECLARE_CONSOLE_COMMAND(typematic, command_typematic, - "[first] [inter]", - "Get/set typematic delays"); -DECLARE_CONSOLE_COMMAND(codeset, command_codeset, - "[set]", - "Get/set keyboard codeset"); -DECLARE_CONSOLE_COMMAND(ctrlram, command_controller_ram, - "index [value]", - "Get/set keyboard controller RAM"); -DECLARE_CONSOLE_COMMAND(kblog, command_keyboard_log, - "[on | off]", - "Print or toggle keyboard event log"); -DECLARE_CONSOLE_COMMAND(kbd, command_keyboard, - "[on | off]", - "Print or toggle keyboard info"); -#endif - -static int command_8042(int argc, char **argv) -{ - if (argc >= 2) { - if (!strcasecmp(argv[1], "internal")) - return command_8042_internal(argc, argv); - else if (!strcasecmp(argv[1], "typematic")) - return command_typematic(argc - 1, argv + 1); - else if (!strcasecmp(argv[1], "codeset")) - return command_codeset(argc - 1, argv + 1); - else if (!strcasecmp(argv[1], "ctrlram")) - return command_controller_ram(argc - 1, argv + 1); - else if (CMD_KEYBOARD_LOG && !strcasecmp(argv[1], "kblog")) - return command_keyboard_log(argc - 1, argv + 1); - else if (!strcasecmp(argv[1], "kbd")) - return command_keyboard(argc - 1, argv + 1); - else - return EC_ERROR_PARAM1; - } else { - char *ctlram_argv[] = {"ctrlram", "0"}; - - ccprintf("\n- Typematic:\n"); - command_typematic(argc, argv); - ccprintf("\n- Codeset:\n"); - command_codeset(argc, argv); - ccprintf("\n- Control RAM:\n"); - command_controller_ram( - sizeof(ctlram_argv) / sizeof(ctlram_argv[0]), - ctlram_argv); - if (CMD_KEYBOARD_LOG) { - ccprintf("\n- Keyboard log:\n"); - command_keyboard_log(argc, argv); - } - ccprintf("\n- Keyboard:\n"); - command_keyboard(argc, argv); - ccprintf("\n- Internal:\n"); - command_8042_internal(argc, argv); - ccprintf("\n"); - } - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(8042, command_8042, - "[internal | typematic | codeset | ctrlram |" - " kblog | kbd]", - "Print 8042 state in one place"); -#endif - - -/*****************************************************************************/ -/* Hooks */ - -/** - * Preserve the states of keyboard controller to keep the initialized states - * between reboot_ec commands. Saving info include: - * - * - code set - * - controller_ram[0]: - * - XLATE - * - KB/TP disabled - * - KB/TP IRQ enabled - */ -static void keyboard_preserve_state(void) -{ - struct kb_state state; - - state.codeset = scancode_set; - state.ctlram = controller_ram[0]; - state.keystroke_enabled = keystroke_enabled; - - system_add_jump_tag(KB_SYSJUMP_TAG, KB_HOOK_VERSION, - sizeof(state), &state); -} -DECLARE_HOOK(HOOK_SYSJUMP, keyboard_preserve_state, HOOK_PRIO_DEFAULT); - -/** - * Restore the keyboard states after reboot_ec command. See above function. - */ -static void keyboard_restore_state(void) -{ - const struct kb_state *prev; - int version, size; - - prev = (const struct kb_state *)system_get_jump_tag(KB_SYSJUMP_TAG, - &version, &size); - if (prev && version == KB_HOOK_VERSION && size == sizeof(*prev)) { - /* Coming back from a sysjump, so restore settings. */ - scancode_set = prev->codeset; - update_ctl_ram(0, prev->ctlram); - keystroke_enabled = prev->keystroke_enabled; - } -} -DECLARE_HOOK(HOOK_INIT, keyboard_restore_state, HOOK_PRIO_DEFAULT); - -#if defined(CONFIG_POWER_BUTTON) && !defined(CONFIG_MKBP_INPUT_DEVICES) -/** - * Handle power button changing state. - */ -static void keyboard_power_button(void) -{ - keyboard_update_button(KEYBOARD_BUTTON_POWER, - power_button_is_pressed()); -} -DECLARE_HOOK(HOOK_POWER_BUTTON_CHANGE, keyboard_power_button, - HOOK_PRIO_DEFAULT); - -#endif /* CONFIG_POWER_BUTTON && !CONFIG_MKBP_INPUT_DEVICES */ - |