summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/build.mk6
-rw-r--r--common/console_output.c1
-rw-r--r--common/gaia_power.c9
-rw-r--r--common/i8042.c6
-rw-r--r--common/keyboard_8042.c (renamed from common/keyboard.c)32
-rw-r--r--common/keyboard_mkbp.c656
-rw-r--r--common/keyboard_scan.c502
-rw-r--r--common/main.c3
8 files changed, 1184 insertions, 31 deletions
diff --git a/common/build.mk b/common/build.mk
index 99865dcfc4..1136015b7d 100644
--- a/common/build.mk
+++ b/common/build.mk
@@ -21,6 +21,10 @@ 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
@@ -30,7 +34,7 @@ common-$(CONFIG_SMART_BATTERY)+=smart_battery.o smart_battery_stub.o
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.o
+common-$(CONFIG_TASK_I8042CMD)+=i8042.o keyboard_8042.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/console_output.c b/common/console_output.c
index 12e35f55a3..b7985883cc 100644
--- a/common/console_output.c
+++ b/common/console_output.c
@@ -26,7 +26,6 @@ static const char *channel_names[CC_CHANNEL_COUNT] = {
"gpio",
"hostcmd",
"i2c",
- "i8042",
"keyboard",
"keyscan",
"lightbar",
diff --git a/common/gaia_power.c b/common/gaia_power.c
index 7f53dd800e..8252240aae 100644
--- a/common/gaia_power.c
+++ b/common/gaia_power.c
@@ -174,7 +174,7 @@ static int check_for_power_off_event(void)
/* Dis/Enable keyboard scanning when the power button state changes */
if (!pressed || pressed != power_button_was_pressed)
- keyboard_enable_scanning(!pressed);
+ keyboard_scan_enable(!pressed);
now = get_time();
@@ -263,13 +263,6 @@ static int gaia_power_init(void)
auto_power_on = 1;
}
- /* Auto power on if the recovery combination was pressed */
- if (keyboard_scan_recovery_pressed()) {
- CPRINTF("[%T auto_power_on is set due to "
- "keyboard_scan_recovery_pressed() ...]\n");
- auto_power_on = 1;
- }
-
return EC_SUCCESS;
}
diff --git a/common/i8042.c b/common/i8042.c
index 5c3b1bbf06..683bfbe1b1 100644
--- a/common/i8042.c
+++ b/common/i8042.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+/* Copyright (c) 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.
*
@@ -11,7 +11,7 @@
#include "config.h"
#include "console.h"
#include "i8042.h"
-#include "keyboard.h"
+#include "keyboard_protocol.h"
#include "lpc.h"
#include "queue.h"
#include "task.h"
@@ -19,7 +19,7 @@
#include "util.h"
/* Console output macros */
-#define CPRINTF(format, args...) cprintf(CC_I8042, format, ## args)
+#define CPRINTF(format, args...) cprintf(CC_KEYBOARD, format, ## args)
static int i8042_irq_enabled;
diff --git a/common/keyboard.c b/common/keyboard_8042.c
index feda6d884a..1a991d7075 100644
--- a/common/keyboard.c
+++ b/common/keyboard_8042.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+/* Copyright (c) 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.
*
@@ -12,8 +12,8 @@
#include "host_command.h"
#include "i8042.h"
#include "i8042_protocol.h"
-#include "keyboard.h"
#include "keyboard_config.h"
+#include "keyboard_protocol.h"
#include "lightbar.h"
#include "lpc.h"
#include "registers.h"
@@ -54,10 +54,10 @@ enum scancode_set_list {
/*
* i8042 global settings.
*/
-static int keyboard_enabled = 0; /* default the keyboard is disabled. */
-static int keystroke_enabled; /* output keystrokes */
+static int keyboard_enabled; /* default the keyboard is disabled. */
+static int keystroke_enabled; /* output keystrokes */
static uint8_t resend_command[MAX_SCAN_CODE_LEN];
-static uint8_t resend_command_len = 0;
+static uint8_t resend_command_len;
static uint8_t controller_ram_address;
static uint8_t controller_ram[0x20] = {
/* the so called "command byte" */
@@ -65,7 +65,7 @@ static uint8_t controller_ram[0x20] = {
/* 0x01 - 0x1f are controller RAM */
};
static uint8_t A20_status;
-static int power_button_pressed = 0;
+static int power_button_pressed;
static void keyboard_special(uint16_t k);
/*
@@ -93,7 +93,7 @@ static uint8_t typematic_value_from_host = DEFAULT_TYPEMATIC_VALUE;
static int refill_first_delay = DEFAULT_FIRST_DELAY; /* unit: ms */
static int refill_inter_delay = DEFAULT_INTER_DELAY; /* unit: ms */
static int typematic_delay; /* unit: us */
-static int typematic_len = 0; /* length of typematic_scan_code */
+static int typematic_len; /* length of typematic_scan_code */
static uint8_t typematic_scan_code[MAX_SCAN_CODE_LEN];
@@ -257,7 +257,7 @@ static void reset_rate_and_delay(void)
}
-void keyboard_clear_underlying_buffer(void)
+void keyboard_clear_buffer(void)
{
i8042_flush_buffer();
}
@@ -493,25 +493,25 @@ int handle_keyboard_data(uint8_t data, uint8_t *output)
case I8042_CMD_ENABLE:
output[out_len++] = I8042_RET_ACK;
keystroke_enable(1);
- keyboard_clear_underlying_buffer();
+ keyboard_clear_buffer();
break;
case I8042_CMD_RESET_DIS:
output[out_len++] = I8042_RET_ACK;
keystroke_enable(0);
reset_rate_and_delay();
- keyboard_clear_underlying_buffer();
+ keyboard_clear_buffer();
break;
case I8042_CMD_RESET_DEF:
output[out_len++] = I8042_RET_ACK;
reset_rate_and_delay();
- keyboard_clear_underlying_buffer();
+ keyboard_clear_buffer();
break;
case I8042_CMD_RESET_BAT:
reset_rate_and_delay();
- keyboard_clear_underlying_buffer();
+ keyboard_clear_buffer();
output[out_len++] = I8042_RET_ACK;
output[out_len++] = I8042_RET_BAT;
output[out_len++] = I8042_RET_BAT;
@@ -644,7 +644,7 @@ int handle_keyboard_command(uint8_t command, uint8_t *output)
} else {
CPRINTF("[%T KB unsupported cmd: 0x%02x]\n", command);
reset_rate_and_delay();
- keyboard_clear_underlying_buffer();
+ keyboard_clear_buffer();
output[out_len++] = I8042_RET_NAK;
data_port_state = STATE_NORMAL;
}
@@ -658,7 +658,7 @@ int handle_keyboard_command(uint8_t command, uint8_t *output)
/* U U D D L R L R b a */
static void keyboard_special(uint16_t k)
{
- static uint8_t s = 0;
+ static uint8_t s;
static const uint16_t a[] = {0xe048, 0xe048, 0xe050, 0xe050, 0xe04b,
0xe04d, 0xe04b, 0xe04d, 0x0030, 0x001e};
#ifdef CONFIG_TASK_LIGHTBAR
@@ -984,7 +984,7 @@ static void keyboard_preserve_state(void)
state.ctlram = controller_ram[0];
system_add_jump_tag(KB_SYSJUMP_TAG, KB_HOOK_VERSION,
- sizeof(state), &state);
+ sizeof(state), &state);
}
DECLARE_HOOK(HOOK_SYSJUMP, keyboard_preserve_state, HOOK_PRIO_DEFAULT);
@@ -997,7 +997,7 @@ static void keyboard_restore_state(void)
int version, size;
prev = (const struct kb_state *)system_get_jump_tag(KB_SYSJUMP_TAG,
- &version, &size);
+ &version, &size);
if (prev && version == KB_HOOK_VERSION && size == sizeof(*prev)) {
/* Coming back from a sysjump, so restore settings. */
scancode_set = prev->codeset;
diff --git a/common/keyboard_mkbp.c b/common/keyboard_mkbp.c
new file mode 100644
index 0000000000..9d2613b187
--- /dev/null
+++ b/common/keyboard_mkbp.c
@@ -0,0 +1,656 @@
+/* Copyright (c) 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 STM32
+ */
+
+#include "atomic.h"
+#include "chipset.h"
+#include "console.h"
+#include "gpio.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 "system.h"
+#include "task.h"
+#include "timer.h"
+#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 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_entries; /* number of existing entries */
+static uint8_t kb_fifo[KB_FIFO_DEPTH][KEYBOARD_COLS];
+
+/*
+ * Our configuration. The debounce parameters are not yet supported.
+ */
+static struct ec_mkbp_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
+ */
+static int kb_fifo_remove(uint8_t *buffp)
+{
+ if (!kb_fifo_entries) {
+ /* no entry remaining in FIFO : return last known state */
+ int last = (kb_fifo_start + KB_FIFO_DEPTH - 1) % KB_FIFO_DEPTH;
+ memcpy(buffp, kb_fifo[last], KEYBOARD_COLS);
+
+ /*
+ * Bail out without changing any FIFO indices and let the
+ * caller know something strange happened. The buffer will
+ * will contain the last known state of the keyboard.
+ */
+ return EC_ERROR_UNKNOWN;
+ }
+ memcpy(buffp, kb_fifo[kb_fifo_start], KEYBOARD_COLS);
+
+ kb_fifo_start = (kb_fifo_start + 1) % KB_FIFO_DEPTH;
+
+ atomic_sub(&kb_fifo_entries, 1);
+
+ return EC_SUCCESS;
+}
+
+/**
+ * Assert host keyboard interrupt line.
+ */
+static void set_host_interrupt(int active)
+{
+ /* interrupt host by using active low EC_INT signal */
+ 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;
+}
+
+/* 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 < 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);
+
+ /*
+ * 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
+ }
+
+ 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);
+ }
+}
+
+void keyboard_scan_task(void)
+{
+ print_state(debounced_state, "init state");
+
+ keyboard_raw_task_start();
+
+ while (1) {
+ if (config.flags & EC_MKBP_FLAGS_ENABLE) {
+ scan_keyboard();
+ } else {
+ keyboard_raw_drive_column(KEYBOARD_COLUMN_NONE);
+ task_wait_event(-1);
+ }
+ }
+}
+
+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 */
+}
+
+static int keyboard_get_scan(struct host_cmd_handler_args *args)
+{
+ kb_fifo_remove(args->response);
+ if (!kb_fifo_entries)
+ set_host_interrupt(0);
+
+ args->response_size = KEYBOARD_COLS;
+
+ return EC_RES_SUCCESS;
+}
+DECLARE_HOST_COMMAND(EC_CMD_MKBP_STATE,
+ keyboard_get_scan,
+ EC_VER_MASK(0));
+
+static int keyboard_get_info(struct host_cmd_handler_args *args)
+{
+ struct ec_response_mkbp_info *r = args->response;
+
+ r->rows = KEYBOARD_ROWS;
+ r->cols = KEYBOARD_COLS;
+ r->switches = 0;
+
+ args->response_size = sizeof(*r);
+
+ return EC_RES_SUCCESS;
+}
+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.
+ *
+ * @param src Source config
+ * @param dst Destination config
+ * @param valid_mask Bits representing which fields to copy - each bit is
+ * from enum mkbp_config_valid
+ * @param valid_flags Bit mask controlling flags to copy. Any 1 bit means
+ * that the corresponding bit in src->flags is copied
+ * over to dst->flags
+ */
+static void keyscan_copy_config(const struct ec_mkbp_config *src,
+ struct ec_mkbp_config *dst,
+ uint32_t valid_mask, uint8_t valid_flags)
+{
+ uint8_t new_flags;
+
+ if (valid_mask & EC_MKBP_VALID_SCAN_PERIOD)
+ dst->scan_period_us = src->scan_period_us;
+ if (valid_mask & EC_MKBP_VALID_POLL_TIMEOUT)
+ dst->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 =
+ MAX(src->min_post_scan_delay_us, 200);
+ }
+ if (valid_mask & EC_MKBP_VALID_OUTPUT_SETTLE)
+ dst->output_settle_us = src->output_settle_us;
+ if (valid_mask & EC_MKBP_VALID_DEBOUNCE_DOWN)
+ dst->debounce_down_us = src->debounce_down_us;
+ if (valid_mask & EC_MKBP_VALID_DEBOUNCE_UP)
+ dst->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;
+
+ /*
+ * If we just enabled key scanning, kick the task so that it will
+ * fall out of the task_wait_event() in keyboard_scan_task().
+ */
+ if ((new_flags & EC_MKBP_FLAGS_ENABLE) &&
+ !(dst->flags & EC_MKBP_FLAGS_ENABLE))
+ task_wake(TASK_ID_KEYSCAN);
+}
+
+static int host_command_mkbp_set_config(struct host_cmd_handler_args *args)
+{
+ const struct ec_params_mkbp_set_config *req = args->params;
+
+ keyscan_copy_config(&req->config, &config,
+ config.valid_mask & req->config.valid_mask,
+ config.valid_flags & req->config.valid_flags);
+
+ 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));
+
+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
new file mode 100644
index 0000000000..c01326d956
--- /dev/null
+++ b/common/keyboard_scan.c
@@ -0,0 +1,502 @@
+/* Copyright (c) 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 "common.h"
+#include "console.h"
+#include "host_command.h"
+#include "keyboard_config.h"
+#include "keyboard_protocol.h"
+#include "keyboard_raw.h"
+#include "keyboard_scan.h"
+#include "switch.h"
+#include "system.h"
+#include "task.h"
+#include "timer.h"
+#include "util.h"
+
+/* Console output macros */
+#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 */
+
+/* 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[] = {
+ {0, 0x00}, /* (none) */
+ {KEYBOARD_COL_ESC, KEYBOARD_MASK_ESC}, /* Esc */
+ {KEYBOARD_COL_DOWN, KEYBOARD_MASK_DOWN}, /* Down-arrow */
+};
+
+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];
+
+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.
+ */
+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();
+}
+
+/**
+ * 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;
+}
+
+/**
+ * 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.
+ */
+static void 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[KEYBOARD_COL_VOL_UP] != KEYBOARD_MASK_VOL_UP)
+ return;
+ if (state[KEYBOARD_COL_RIGHT_ALT] != KEYBOARD_MASK_RIGHT_ALT &&
+ state[KEYBOARD_COL_LEFT_ALT] != KEYBOARD_MASK_LEFT_ALT)
+ return;
+
+ /*
+ * 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;
+
+ /* Check individual keys */
+ if (state[KEYBOARD_COL_KEY_R] == KEYBOARD_MASK_KEY_R) {
+ /* R = reboot */
+ CPRINTF("[%T KB warm reboot]\n");
+ chipset_reset(0);
+ } else if (state[KEYBOARD_COL_KEY_H] == KEYBOARD_MASK_KEY_H) {
+ /* H = hibernate */
+ CPRINTF("[%T KB hibernate]\n");
+ system_hibernate(0, 0);
+ }
+}
+
+/**
+ * Check for ghosting in the keyboard state.
+ *
+ * @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;
+}
+
+/**
+ * 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);
+
+ /* Ignore if so many keys are pressed that we're ghosting */
+ /*
+ * TODO: maybe in this case we should reset all the debounce times,
+ * because in the ghosting case we're not paying attention to any of
+ * the keys which aren't 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] ^ 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 ? DEBOUNCE_DOWN_US : 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;
+
+ /* Inform keyboard module if scanning is enabled */
+ if (is_scanning_enabled())
+ keyboard_state_changed(i, c, new_mask ? 1 : 0);
+ }
+ }
+
+ if (any_change) {
+ if (print_state_changes)
+ 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
+
+ check_runtime_keys(state);
+ }
+
+ return any_pressed;
+}
+
+/*
+ * Return non-zero if the specified key is pressed, with at most the keys used
+ * for keyboard-controlled reset also pressed.
+ */
+static int check_key(const uint8_t *state, int index, int mask)
+{
+ uint8_t allowed_mask[KEYBOARD_COLS] = {0};
+ int c;
+
+ /* Check for the key */
+ if (mask && !(state[index] & mask))
+ return 0;
+
+ /* Check for other allowed keys */
+ allowed_mask[index] |= mask;
+ allowed_mask[KEYBOARD_COL_REFRESH] |= KEYBOARD_MASK_REFRESH;
+
+ for (c = 0; c < KEYBOARD_COLS; c++) {
+ if (state[c] & ~allowed_mask[c])
+ return 0; /* Disallowed key pressed */
+ }
+ return 1;
+}
+
+/**
+ * Check what boot key is down, if any.
+ *
+ * @param state Keyboard state at boot.
+ *
+ * @return the key which is down, or BOOT_KEY_OTHER if an unrecognized
+ * 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)
+{
+ const struct boot_key_entry *k = boot_key_list;
+ int i;
+
+ /*
+ * 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_to_this_image())
+ return BOOT_KEY_OTHER;
+
+ /* If reset was not caused by reset pin, refresh must be held down */
+ if (!(system_get_reset_flags() & RESET_FLAG_RESET_PIN) &&
+ !(state[KEYBOARD_COL_REFRESH] & KEYBOARD_MASK_REFRESH))
+ return BOOT_KEY_OTHER;
+
+ /* Check what single key is down */
+ for (i = 0; i < ARRAY_SIZE(boot_key_list); i++, k++) {
+ if (check_key(state, k->mask_index, k->mask_value)) {
+ CPRINTF("[%T KB boot key %d]\n", i);
+ return i;
+ }
+ }
+
+ return BOOT_KEY_OTHER;
+}
+
+enum boot_key keyboard_scan_get_boot_key(void)
+{
+ return boot_key_value;
+}
+
+void keyboard_scan_init(void)
+{
+ /* Configure GPIO */
+ keyboard_raw_init();
+
+ /* Tri-state the columns */
+ keyboard_raw_drive_column(KEYBOARD_COLUMN_NONE);
+
+ /* Initialize raw state */
+ read_matrix(debounced_state);
+ 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);
+
+ /* Trigger event if recovery key was pressed */
+ if (boot_key_value == BOOT_KEY_ESC)
+ host_set_single_event(EC_HOST_EVENT_KEYBOARD_RECOVERY);
+}
+
+void keyboard_scan_task(void)
+{
+ int key_press_timer = 0;
+
+ print_state(debounced_state, "init state");
+
+ keyboard_raw_task_start();
+
+ while (1) {
+ /* Enable all outputs */
+ enable_interrupt();
+
+ /* Wait for scanning enabled and key pressed. */
+ do {
+ /*
+ * Don't wait 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 (!keyboard_raw_read_rows() || !is_scanning_enabled())
+ task_wait_event(-1);
+ } while (!is_scanning_enabled());
+
+ enter_polling_mode();
+ /* Busy polling keyboard state. */
+ while (is_scanning_enabled()) {
+ /* 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;
+ break;
+ }
+
+ /* Delay between scans */
+ usleep(SCAN_LOOP_DELAY);
+ }
+ }
+}
+
+void keyboard_scan_enable(int enable)
+{
+ enable_scanning = enable;
+
+ if (enable) {
+ /*
+ * A power button press had tri-stated all columns (see the
+ * 'else' statement below); we need a wake-up to unlock the
+ * task_wait_event() loop after enable_interrupt().
+ */
+ task_wake(TASK_ID_KEYSCAN);
+ } else {
+ keyboard_raw_drive_column(KEYBOARD_COLUMN_NONE);
+ keyboard_clear_buffer();
+ }
+}
+
+/*****************************************************************************/
+/* Console commands */
+
+static int command_ksstate(int argc, char **argv)
+{
+ if (argc > 1) {
+ if (!strcasecmp(argv[1], "on"))
+ print_state_changes = 1;
+ else if (!strcasecmp(argv[1], "off"))
+ print_state_changes = 0;
+ else
+ return EC_ERROR_PARAM1;
+ } else {
+ print_state(debounced_state, "debounced ");
+ print_state(prev_state, "prev ");
+ print_state(debouncing, "debouncing");
+ }
+
+ ccprintf("Keyboard scan state printing %s\n",
+ print_state_changes ? "on" : "off");
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(ksstate, command_ksstate,
+ "ksstate [on | off]",
+ "Show or toggle printing keyboard scan state",
+ NULL);
diff --git a/common/main.c b/common/main.c
index 0830cc6879..cb6b0b8bbb 100644
--- a/common/main.c
+++ b/common/main.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+/* Copyright (c) 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.
*
@@ -15,7 +15,6 @@
#include "gpio.h"
#include "hooks.h"
#include "jtag.h"
-#include "keyboard.h"
#include "keyboard_scan.h"
#include "system.h"
#include "task.h"