diff options
Diffstat (limited to 'common')
-rw-r--r-- | common/build.mk | 1 | ||||
-rw-r--r-- | common/keyboard_test.c | 201 |
2 files changed, 202 insertions, 0 deletions
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)); |