diff options
author | Randall Spangler <rspangler@chromium.org> | 2013-03-22 09:35:00 -0700 |
---|---|---|
committer | ChromeBot <chrome-bot@google.com> | 2013-03-29 11:40:34 -0700 |
commit | fe3ccdf70a002e651bc1a13bcc305ae1e3422154 (patch) | |
tree | a4e1f70850c720d2a1599285304816269fc489f1 | |
parent | 4311bc5ccf666d7425f1ea50eee08e3a032e2508 (diff) | |
download | chrome-ec-fe3ccdf70a002e651bc1a13bcc305ae1e3422154.tar.gz |
Merge lm4 and stm32 implementations of keyboard_scan
Scanning is now performed identically on all platforms. keyboard_scan
talks to chip-specific keyboard_raw on the bottom end, and 8042 or mkbp
keyboard protocol on the top end.
8042 can now take advantage of CONFIG_KEYBOARD_TEST to simulate scan results.
BUG=chrome-os-partner:18360
BRANCH=none
TEST=compile all boards; test keyboard on spring and link
1) Type on keyboard. Should produce keystrokes.
2) At EC console:
kbpress 3 7 1
kbpress 3 7 0
Should produce 'r' keystroke(s); key repeat should kick in if you wait
a while between the commands.
3) Hold power button while typing. Should not produce keystrokes.
4) Reboot with power+refresh+esc. Should go to recovery mode.
5) While the system is up, alt+volup+R should reboot the AP.
Change-Id: I48e0bca15b869162672b5f54ffcb561f6fcf0f45
Signed-off-by: Randall Spangler <rspangler@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/46666
Reviewed-by: Bill Richardson <wfrichar@chromium.org>
-rw-r--r-- | common/build.mk | 4 | ||||
-rw-r--r-- | common/keyboard_8042.c | 76 | ||||
-rw-r--r-- | common/keyboard_mkbp.c | 563 | ||||
-rw-r--r-- | common/keyboard_scan.c | 290 | ||||
-rw-r--r-- | include/keyboard_scan.h | 34 |
5 files changed, 358 insertions, 609 deletions
diff --git a/common/build.mk b/common/build.mk index 1136015b7d..fffb708394 100644 --- a/common/build.mk +++ b/common/build.mk @@ -21,10 +21,7 @@ common-$(CONFIG_FLASH)+=flash_common.o fmap.o common-$(CONFIG_I2C)+=i2c_commands.o common-$(CONFIG_I2C_ARBITRATION)+=i2c_arbitration.o common-$(CONFIG_IR357x)+=ir357x.o -# TODO: combine common bits of keyboard_scan into one file -# (coming in a follow-up CL) common-$(CONFIG_KEYBOARD_PROTOCOL_MKBP)+=keyboard_mkbp.o -common-$(CONFIG_KEYBOARD_PROTOCOL_8042)+=keyboard_scan.o common-$(CONFIG_KEYBOARD_TEST)+=keyboard_test.o common-$(CONFIG_LP5562)+=lp5562.o lp5562_battery_led.o common-$(CONFIG_LPC)+=port80.o @@ -35,6 +32,7 @@ common-$(CONFIG_TASK_CHARGER)+=charge_state.o battery_precharge.o common-$(CONFIG_TASK_CONSOLE)+=console.o common-$(CONFIG_TASK_HOSTCMD)+=host_command.o host_event_commands.o common-$(CONFIG_TASK_I8042CMD)+=i8042.o keyboard_8042.o +common-$(CONFIG_TASK_KEYSCAN)+=keyboard_scan.o common-$(CONFIG_TASK_LIGHTBAR)+=lightbar.o common-$(CONFIG_TASK_THERMAL)+=thermal.o common-$(CONFIG_TASK_VBOOTHASH)+=sha256.o vboot_hash.o diff --git a/common/keyboard_8042.c b/common/keyboard_8042.c index 1a991d7075..54cc5d0310 100644 --- a/common/keyboard_8042.c +++ b/common/keyboard_8042.c @@ -146,11 +146,6 @@ static const uint16_t scancode_set2[KEYBOARD_ROWS][KEYBOARD_COLS] = { 0x0044, 0x0000, 0xe075, 0xe06b}, }; - -/* Recording which key is being simulated pressed. */ -static uint8_t simulated_key[KEYBOARD_COLS]; - - /* Log the traffic between EC and host -- for debug only */ #define MAX_KBLOG 512 /* Max events in keyboard log */ struct kblog_t { @@ -834,53 +829,6 @@ DECLARE_CONSOLE_COMMAND(ctrlram, command_controller_ram, "Get/set keyboard controller RAM", NULL); - -static int command_keyboard_press(int argc, char **argv) -{ - if (argc == 1) { - int i, j; - - ccputs("Simulated key:\n"); - for (i = 0; i < KEYBOARD_COLS; ++i) { - if (simulated_key[i] == 0) - continue; - for (j = 0; j < KEYBOARD_ROWS; ++j) - if (simulated_key[i] & (1 << j)) - ccprintf("\t%d %d\n", i, j); - } - - } else if (argc == 4) { - int r, c, p; - char *e; - - c = strtoi(argv[1], &e, 0); - if (*e || c < 0 || c >= KEYBOARD_COLS) - return EC_ERROR_PARAM1; - - r = strtoi(argv[2], &e, 0); - if (*e || r < 0 || r >= KEYBOARD_ROWS) - return EC_ERROR_PARAM2; - - p = strtoi(argv[3], &e, 0); - if (*e || p < 0 || p > 1) - return EC_ERROR_PARAM3; - - if ((simulated_key[c] & (1 << r)) == (p << r)) - return EC_SUCCESS; - - simulated_key[c] = (simulated_key[c] & ~(1 << r)) | (p << r); - - keyboard_state_changed(r, c, p); - } - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(kbpress, command_keyboard_press, - "[col] [row] [0 | 1]", - "Simulate keypress", - NULL); - - static int command_keyboard_log(int argc, char **argv) { int i; @@ -940,30 +888,6 @@ DECLARE_CONSOLE_COMMAND(kbd, command_keyboard, NULL); /*****************************************************************************/ -/* Host commands */ - -static int mkbp_command_simulate_key(struct host_cmd_handler_args *args) -{ - const struct ec_params_mkbp_simulate_key *p = args->params; - - /* Only available on unlocked systems */ - if (system_is_locked()) - return EC_RES_ACCESS_DENIED; - - if (p->col >= ARRAY_SIZE(simulated_key)) - return EC_RES_INVALID_PARAM; - - simulated_key[p->col] = (simulated_key[p->col] & ~(1 << p->row)) | - (p->pressed << p->row); - - keyboard_state_changed(p->row, p->col, p->pressed); - return EC_RES_SUCCESS; -} -DECLARE_HOST_COMMAND(EC_CMD_MKBP_SIMULATE_KEY, - mkbp_command_simulate_key, - EC_VER_MASK(0)); - -/*****************************************************************************/ /* Hooks */ /** diff --git a/common/keyboard_mkbp.c b/common/keyboard_mkbp.c index 9d2613b187..336fd7e0be 100644 --- a/common/keyboard_mkbp.c +++ b/common/keyboard_mkbp.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. * - * Keyboard scanner module for Chrome EC STM32 + * MKBP keyboard protocol */ #include "atomic.h" @@ -21,86 +21,49 @@ #include "util.h" /* Console output macros */ -#define CPUTS(outstr) cputs(CC_KEYSCAN, outstr) -#define CPRINTF(format, args...) cprintf(CC_KEYSCAN, format, ## args) +#define CPUTS(outstr) cputs(CC_KEYBOARD, outstr) +#define CPRINTF(format, args...) cprintf(CC_KEYBOARD, format, ## args) -#define SCAN_TIME_COUNT 32 - -static struct mutex scanning_enabled; - -static uint8_t debounced_state[KEYBOARD_COLS]; /* Debounced key matrix */ -static uint8_t prev_state[KEYBOARD_COLS]; /* Matrix from previous scan */ -static uint8_t debouncing[KEYBOARD_COLS]; /* Mask of keys being debounced */ -static uint32_t scan_time[SCAN_TIME_COUNT]; /* Times of last scans */ -static int scan_time_index; /* Current scan_time[] index */ -/* Index into scan_time[] when each key started debouncing */ -static uint8_t scan_edge_index[KEYBOARD_COLS][KEYBOARD_ROWS]; +#define KB_FIFO_DEPTH 16 /* FIXME: this is pretty huge */ -/*****************************************************************************/ +/* Changes to col,row here need to also be reflected in kernel. + * drivers/input/mkbp.c ... see KEY_BATTERY. + */ +#define BATTERY_KEY_COL 0 +#define BATTERY_KEY_ROW 7 +#define BATTERY_KEY_ROW_MASK (1 << BATTERY_KEY_ROW) -#define KB_FIFO_DEPTH 16 /* FIXME: this is pretty huge */ static uint32_t kb_fifo_start; /* first entry */ -static uint32_t kb_fifo_end; /* last entry */ +static uint32_t kb_fifo_end; /* last entry */ static uint32_t kb_fifo_entries; /* number of existing entries */ static uint8_t kb_fifo[KB_FIFO_DEPTH][KEYBOARD_COLS]; +static struct mutex fifo_mutex; -/* - * Our configuration. The debounce parameters are not yet supported. - */ -static struct ec_mkbp_config config = { +/* Config for mkbp protocol; does not include fields from scan config */ +struct ec_mkbp_protocol_config { + uint32_t valid_mask; /* valid fields */ + uint8_t flags; /* some flags (enum mkbp_config_flags) */ + uint8_t valid_flags; /* which flags are valid */ + + /* maximum depth to allow for fifo (0 = no keyscan output) */ + uint8_t fifo_max_depth; +} __packed; + +static struct ec_mkbp_protocol_config config = { .valid_mask = EC_MKBP_VALID_SCAN_PERIOD | EC_MKBP_VALID_POLL_TIMEOUT | EC_MKBP_VALID_MIN_POST_SCAN_DELAY | EC_MKBP_VALID_OUTPUT_SETTLE | EC_MKBP_VALID_DEBOUNCE_DOWN | EC_MKBP_VALID_DEBOUNCE_UP | EC_MKBP_VALID_FIFO_MAX_DEPTH, .valid_flags = EC_MKBP_FLAGS_ENABLE, .flags = EC_MKBP_FLAGS_ENABLE, - .scan_period_us = 3000, - .poll_timeout_us = 100 * 1000, - .min_post_scan_delay_us = 1000, - .output_settle_us = 50, - .debounce_down_us = 9000, - .debounce_up_us = 30000, .fifo_max_depth = KB_FIFO_DEPTH, }; -void keyboard_clear_state(void) -{ - int i; - - CPRINTF("clearing keyboard fifo\n"); - kb_fifo_start = 0; - kb_fifo_end = 0; - kb_fifo_entries = 0; - for (i = 0; i < KB_FIFO_DEPTH; i++) - memset(kb_fifo[i], 0, KEYBOARD_COLS); -} - -int keyboard_fifo_add(const uint8_t *buffp) -{ - int ret = EC_SUCCESS; - - if (kb_fifo_entries >= config.fifo_max_depth) { - CPRINTF("%s: FIFO depth %d reached\n", __func__, - config.fifo_max_depth); - ret = EC_ERROR_OVERFLOW; - goto kb_fifo_push_done; - } - - memcpy(kb_fifo[kb_fifo_end], buffp, KEYBOARD_COLS); - - kb_fifo_end = (kb_fifo_end + 1) % KB_FIFO_DEPTH; - - atomic_add(&kb_fifo_entries, 1); - -kb_fifo_push_done: - return ret; -} - /** - * Pop keyboard state from FIFO - * - * @return EC_SUCCESS if entry popped, EC_ERROR_UNKNOWN if FIFO is empty - */ + * Pop keyboard state from FIFO + * + * @return EC_SUCCESS if entry popped, EC_ERROR_UNKNOWN if FIFO is empty + */ static int kb_fifo_remove(uint8_t *buffp) { if (!kb_fifo_entries) { @@ -133,331 +96,68 @@ static void set_host_interrupt(int active) gpio_set_level(GPIO_EC_INT, !active); } -/** - * Check special runtime key combinations. - * - * @param state Keyboard state to use when checking keys. - * @return 1 if a special key was pressed, 0 if not - */ -static int check_runtime_keys(const uint8_t *state) -{ - int num_press; - int c; - - /* Count number of key pressed */ - for (c = num_press = 0; c < KEYBOARD_COLS; c++) { - if (state[c]) - ++num_press; - } - - if (num_press != 3) - return 0; - - if (state[KEYBOARD_COL_KEY_R] == KEYBOARD_MASK_KEY_R && - state[KEYBOARD_COL_VOL_UP] == KEYBOARD_MASK_VOL_UP && - (state[KEYBOARD_COL_RIGHT_ALT] == KEYBOARD_MASK_RIGHT_ALT || - state[KEYBOARD_COL_LEFT_ALT] == KEYBOARD_MASK_LEFT_ALT)) { - keyboard_clear_state(); - chipset_reset(0); - return 1; - } - - return 0; -} +/*****************************************************************************/ +/* Interface */ -/* Print the keyboard state. */ -static void print_state(const uint8_t *state, const char *msg) +void keyboard_clear_buffer(void) { - int c; - - CPRINTF("[%T KB %s:", msg); - for (c = 0; c < KEYBOARD_COLS; c++) { - if (state[c]) - CPRINTF(" %02x", state[c]); - else - CPUTS(" --"); - } - CPUTS("]\n"); -} + int i; -/** - * Read the raw keyboard matrix state. - * - * Used in pre-init, so must not make task-switching-dependent calls; udelay() - * is ok because it's a spin-loop. - * - * @param state Destination for new state (must be KEYBOARD_COLS long). - * - * @return 1 if at least one key is pressed, else zero. - */ -static int read_matrix(uint8_t *state) -{ - int c; - uint8_t r; - int pressed = 0; - - for (c = 0; c < KEYBOARD_COLS; c++) { - /* Assert output, then wait a bit for it to settle */ - keyboard_raw_drive_column(c); - udelay(config.output_settle_us); - - r = keyboard_raw_read_rows(); - -#ifdef CONFIG_KEYBOARD_TEST - /* Use simulated keyscan sequence instead if testing active */ - r = keyscan_seq_get_scan(c, r); -#endif - -#ifdef OR_WITH_CURRENT_STATE_FOR_TESTING - /* KLUDGE - or current state in, so we can make sure - * all the lines are hooked up */ - r |= state[c]; -#endif - - state[c] = r; - pressed |= r; - } - keyboard_raw_drive_column(KEYBOARD_COLUMN_NONE); + CPRINTF("clearing keyboard fifo\n"); - return pressed ? 1 : 0; + kb_fifo_start = 0; + kb_fifo_end = 0; + kb_fifo_entries = 0; + for (i = 0; i < KB_FIFO_DEPTH; i++) + memset(kb_fifo[i], 0, KEYBOARD_COLS); } -/** - * Update keyboard state using low-level interface to read keyboard. - * - * @param state Keyboard state to update. - * - * @return 1 if any key is still pressed, 0 if no key is pressed. - */ -static int check_keys_changed(uint8_t *state) +int keyboard_fifo_add(const uint8_t *buffp) { - int any_pressed = 0; - int c, i; - int any_change = 0; - uint8_t new_state[KEYBOARD_COLS]; - uint32_t tnow = get_time().le.lo; - - /* Save the current scan time */ - if (++scan_time_index >= SCAN_TIME_COUNT) - scan_time_index = 0; - scan_time[scan_time_index] = tnow; - - /* Read the raw key state */ - any_pressed = read_matrix(new_state); - - /* Check for changes between previous scan and this one */ - for (c = 0; c < KEYBOARD_COLS; c++) { - int diff = new_state[c] ^ prev_state[c]; - - if (!diff) - continue; - - for (i = 0; i < KEYBOARD_ROWS; i++) { - if (diff & (1 << i)) - scan_edge_index[c][i] = scan_time_index; - } - - debouncing[c] |= diff; - prev_state[c] = new_state[c]; - } - - /* Check for keys which are done debouncing */ - for (c = 0; c < KEYBOARD_COLS; c++) { - int debc = debouncing[c]; - - if (!debc) - continue; - - for (i = 0; i < KEYBOARD_ROWS; i++) { - int mask = 1 << i; - int new_mask = new_state[c] & mask; - - /* Are we done debouncing this key? */ - if (!(debc & mask)) - continue; /* Not debouncing this key */ - if (tnow - scan_time[scan_edge_index[c][i]] < - (new_mask ? config.debounce_down_us : - config.debounce_up_us)) - continue; /* Not done debouncing */ - - debouncing[c] &= ~mask; - - /* Did the key change from its previous state? */ - if ((state[c] & mask) == new_mask) - continue; /* No */ - - state[c] ^= mask; - any_change = 1; - } - } - - if (any_change) { -#ifdef CONFIG_KEYBOARD_SUPPRESS_NOISE - keyboard_suppress_noise(); -#endif - print_state(state, "state"); - -#ifdef PRINT_SCAN_TIMES - /* Print delta times from now back to each previous scan */ - for (i = 0; i < SCAN_TIME_COUNT; i++) { - int tnew = scan_time[ - (SCAN_TIME_COUNT + scan_time_index - i) % - SCAN_TIME_COUNT]; - CPRINTF(" %d", tnow - tnew); - } - CPRINTF("\n"); -#endif - - /* Swallow special keys */ - if (check_runtime_keys(state)) - return 0; - else if (keyboard_fifo_add(state) == EC_SUCCESS) - set_host_interrupt(1); - else - CPRINTF("dropped keystroke\n"); - } + int ret = EC_SUCCESS; - return any_pressed; -} + /* + * If keyboard protocol is not enabled, don't save the state to the + * FIFO or trigger an interrupt. + */ + if (!(config.flags & EC_MKBP_FLAGS_ENABLE)) + return EC_SUCCESS; -/* - * Check if the user has triggered a recovery reset - * - * Pressing Power + Refresh + ESC. triggers a recovery reset. Here we check - * for this. - * - * @param state Keyboard state to check - * @return 1 if there is a recovery reset, else 0 - */ -static int check_recovery_key(const uint8_t *state) -{ - int c; - - /* check the recovery key only if we're booting due to a - * reset-pin-caused reset. */ - if (!(system_get_reset_flags() & RESET_FLAG_RESET_PIN)) - return 0; - - /* cold boot : Power + Refresh were pressed, - * check if ESC is also pressed for recovery. */ - if (!(state[KEYBOARD_COL_ESC] & KEYBOARD_MASK_ESC)) - return 0; - - /* Make sure only other allowed keys are pressed. This protects - * against accidentally triggering the special key when a cat sits on - * your keyboard. Currently, only the requested key and ESC are - * allowed. */ - for (c = 0; c < KEYBOARD_COLS; c++) { - if (state[c] && - (c != KEYBOARD_COL_ESC || state[c] != KEYBOARD_MASK_ESC) && - (c != KEYBOARD_COL_REFRESH || - state[c] != KEYBOARD_MASK_REFRESH)) - return 0; /* Additional disallowed key pressed */ + if (kb_fifo_entries >= config.fifo_max_depth) { + CPRINTF("[%T KB FIFO depth %d reached]\n", + config.fifo_max_depth); + ret = EC_ERROR_OVERFLOW; + goto kb_fifo_push_done; } - CPRINTF("Keyboard RECOVERY detected !\n"); - - host_set_single_event(EC_HOST_EVENT_KEYBOARD_RECOVERY); - - return 1; -} - -void keyboard_scan_init(void) -{ - keyboard_raw_init(); - - /* Tri-state (put into Hi-Z) the outputs */ - keyboard_raw_drive_column(KEYBOARD_COLUMN_NONE); - - /* Initialize raw state */ - read_matrix(debounced_state); - memcpy(prev_state, debounced_state, sizeof(prev_state)); - - /* is recovery key pressed on cold startup ? */ - check_recovery_key(debounced_state); -} - -/* Scan the keyboard until all keys are released */ -static void scan_keyboard(void) -{ - timestamp_t poll_deadline, start; - int keys_changed = 1; + mutex_lock(&fifo_mutex); + memcpy(kb_fifo[kb_fifo_end], buffp, KEYBOARD_COLS); + kb_fifo_end = (kb_fifo_end + 1) % KB_FIFO_DEPTH; + atomic_add(&kb_fifo_entries, 1); + mutex_unlock(&fifo_mutex); - mutex_lock(&scanning_enabled); - keyboard_raw_drive_column(KEYBOARD_COLUMN_ALL); - keyboard_raw_enable_interrupt(1); - mutex_unlock(&scanning_enabled); +kb_fifo_push_done: - /* - * if a key was pressed after the last polling, - * re-start immediatly polling instead of waiting - * for the next interrupt. - */ - if (!keyboard_raw_read_rows()) { -#ifdef CONFIG_KEYBOARD_TEST - task_wait_event(keyscan_seq_next_event_delay()); -#else - task_wait_event(-1); -#endif - } + if (ret == EC_SUCCESS) + set_host_interrupt(1); - keyboard_raw_enable_interrupt(0); - keyboard_raw_drive_column(KEYBOARD_COLUMN_NONE); - - /* Busy polling keyboard state. */ - while (1) { - int wait_time; - - if (!(config.flags & EC_MKBP_FLAGS_ENABLE)) - break; - - /* If we saw any keys pressed, reset deadline */ - start = get_time(); - if (keys_changed) - poll_deadline.val = start.val + config.poll_timeout_us; - else if (timestamp_expired(poll_deadline, &start)) - break; - - /* Scan immediately, with no delay */ - mutex_lock(&scanning_enabled); - keys_changed = check_keys_changed(debounced_state); - mutex_unlock(&scanning_enabled); - - /* Wait a bit before scanning again */ - wait_time = config.scan_period_us - - (get_time().val - start.val); - if (wait_time < config.min_post_scan_delay_us) - wait_time = config.min_post_scan_delay_us; - task_wait_event(wait_time); - } + return ret; } -void keyboard_scan_task(void) +void keyboard_send_battery_key(void) { - print_state(debounced_state, "init state"); + uint8_t state[KEYBOARD_COLS]; - keyboard_raw_task_start(); + /* Copy debounced state and add battery pseudo-key */ + memcpy(state, keyboard_scan_get_state(), sizeof(state)); + state[BATTERY_KEY_COL] ^= BATTERY_KEY_ROW_MASK; - while (1) { - if (config.flags & EC_MKBP_FLAGS_ENABLE) { - scan_keyboard(); - } else { - keyboard_raw_drive_column(KEYBOARD_COLUMN_NONE); - task_wait_event(-1); - } - } + /* Add to FIFO */ + keyboard_fifo_add(state); } -int keyboard_has_char(void) -{ - /* TODO: needs to be implemented */ - return 0; -} - -void keyboard_put_char(uint8_t chr, int send_irq) -{ - /* TODO: needs to be implemented */ -} +/*****************************************************************************/ +/* Host commands */ static int keyboard_get_scan(struct host_cmd_handler_args *args) { @@ -489,91 +189,13 @@ DECLARE_HOST_COMMAND(EC_CMD_MKBP_INFO, keyboard_get_info, EC_VER_MASK(0)); -void keyboard_scan_enable(int enable) -{ - if (enable) { - mutex_unlock(&scanning_enabled); - task_wake(TASK_ID_KEYSCAN); - } else { - /* - * TODO: using a mutex to control scanning isn't very - * responsive. If we just started scanning the matrix, the - * mutex will already be locked, and we'll finish the entire - * matrix scan before we stop driving columns. We should - * instead do something like link, where disabling scanning - * immediately stops driving the columns. - */ - mutex_lock(&scanning_enabled); - keyboard_raw_drive_column(KEYBOARD_COLUMN_NONE); - } -} - -/* Changes to col,row here need to also be reflected in kernel. - * drivers/input/mkbp.c ... see KEY_BATTERY. - */ -#define BATTERY_KEY_COL 0 -#define BATTERY_KEY_ROW 7 -#define BATTERY_KEY_ROW_MASK (1 << BATTERY_KEY_ROW) - -void keyboard_send_battery_key(void) -{ - mutex_lock(&scanning_enabled); - debounced_state[BATTERY_KEY_COL] ^= BATTERY_KEY_ROW_MASK; - if (keyboard_fifo_add(debounced_state) == EC_SUCCESS) - set_host_interrupt(1); - else - CPRINTF("dropped battery keystroke\n"); - mutex_unlock(&scanning_enabled); -} - -static int command_keyboard_press(int argc, char **argv) -{ - int r, c, p; - char *e; - - if (argc != 4) - return EC_ERROR_PARAM_COUNT; - - c = strtoi(argv[1], &e, 0); - if (*e || c < 0 || c >= KEYBOARD_COLS) - return EC_ERROR_PARAM1; - - r = strtoi(argv[2], &e, 0); - if (*e || r < 0 || r >= KEYBOARD_ROWS) - return EC_ERROR_PARAM2; - - p = strtoi(argv[3], &e, 0); - if (*e || p < 0 || p > 1) - return EC_ERROR_PARAM3; - - /* - * TODO(sjg@chromium.org): This ignores debouncing, so is a bit - * dodgy and might have strange side-effects on real key scans. - */ - if (p) - debounced_state[c] |= (1 << r); - else - debounced_state[c] &= ~(1 << r); - - if (keyboard_fifo_add(debounced_state) == EC_SUCCESS) - set_host_interrupt(1); - else - ccprintf("dropped keystroke\n"); - - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(kbpress, command_keyboard_press, - "[col] [row] [0 | 1]", - "Simulate keypress", - NULL); - /** * Copy keyscan configuration from one place to another according to flags * * This is like a structure copy, except that only selected fields are * copied. * - * TODO(sjg@chromium.org): Consider making this table drive as ectool. + * TODO(sjg@chromium.org): Consider making this table driven as ectool. * * @param src Source config * @param dst Destination config @@ -584,35 +206,43 @@ DECLARE_CONSOLE_COMMAND(kbpress, command_keyboard_press, * over to dst->flags */ static void keyscan_copy_config(const struct ec_mkbp_config *src, - struct ec_mkbp_config *dst, + struct ec_mkbp_protocol_config *dst, uint32_t valid_mask, uint8_t valid_flags) { + struct keyboard_scan_config *ksc = keyboard_scan_get_config(); uint8_t new_flags; if (valid_mask & EC_MKBP_VALID_SCAN_PERIOD) - dst->scan_period_us = src->scan_period_us; + ksc->scan_period_us = src->scan_period_us; + if (valid_mask & EC_MKBP_VALID_POLL_TIMEOUT) - dst->poll_timeout_us = src->poll_timeout_us; + ksc->poll_timeout_us = src->poll_timeout_us; + if (valid_mask & EC_MKBP_VALID_MIN_POST_SCAN_DELAY) { /* * Key scanning is high priority, so we should require at * least 100us min delay here. Setting this to 0 will cause * watchdog events. Use 200 to be safe. */ - dst->min_post_scan_delay_us = + ksc->min_post_scan_delay_us = MAX(src->min_post_scan_delay_us, 200); } + if (valid_mask & EC_MKBP_VALID_OUTPUT_SETTLE) - dst->output_settle_us = src->output_settle_us; + ksc->output_settle_us = src->output_settle_us; + if (valid_mask & EC_MKBP_VALID_DEBOUNCE_DOWN) - dst->debounce_down_us = src->debounce_down_us; + ksc->debounce_down_us = src->debounce_down_us; + if (valid_mask & EC_MKBP_VALID_DEBOUNCE_UP) - dst->debounce_up_us = src->debounce_up_us; + ksc->debounce_up_us = src->debounce_up_us; + if (valid_mask & EC_MKBP_VALID_FIFO_MAX_DEPTH) { /* Sanity check for fifo depth */ dst->fifo_max_depth = MIN(src->fifo_max_depth, KB_FIFO_DEPTH); } + new_flags = dst->flags & ~valid_flags; new_flags |= src->flags & valid_flags; dst->flags = new_flags; @@ -636,21 +266,36 @@ static int host_command_mkbp_set_config(struct host_cmd_handler_args *args) return EC_RES_SUCCESS; } +DECLARE_HOST_COMMAND(EC_CMD_MKBP_SET_CONFIG, + host_command_mkbp_set_config, + EC_VER_MASK(0)); static int host_command_mkbp_get_config(struct host_cmd_handler_args *args) { + const struct keyboard_scan_config *ksc = keyboard_scan_get_config(); struct ec_response_mkbp_get_config *resp = args->response; + struct ec_mkbp_config *dst = &resp->config; memcpy(&resp->config, &config, sizeof(config)); + + /* Copy fields from mkbp protocol config to mkbp config */ + dst->valid_mask = config.valid_mask; + dst->flags = config.flags; + dst->valid_flags = config.valid_flags; + dst->fifo_max_depth = config.fifo_max_depth; + + /* Copy fields from keyscan config to mkbp config */ + dst->output_settle_us = ksc->output_settle_us; + dst->debounce_down_us = ksc->debounce_down_us; + dst->debounce_up_us = ksc->debounce_up_us; + dst->scan_period_us = ksc->scan_period_us; + dst->min_post_scan_delay_us = ksc->min_post_scan_delay_us; + dst->poll_timeout_us = ksc->poll_timeout_us; + args->response_size = sizeof(*resp); return EC_RES_SUCCESS; } - -DECLARE_HOST_COMMAND(EC_CMD_MKBP_SET_CONFIG, - host_command_mkbp_set_config, - EC_VER_MASK(0)); - DECLARE_HOST_COMMAND(EC_CMD_MKBP_GET_CONFIG, host_command_mkbp_get_config, EC_VER_MASK(0)); diff --git a/common/keyboard_scan.c b/common/keyboard_scan.c index c01326d956..45eb45a35a 100644 --- a/common/keyboard_scan.c +++ b/common/keyboard_scan.c @@ -23,42 +23,52 @@ #define CPUTS(outstr) cputs(CC_KEYSCAN, outstr) #define CPRINTF(format, args...) cprintf(CC_KEYSCAN, format, ## args) -/* Time constants */ -#define POLLING_MODE_TIMEOUT SECOND /* Max time to poll if no keys are down */ -#define DEBOUNCE_UP_US (30 * MSEC) /* Debounce time for key-up */ -#define DEBOUNCE_DOWN_US (6 * MSEC) /* Debounce time for key-down */ -#define SCAN_LOOP_DELAY MSEC /* Delay in scan loop */ -#define COLUMN_CHARGE_US 40 /* Column charge time in usec */ - #define SCAN_TIME_COUNT 32 /* Number of last scan times to track */ +static struct keyboard_scan_config config = { +#ifdef BOARD_link + .output_settle_us = 40, + .debounce_down_us = 6 * MSEC, + .debounce_up_us = 30 * MSEC, + .scan_period_us = 1500, + .min_post_scan_delay_us = 1000, + .poll_timeout_us = SECOND, +#else + .output_settle_us = 50, + .debounce_down_us = 9 * MSEC, + .debounce_up_us = 30 * MSEC, + .scan_period_us = 3 * MSEC, + .min_post_scan_delay_us = 1000, + .poll_timeout_us = 100 * MSEC, +#endif + .actual_key_mask = { + 0x14, 0xff, 0xff, 0xff, 0xff, 0xf5, 0xff, + 0xa4, 0xff, 0xf6, 0x55, 0xfa, 0xc8 /* full set */ + }, +}; + /* Boot key list. Must be in same order as enum boot_key. */ struct boot_key_entry { uint8_t mask_index; uint8_t mask_value; }; -const struct boot_key_entry boot_key_list[] = { +static const struct boot_key_entry boot_key_list[] = { {0, 0x00}, /* (none) */ {KEYBOARD_COL_ESC, KEYBOARD_MASK_ESC}, /* Esc */ {KEYBOARD_COL_DOWN, KEYBOARD_MASK_DOWN}, /* Down-arrow */ }; +static enum boot_key boot_key_value = BOOT_KEY_OTHER; static uint8_t debounced_state[KEYBOARD_COLS]; /* Debounced key matrix */ static uint8_t prev_state[KEYBOARD_COLS]; /* Matrix from previous scan */ static uint8_t debouncing[KEYBOARD_COLS]; /* Mask of keys being debounced */ +static uint8_t simulated_key[KEYBOARD_COLS]; /* Keys simulated-pressed */ + static uint32_t scan_time[SCAN_TIME_COUNT]; /* Times of last scans */ static int scan_time_index; /* Current scan_time[] index */ /* Index into scan_time[] when each key started debouncing */ static uint8_t scan_edge_index[KEYBOARD_COLS][KEYBOARD_ROWS]; -enum boot_key boot_key_value = BOOT_KEY_OTHER; - -/* Mask with 1 bits only for keys that actually exist */ -static const uint8_t actual_key_mask[KEYBOARD_COLS] = { - 0x14, 0xff, 0xff, 0xff, 0xff, 0xf5, 0xff, - 0xa4, 0xff, 0xf6, 0x55, 0xfa, 0xc8 /* full set */ -}; - /* * Print all keyboard scan state changes? Off by default because it generates * a lot of debug output, which makes the saved EC console data less useful. @@ -67,27 +77,56 @@ static int print_state_changes; static int enable_scanning = 1; /* Must init to 1 for scanning at boot */ -static void enable_interrupt(void) +static int is_scanning_enabled(void) { - CPRINTF("[%T KB wait]\n"); - - if (enable_scanning) - keyboard_raw_drive_column(KEYBOARD_COLUMN_ALL); +#ifdef BOARD_link + /* TODO: should apply to ARM too, but need standard lid API */ + /* Scanning is never enabled when lid is closed */ + if (!switch_get_lid_open()) + return 0; +#endif - keyboard_raw_enable_interrupt(1); + return enable_scanning; } -static void enter_polling_mode(void) +/** + * Print the keyboard state. + * + * @param state State array to print + * @param msg Description of state + */ +static void print_state(const uint8_t *state, const char *msg) { - CPRINTF("[%T KB poll]\n"); - keyboard_raw_enable_interrupt(0); - keyboard_raw_drive_column(KEYBOARD_COLUMN_NONE); + int c; + + CPRINTF("[%T KB %s:", msg); + for (c = 0; c < KEYBOARD_COLS; c++) { + if (state[c]) + CPRINTF(" %02x", state[c]); + else + CPUTS(" --"); + } + CPUTS("]\n"); } -static int is_scanning_enabled(void) +/** + * Simulate a keypress. + * + * @param row Row of key + * @param col Column of key + * @param pressed Non-zero if pressed, zero if released + */ +static void simulate_key(int row, int col, int pressed) { - /* Scan only if enabled AND lid is open. */ - return enable_scanning && switch_get_lid_open(); + if ((simulated_key[col] & (1 << row)) == ((pressed ? 1 : 0) << row)) + return; /* No change */ + + simulated_key[col] ^= (1 << row); + + print_state(simulated_key, "simulated "); + + /* Wake the task to handle changes in simulated keys */ + task_wake(TASK_ID_KEYSCAN); } /** @@ -107,19 +146,29 @@ static int read_matrix(uint8_t *state) int pressed = 0; for (c = 0; c < KEYBOARD_COLS; c++) { - /* Stop if scanning becomes disabled */ + /* + * Stop if scanning becomes disabled. Check enable_cscanning + * instead of is_scanning_enabled() so that we can scan the + * matrix at boot time before the lid switch is readable. + */ if (!enable_scanning) break; /* Select column, then wait a bit for it to settle */ keyboard_raw_drive_column(c); - udelay(COLUMN_CHARGE_US); + udelay(config.output_settle_us); /* Read the row state */ r = keyboard_raw_read_rows(); - /* Mask off keys that don't exist so they never show - * as pressed */ - r &= actual_key_mask[c]; + /* Mask off keys that don't exist on the actual keyboard */ + r &= config.actual_key_mask[c]; + /* Add in simulated keypresses */ + r |= simulated_key[c]; + +#ifdef CONFIG_KEYBOARD_TEST + /* Use simulated keyscan sequence instead if testing active */ + r = keyscan_seq_get_scan(c, r); +#endif state[c] = r; pressed |= r; @@ -131,31 +180,13 @@ static int read_matrix(uint8_t *state) } /** - * Print the keyboard state. - * - * @param state State array to print - * @param msg Description of state - */ -static void print_state(const uint8_t *state, const char *msg) -{ - int c; - - CPRINTF("[%T KB %s:", msg); - for (c = 0; c < KEYBOARD_COLS; c++) { - if (state[c]) - CPRINTF(" %02x", state[c]); - else - CPUTS(" --"); - } - CPUTS("]\n"); -} - -/** * Check special runtime key combinations. * * @param state Keyboard state to use when checking keys. + * + * @return 1 if a special key was pressed, 0 if not */ -static void check_runtime_keys(const uint8_t *state) +static int check_runtime_keys(const uint8_t *state) { int num_press = 0; int c; @@ -165,10 +196,11 @@ static void check_runtime_keys(const uint8_t *state) * key NOT on the same col as alt or volume up ) */ if (state[KEYBOARD_COL_VOL_UP] != KEYBOARD_MASK_VOL_UP) - return; + return 0; + if (state[KEYBOARD_COL_RIGHT_ALT] != KEYBOARD_MASK_RIGHT_ALT && state[KEYBOARD_COL_LEFT_ALT] != KEYBOARD_MASK_LEFT_ALT) - return; + return 0; /* * Count number of columns with keys pressed. We know two columns are @@ -179,24 +211,34 @@ static void check_runtime_keys(const uint8_t *state) if (state[c]) num_press++; } + if (num_press != 3) - return; + return 0; /* Check individual keys */ if (state[KEYBOARD_COL_KEY_R] == KEYBOARD_MASK_KEY_R) { /* R = reboot */ CPRINTF("[%T KB warm reboot]\n"); + keyboard_clear_buffer(); chipset_reset(0); + return 1; } else if (state[KEYBOARD_COL_KEY_H] == KEYBOARD_MASK_KEY_H) { /* H = hibernate */ CPRINTF("[%T KB hibernate]\n"); system_hibernate(0, 0); + return 1; } + + return 0; } /** * Check for ghosting in the keyboard state. * + * Assumes that the state has already been masked with the actual key mask, so + * that coords which don't correspond with actual keys don't trigger ghosting + * detection. + * * @param state Keyboard state to check. * * @return 1 if ghosting detected, else 0. @@ -262,6 +304,7 @@ static int check_keys_changed(uint8_t *state) /* Check for changes between previous scan and this one */ for (c = 0; c < KEYBOARD_COLS; c++) { int diff = new_state[c] ^ prev_state[c]; + if (!diff) continue; @@ -277,6 +320,7 @@ static int check_keys_changed(uint8_t *state) /* Check for keys which are done debouncing */ for (c = 0; c < KEYBOARD_COLS; c++) { int debc = debouncing[c]; + if (!debc) continue; @@ -288,7 +332,8 @@ static int check_keys_changed(uint8_t *state) if (!(debc & mask)) continue; /* Not debouncing this key */ if (tnow - scan_time[scan_edge_index[c][i]] < - (new_mask ? DEBOUNCE_DOWN_US : DEBOUNCE_UP_US)) + (new_mask ? config.debounce_down_us : + config.debounce_up_us)) continue; /* Not done debouncing */ debouncing[c] &= ~mask; @@ -300,13 +345,21 @@ static int check_keys_changed(uint8_t *state) state[c] ^= mask; any_change = 1; +#ifdef CONFIG_KEYBOARD_PROTOCOL_8042 /* Inform keyboard module if scanning is enabled */ if (is_scanning_enabled()) keyboard_state_changed(i, c, new_mask ? 1 : 0); +#endif } } if (any_change) { + +#ifdef CONFIG_KEYBOARD_SUPPRESS_NOISE + /* Suppress keyboard noise */ + keyboard_suppress_noise(); +#endif + if (print_state_changes) print_state(state, "state"); @@ -321,7 +374,13 @@ static int check_keys_changed(uint8_t *state) CPRINTF("\n"); #endif - check_runtime_keys(state); + /* Swallow special keys */ + if (check_runtime_keys(state)) + return 0; + +#ifdef CONFIG_KEYBOARD_PROTOCOL_MKBP + keyboard_fifo_add(state); +#endif } return any_pressed; @@ -360,7 +419,7 @@ static int check_key(const uint8_t *state, int index, int mask) * key combination is down or this isn't the right type of boot to look at * boot keys. */ -static enum boot_key keyboard_scan_check_boot_key(const uint8_t *state) +static enum boot_key check_boot_key(const uint8_t *state) { const struct boot_key_entry *k = boot_key_list; int i; @@ -389,11 +448,24 @@ static enum boot_key keyboard_scan_check_boot_key(const uint8_t *state) return BOOT_KEY_OTHER; } +/*****************************************************************************/ +/* Interface */ + +struct keyboard_scan_config *keyboard_scan_get_config(void) +{ + return &config; +} + enum boot_key keyboard_scan_get_boot_key(void) { return boot_key_value; } +const uint8_t *keyboard_scan_get_state(void) +{ + return debounced_state; +} + void keyboard_scan_init(void) { /* Configure GPIO */ @@ -407,7 +479,7 @@ void keyboard_scan_init(void) memcpy(prev_state, debounced_state, sizeof(prev_state)); /* Check for keys held down at boot */ - boot_key_value = keyboard_scan_check_boot_key(debounced_state); + boot_key_value = check_boot_key(debounced_state); /* Trigger event if recovery key was pressed */ if (boot_key_value == BOOT_KEY_ESC) @@ -416,7 +488,8 @@ void keyboard_scan_init(void) void keyboard_scan_task(void) { - int key_press_timer = 0; + timestamp_t poll_deadline, start; + int wait_time; print_state(debounced_state, "init state"); @@ -424,7 +497,10 @@ void keyboard_scan_task(void) while (1) { /* Enable all outputs */ - enable_interrupt(); + CPRINTF("[%T KB wait]\n"); + if (is_scanning_enabled()) + keyboard_raw_drive_column(KEYBOARD_COLUMN_ALL); + keyboard_raw_enable_interrupt(1); /* Wait for scanning enabled and key pressed. */ do { @@ -438,21 +514,31 @@ void keyboard_scan_task(void) task_wait_event(-1); } while (!is_scanning_enabled()); - enter_polling_mode(); + /* Enter polling mode */ + CPRINTF("[%T KB poll]\n"); + keyboard_raw_enable_interrupt(0); + keyboard_raw_drive_column(KEYBOARD_COLUMN_NONE); + /* Busy polling keyboard state. */ while (is_scanning_enabled()) { + start = get_time(); + /* Check for keys down */ if (check_keys_changed(debounced_state)) { - key_press_timer = 0; - } else if (++key_press_timer >= - (POLLING_MODE_TIMEOUT / SCAN_LOOP_DELAY)) { - /* Stop polling */ - key_press_timer = 0; + poll_deadline.val = start.val + + config.poll_timeout_us; + } else if (timestamp_expired(poll_deadline, &start)) { break; } /* Delay between scans */ - usleep(SCAN_LOOP_DELAY); + wait_time = config.scan_period_us - + (get_time().val - start.val); + + if (wait_time < config.min_post_scan_delay_us) + wait_time = config.min_post_scan_delay_us; + + usleep(wait_time); } } } @@ -475,6 +561,28 @@ void keyboard_scan_enable(int enable) } /*****************************************************************************/ +/* Host commands */ + +static int mkbp_command_simulate_key(struct host_cmd_handler_args *args) +{ + const struct ec_params_mkbp_simulate_key *p = args->params; + + /* Only available on unlocked systems */ + if (system_is_locked()) + return EC_RES_ACCESS_DENIED; + + if (p->col >= KEYBOARD_COLS || p->row >= KEYBOARD_ROWS) + return EC_RES_INVALID_PARAM; + + simulate_key(p->row, p->col, p->pressed); + + return EC_RES_SUCCESS; +} +DECLARE_HOST_COMMAND(EC_CMD_MKBP_SIMULATE_KEY, + mkbp_command_simulate_key, + EC_VER_MASK(0)); + +/*****************************************************************************/ /* Console commands */ static int command_ksstate(int argc, char **argv) @@ -500,3 +608,43 @@ DECLARE_CONSOLE_COMMAND(ksstate, command_ksstate, "ksstate [on | off]", "Show or toggle printing keyboard scan state", NULL); + +static int command_keyboard_press(int argc, char **argv) +{ + if (argc == 1) { + int i, j; + + ccputs("Simulated keys:\n"); + for (i = 0; i < KEYBOARD_COLS; ++i) { + if (simulated_key[i] == 0) + continue; + for (j = 0; j < KEYBOARD_ROWS; ++j) + if (simulated_key[i] & (1 << j)) + ccprintf("\t%d %d\n", i, j); + } + + } else if (argc == 4) { + int r, c, p; + char *e; + + c = strtoi(argv[1], &e, 0); + if (*e || c < 0 || c >= KEYBOARD_COLS) + return EC_ERROR_PARAM1; + + r = strtoi(argv[2], &e, 0); + if (*e || r < 0 || r >= KEYBOARD_ROWS) + return EC_ERROR_PARAM2; + + p = strtoi(argv[3], &e, 0); + if (*e || p < 0 || p > 1) + return EC_ERROR_PARAM3; + + simulate_key(r, c, p); + } + + return EC_SUCCESS; +} +DECLARE_CONSOLE_COMMAND(kbpress, command_keyboard_press, + "[col] [row] [0 | 1]", + "Simulate keypress", + NULL); diff --git a/include/keyboard_scan.h b/include/keyboard_scan.h index 04a21838bb..e775477421 100644 --- a/include/keyboard_scan.h +++ b/include/keyboard_scan.h @@ -9,12 +9,40 @@ #define __CROS_EC_KEYBOARD_SCAN_H #include "common.h" +#include "keyboard_config.h" + +struct keyboard_scan_config { + /* Delay between setting up output and waiting for it to settle */ + uint16_t output_settle_us; + /* Times for debouncing key-down and key-up */ + uint16_t debounce_down_us; + uint16_t debounce_up_us; + /* Time between start of scans when in polling mode */ + uint16_t scan_period_us; + /* + * Minimum time between end of one scan and start of the next one. + * This ensures keyboard scanning doesn't starve the rest of the system + * if the scan period is set too short, or if other higher-priority + * system activity is starving the keyboard scan task too. + */ + uint16_t min_post_scan_delay_us; + + /* Revert to interrupt mode after no keyboard activity for this long */ + uint32_t poll_timeout_us; + /* Mask with 1 bits only for keys that actually exist */ + uint8_t actual_key_mask[KEYBOARD_COLS]; +}; /** * Initializes the module. */ void keyboard_scan_init(void); +/** + * Return a pointer to the keyboard scan config. + */ +struct keyboard_scan_config *keyboard_scan_get_config(void); + /* Key held down at keyboard-controlled reset boot time. */ enum boot_key { BOOT_KEY_NONE, /* No keys other than keyboard-controlled reset keys */ @@ -31,6 +59,12 @@ enum boot_key { enum boot_key keyboard_scan_get_boot_key(void); /** + * Return a pointer to the current debounced keyboard matrix state, which is + * KEYBOARD_COLS bytes long. + */ +const uint8_t *keyboard_scan_get_state(void); + +/** * Enables/disables keyboard matrix scan. */ void keyboard_scan_enable(int enable); |