mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-12-28 02:35:28 +00:00
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>
This commit is contained in:
committed by
ChromeBot
parent
4311bc5ccf
commit
fe3ccdf70a
@@ -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
|
||||
|
||||
@@ -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;
|
||||
@@ -939,30 +887,6 @@ DECLARE_CONSOLE_COMMAND(kbd, command_keyboard,
|
||||
"Print or toggle keyboard info",
|
||||
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 */
|
||||
|
||||
|
||||
@@ -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 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 CPUTS(outstr) cputs(CC_KEYBOARD, outstr)
|
||||
#define CPRINTF(format, args...) cprintf(CC_KEYBOARD, format, ## args)
|
||||
|
||||
#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)
|
||||
|
||||
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)
|
||||
/*****************************************************************************/
|
||||
/* Interface */
|
||||
|
||||
void keyboard_clear_buffer(void)
|
||||
{
|
||||
int num_press;
|
||||
int c;
|
||||
int i;
|
||||
|
||||
/* Count number of key pressed */
|
||||
for (c = num_press = 0; c < KEYBOARD_COLS; c++) {
|
||||
if (state[c])
|
||||
++num_press;
|
||||
}
|
||||
CPRINTF("clearing keyboard fifo\n");
|
||||
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
/* Print the keyboard state. */
|
||||
static void print_state(const uint8_t *state, const char *msg)
|
||||
int keyboard_fifo_add(const uint8_t *buffp)
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
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[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");
|
||||
}
|
||||
|
||||
return any_pressed;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
}
|
||||
|
||||
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(&scanning_enabled);
|
||||
keyboard_raw_drive_column(KEYBOARD_COLUMN_ALL);
|
||||
keyboard_raw_enable_interrupt(1);
|
||||
mutex_unlock(&scanning_enabled);
|
||||
int ret = EC_SUCCESS;
|
||||
|
||||
/*
|
||||
* if a key was pressed after the last polling,
|
||||
* re-start immediatly polling instead of waiting
|
||||
* for the next interrupt.
|
||||
* If keyboard protocol is not enabled, don't save the state to the
|
||||
* FIFO or trigger an 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 (!(config.flags & EC_MKBP_FLAGS_ENABLE))
|
||||
return EC_SUCCESS;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
keyboard_raw_enable_interrupt(0);
|
||||
keyboard_raw_drive_column(KEYBOARD_COLUMN_NONE);
|
||||
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);
|
||||
|
||||
/* Busy polling keyboard state. */
|
||||
while (1) {
|
||||
int wait_time;
|
||||
kb_fifo_push_done:
|
||||
|
||||
if (!(config.flags & EC_MKBP_FLAGS_ENABLE))
|
||||
break;
|
||||
if (ret == EC_SUCCESS)
|
||||
set_host_interrupt(1);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
static int host_command_mkbp_get_config(struct host_cmd_handler_args *args)
|
||||
{
|
||||
struct ec_response_mkbp_get_config *resp = args->response;
|
||||
|
||||
memcpy(&resp->config, &config, sizeof(config));
|
||||
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));
|
||||
|
||||
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_GET_CONFIG,
|
||||
host_command_mkbp_get_config,
|
||||
EC_VER_MASK(0));
|
||||
|
||||
@@ -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,67 +77,16 @@ static int print_state_changes;
|
||||
|
||||
static int enable_scanning = 1; /* Must init to 1 for scanning at boot */
|
||||
|
||||
static void enable_interrupt(void)
|
||||
{
|
||||
CPRINTF("[%T KB wait]\n");
|
||||
|
||||
if (enable_scanning)
|
||||
keyboard_raw_drive_column(KEYBOARD_COLUMN_ALL);
|
||||
|
||||
keyboard_raw_enable_interrupt(1);
|
||||
}
|
||||
|
||||
static void enter_polling_mode(void)
|
||||
{
|
||||
CPRINTF("[%T KB poll]\n");
|
||||
keyboard_raw_enable_interrupt(0);
|
||||
keyboard_raw_drive_column(KEYBOARD_COLUMN_NONE);
|
||||
}
|
||||
|
||||
static int is_scanning_enabled(void)
|
||||
{
|
||||
/* Scan only if enabled AND lid is open. */
|
||||
return enable_scanning && switch_get_lid_open();
|
||||
}
|
||||
#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
|
||||
|
||||
/**
|
||||
* 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++) {
|
||||
/* Stop if scanning becomes disabled */
|
||||
if (!enable_scanning)
|
||||
break;
|
||||
|
||||
/* Select column, then wait a bit for it to settle */
|
||||
keyboard_raw_drive_column(c);
|
||||
udelay(COLUMN_CHARGE_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];
|
||||
|
||||
state[c] = r;
|
||||
pressed |= r;
|
||||
}
|
||||
|
||||
keyboard_raw_drive_column(KEYBOARD_COLUMN_NONE);
|
||||
|
||||
return pressed ? 1 : 0;
|
||||
return enable_scanning;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -150,12 +109,84 @@ static void print_state(const uint8_t *state, const char *msg)
|
||||
CPUTS("]\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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++) {
|
||||
/*
|
||||
* 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(config.output_settle_us);
|
||||
|
||||
/* Read the row state */
|
||||
r = keyboard_raw_read_rows();
|
||||
/* 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;
|
||||
}
|
||||
|
||||
keyboard_raw_drive_column(KEYBOARD_COLUMN_NONE);
|
||||
|
||||
return pressed ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -474,6 +560,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 */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
@@ -30,6 +58,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.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user