diff options
-rw-r--r-- | README | 8 | ||||
-rw-r--r-- | board/snow/ec.tasklist | 2 | ||||
-rw-r--r-- | chip/stm32/keyboard_scan.c | 21 | ||||
-rw-r--r-- | common/build.mk | 1 | ||||
-rw-r--r-- | common/keyboard_test.c | 201 | ||||
-rw-r--r-- | include/keyboard_test.h | 42 |
6 files changed, 273 insertions, 2 deletions
@@ -73,3 +73,11 @@ Build Options power rails during AP startup and shutdown sequences. This is mainly useful for bringup when we don't have the corresponding sequences in the AP code. + +- CONFIG_KEYBOARD_TEST + + Turn on keyboard testing functionality. This enables a message which + received a list of keyscan events from the AP and processes them. + This will cause keypresses to appear on the AP through the same + mechanism as a normal keyboard press. + diff --git a/board/snow/ec.tasklist b/board/snow/ec.tasklist index a07dfca87e..80d808ede9 100644 --- a/board/snow/ec.tasklist +++ b/board/snow/ec.tasklist @@ -19,7 +19,7 @@ TASK(VBOOTHASH, vboot_hash_task, NULL, TASK_STACK_SIZE) \ TASK(POWERLED, power_led_task, NULL, 256) \ TASK(PMU_TPS65090_CHARGER, pmu_charger_task, NULL, TASK_STACK_SIZE) \ - TASK(KEYSCAN, keyboard_scan_task, NULL, 256) \ + TASK(KEYSCAN, keyboard_scan_task, NULL, 360) \ TASK(GAIAPOWER, gaia_power_task, NULL, TASK_STACK_SIZE) \ TASK(CONSOLE, console_task, NULL, TASK_STACK_SIZE) \ TASK(HOSTCMD, host_command_task, NULL, TASK_STACK_SIZE) diff --git a/chip/stm32/keyboard_scan.c b/chip/stm32/keyboard_scan.c index 34c4f7147b..5f74938312 100644 --- a/chip/stm32/keyboard_scan.c +++ b/chip/stm32/keyboard_scan.c @@ -16,6 +16,7 @@ #include "host_command.h" #include "keyboard.h" #include "keyboard_scan.h" +#include "keyboard_test.h" #include "registers.h" #include "system.h" #include "task.h" @@ -286,6 +287,14 @@ static void print_state(const uint8_t *state, const char *msg) CPUTS("]\n"); } +/** + * Read the raw row state for the currently selected column + * + * It is assumed that the column is already selected by the scanning + * hardware. The column number is only used by test code. + * + * @return row state, one bit for each row + */ static uint8_t read_raw_row_state(void) { uint16_t tmp; @@ -347,6 +356,11 @@ static int read_matrix(uint8_t *state) r = read_raw_row_state(); +#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 */ @@ -527,8 +541,13 @@ static void scan_keyboard(void) * re-start immediatly polling instead of waiting * for the next interrupt. */ - if (!read_raw_row_state()) + if (!read_raw_row_state()) { +#ifdef CONFIG_KEYBOARD_TEST + task_wait_event(keyscan_seq_next_event_delay()); +#else task_wait_event(-1); +#endif + } enter_polling_mode(); diff --git a/common/build.mk b/common/build.mk index 11215bb6de..45c3ce636f 100644 --- a/common/build.mk +++ b/common/build.mk @@ -17,6 +17,7 @@ common-$(CONFIG_EOPTION)+=eoption.o common-$(CONFIG_FLASH)+=flash_common.o fmap.o common-$(CONFIG_I2C)+=i2c_commands.o common-$(CONFIG_IR357x)+=ir357x.o +common-$(CONFIG_KEYBOARD_TEST)+=keyboard_test.o common-$(CONFIG_LPC)+=port80.o common-$(CONFIG_POWER_LED)+=power_led.o common-$(CONFIG_PSTORE)+=pstore_commands.o diff --git a/common/keyboard_test.c b/common/keyboard_test.c new file mode 100644 index 0000000000..03d31fd63b --- /dev/null +++ b/common/keyboard_test.c @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2012 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. + */ + +#include <common.h> +#include <console.h> +#include <ec_commands.h> +#include <host_command.h> +#include <keyboard_test.h> +#include <task.h> +#include <util.h> + +enum { + KEYSCAN_MAX_LENGTH = 20, + KEYSCAN_SEQ_START_DELAY_US = 10000, +}; + +static uint8_t keyscan_seq_count; +static int8_t keyscan_seq_upto = -1; +static struct keyscan_item keyscan_items[KEYSCAN_MAX_LENGTH]; +struct keyscan_item *keyscan_seq_cur; + +static int keyscan_seq_is_active(void) +{ + return keyscan_seq_upto != -1; +} + +/** + * Get the current item in the keyscan sequence + * + * This looks at the current time, and returns the correct key scan for that + * time. + * + * @return pointer to keyscan item, or NULL if none + */ +static const struct keyscan_item *keyscan_seq_get(void) +{ + struct keyscan_item *ksi; + + if (!keyscan_seq_is_active()) + return NULL; + + ksi = &keyscan_items[keyscan_seq_upto]; + while (keyscan_seq_upto < keyscan_seq_count) { + /* + * If we haven't reached the time for the next one, return + * this one. + */ + if (!timestamp_expired(ksi->abs_time, NULL)) { + /* Yippee, we get to present this one! */ + if (keyscan_seq_cur) + keyscan_seq_cur->done = 1; + return keyscan_seq_cur; + } + + keyscan_seq_cur = ksi; + keyscan_seq_upto++; + ksi++; + } + + ccprintf("%T: keyscan_seq done, upto=%d\n", keyscan_seq_upto); + keyscan_seq_upto = -1; + keyscan_seq_cur = NULL; + return NULL; +} + +uint8_t keyscan_seq_get_scan(int column, uint8_t scan) +{ + const struct keyscan_item *item; + + /* Use simulated keyscan sequence instead if active */ + item = keyscan_seq_get(); + if (item) { + /* OR all columns together */ + if (column == -1) { + int c; + + scan = 0; + for (c = 0; c < KB_OUTPUTS; c++) + scan |= item->scan[c]; + } else { + scan = item->scan[column]; + } + } + + return scan; +} + +int keyscan_seq_next_event_delay(void) +{ + const struct keyscan_item *ksi; + int delay; + + /* + * Make sure we are pointing to the right event. This function will + * return the event that should currently be presented. In fact we + * want to look at the next event to be presented, so we manually + * look that up after calling this function. + */ + ksi = keyscan_seq_get(); + + if (!keyscan_seq_is_active()) + return -1; + + /* Calculate the delay until the event */ + ksi = &keyscan_items[keyscan_seq_upto]; + delay = MAX(ksi->abs_time.val - get_time().val, 0); + + return delay; +} + +static void keyscan_seq_start(void) +{ + timestamp_t start; + int i; + + start = get_time(); + start.val += KEYSCAN_SEQ_START_DELAY_US; + for (i = 0; i < keyscan_seq_count; i++) { + struct keyscan_item *ksi = &keyscan_items[i]; + + ksi->abs_time = start; + ksi->abs_time.val += ksi->time_us; + } + + keyscan_seq_upto = 0; + keyscan_seq_cur = NULL; + task_wake(TASK_ID_KEYSCAN); +} + +static int keyscan_seq_collect(struct ec_params_keyscan_seq_ctrl *req, + struct ec_result_keyscan_seq_ctrl *resp) +{ + struct keyscan_item *ksi; + int start, end; + int i; + + /* Range check the input values */ + start = req->collect.start_item; + end = start + req->collect.num_items; + if (start >= keyscan_seq_count) + end = start; + else + end = MIN(end, keyscan_seq_count); + start = MIN(start, end); + + /* Response plus one byte per item */ + end = MIN(end - start, EC_HOST_PARAM_SIZE - sizeof(*resp)); + resp->collect.num_items = end - start; + + for (i = start, ksi = keyscan_items; i < end; i++, ksi++) + resp->collect.item[i].flags = ksi->done ? + EC_KEYSCAN_SEQ_FLAG_DONE : 0; + + return sizeof(*resp) + resp->collect.num_items; +} + +static int keyscan_seq_ctrl(struct host_cmd_handler_args *args) +{ + struct ec_params_keyscan_seq_ctrl req, *msg; + struct keyscan_item *ksi; + + /* For now we must do our own alignment */ + memcpy(&req, args->params, sizeof(req)); + + ccprintf("keyscan %d\n", req.cmd); + switch (req.cmd) { + case EC_KEYSCAN_SEQ_CLEAR: + keyscan_seq_count = 0; + break; + case EC_KEYSCAN_SEQ_ADD: + if (keyscan_seq_count == KEYSCAN_MAX_LENGTH) + return EC_RES_OVERFLOW; + + ksi = &keyscan_items[keyscan_seq_count]; + ksi->time_us = req.add.time_us; + ksi->done = 0; + ksi->abs_time.val = 0; + msg = (struct ec_params_keyscan_seq_ctrl *)args->params; + memcpy(ksi->scan, msg->add.scan, sizeof(ksi->scan)); + keyscan_seq_count++; + break; + case EC_KEYSCAN_SEQ_START: + keyscan_seq_start(); + break; + case EC_KEYSCAN_SEQ_COLLECT: + args->response_size = keyscan_seq_collect(&req, + (struct ec_result_keyscan_seq_ctrl *)args->response); + break; + default: + return EC_RES_INVALID_COMMAND; + } + + return EC_RES_SUCCESS; +} + +DECLARE_HOST_COMMAND(EC_CMD_KEYSCAN_SEQ_CTRL, + keyscan_seq_ctrl, + EC_VER_MASK(0)); diff --git a/include/keyboard_test.h b/include/keyboard_test.h new file mode 100644 index 0000000000..df95d9c081 --- /dev/null +++ b/include/keyboard_test.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2012 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 test module for Chrome EC */ + +#ifndef __CROS_EC_KEYBOARD_TEST_H +#define __CROS_EC_KEYBOARD_TEST_H + +#include <timer.h> + +/* + * Keyboard scan test item - contains a single scan to 'present' to key scan + * logic. + */ +struct keyscan_item { + timestamp_t abs_time; /* absolute timestamp to present this item */ + uint32_t time_us; /* time for this item relative to test start */ + uint8_t done; /* 1 if we managed to present this */ + uint8_t scan[KB_OUTPUTS]; +}; + +/** + * Get the next key scan from the test sequence, if any + * + * @param column Column to read (-1 to OR all columns together + * @param scan Raw scan data read from GPIOs + * @return test scan, or just 'scan' if no test is active + */ +uint8_t keyscan_seq_get_scan(int column, uint8_t scan); + +/** + * Calculate the delay until the next key scan event needs to be presented + * + * @return number of microseconds from now until the next key scan event, or + * -1 if there is no future key scan event (e.g. testing is complete) + */ +int keyscan_seq_next_event_delay(void); + +#endif |