diff options
author | Simon Glass <sjg@chromium.org> | 2012-10-16 16:23:01 -0700 |
---|---|---|
committer | Simon Glass <sjg@chromium.org> | 2012-10-29 14:47:41 -0700 |
commit | 715eee2608289dca520cb7b39cb8c6ab6aea278e (patch) | |
tree | d8441338da4d81d714af9e09fea750dafb76b4fd | |
parent | 5ca5447b4c68bbcc4406c13929406981e577fa42 (diff) | |
download | chrome-ec-715eee2608289dca520cb7b39cb8c6ab6aea278e.tar.gz |
Revert "Revert "stm32: Implement keyboard debouncing""
This reverts commit 0266c613eea5e60833bebb99c4972b7732df8bd3.
This brings the ToT code back into the branch.
BUG=chrome-os-partner:12179
BRANCH=snow
TEST=manual
Use keyboard in U-Boot, browser, gmail and write a paragraph
of text. Try pressing keys quickly. See that things seem to work
correctly.
Change-Id: I7a4f7fb6d4abcc5db0531697971001b00596e428
Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/36080
Reviewed-by: Randall Spangler <rspangler@chromium.org>
Reviewed-by: David Hendricks <dhendrix@chromium.org>
-rw-r--r-- | chip/stm32/keyboard_scan.c | 231 |
1 files changed, 180 insertions, 51 deletions
diff --git a/chip/stm32/keyboard_scan.c b/chip/stm32/keyboard_scan.c index 0dd3877f9d..73e20db356 100644 --- a/chip/stm32/keyboard_scan.c +++ b/chip/stm32/keyboard_scan.c @@ -33,13 +33,22 @@ enum COL_INDEX { /* 0 ~ 12 for the corresponding column */ }; +#define KB_INPUTS 8 + +#define SCAN_TIME_COUNT 32 + /* 15:14, 12:8, 2 */ #define IRQ_MASK 0xdf04 static struct mutex scanning_enabled; -/* The keyboard state from the last read */ -static uint8_t raw_state[KB_OUTPUTS]; +static uint8_t debounced_state[KB_OUTPUTS]; /* Debounced key matrix */ +static uint8_t prev_state[KB_OUTPUTS]; /* Matrix from previous scan */ +static uint8_t debouncing[KB_OUTPUTS]; /* 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[KB_OUTPUTS][KB_INPUTS]; /* Key masks for special boot keys */ #define MASK_INDEX_ESC 1 @@ -97,6 +106,8 @@ static struct ec_mkbp_config config = { .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, }; @@ -228,24 +239,62 @@ void enter_polling_mode(void) select_column(COL_TRI_STATE_ALL); } -static int check_warm_reboot_keys(void) +/** + * Check special runtime key combinations. + * + * @param state Keyboard state to use when checking keys. + */ +static void check_runtime_keys(const uint8_t *state) { - if (raw_state[MASK_INDEX_KEYR] == MASK_VALUE_KEYR && - raw_state[MASK_INDEX_VOL_UP] == MASK_VALUE_VOL_UP && - (raw_state[MASK_INDEX_RIGHT_ALT] == MASK_VALUE_RIGHT_ALT || - raw_state[MASK_INDEX_LEFT_ALT] == MASK_VALUE_LEFT_ALT)) - return 1; + int num_press; + int c; - return 0; + /* Count number of key pressed */ + for (c = num_press = 0; c < KB_OUTPUTS; c++) { + if (state[c]) + ++num_press; + } + + if (num_press != 3) + return; + + if (state[MASK_INDEX_KEYR] == MASK_VALUE_KEYR && + state[MASK_INDEX_VOL_UP] == MASK_VALUE_VOL_UP && + (state[MASK_INDEX_RIGHT_ALT] == MASK_VALUE_RIGHT_ALT || + state[MASK_INDEX_LEFT_ALT] == MASK_VALUE_LEFT_ALT)) + system_warm_reboot(); +} + +/* Print the keyboard state. */ +static void print_state(const uint8_t *state, const char *msg) +{ + int c; + + CPRINTF("[%T KB %s:", msg); + for (c = 0; c < KB_OUTPUTS; c++) { + if (state[c]) + CPRINTF(" %02x", state[c]); + else + CPUTS(" --"); + } + CPUTS("]\n"); } -/* Returns 1 if any key is still pressed. 0 if no key is pressed. */ -static int check_keys_changed(void) +/** + * 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 KB_OUTPUTS 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 change = 0; - int num_press = 0; + int pressed = 0; for (c = 0; c < KB_OUTPUTS; c++) { uint16_t tmp; @@ -254,6 +303,10 @@ static int check_keys_changed(void) select_column(c); udelay(config.output_settle_us); + /* + * TODO(sjg@chromium.org): This code can be improved by doing + * the job in 3 shift/or operations. + */ r = 0; tmp = STM32_GPIO_IDR(C); /* KB_COL00:04 = PC8:12 */ @@ -284,53 +337,122 @@ static int check_keys_changed(void) #ifdef OR_WITH_CURRENT_STATE_FOR_TESTING /* KLUDGE - or current state in, so we can make sure * all the lines are hooked up */ - r |= raw_state[c]; + r |= state[c]; #endif - /* Check for changes */ - if (r != raw_state[c]) { - raw_state[c] = r; - change = 1; - } + state[c] = r; + pressed |= r; } select_column(COL_TRI_STATE_ALL); - /* Count number of key pressed */ + return pressed ? 1 : 0; +} + +/** + * 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 any_pressed = 0; + int c, i; + int any_change = 0; + uint8_t new_state[KB_OUTPUTS]; + 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 < KB_OUTPUTS; c++) { - if (raw_state[c]) - ++num_press; + int diff = new_state[c] ^ prev_state[c]; + + if (!diff) + continue; + + for (i = 0; i < KB_INPUTS; i++) { + if (diff & (1 << i)) + scan_edge_index[c][i] = scan_time_index; + } + + debouncing[c] |= diff; + prev_state[c] = new_state[c]; } - if (change) { - board_keyboard_suppress_noise(); + /* Check for keys which are done debouncing */ + for (c = 0; c < KB_OUTPUTS; c++) { + int debc = debouncing[c]; + + if (!debc) + continue; + + for (i = 0; i < KB_INPUTS; i++) { + int mask = 1 << i; + int new_mask = new_state[c] & mask; - CPRINTF("[%d keys pressed: ", num_press); - for (c = 0; c < KB_OUTPUTS; c++) { - if (raw_state[c]) - CPRINTF(" %02x", raw_state[c]); - else - CPUTS(" --"); + /* 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; } - CPUTS("]\n"); + } - if (num_press == 3) { - if (check_warm_reboot_keys()) - system_warm_reboot(); + if (any_change) { + board_keyboard_suppress_noise(); + 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 - if (kb_fifo_add(raw_state) == EC_SUCCESS) + check_runtime_keys(state); + + if (kb_fifo_add(state) == EC_SUCCESS) board_interrupt_host(1); else CPRINTF("dropped keystroke\n"); } - return num_press ? 1 : 0; + return any_pressed; } - -/* Returns non-zero if the user has triggered a recovery reset by pushing - * Power + Refresh + ESC. */ -static int check_recovery_key(void) +/* + * 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; @@ -341,7 +463,7 @@ static int check_recovery_key(void) /* cold boot : Power + Refresh were pressed, * check if ESC is also pressed for recovery. */ - if (!(raw_state[MASK_INDEX_ESC] & MASK_VALUE_ESC)) + if (!(state[MASK_INDEX_ESC] & MASK_VALUE_ESC)) return 0; /* Make sure only other allowed keys are pressed. This protects @@ -349,9 +471,9 @@ static int check_recovery_key(void) * your keyboard. Currently, only the requested key and ESC are * allowed. */ for (c = 0; c < KB_OUTPUTS; c++) { - if (raw_state[c] && - (c != MASK_INDEX_ESC || raw_state[c] != MASK_VALUE_ESC) && - (c != MASK_INDEX_REFRESH || raw_state[c] != MASK_VALUE_REFRESH)) + if (state[c] && + (c != MASK_INDEX_ESC || state[c] != MASK_VALUE_ESC) && + (c != MASK_INDEX_REFRESH || state[c] != MASK_VALUE_REFRESH)) return 0; /* Additional disallowed key pressed */ } @@ -369,10 +491,11 @@ int keyboard_scan_init(void) select_column(COL_TRI_STATE_ALL); /* Initialize raw state */ - check_keys_changed(); + read_matrix(debounced_state); + memcpy(prev_state, debounced_state, sizeof(prev_state)); /* is recovery key pressed on cold startup ? */ - check_recovery_key(); + check_recovery_key(debounced_state); return EC_SUCCESS; } @@ -408,7 +531,7 @@ static void scan_keyboard(void) /* Scan immediately, with no delay */ mutex_lock(&scanning_enabled); - keys_changed = check_keys_changed(); + keys_changed = check_keys_changed(debounced_state); mutex_unlock(&scanning_enabled); /* Wait a bit before scanning again */ @@ -437,6 +560,8 @@ void keyboard_scan_task(void) gpio_enable_interrupt(GPIO_KB_IN06); gpio_enable_interrupt(GPIO_KB_IN07); + print_state(debounced_state, "init state"); + while (1) { if (config.flags & EC_MKBP_FLAGS_ENABLE) { scan_keyboard(); @@ -488,7 +613,7 @@ static int keyboard_get_info(struct host_cmd_handler_args *args) { struct ec_response_mkbp_info *r = args->response; - r->rows = 8; + r->rows = KB_INPUTS; r->cols = KB_OUTPUTS; r->switches = 0; @@ -531,12 +656,16 @@ static int command_keyboard_press(int argc, char **argv) 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) - raw_state[c] |= (1 << r); + debounced_state[c] |= (1 << r); else - raw_state[c] &= ~(1 << r); + debounced_state[c] &= ~(1 << r); - if (kb_fifo_add(raw_state) == EC_SUCCESS) + if (kb_fifo_add(debounced_state) == EC_SUCCESS) board_interrupt_host(1); else ccprintf("dropped keystroke\n"); |