summaryrefslogtreecommitdiff
path: root/util
diff options
context:
space:
mode:
authorSimon Glass <sjg@chromium.org>2012-10-10 09:54:34 -0700
committerGerrit <chrome-bot@google.com>2012-11-01 14:09:34 -0700
commit4769627290a2cdfa851b76f2d1a550654b3b04fb (patch)
tree02e8255c4d1bcfab765199cc72d00aff1c9cb49a /util
parente9e02762dd4b09060911bda9b160502a8f36d4fd (diff)
downloadchrome-ec-4769627290a2cdfa851b76f2d1a550654b3b04fb.tar.gz
ectool: Add keyscan test features
Add a way of easily setting up keyscan tests using a simple text file format. The steps to run a test are as follows: - read the test file - read the key matrix information - translate the ascii characters from tests into keyscan codes - send the keyscan codes to the EC - tell the EC to start the test - wait for the required time, then collect what input we have received - check that the input matches the expected input BUG=chrome-os-partner:12179 BRANCH=none TEST=manual for now: On snow: ./ectool keyscan 10000 key_sequence.txt See that the test passes. Change-Id: I7de646205803a99443503a1b4bbf32f5fe89c534 Signed-off-by: Simon Glass <sjg@chromium.org> Reviewed-on: https://gerrit.chromium.org/gerrit/35119 Reviewed-by: Randall Spangler <rspangler@chromium.org>
Diffstat (limited to 'util')
-rw-r--r--util/build.mk5
-rw-r--r--util/ectool.c4
-rw-r--r--util/ectool.h30
-rw-r--r--util/ectool_keyscan.c682
4 files changed, 719 insertions, 2 deletions
diff --git a/util/build.mk b/util/build.mk
index fc021471f2..62b25b4e6b 100644
--- a/util/build.mk
+++ b/util/build.mk
@@ -7,9 +7,10 @@
#
host-util-bin=ectool lbplay burn_my_ec
+host-util-common=ectool_keyscan
ifeq ($(CONFIG_LPC),y)
-host-util-common=comm-lpc
+host-util-common+=comm-lpc
else
-host-util-common=comm-i2c
+host-util-common+=comm-i2c
endif
build-util-bin=ec_uartd stm32mon
diff --git a/util/ectool.c b/util/ectool.c
index b72f28ee13..eaf1147950 100644
--- a/util/ectool.c
+++ b/util/ectool.c
@@ -14,6 +14,7 @@
#include "battery.h"
#include "comm-host.h"
+#include "ectool.h"
#include "lightbar.h"
#include "lock/gec_lock.h"
@@ -91,6 +92,8 @@ const char help_str[] =
" Read I2C bus\n"
" i2cwrite\n"
" Write I2C bus\n"
+ " keyscan <beat_us> <filename>\n"
+ " Test low-level key scanning\n"
" lightbar [CMDS]\n"
" Various lightbar control commands\n"
" port80flood\n"
@@ -2696,6 +2699,7 @@ const struct command commands[] = {
{"i2cwrite", cmd_i2c_write},
{"lightbar", cmd_lightbar},
{"keyconfig", cmd_keyconfig},
+ {"keyscan", cmd_keyscan},
{"pstoreinfo", cmd_pstore_info},
{"pstoreread", cmd_pstore_read},
{"pstorewrite", cmd_pstore_write},
diff --git a/util/ectool.h b/util/ectool.h
new file mode 100644
index 0000000000..65eebcf6f2
--- /dev/null
+++ b/util/ectool.h
@@ -0,0 +1,30 @@
+/* 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.
+ */
+
+/**
+ * Test low-level key scanning
+ *
+ * ectool keyscan <beat_us> <filename>
+ *
+ * <beat_us> is the length of a beat in microseconds. This indicates the
+ * typing speed. Typically we scan at 10ms in the EC, so the beat period
+ * will typically be 1-5ms, with the scan changing only every 20-30ms at
+ * most.
+ * <filename> specifies a file containing keys that are depressed on each
+ * beat in the following format:
+ *
+ * <beat> <keys_pressed>
+ *
+ * <beat> is a beat number (0, 1, 2). The timestamp of this event will
+ * be <start_time> + <beat> * <beat_us>.
+ * <keys_pressed> is a (possibly empty) list of ASCII keys
+ *
+ * The key matrix is read from the fdt.
+ *
+ * @param argc Number of arguments (excluding 'ectool')
+ * @param argv List of arguments
+ * @return 0 if ok, -1 on error
+ */
+int cmd_keyscan(int argc, char *argv[]);
diff --git a/util/ectool_keyscan.c b/util/ectool_keyscan.c
new file mode 100644
index 0000000000..7ca0af660e
--- /dev/null
+++ b/util/ectool_keyscan.c
@@ -0,0 +1,682 @@
+/* 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 <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include "comm-host.h"
+#include "ectool.h"
+
+enum {
+ /* Alloc this many more scans when needed */
+ KEYSCAN_ALLOC_STEP = 64,
+ KEYSCAN_MAX_TESTS = 10, /* Maximum number of tests supported */
+ KEYSCAN_MAX_INPUT_LEN = 20, /* Maximum characters we can receive */
+};
+
+#ifdef KB_OUTPUTS
+#define KEYSCAN_OUTPUTS KB_OUTPUTS
+#else
+/* Use a suitable default */
+#define KEYSCAN_OUTPUTS 13
+#endif
+
+/* A single entry of the key matrix */
+struct matrix_entry {
+ int row; /* key matrix row */
+ int col; /* key matrix column */
+ int keycode; /* corresponding linux key code */
+};
+
+struct keyscan_test_item {
+ uint32_t beat; /* Beat number */
+ uint8_t scan[KEYSCAN_OUTPUTS]; /* Scan data */
+};
+
+/* A single test, consisting of a list of key scans and expected ascii input */
+struct keyscan_test {
+ char *name; /* name of test */
+ char *expect; /* resulting input we expect to see */
+ int item_count; /* number of items in data */
+ int item_alloced; /* number of items alloced in data */
+ struct keyscan_test_item *items; /* key data for EC */
+};
+
+/* A list of tests that we can run */
+struct keyscan_info {
+ unsigned int beat_us; /* length of each beat in microseconds */
+ struct keyscan_test tests[KEYSCAN_MAX_TESTS]; /* the tests */
+ int test_count; /* number of tests */
+ struct matrix_entry *matrix; /* the key matrix info */
+ int matrix_count; /* number of keys in matrix */
+};
+
+/**
+ * Read the key matrix from the device tree
+ *
+ * Keymap entries in the fdt take the form of 0xRRCCKKKK where
+ * RR=Row CC=Column KKKK=Key Code
+ *
+ * @param keyscan keyscan information
+ * @param path path to device tree file containing data
+ * @return 0 if ok, -1 on error
+ */
+static int keyscan_read_fdt_matrix(struct keyscan_info *keyscan,
+ const char *path)
+{
+ struct stat buf;
+ uint32_t word;
+ int upto;
+ FILE *f;
+ int err;
+
+ /* Allocate memory for key matrix */
+ if (stat(path, &buf)) {
+ fprintf(stderr, "Cannot stat key matrix file '%s'\n", path);
+ return -1;
+ }
+ keyscan->matrix_count = buf.st_size / 4;
+ keyscan->matrix = calloc(keyscan->matrix_count,
+ sizeof(*keyscan->matrix));
+ if (!keyscan->matrix) {
+ fprintf(stderr, "Out of memory for key matrix\n");
+ return -1;
+ }
+
+ f = fopen(path, "rb");
+ if (!f) {
+ fprintf(stderr, "Cannot open key matrix file '%s'\n", path);
+ return -1;
+ }
+
+ /* Now read the data */
+ upto = err = 0;
+ while (fread(&word, 1, sizeof(word), f) == sizeof(word)) {
+ struct matrix_entry *matrix = &keyscan->matrix[upto++];
+
+ word = be32toh(word);
+ matrix->row = word >> 24;
+ matrix->col = (word >> 16) & 0xff;
+ matrix->keycode = word & 0xffff;
+
+ /* Hard-code some sanity limits for now */
+ if (matrix->row >= 8 || matrix->col >= 13) {
+ fprintf(stderr, "Matrix pos out of range (%d,%d)\n",
+ matrix->row, matrix->col);
+ return -1;
+ }
+ }
+ fclose(f);
+ if (!err && upto != keyscan->matrix_count) {
+ fprintf(stderr, "Read mismatch from matrix file '%s'\n", path);
+ err = -1;
+ }
+
+ return err;
+}
+
+/*
+ * translate Linux's KEY_... values into ascii. We change space into 0xfe
+ * since we use the numeric value (&32) for space. That avoids ambiguity
+ * when we see a space in a key sequence file.
+ */
+static const unsigned char kbd_plain_xlate[] = {
+ 0xff, 0x1b, '1', '2', '3', '4', '5', '6',
+ '7', '8', '9', '0', '-', '=', '\b', '\t', /* 0x00 - 0x0f */
+ 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i',
+ 'o', 'p', '[', ']', '\r', 0xff, 'a', 's', /* 0x10 - 0x1f */
+ 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',
+ '\'', '`', 0xff, '\\', 'z', 'x', 'c', 'v', /* 0x20 - 0x2f */
+ 'b', 'n', 'm', ',' , '.', '/', 0xff, 0xff, 0xff,
+ 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30 - 0x3f */
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, '7',
+ '8', '9', '-', '4', '5', '6', '+', '1', /* 0x40 - 0x4f */
+ '2', '3', '0', '.', 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50 - 0x5F */
+ '\r', 0xff, 0xff
+};
+
+/**
+ * Add a new key to a scan
+ *
+ * Given a new key, this looks it up in the matrix and adds it to the scan,
+ * so that if this scan were reported by the EC, the AP would the given key.
+ *
+ * The format of keys is a list of ascii characters, or & followed by a numeric
+ * ascii value, or * followed by a numeric keycode value. Spaces are ignored
+ * (use '*32' for space).
+ *
+ * Examples:
+ * a - a
+ * &20 - space
+ * *58 - KEY_CAPSLOCK
+ *
+ * @param keyscan keyscan information
+ * @param keysp point to the current key string on entry; on exit it
+ * is updated to point to just after the string, plus any
+ * following space
+ * @param path path to device tree file containing data
+ * @return 0 if ok, -1 on error
+ */
+static int keyscan_add_to_scan(struct keyscan_info *keyscan, char **keysp,
+ uint8_t scan[])
+{
+ const uint8_t *pos;
+ struct matrix_entry *matrix;
+ int keycode = -1, i;
+ char *keys = *keysp;
+ int key = ' ';
+
+ if (*keys == '&') {
+ /* Numeric ascii code */
+ keys++;
+ key = strtol(keys, &keys, 10);
+ if (!key || keys == *keysp) {
+ fprintf(stderr, "Invalid numeric ascii\n");
+ return -1;
+ }
+ if (*keys == ' ')
+ keys++;
+ else if (*keys) {
+ fprintf(stderr, "Expect space after numeric ascii\n");
+ return -1;
+ }
+ } else if (*keys == '*') {
+ /* Numeric ascii code */
+ keys++;
+ keycode = strtol(keys, &keys, 10);
+ if (!keycode || keys == *keysp) {
+ fprintf(stderr, "Invalid numeric keycode\n");
+ return -1;
+ }
+ if (*keys == ' ')
+ keys++;
+ else if (*keys) {
+ fprintf(stderr, "Expect space after num. keycode\n");
+ return -1;
+ }
+ } else {
+ key = *keys++;
+ }
+
+ /* Convert keycode to key if needed */
+ if (keycode == -1) {
+ pos = strchr(kbd_plain_xlate, key);
+ if (!pos) {
+ fprintf(stderr, "Key '%c' not found in xlate table\n",
+ key);
+ return -1;
+ }
+ keycode = pos - kbd_plain_xlate;
+ }
+
+ /* Look up keycode in matrix */
+ for (i = 0, matrix = keyscan->matrix; i < keyscan->matrix_count;
+ i++, matrix++) {
+ if (matrix->keycode == keycode) {
+#ifdef DEBUG
+ printf("%d: %d,%d\n", matrix->keycode, matrix->row,
+ matrix->col);
+#endif
+ scan[matrix->col] |= 1 << matrix->row;
+ *keysp = keys;
+ return 0;
+ }
+ }
+ fprintf(stderr, "Key '%c' (keycode %d) not found in matrix\n", key,
+ keycode);
+
+ return -1;
+}
+
+/**
+ * Add a new keyscan to the given test
+ *
+ * This processes a new keyscan, consisting of a beat number and a sequence
+ * of keys.
+ *
+ * The format of keys is a beat number followed by a list of keys, each
+ * either ascii characters, or & followed by a numeric ascii value, or *
+ * followed by a numeric keycode value. Spaces are ignored (use '*32' for
+ * space).
+ *
+ * Examples:
+ * 0 abc - beat 0, press a, b and c
+ * 4 a &20 - beat 4, press a and space
+ * 8 *58 &13 - beat 8, press KEY_CAPSLOCK
+ *
+ * @param keyscan keyscan information
+ * @param linenum line number we are reading from (for error reporting)
+ * @param test test to add this scan to
+ * @param keys key string to process
+ * @return 0 if ok, -1 on error
+ */
+static int keyscan_process_keys(struct keyscan_info *keyscan, int linenum,
+ struct keyscan_test *test, char *keys)
+{
+ struct keyscan_test_item *item;
+ unsigned long int beat;
+
+ /* Allocate more items if needed */
+ if (!test->item_alloced || test->item_count == test->item_alloced) {
+ int size, new_size;
+
+ size = test->item_alloced * sizeof(struct keyscan_test_item);
+ new_size = size + KEYSCAN_ALLOC_STEP *
+ sizeof(struct keyscan_test_item);
+ test->items = realloc(test->items, new_size);
+ if (!test->items) {
+ fprintf(stderr, "Out of memory realloc()\n");
+ return -1;
+ }
+ memset((char *)test->items + size, '\0', new_size - size);
+ test->item_alloced += KEYSCAN_ALLOC_STEP;
+ new_size = size;
+ }
+
+ /* read the beat number */
+ item = &test->items[test->item_count];
+ beat = strtol(keys, &keys, 10);
+ item->beat = beat;
+
+ /* Read keys and add them to our scan */
+ if (*keys == ' ') {
+ keys++;
+ while (*keys) {
+ if (keyscan_add_to_scan(keyscan, &keys, item->scan)) {
+ fprintf(stderr, "Line %d: Cannot parse"
+ " key input '%s'\n", linenum,
+ keys);
+ return -1;
+ }
+ }
+ } else if (*keys) {
+ fprintf(stderr, "Line %d: Need space after beat\n",
+ linenum);
+ return -1;
+ }
+ test->item_count++;
+
+ return 0;
+}
+
+/* These are the commands we understand in a key sequence file */
+enum keyscan_cmd {
+ KEYSCAN_CMD_TEST, /* start a new test */
+ KEYSCAN_CMD_ENDTEST, /* end a test */
+ KEYSCAN_CMD_SEQ, /* add a keyscan to a test sequence */
+ KEYSCAN_CMD_EXPECT, /* indicate what input is expected */
+
+ KEYSCAN_CMD_COUNT
+};
+
+/* Names for each of the keyscan commands */
+static const char *keyscan_cmd_name[KEYSCAN_CMD_COUNT] = {
+ "test",
+ "endtest",
+ "seq",
+ "expect",
+};
+
+/**
+ * Read a command from a string and return it
+ *
+ * @param str String containing command
+ * @param len Length of command string
+ * @return detected command, or -1 if none
+ */
+static enum keyscan_cmd keyscan_read_cmd(const char *str, int len)
+{
+ int i;
+
+ for (i = 0; i < KEYSCAN_CMD_COUNT; i++) {
+ if (!strncmp(keyscan_cmd_name[i], str, len))
+ return i;
+ }
+
+ return -1;
+}
+
+/**
+ * Process a key sequence file a build up a list of tets from it
+ *
+ * @param f File containing keyscan info
+ * @param keyscan keyscan information
+ * @return 0 if ok, -1 on error
+ */
+static int keyscan_process_file(FILE *f, struct keyscan_info *keyscan)
+{
+ struct keyscan_test *cur_test;
+ char line[256];
+ char *str;
+ int linenum;
+
+ keyscan->test_count = 0;
+
+ linenum = 0;
+ cur_test = NULL;
+ while (str = fgets(line, sizeof(line), f), str) {
+ char *args, *end;
+ int cmd, len;
+
+ linenum++;
+ len = strlen(str);
+
+ /* chomp the trailing newline */
+ if (len > 0 && str[len - 1] == '\n') {
+ len--;
+ str[len] = '\0';
+ }
+
+ /* deal with blank lines and comments */
+ if (!len || *str == '#')
+ continue;
+
+ /* get the command */
+ for (end = str; *end && *end != ' '; end++)
+ ;
+
+ cmd = keyscan_read_cmd(str, end - str);
+ args = end + (*end != '\0');
+ switch (cmd) {
+ case KEYSCAN_CMD_TEST:
+ /* Start a new test */
+ if (keyscan->test_count == KEYSCAN_MAX_TESTS) {
+ fprintf(stderr, "KEYSCAN_MAX_TESTS "
+ "exceeded\n");
+ return -1;
+ }
+ cur_test = &keyscan->tests[keyscan->test_count];
+ cur_test->name = strdup(args);
+ if (!cur_test->name) {
+ fprintf(stderr, "Line %d: out of memory\n",
+ linenum);
+ }
+ break;
+ case KEYSCAN_CMD_EXPECT:
+ /* Get expect string */
+ if (!cur_test) {
+ fprintf(stderr, "Line %d: expect should be "
+ "inside test\n", linenum);
+ return -1;
+ }
+ cur_test->expect = strdup(args);
+ if (!cur_test->expect) {
+ fprintf(stderr, "Line %d: out of memory\n",
+ linenum);
+ }
+ break;
+ case KEYSCAN_CMD_ENDTEST:
+ /* End of a test */
+ keyscan->test_count++;
+ cur_test = NULL;
+ break;
+ case KEYSCAN_CMD_SEQ:
+ if (keyscan_process_keys(keyscan, linenum, cur_test,
+ args)) {
+ fprintf(stderr, "Line %d: Cannot parse key "
+ "input '%s'\n", linenum, args);
+ return -1;
+ }
+ break;
+ default:
+ fprintf(stderr, "Line %d: Uknown command '%1.*s'\n",
+ linenum, (int)(end - str), str);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Print out a list of all tests
+ *
+ * @param keyscan keyscan information
+ */
+static void keyscan_print(struct keyscan_info *keyscan)
+{
+ int testnum;
+ int i;
+
+ for (testnum = 0; testnum < keyscan->test_count; testnum++) {
+ struct keyscan_test *test = &keyscan->tests[testnum];
+
+ printf("Test: %s\n", test->name);
+ for (i = 0; i < test->item_count; i++) {
+ struct keyscan_test_item *item;
+ int j;
+
+ item = &test->items[i];
+ printf("%2d %7d: ", i, item->beat);
+ for (j = 0; j < sizeof(item->scan); j++)
+ printf("%02x ", item->scan[j]);
+ printf("\n");
+ }
+ printf("\n");
+ }
+}
+
+/**
+ * Set the terminal to raw mode, or cooked
+ *
+ * @param tty_fd Terminal file descriptor to change
+ * @Param raw 0 for cooked, non-zero for raw
+ */
+static void set_to_raw(int tty_fd, int raw)
+{
+ struct termios tty_attr;
+ int value;
+
+ value = fcntl(tty_fd, F_GETFL);
+
+ tcgetattr(tty_fd, &tty_attr);
+ if (raw) {
+ tty_attr.c_lflag &= ~ICANON;
+ value |= O_NONBLOCK;
+ } else {
+ tty_attr.c_lflag |= ICANON;
+ value &= ~O_NONBLOCK;
+ }
+ tcsetattr(tty_fd, TCSANOW, &tty_attr);
+ fcntl(tty_fd, F_SETFL, value);
+}
+
+/**
+ * Read input for a whlie until wee see no more
+ *
+ * @param fd File descriptor for input
+ * @param input Place to put input string
+ * @param max_len Maximum length of input string
+ * @param wait Number of microseconds to wait for input
+ */
+static void keyscan_get_input(int fd, char *input, int max_len, int wait)
+{
+ int len;
+
+ usleep(wait);
+ input[0] = '\0';
+ len = read(fd, input, max_len - 1);
+ if (len > 0)
+ input[len] = '\0';
+}
+
+static int keyscan_send_sequence(struct keyscan_info *keyscan,
+ struct keyscan_test *test)
+{
+ struct ec_params_keyscan_seq_ctrl *req;
+ struct keyscan_test_item *item;
+ int upto, size, rv;
+
+ size = sizeof(*req) + sizeof(item->scan);
+ req = (struct ec_params_keyscan_seq_ctrl *)malloc(size);
+ if (!req) {
+ fprintf(stderr, "Out of memory for message\n");
+ return -1;
+ }
+ for (upto = rv = 0, item = test->items; rv >= 0 &&
+ upto < test->item_count; upto++, item++) {
+ req->cmd = EC_KEYSCAN_SEQ_ADD;
+ req->add.time_us = item->beat * keyscan->beat_us;
+ memcpy(req->add.scan, item->scan, sizeof(item->scan));
+ rv = ec_command(EC_CMD_KEYSCAN_SEQ_CTRL, 0, req, size, NULL, 0);
+ }
+ free(req);
+ if (rv < 0)
+ return rv;
+
+ return 0;
+}
+
+/**
+ * Run a single test
+ *
+ * @param keyscan keyscan information
+ * @param test test to run
+ * @return 0 if test passes, -ve if some error occurred
+ */
+static int run_test(struct keyscan_info *keyscan, struct keyscan_test *test)
+{
+ struct ec_params_keyscan_seq_ctrl ctrl;
+ char input[KEYSCAN_MAX_INPUT_LEN];
+ struct ec_result_keyscan_seq_ctrl *resp;
+ int wait_us;
+ int size;
+ int rv;
+ int fd = 0;
+ int i;
+
+ /* First clear the sequence */
+ ctrl.cmd = EC_KEYSCAN_SEQ_CLEAR;
+ rv = ec_command(EC_CMD_KEYSCAN_SEQ_CTRL, 0, &ctrl, sizeof(ctrl),
+ NULL, 0);
+ if (rv < 0)
+ return rv;
+
+ rv = keyscan_send_sequence(keyscan, test);
+ if (rv < 0)
+ return rv;
+
+ /* Start it */
+ set_to_raw(fd, 1);
+ ctrl.cmd = EC_KEYSCAN_SEQ_START;
+ rv = ec_command(EC_CMD_KEYSCAN_SEQ_CTRL, 0, &ctrl, sizeof(ctrl),
+ NULL, 0);
+ if (rv < 0)
+ return rv;
+
+ /* Work out how long we need to wait */
+ wait_us = 100 * 1000; /* Wait 100ms to at least */
+ if (test->item_count) {
+ struct keyscan_test_item *ksi;
+
+ ksi = &test->items[test->item_count - 1];
+ wait_us += ksi->beat * keyscan->beat_us;
+ }
+
+ /* Wait for input */
+ keyscan_get_input(fd, input, sizeof(input), wait_us);
+ set_to_raw(fd, 0);
+
+ /* Ask EC for results */
+ size = sizeof(*resp) + test->item_count;
+ resp = malloc(size);
+ if (!resp) {
+ fprintf(stderr, "Out of memory for results\n");
+ return -1;
+ }
+ ctrl.cmd = EC_KEYSCAN_SEQ_COLLECT;
+ ctrl.collect.start_item = 0;
+ ctrl.collect.num_items = test->item_count;
+ rv = ec_command(EC_CMD_KEYSCAN_SEQ_CTRL, 0, &ctrl, sizeof(ctrl),
+ resp, size);
+ if (rv < 0)
+ return rv;
+
+ /* Check what scans were skipped */
+ for (i = 0; i < resp->collect.num_items; i++) {
+ struct ec_collect_item *item;
+ struct keyscan_test_item *ksi;
+
+ item = &resp->collect.item[i];
+ ksi = &test->items[i];
+ if (!(item->flags & EC_KEYSCAN_SEQ_FLAG_DONE))
+ printf(" [skip %d at beat %u] ", i, ksi->beat);
+ }
+
+ if (strcmp(input, test->expect)) {
+ printf("Expected '%s', got '%s' ", test->expect, input);
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Run all tests
+ *
+ * @param keyscan keyscan information
+ * @return 0 if ok, -1 on error
+ */
+static int keyscan_run_tests(struct keyscan_info *keyscan)
+{
+ int testnum;
+ int any_err = 0;
+
+ for (testnum = 0; testnum < keyscan->test_count; testnum++) {
+ struct keyscan_test *test = &keyscan->tests[testnum];
+ int err;
+
+ fflush(stdout);
+ err = run_test(keyscan, test);
+ any_err |= err;
+ if (err) {
+ printf("%d: %s: ", testnum, test->name);
+ printf(" : %s\n", err ? "FAIL" : "pass");
+ }
+ }
+
+ return any_err ? -1 : 0;
+}
+
+int cmd_keyscan(int argc, char *argv[])
+{
+ struct keyscan_info keyscan;
+ FILE *f;
+ int err;
+
+ argc--;
+ argv++;
+ if (argc < 2) {
+ fprintf(stderr, "Must specify beat period and filename\n");
+ return -1;
+ }
+ memset(&keyscan, '\0', sizeof(keyscan));
+ keyscan.beat_us = atoi(argv[0]);
+ if (keyscan.beat_us < 100)
+ fprintf(stderr, "Warning: beat period is normally > 100us\n");
+ f = fopen(argv[1], "r");
+ if (!f) {
+ perror("Cannot open file\n");
+ return -1;
+ }
+
+ /* TODO(sjg@chromium.org): Read key matrix from fdt */
+ err = keyscan_read_fdt_matrix(&keyscan, "test/test-matrix.bin");
+ if (!err)
+ err = keyscan_process_file(f, &keyscan);
+ if (!err)
+ keyscan_print(&keyscan);
+ if (!err)
+ err = keyscan_run_tests(&keyscan);
+ fclose(f);
+
+ return err;
+}