diff options
Diffstat (limited to 'common/keyboard_scan.c')
-rw-r--r-- | common/keyboard_scan.c | 1094 |
1 files changed, 0 insertions, 1094 deletions
diff --git a/common/keyboard_scan.c b/common/keyboard_scan.c deleted file mode 100644 index 6584a55d84..0000000000 --- a/common/keyboard_scan.c +++ /dev/null @@ -1,1094 +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. - */ - -/* Keyboard scanner module for Chrome EC */ - -#include "chipset.h" -#include "clock.h" -#include "common.h" -#include "console.h" -#include "ec_commands.h" -#include "hooks.h" -#include "host_command.h" -#include "keyboard_config.h" -#include "keyboard_protocol.h" -#include "keyboard_raw.h" -#include "keyboard_scan.h" -#include "keyboard_test.h" -#include "lid_switch.h" -#include "switch.h" -#include "system.h" -#include "tablet_mode.h" -#include "task.h" -#include "timer.h" -#include "usb_api.h" -#include "util.h" - -/* Console output macros */ -#define CPUTS(outstr) cputs(CC_KEYSCAN, outstr) -#define CPRINTF(format, args...) cprintf(CC_KEYSCAN, format, ## args) -#define CPRINTS(format, args...) cprints(CC_KEYSCAN, format, ## args) - -#ifdef CONFIG_KEYBOARD_DEBUG -#define CPUTS5(outstr) cputs(CC_KEYSCAN, outstr) -#define CPRINTS5(format, args...) cprints(CC_KEYBOARD, format, ## args) -#else -#define CPUTS5(outstr) -#define CPRINTS5(format, args...) -#endif - -#define SCAN_TIME_COUNT 32 /* Number of last scan times to track */ - -/* If we're waiting for a scan to happen, we'll give it this long */ -#define SCAN_TASK_TIMEOUT_US (100 * MSEC) - -#ifndef CONFIG_KEYBOARD_POST_SCAN_CLOCKS -/* - * Default delay in clocks; this was experimentally determined to be long - * enough to avoid watchdog warnings or I2C errors on a typical notebook - * config on STM32. - */ -#define CONFIG_KEYBOARD_POST_SCAN_CLOCKS 16000 -#endif - -__overridable struct keyboard_scan_config keyscan_config = { -#ifdef CONFIG_KEYBOARD_COL2_INVERTED - /* - * CONFIG_KEYBOARD_COL2_INVERTED is defined for passing the column 2 - * to H1 which inverts the signal. The signal passing through H1 - * adds more delay. Need a larger delay value. Otherwise, pressing - * Refresh key will also trigger T key, which is in the next scanning - * column line. See http://b/156007029. - */ - .output_settle_us = 80, -#else - .output_settle_us = 50, -#endif /* CONFIG_KEYBOARD_COL2_INVERTED */ - .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, - .actual_key_mask = { - 0x1c, 0xff, 0xff, 0xff, 0xff, 0xf5, 0xff, - 0xa4, 0xff, 0xfe, 0x55, 0xfa, 0xca /* 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; -}; - -#ifdef CONFIG_KEYBOARD_BOOT_KEYS -static const struct boot_key_entry boot_key_list[] = { - {KEYBOARD_COL_ESC, KEYBOARD_MASK_ESC}, /* Esc */ - {KEYBOARD_COL_DOWN, KEYBOARD_MASK_DOWN}, /* Down-arrow */ - {KEYBOARD_COL_LEFT_SHIFT, KEYBOARD_MASK_LEFT_SHIFT}, /* Left-Shift */ -}; -static uint32_t boot_key_value = BOOT_KEY_NONE; -#endif - -uint8_t keyboard_cols = KEYBOARD_COLS_MAX; - -/* Debounced key matrix */ -static uint8_t __bss_slow debounced_state[KEYBOARD_COLS_MAX]; -/* Mask of keys being debounced */ -static uint8_t __bss_slow debouncing[KEYBOARD_COLS_MAX]; -/* Keys simulated-pressed */ -static uint8_t __bss_slow simulated_key[KEYBOARD_COLS_MAX]; -#ifdef CONFIG_KEYBOARD_LANGUAGE_ID -static uint8_t __bss_slow keyboard_id[KEYBOARD_IDS]; -#endif - -/* Times of last scans */ -static uint32_t __bss_slow scan_time[SCAN_TIME_COUNT]; -/* Current scan_time[] index */ -static int __bss_slow scan_time_index; - -/* Index into scan_time[] when each key started debouncing */ -static uint8_t __bss_slow scan_edge_index[KEYBOARD_COLS_MAX][KEYBOARD_ROWS]; - -/* Minimum delay between keyboard scans based on current clock frequency */ -static uint32_t __bss_slow post_scan_clock_us; - -/* - * 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. - */ -static int __bss_slow print_state_changes; - -/* Must init to 0 for scanning at boot */ -static volatile uint32_t __bss_slow disable_scanning_mask; - -/* Constantly incrementing counter of the number of times we polled */ -static volatile int kbd_polls; - -/* If true, we'll force a keyboard poll */ -static volatile int force_poll; - -static int keyboard_scan_is_enabled(void) -{ - /* NOTE: this is just an instantaneous glimpse of the variable. */ - return !disable_scanning_mask; -} - -void keyboard_scan_enable(int enable, enum kb_scan_disable_masks mask) -{ - /* Access atomically */ - if (enable) { - atomic_clear_bits((uint32_t *)&disable_scanning_mask, mask); - } else { - atomic_or((uint32_t *)&disable_scanning_mask, mask); - clear_typematic_key(); - } - - /* Let the task figure things out */ - task_wake(TASK_ID_KEYSCAN); -} - -/** - * 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("[%pT KB %s:", PRINTF_TIMESTAMP_NOW, msg); - for (c = 0; c < keyboard_cols; c++) { - if (state[c]) - CPRINTF(" %02x", state[c]); - else - CPUTS(" --"); - } - CPUTS("]\n"); -} - -/** - * Ensure that the keyboard has been scanned. - * - * Makes sure that we've fully gone through the keyboard scanning loop at - * least once. - */ -static void ensure_keyboard_scanned(int old_polls) -{ - uint64_t start_time; - - start_time = get_time().val; - - /* - * Ensure we see the poll task run. - * - * Note that the poll task is higher priority than ours so we know that - * while we're running it's not partway through a poll. That means that - * if kbd_polls changes we've gone through a whole cycle. - */ - while ((kbd_polls == old_polls) && - (get_time().val - start_time < SCAN_TASK_TIMEOUT_US)) - usleep(keyscan_config.scan_period_us); -} - -/** - * 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) -{ - int old_polls; - - if ((simulated_key[col] & BIT(row)) == ((pressed ? 1 : 0) << row)) - return; /* No change */ - - simulated_key[col] ^= BIT(row); - - /* Keep track of polls now that we've got keys simulated */ - old_polls = kbd_polls; - - print_state(simulated_key, "simulated "); - - /* Force a poll even though no keys are pressed */ - force_poll = 1; - - /* Wake the task to handle changes in simulated keys */ - task_wake(TASK_ID_KEYSCAN); - - /* - * Make sure that the keyboard task sees the key for long enough. - * That means it needs to have run and for enough time. - */ - ensure_keyboard_scanned(old_polls); - usleep(pressed ? - keyscan_config.debounce_down_us : keyscan_config.debounce_up_us); - ensure_keyboard_scanned(kbd_polls); -} - -/** - * 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_MAX - * long). - * - * @return 1 if at least one key is pressed, else zero. - */ -static int read_matrix(uint8_t *state) -{ - int c; - int pressed = 0; - - /* 1. Read input pins */ - for (c = 0; c < keyboard_cols; c++) { - /* - * Skip if scanning becomes disabled. Clear the state - * to make sure we don't mix new and old states in the - * same array. - * - * Note, scanning is enabled on boot by default. - */ - if (!keyboard_scan_is_enabled()) { - state[c] = 0; - continue; - } - - /* Select column, then wait a bit for it to settle */ - keyboard_raw_drive_column(c); - udelay(keyscan_config.output_settle_us); - - /* Read the row state */ - state[c] = keyboard_raw_read_rows(); - - /* Use simulated keyscan sequence instead if testing active */ - if (IS_ENABLED(CONFIG_KEYBOARD_TEST)) - state[c] = keyscan_seq_get_scan(c, state[c]); - } - - /* 2. Detect transitional ghost */ - for (c = 0; c < keyboard_cols; c++) { - int c2; - - for (c2 = 0; c2 < c; c2++) { - /* - * If two columns shares at least one key but their - * states are different, maybe the state changed between - * two "keyboard_raw_read_rows"s. If this happened, - * update both columns to the union of them. - * - * Note that in theory we need to rescan from col 0 if - * anything is updated, to make sure the newly added - * bits does not introduce more inconsistency. - * Let's ignore this rare case for now. - */ - if ((state[c] & state[c2]) && (state[c] != state[c2])) { - uint8_t merged = state[c] | state[c2]; - - state[c] = state[c2] = merged; - } - } - } - - /* 3. Fix result */ - for (c = 0; c < keyboard_cols; c++) { - /* Add in simulated keypresses */ - state[c] |= simulated_key[c]; - - /* - * Keep track of what keys appear to be pressed. Even if they - * don't exist in the matrix, they'll keep triggering - * interrupts, so we can't leave scanning mode. - */ - pressed |= state[c]; - - /* Mask off keys that don't exist on the actual keyboard */ - state[c] &= keyscan_config.actual_key_mask[c]; - - } - - keyboard_raw_drive_column(KEYBOARD_COLUMN_NONE); - - return pressed ? 1 : 0; -} - -#ifdef CONFIG_KEYBOARD_LANGUAGE_ID -/** - * Read the raw keyboard IDs state. - * - * Used in pre-init, so must not make task-switching-dependent calls; udelay() - * is ok because it's a spin-loop. - * - * @param id Destination for keyboard id (must be KEYBOARD_IDS long). - * - */ -static void read_matrix_id(uint8_t *id) -{ - int c; - - for (c = 0; c < KEYBOARD_IDS; c++) { - /* Select the ID pin, then wait a bit for it to settle. - * Caveat: If a keyboard maker puts ID pins right after scan - * columns, we can't support variable column size with a single - * image. */ - keyboard_raw_drive_column(KEYBOARD_COLS_MAX + c); - udelay(keyscan_config.output_settle_us); - - /* Read the row state */ - id[c] = keyboard_raw_read_rows(); - - CPRINTS("Keyboard ID%u: 0x%02x", c, id[c]); - } - - keyboard_raw_drive_column(KEYBOARD_COLUMN_NONE); -} -#endif - -#ifdef CONFIG_KEYBOARD_RUNTIME_KEYS - -static uint8_t key_vol_up_row = KEYBOARD_DEFAULT_ROW_VOL_UP; -static uint8_t key_vol_up_col = KEYBOARD_DEFAULT_COL_VOL_UP; - -void set_vol_up_key(uint8_t row, uint8_t col) -{ - if (col < KEYBOARD_COLS_MAX && row < KEYBOARD_ROWS) { - key_vol_up_row = row; - key_vol_up_col = col; - } -} - -/** - * 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 = 0; - int c; - - /* - * All runtime key combos are (right or left ) alt + volume up + (some - * key NOT on the same col as alt or volume up ) - */ - if (state[key_vol_up_col] != KEYBOARD_ROW_TO_MASK(key_vol_up_row)) - return 0; - - if (state[KEYBOARD_COL_RIGHT_ALT] != KEYBOARD_MASK_RIGHT_ALT && - state[KEYBOARD_COL_LEFT_ALT] != KEYBOARD_MASK_LEFT_ALT) - return 0; - - /* - * Count number of columns with keys pressed. We know two columns are - * pressed for volume up and alt, so if only one more key is pressed - * there will be exactly 3 non-zero columns. - */ - for (c = 0; c < keyboard_cols; c++) { - if (state[c]) - num_press++; - } - - if (num_press != 3) - return 0; - - /* Check individual keys */ - if (state[KEYBOARD_COL_KEY_R] == KEYBOARD_MASK_KEY_R) { - /* R = reboot */ - CPRINTS("KB warm reboot"); - keyboard_clear_buffer(); - chipset_reset(CHIPSET_RESET_KB_WARM_REBOOT); - return 1; - } else if (state[KEYBOARD_COL_KEY_H] == KEYBOARD_MASK_KEY_H) { - /* H = hibernate */ - CPRINTS("KB hibernate"); - system_enter_hibernate(0, 0); - return 1; - } - - return 0; -} -#endif /* CONFIG_KEYBOARD_RUNTIME_KEYS */ - -/** - * 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. - */ -static int has_ghosting(const uint8_t *state) -{ - int c, c2; - - for (c = 0; c < keyboard_cols; c++) { - if (!state[c]) - continue; - - for (c2 = c + 1; c2 < keyboard_cols; c2++) { - /* - * A little bit of cleverness here. Ghosting happens - * if 2 columns share at least 2 keys. So we OR the - * columns together and then see if more than one bit - * is set. x&(x-1) is non-zero only if x has more than - * one bit set. - */ - uint8_t common = state[c] & state[c2]; - - if (common & (common - 1)) - return 1; - } - } - - return 0; -} - -/* Inform keyboard module if scanning is enabled */ -static void key_state_changed(int row, int col, uint8_t state) -{ - if (!keyboard_scan_is_enabled()) - return; - - /* No-op for protocols that require full keyboard matrix (e.g. MKBP). */ - keyboard_state_changed(row, col, !!(state & BIT(row))); -} - -/** - * 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; - static uint8_t __bss_slow new_state[KEYBOARD_COLS_MAX]; - 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); - - /* Ignore if so many keys are pressed that we're ghosting. */ - if (has_ghosting(new_state)) - return any_pressed; - - /* Check for changes between previous scan and this one */ - for (c = 0; c < keyboard_cols; c++) { - int diff = new_state[c] ^ state[c]; - - /* Clear debouncing flag, if sufficient time has elapsed. */ - for (i = 0; i < KEYBOARD_ROWS && debouncing[c]; i++) { - if (!(debouncing[c] & BIT(i))) - continue; - if (tnow - scan_time[scan_edge_index[c][i]] < - (state[c] ? keyscan_config.debounce_down_us : - keyscan_config.debounce_up_us)) - continue; /* Not done debouncing */ - debouncing[c] &= ~BIT(i); - - if (!IS_ENABLED(CONFIG_KEYBOARD_STRICT_DEBOUNCE)) - continue; - if (!(diff & BIT(i))) - /* Debounced but no difference. */ - continue; - any_change = 1; - key_state_changed(i, c, new_state[c]); - /* - * This makes state[c] == new_state[c] for row i. - * Thus, when diff is calculated below, it won't - * be asserted (for row i). - */ - state[c] ^= diff & BIT(i); - } - - /* Recognize change in state, unless debounce in effect. */ - diff = (new_state[c] ^ state[c]) & ~debouncing[c]; - if (!diff) - continue; - for (i = 0; i < KEYBOARD_ROWS; i++) { - if (!(diff & BIT(i))) - continue; - scan_edge_index[c][i] = scan_time_index; - - if (!IS_ENABLED(CONFIG_KEYBOARD_STRICT_DEBOUNCE)) { - any_change = 1; - key_state_changed(i, c, new_state[c]); - } - } - - /* For any keyboard events just sent, turn on debouncing. */ - debouncing[c] |= diff; - /* - * Note: In order to "remember" what was last reported - * (up or down), the state bits are only updated if the - * edge was not suppressed due to debouncing. - */ - if (!IS_ENABLED(CONFIG_KEYBOARD_STRICT_DEBOUNCE)) - state[c] ^= diff; - } - - if (any_change) { - -#ifdef CONFIG_KEYBOARD_SUPPRESS_NOISE - /* Suppress keyboard noise */ - keyboard_suppress_noise(); -#endif - - if (print_state_changes) - print_state(state, "state"); - -#ifdef CONFIG_KEYBOARD_PRINT_SCAN_TIMES - /* Print delta times from now back to each previous scan */ - CPRINTF("[%pT kb deltaT", PRINTF_TIMESTAMP_NOW); - 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 - -#ifdef CONFIG_KEYBOARD_RUNTIME_KEYS - /* Swallow special keys */ - if (check_runtime_keys(state)) - return 0; -#endif - -#ifdef CONFIG_KEYBOARD_PROTOCOL_MKBP - mkbp_keyboard_add(state); -#endif - } - - kbd_polls++; - - return any_pressed; -} - -static uint8_t keyboard_mask_refresh; -__overridable uint8_t board_keyboard_row_refresh(void) -{ - if (IS_ENABLED(CONFIG_KEYBOARD_REFRESH_ROW3)) - return 3; - else - return 2; -} - -#ifdef CONFIG_KEYBOARD_BOOT_KEYS -/* - * Returns mask of the boot keys that are pressed, with at most the keys used - * for keyboard-controlled reset also pressed. - */ -static uint32_t check_key_list(const uint8_t *state) -{ - uint8_t curr_state[KEYBOARD_COLS_MAX]; - int c; - uint32_t boot_key_mask = BOOT_KEY_NONE; - const struct boot_key_entry *k; - - /* Make copy of current debounced state. */ - memcpy(curr_state, state, sizeof(curr_state)); - -#ifdef KEYBOARD_MASK_PWRBTN - /* - * Check if KSI2 or KSI3 is asserted for all columns due to power - * button hold, and ignore it if so. - */ - for (c = 0; c < keyboard_cols; c++) - if ((keyscan_config.actual_key_mask[c] & KEYBOARD_MASK_PWRBTN) - && !(curr_state[c] & KEYBOARD_MASK_PWRBTN)) - break; - - if (c == keyboard_cols) - for (c = 0; c < keyboard_cols; c++) - curr_state[c] &= ~KEYBOARD_MASK_PWRBTN; -#endif - - curr_state[KEYBOARD_COL_REFRESH] &= ~keyboard_mask_refresh; - - /* Update mask with all boot keys that were pressed. */ - k = boot_key_list; - for (c = 0; c < ARRAY_SIZE(boot_key_list); c++, k++) { - if (curr_state[k->mask_index] & k->mask_value) { - boot_key_mask |= BIT(c); - curr_state[k->mask_index] &= ~k->mask_value; - } - } - - /* If any other key was pressed, ignore all boot keys. */ - for (c = 0; c < keyboard_cols; c++) { - if (curr_state[c]) - return BOOT_KEY_NONE; - } - - CPRINTS("KB boot key mask %x", boot_key_mask); - return boot_key_mask; -} - -/** - * Check what boot key is down, if any. - * - * @param state Keyboard state at boot. - * - * @return the key which is down, or BOOT_KEY_NONE if an unrecognized - * key combination is down or this isn't the right type of boot to look at - * boot keys. - */ -static uint32_t check_boot_key(const uint8_t *state) -{ - /* - * If we jumped to this image, ignore boot keys. This prevents - * re-triggering events in RW firmware that were already processed by - * RO firmware. - */ - if (system_jumped_late()) - return BOOT_KEY_NONE; - - /* If reset was not caused by reset pin, refresh must be held down */ - if (!(system_get_reset_flags() & EC_RESET_FLAG_RESET_PIN) && - !(state[KEYBOARD_COL_REFRESH] & keyboard_mask_refresh)) - return BOOT_KEY_NONE; - - return check_key_list(state); -} -#endif - -static void keyboard_freq_change(void) -{ - post_scan_clock_us = (CONFIG_KEYBOARD_POST_SCAN_CLOCKS * 1000) / - (clock_get_freq() / 1000); -} -DECLARE_HOOK(HOOK_FREQ_CHANGE, keyboard_freq_change, HOOK_PRIO_DEFAULT); - -/*****************************************************************************/ -/* Interface */ - -struct keyboard_scan_config *keyboard_scan_get_config(void) -{ - return &keyscan_config; -} - -#ifdef CONFIG_KEYBOARD_BOOT_KEYS -uint32_t keyboard_scan_get_boot_keys(void) -{ - return boot_key_value; -} -#endif - -const uint8_t *keyboard_scan_get_state(void) -{ - return debounced_state; -} - -void keyboard_scan_init(void) -{ - if (IS_ENABLED(CONFIG_KEYBOARD_STRICT_DEBOUNCE) && - keyscan_config.debounce_down_us != keyscan_config.debounce_up_us) { - /* - * Strict debouncer is prone to keypress reordering if debounce - * durations for down and up are not equal. crbug.com/547131 - */ - CPRINTS("KB WARN: Debounce durations not equal"); - } - - /* Configure refresh key matrix */ - keyboard_mask_refresh = KEYBOARD_ROW_TO_MASK( - board_keyboard_row_refresh()); - - /* Configure GPIO */ - keyboard_raw_init(); - - /* Tri-state the columns */ - keyboard_raw_drive_column(KEYBOARD_COLUMN_NONE); - - /* Initialize raw state */ - read_matrix(debounced_state); - -#ifdef CONFIG_KEYBOARD_LANGUAGE_ID - /* Check keyboard ID state */ - read_matrix_id(keyboard_id); -#endif - -#ifdef CONFIG_KEYBOARD_BOOT_KEYS - /* Check for keys held down at boot */ - boot_key_value = check_boot_key(debounced_state); - - /* - * If any key other than Esc or Left_Shift was pressed, do not trigger - * recovery. - */ - if (boot_key_value & ~(BOOT_KEY_ESC | BOOT_KEY_LEFT_SHIFT)) - return; - -#ifdef CONFIG_HOSTCMD_EVENTS - if (boot_key_value & BOOT_KEY_ESC) { - host_set_single_event(EC_HOST_EVENT_KEYBOARD_RECOVERY); - /* - * In recovery mode, we should force clamshell mode in order to - * prevent the keyboard from being disabled unintentionally due - * to unstable accel readings. - * - * You get the same effect if motion sensors or a motion sense - * task are disabled in RO. - */ - if (IS_ENABLED(CONFIG_TABLET_MODE)) - tablet_disable(); - if (boot_key_value & BOOT_KEY_LEFT_SHIFT) - host_set_single_event( - EC_HOST_EVENT_KEYBOARD_RECOVERY_HW_REINIT); - } -#endif -#endif /* CONFIG_KEYBOARD_BOOT_KEYS */ -} - -void keyboard_scan_task(void *u) -{ - timestamp_t poll_deadline, start; - int wait_time; - uint32_t local_disable_scanning = 0; - - print_state(debounced_state, "init state"); - - keyboard_raw_task_start(); - - /* Set initial clock frequency-based minimum delay between scans */ - keyboard_freq_change(); - - while (1) { - /* Enable all outputs */ - CPRINTS5("KB wait"); - - keyboard_raw_enable_interrupt(1); - - /* Wait for scanning enabled and key pressed. */ - while (1) { - uint32_t new_disable_scanning; - - /* Read it once to get consistent glimpse */ - new_disable_scanning = disable_scanning_mask; - - if (local_disable_scanning != new_disable_scanning) - CPRINTS("KB disable_scanning_mask changed: " - "0x%08x", new_disable_scanning); - - if (!new_disable_scanning) { - /* Enabled now */ - keyboard_raw_drive_column(KEYBOARD_COLUMN_ALL); - } else if (!local_disable_scanning) { - /* - * Scanning isn't enabled but it was last time - * we looked. - * - * No race here even though we're basing on a - * glimpse of disable_scanning_mask since if - * someone changes disable_scanning_mask they - * are guaranteed to call task_wake() on us - * afterward so we'll run the loop again. - */ - keyboard_raw_drive_column(KEYBOARD_COLUMN_NONE); - keyboard_clear_buffer(); - } - - local_disable_scanning = new_disable_scanning; - - /* - * Done waiting if scanning is enabled and a key is - * already pressed. This prevents a race between the - * user pressing a key and enable_interrupt() - * starting to pay attention to edges. - */ - if (!local_disable_scanning && - (keyboard_raw_read_rows() || force_poll)) - break; - else - task_wait_event(-1); - } - - /* We're about to poll, so any existing forces are fulfilled */ - force_poll = 0; - - /* Enter polling mode */ - CPRINTS5("KB poll"); - keyboard_raw_enable_interrupt(0); - keyboard_raw_drive_column(KEYBOARD_COLUMN_NONE); - - /* Busy polling keyboard state. */ - while (keyboard_scan_is_enabled()) { - start = get_time(); - - /* Check for keys down */ - if (check_keys_changed(debounced_state)) { - poll_deadline.val = start.val - + keyscan_config.poll_timeout_us; - } else if (timestamp_expired(poll_deadline, &start)) { - break; - } - - /* Delay between scans */ - wait_time = keyscan_config.scan_period_us - - (get_time().val - start.val); - - if (wait_time < keyscan_config.min_post_scan_delay_us) - wait_time = - keyscan_config.min_post_scan_delay_us; - - if (wait_time < post_scan_clock_us) - wait_time = post_scan_clock_us; - - usleep(wait_time); - } - } -} - -#ifdef CONFIG_LID_SWITCH - -static void keyboard_lid_change(void) -{ - if (lid_is_open()) - keyboard_scan_enable(1, KB_SCAN_DISABLE_LID_CLOSED); - else - keyboard_scan_enable(0, KB_SCAN_DISABLE_LID_CLOSED); -} -DECLARE_HOOK(HOOK_LID_CHANGE, keyboard_lid_change, HOOK_PRIO_DEFAULT); -DECLARE_HOOK(HOOK_INIT, keyboard_lid_change, HOOK_PRIO_INIT_LID + 1); - -#endif - -#ifdef CONFIG_USB_SUSPEND -static void keyboard_usb_pm_change(void) -{ - /* - * If USB interface is suspended, and host is not asking us to do remote - * wakeup, we can turn off the key scanning. - */ - if (usb_is_suspended() && !usb_is_remote_wakeup_enabled()) - keyboard_scan_enable(0, KB_SCAN_DISABLE_USB_SUSPENDED); - else - keyboard_scan_enable(1, KB_SCAN_DISABLE_USB_SUSPENDED); -} -DECLARE_HOOK(HOOK_USB_PM_CHANGE, keyboard_usb_pm_change, HOOK_PRIO_DEFAULT); -#endif - -/*****************************************************************************/ -/* Host commands */ - -static enum ec_status -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)); - -#ifdef CONFIG_KEYBOARD_FACTORY_TEST - -/* Run keyboard factory testing, scan out KSO/KSI if any shorted. */ -int keyboard_factory_test_scan(void) -{ - int i, j, flags; - uint16_t shorted = 0; - int port, id; - - /* Disable keyboard scan while testing */ - keyboard_scan_enable(0, KB_SCAN_DISABLE_LID_CLOSED); - flags = gpio_get_default_flags(GPIO_KBD_KSO2); - - /* Set all of KSO/KSI pins to internal pull-up and input */ - for (i = 0; i < keyboard_factory_scan_pins_used; i++) { - - if (keyboard_factory_scan_pins[i][0] < 0) - continue; - - port = keyboard_factory_scan_pins[i][0]; - id = keyboard_factory_scan_pins[i][1]; - - gpio_set_alternate_function(port, 1 << id, - GPIO_ALT_FUNC_NONE); - gpio_set_flags_by_mask(port, 1 << id, - GPIO_INPUT | GPIO_PULL_UP); - } - - /* - * Set start pin to output low, then check other pins - * going to low level, it indicate the two pins are shorted. - */ - for (i = 0; i < keyboard_factory_scan_pins_used; i++) { - - if (keyboard_factory_scan_pins[i][0] < 0) - continue; - - port = keyboard_factory_scan_pins[i][0]; - id = keyboard_factory_scan_pins[i][1]; - - gpio_set_flags_by_mask(port, 1 << id, GPIO_OUT_LOW); - - for (j = 0; j < i; j++) { - - if (keyboard_factory_scan_pins[j][0] < 0) - continue; - - if (keyboard_raw_is_input_low( - keyboard_factory_scan_pins[j][0], - keyboard_factory_scan_pins[j][1])) { - shorted = i << 8 | j; - goto done; - } - } - gpio_set_flags_by_mask(port, 1 << id, - GPIO_INPUT | GPIO_PULL_UP); - } -done: - gpio_config_module(MODULE_KEYBOARD_SCAN, 1); - gpio_set_flags(GPIO_KBD_KSO2, flags); - keyboard_scan_enable(1, KB_SCAN_DISABLE_LID_CLOSED); - - return shorted; -} - -static enum ec_status keyboard_factory_test(struct host_cmd_handler_args *args) -{ - struct ec_response_keyboard_factory_test *r = args->response; - - /* Only available on unlocked systems */ - if (system_is_locked()) - return EC_RES_ACCESS_DENIED; - - if (keyboard_factory_scan_pins_used == 0) - return EC_RES_INVALID_COMMAND; - - r->shorted = keyboard_factory_test_scan(); - - args->response_size = sizeof(*r); - - return EC_RES_SUCCESS; -} - -DECLARE_HOST_COMMAND(EC_CMD_KEYBOARD_FACTORY_TEST, - keyboard_factory_test, - EC_VER_MASK(0)); -#endif - -#ifdef CONFIG_KEYBOARD_LANGUAGE_ID -int keyboard_get_keyboard_id(void) -{ - int c; - uint32_t id = 0; - - BUILD_ASSERT(sizeof(id) >= KEYBOARD_IDS); - - for (c = 0; c < KEYBOARD_IDS; c++) { - /* Check ID ghosting if more than one bit in any KSIs was set */ - if (keyboard_id[c] & (keyboard_id[c] - 1)) - /* ID ghosting is found */ - return KEYBOARD_ID_UNREADABLE; - else - id |= keyboard_id[c] << (c * 8); - } - return id; -} -#endif - -/*****************************************************************************/ -/* Console commands */ -#ifdef CONFIG_CMD_KEYBOARD -static int command_ksstate(int argc, char **argv) -{ - if (argc > 1) { - if (!strcasecmp(argv[1], "force")) { - print_state_changes = 1; - keyboard_scan_enable(1, -1); - } else if (!parse_bool(argv[1], &print_state_changes)) { - return EC_ERROR_PARAM1; - } - } - - print_state(debounced_state, "debounced "); - print_state(debouncing, "debouncing"); - - ccprintf("Keyboard scan disable mask: 0x%08x\n", - disable_scanning_mask); - ccprintf("Keyboard scan state printing %s\n", - print_state_changes ? "on" : "off"); - return EC_SUCCESS; -} -DECLARE_CONSOLE_COMMAND(ksstate, command_ksstate, - "ksstate [on | off | force]", - "Show or toggle printing keyboard scan state"); - -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] & BIT(j)) - ccprintf("\t%d %d\n", i, j); - } - - } else if (argc == 3 || 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; - - if (argc == 3) { - /* Simulate a press and release */ - simulate_key(r, c, 1); - simulate_key(r, c, 0); - } else { - 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"); -#endif |