summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Glass <sjg@chromium.org>2012-10-10 09:47:00 -0700
committerGerrit <chrome-bot@google.com>2012-10-31 17:21:00 -0700
commitf3c88fa1ab60101f32f9117dced3afdc3f4c9393 (patch)
treeb751fa8ac6ff6861b33a23a520c34cde1eced1cd
parenteee95c9448a8accc8416b32c55a89d5796cdf35b (diff)
downloadchrome-ec-f3c88fa1ab60101f32f9117dced3afdc3f4c9393.tar.gz
stm32: Implement keyscan test infrastructure
Support the keyscan test functionality on stm32. Note: This is enabled by default so that it continues to build. But it is unlikely that we will want this in a shipping image. I suggest we add the facility for a dev build. Secondly, the stack has to be larger due to a printf (which admittedly I could just remove). Should we make the stack size conditional on the CONFIG? Seems a bit ugly, on the other hand we don't want to waste IRAM. BUG=chrome-os-partner:12179 BRANCH=none TEST=manual for now: On snow: ./ectool keyscan 20000 key_sequence.txt See that the test passes. Change-Id: Ic441ca0bde1be9589a924374605e2f146d16f423 Signed-off-by: Simon Glass <sjg@chromium.org> Reviewed-on: https://gerrit.chromium.org/gerrit/35118
-rw-r--r--README8
-rw-r--r--board/snow/ec.tasklist2
-rw-r--r--chip/stm32/keyboard_scan.c21
-rw-r--r--common/build.mk1
-rw-r--r--common/keyboard_test.c201
-rw-r--r--include/keyboard_test.h42
6 files changed, 273 insertions, 2 deletions
diff --git a/README b/README
index 76a10b1852..47ee4c1635 100644
--- a/README
+++ b/README
@@ -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