diff options
author | Randall Spangler <rspangler@chromium.org> | 2013-03-28 12:42:44 -0700 |
---|---|---|
committer | ChromeBot <chrome-bot@google.com> | 2013-03-29 11:40:35 -0700 |
commit | 95a3a4107ae9c9013dbf3fb51ca3e98346cb82bb (patch) | |
tree | 5626015c33a2850cf6dc10a0194ab7dd20fa4d09 /common/keyboard_8042.c | |
parent | fe3ccdf70a002e651bc1a13bcc305ae1e3422154 (diff) | |
download | chrome-ec-95a3a4107ae9c9013dbf3fb51ca3e98346cb82bb.tar.gz |
Merge i8042.c into keyboard_8042.c
This is in preparation for cleaning up the 8042 protocol stack and
merging the typematic and i8042cmd tasks.
No functionality change, just shuffling code and renaming functions.
BUG=chrome-os-partner:18360
BRANCH=none
TEST=boot link and type on its keyboard
Change-Id: Iefc41cd5b8d18ac87830bff3080cfff92e9d10d2
Signed-off-by: Randall Spangler <rspangler@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/46805
Reviewed-by: Bill Richardson <wfrichar@chromium.org>
Diffstat (limited to 'common/keyboard_8042.c')
-rw-r--r-- | common/keyboard_8042.c | 325 |
1 files changed, 245 insertions, 80 deletions
diff --git a/common/keyboard_8042.c b/common/keyboard_8042.c index 54cc5d0310..66de33f4a2 100644 --- a/common/keyboard_8042.c +++ b/common/keyboard_8042.c @@ -2,7 +2,7 @@ * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. * - * Chrome OS EC keyboard common code. + * 8042 keyboard protocol */ #include "chipset.h" @@ -10,12 +10,12 @@ #include "console.h" #include "hooks.h" #include "host_command.h" -#include "i8042.h" #include "i8042_protocol.h" #include "keyboard_config.h" #include "keyboard_protocol.h" #include "lightbar.h" #include "lpc.h" +#include "queue.h" #include "registers.h" #include "shared_mem.h" #include "system.h" @@ -23,18 +23,11 @@ #include "timer.h" #include "util.h" -#define KEYBOARD_DEBUG 1 - /* Console output macros */ -#if KEYBOARD_DEBUG >= 1 #define CPUTS(outstr) cputs(CC_KEYBOARD, outstr) #define CPRINTF(format, args...) cprintf(CC_KEYBOARD, format, ## args) -#else -#define CPUTS(outstr) -#define CPRINTF(format, args...) -#endif -#if KEYBOARD_DEBUG >= 5 +#ifdef CONFIG_KEYBOARD_DEBUG_MORE #define CPUTS5(outstr) cputs(CC_KEYBOARD, outstr) #define CPRINTF5(format, args...) cprintf(CC_KEYBOARD, format, ## args) #else @@ -42,6 +35,19 @@ #define CPRINTF5(format, args...) #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, @@ -50,10 +56,42 @@ enum scancode_set_list { SCANCODE_MAX = SCANCODE_SET_3, }; +#define MAX_SCAN_CODE_LEN 4 /* - * i8042 global settings. + * 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 struct mutex to_host_mutex; + +static uint8_t to_host_buffer[16]; +static struct queue to_host = { + .buf_bytes = sizeof(to_host_buffer), + .unit_bytes = sizeof(uint8_t), + .buf = to_host_buffer, +}; + +/* Queue command/data from the host */ +enum { + HOST_COMMAND = 0, + HOST_DATA, +}; +struct host_byte { + uint8_t type; + uint8_t byte; +}; + +/* 4 is big enough for all i8042 commands */ +static uint8_t from_host_buffer[4 * sizeof(struct host_byte)]; +static struct queue from_host = { + .buf_bytes = sizeof(from_host_buffer), + .unit_bytes = sizeof(struct host_byte), + .buf = from_host_buffer, +}; + +static int i8042_irq_enabled; + +/* i8042 global settings */ static int keyboard_enabled; /* default the keyboard is disabled. */ static int keystroke_enabled; /* output keystrokes */ static uint8_t resend_command[MAX_SCAN_CODE_LEN]; @@ -88,7 +126,7 @@ static enum scancode_set_list scancode_set = SCANCODE_SET_2; #define DEFAULT_TYPEMATIC_VALUE ((1 << 5) || (1 << 3) || (3 << 0)) #define DEFAULT_FIRST_DELAY 500 #define DEFAULT_INTER_DELAY 91 -#define TYPEMATIC_DELAY_UNIT 1000 /* 1ms = 1000us */ +#define TYPEMATIC_DELAY_UNIT MSEC static uint8_t typematic_value_from_host = DEFAULT_TYPEMATIC_VALUE; static int refill_first_delay = DEFAULT_FIRST_DELAY; /* unit: ms */ static int refill_inter_delay = DEFAULT_INTER_DELAY; /* unit: ms */ @@ -96,7 +134,6 @@ static int typematic_delay; /* unit: us */ static int typematic_len; /* length of typematic_scan_code */ static uint8_t typematic_scan_code[MAX_SCAN_CODE_LEN]; - #define KB_SYSJUMP_TAG 0x4b42 /* "KB" */ #define KB_HOOK_VERSION 1 /* the previous keyboard state before reboot_ec. */ @@ -106,7 +143,6 @@ struct kb_state { uint8_t pad[2]; /* Pad to 4 bytes for system_add_jump_tag(). */ }; - /* The standard Chrome OS keyboard matrix table. */ static const uint16_t scancode_set1[KEYBOARD_ROWS][KEYBOARD_COLS] = { {0x0000, 0xe05b, 0x003b, 0x0030, 0x0044, 0x0073, 0x0031, 0x0000, 0x000d, @@ -146,15 +182,107 @@ static const uint16_t scancode_set2[KEYBOARD_ROWS][KEYBOARD_COLS] = { 0x0044, 0x0000, 0xe075, 0xe06b}, }; +/*****************************************************************************/ +/* 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 + * 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 + */ uint8_t type; uint8_t byte; }; -static struct kblog_t *kblog; /* Log buffer, or NULL if not logging */ -static int kblog_len; /* Current log length */ +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++; + } +} + +/*****************************************************************************/ + +/** + * Flush and reset all i8042 keyboard buffers. + */ +static void i8042_flush_buffer(void) +{ + mutex_lock(&to_host_mutex); + queue_reset(&to_host); + mutex_unlock(&to_host_mutex); + lpc_keyboard_clear_buffer(); +} + +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_units(&from_host, &h, 1); + task_wake(TASK_ID_I8042CMD); +} + +/** + * Enable keyboard IRQ generation. + * + * @param enable Enable (!=0) or disable (0) IRQ generation. + */ +static void keyboard_enable_irq(int enable) +{ + i8042_irq_enabled = enable; + if (enable) + lpc_keyboard_resume_irq(); +} + +/** + * 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 + */ +static void i8042_send_to_host(int len, const uint8_t *bytes) +{ + int i; + + for (i = 0; i < len; i++) + kblog_put('s', bytes[i]); + + /* Enqueue output data if there's space */ + mutex_lock(&to_host_mutex); + if (queue_has_space(&to_host, len)) { + kblog_put('t', to_host.tail); + queue_add_units(&to_host, bytes, len); + } + mutex_unlock(&to_host_mutex); + + /* Wake up the task to move from queue to host */ + task_wake(TASK_ID_I8042CMD); +} /* Change to set 1 if the I8042_XLATE flag is set. */ static enum scancode_set_list acting_code_set(enum scancode_set_list set) @@ -167,11 +295,10 @@ static enum scancode_set_list acting_code_set(enum scancode_set_list set) return set; } - 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) + uint8_t *scan_code, int32_t *len) { uint16_t make_code; @@ -227,12 +354,14 @@ static enum ec_error_list matrix_callback(int8_t row, int8_t col, break; case SCANCODE_SET_2: - /* insert the break byte, move back the last byte and insert a - * 0xf0 byte before that. */ + /* + * Insert the break byte, move back the last byte and insert a + * 0xf0 byte before that. + */ if (!pressed) { ASSERT(*len >= 1); scan_code[*len] = scan_code[*len - 1]; - scan_code[*len - 1] = 0xF0; + scan_code[*len - 1] = 0xf0; *len += 1; } break; @@ -243,7 +372,6 @@ static enum ec_error_list matrix_callback(int8_t row, int8_t col, return EC_SUCCESS; } - static void reset_rate_and_delay(void) { typematic_value_from_host = DEFAULT_TYPEMATIC_VALUE; @@ -251,7 +379,6 @@ static void reset_rate_and_delay(void) refill_inter_delay = DEFAULT_INTER_DELAY; } - void keyboard_clear_buffer(void) { i8042_flush_buffer(); @@ -268,8 +395,7 @@ void keyboard_state_changed(int row, int col, int is_pressed) int32_t len; enum ec_error_list ret; - CPRINTF5("[%T KB %s(): row=%d col=%d is_pressed=%d]\n", - __func__, row, col, is_pressed); + CPRINTF5("[%T KB (%d,%d)=%d]\n", row, col, is_pressed); ret = matrix_callback(row, col, is_pressed, scancode_set, scan_code, &len); @@ -282,7 +408,7 @@ void keyboard_state_changed(int row, int col, int is_pressed) if (is_pressed) { keyboard_wakeup(); - typematic_delay = refill_first_delay * 1000; + typematic_delay = refill_first_delay * MSEC; memcpy(typematic_scan_code, scan_code, len); typematic_len = len; task_wake(TASK_ID_TYPEMATIC); @@ -291,7 +417,6 @@ void keyboard_state_changed(int row, int col, int is_pressed) } } - static void keyboard_enable(int enable) { if (!keyboard_enabled && enable) { @@ -314,7 +439,6 @@ static void keystroke_enable(int enable) keystroke_enabled = enable; } - static uint8_t read_ctl_ram(uint8_t addr) { if (addr < ARRAY_SIZE(controller_ram)) @@ -323,9 +447,11 @@ static uint8_t read_ctl_ram(uint8_t addr) return 0; } - -/* Manipulate the controller_ram[]. Some bits change may trigger internal - * state change. */ +/** + * 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; @@ -338,37 +464,31 @@ static void update_ctl_ram(uint8_t addr, uint8_t data) CPRINTF5("[%T KB set CTR_RAM(0x%02x)=0x%02x (old:0x%02x)]\n", addr, data, orig); - if (addr == 0x00) { /* the controller RAM */ + if (addr == 0x00) { + /* Keyboard enable/disable */ + /* Enable IRQ before enable keyboard (queue chars to host) */ if (!(orig & I8042_ENIRQ1) && (data & I8042_ENIRQ1)) - i8042_enable_keyboard_irq(1); + keyboard_enable_irq(1); /* Handle the I8042_KBD_DIS bit */ keyboard_enable(!(data & I8042_KBD_DIS)); - /* Disable IRQ after disable keyboard so that every char - * must have informed the host. */ + /* + * Disable IRQ after disable keyboard so that every char must + * have informed the host. + */ if ((orig & I8042_ENIRQ1) && !(data & I8042_ENIRQ1)) - i8042_enable_keyboard_irq(0); + keyboard_enable_irq(0); } } - -static enum { - STATE_NORMAL = 0, - STATE_SCANCODE, - STATE_SETLEDS, - STATE_EX_SETLEDS_1, /* expect 2-byte parameter coming */ - 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; - - -int handle_keyboard_data(uint8_t data, uint8_t *output) +/** + * 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; @@ -434,7 +554,7 @@ int handle_keyboard_data(uint8_t data, uint8_t *output) typematic_value_from_host = data; refill_first_delay = (((typematic_value_from_host & 0x60) >> 5) + 1) * 250; - refill_inter_delay = 1000 * /* ms */ + refill_inter_delay = MSEC * (1 << ((typematic_value_from_host & 0x18) >> 3)) * ((typematic_value_from_host & 0x7) + 8) / 240; @@ -457,8 +577,7 @@ int handle_keyboard_data(uint8_t data, uint8_t *output) break; case I8042_CMD_SETLEDS: - /* We use screen indicator. Do nothing in keyboard - * controller. */ + /* Chrome OS doesn't have keyboard LEDs, so ignore */ output[out_len++] = I8042_RET_ACK; data_port_state = STATE_SETLEDS; break; @@ -518,13 +637,16 @@ int handle_keyboard_data(uint8_t data, uint8_t *output) output[out_len++] = resend_command[i]; break; - /* u-boot hack */ - case 0x60: /* see CONFIG_USE_CPCIDVI in */ - case 0x45: /* third_party/u-boot/files/drivers/input/i8042.c */ - /* just ignore, don't reply anything. */ + case 0x60: /* fall-thru */ + case 0x45: + /* + * U-boot hack. See CONFIG_USE_CPCIDVI in + * third_party/u-boot/files/drivers/input/i8042.c. + * Just ignore; don't reply. + */ break; - case I8042_CMD_SETALL_MB: /* fall-thru below */ + case I8042_CMD_SETALL_MB: /* fall-thru */ case I8042_CMD_SETALL_MBR: case I8042_CMD_EX_ENABLE: default: @@ -547,8 +669,13 @@ int handle_keyboard_data(uint8_t data, uint8_t *output) return out_len; } - -int handle_keyboard_command(uint8_t command, uint8_t *output) +/** + * 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; @@ -649,6 +776,21 @@ int handle_keyboard_command(uint8_t command, uint8_t *output) return out_len; } +static void i8042_handle_from_host(void) +{ + struct host_byte h; + int ret_len; + uint8_t output[MAX_SCAN_CODE_LEN]; + + while (queue_remove_unit(&from_host, &h)) { + if (h.type == HOST_COMMAND) + ret_len = handle_keyboard_command(h.byte, output); + else + ret_len = handle_keyboard_data(h.byte, output); + + i8042_send_to_host(ret_len, output); + } +} /* U U D D L R L R b a */ static void keyboard_special(uint16_t k) @@ -723,17 +865,37 @@ void keyboard_set_power_button(int pressed) } } - -void kblog_put(char type, uint8_t byte) +void i8042_command_task(void) { - if (kblog && kblog_len < MAX_KBLOG) { - kblog[kblog_len].type = type; - kblog[kblog_len].byte = byte; - kblog_len++; + while (1) { + /* Wait for next host read/write */ + task_wait_event(-1); + + while (1) { + uint8_t chr; + + /* 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; + + /* Host interface must have space */ + if (lpc_keyboard_has_char()) + break; + + /* Get a char from buffer. */ + kblog_put('k', to_host.head); + queue_remove_unit(&to_host, &chr); + kblog_put('K', chr); + + /* Write to host. */ + lpc_keyboard_put_char(chr, i8042_irq_enabled); + } } } - void keyboard_typematic_task(void) { while (1) { @@ -748,7 +910,7 @@ void keyboard_typematic_task(void) if (keystroke_enabled) i8042_send_to_host(typematic_len, typematic_scan_code); - typematic_delay = refill_inter_delay * 1000; + typematic_delay = refill_inter_delay * MSEC; } } } @@ -835,8 +997,9 @@ static int command_keyboard_log(int argc, char **argv) if (argc == 1) { ccprintf("KBC log (len=%d):\n", kblog_len); - for (i = 0; kblog && i < kblog_len; ++i) { - ccprintf("%c.%02x ", kblog[i].type, kblog[i].byte); + 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(); @@ -844,21 +1007,23 @@ static int command_keyboard_log(int argc, char **argv) } ccputs("\n"); } else if (argc == 2 && !strcasecmp("on", argv[1])) { - if (!kblog) { - int rv = shared_mem_acquire(sizeof(*kblog) * MAX_KBLOG, - (char **)&kblog); + if (!kblog_buf) { + int rv = shared_mem_acquire( + sizeof(*kblog_buf) * MAX_KBLOG, + (char **)&kblog_buf); if (rv != EC_SUCCESS) - kblog = NULL; + kblog_buf = NULL; kblog_len = 0; return rv; } } else if (argc == 2 && !strcasecmp("off", argv[1])) { kblog_len = 0; - if (kblog) - shared_mem_release(kblog); - kblog = NULL; - } else + if (kblog_buf) + shared_mem_release(kblog_buf); + kblog_buf = NULL; + } else { return EC_ERROR_PARAM1; + } return EC_SUCCESS; } |