diff options
author | Bill Richardson <wfrichar@chromium.org> | 2015-07-10 13:07:50 -0700 |
---|---|---|
committer | ChromeOS Commit Bot <chromeos-commit-bot@chromium.org> | 2015-07-11 00:28:11 +0000 |
commit | 47ccb26dd06fbe4782fac349d356f6c58fc8951e (patch) | |
tree | 35238920d2030ba09722b7a0cf8388ccfeea0a88 /extra/sps_errs | |
parent | 18ea8f4ec3c328cbbe2e9a432caa3337f26d4340 (diff) | |
download | chrome-ec-47ccb26dd06fbe4782fac349d356f6c58fc8951e.tar.gz |
Cr50: Add extra/sps_errs/ test program
This adds another test program to use with the EC connected to
the build host via an FTDI USB-to-SPI adapater, This program
sends an EC_CMD_HELLO host command to the EC. Options exist to
display the bytes transferred over the SPI interface, and to
truncate the message before its complete, to see how the EC reacts.
BUG=chrome-os-partner:40969
BRANCH=none
TEST=make buildall
To try out the new test program:
cd extra/sps_errs
make
./prog
./prog -v
./prog -v -c 22
Change-Id: I1d370ecdbae047d9504bc6e5f73949d4e3aed9d9
Signed-off-by: Bill Richardson <wfrichar@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/284865
Reviewed-by: Vadim Bendebury <vbendeb@google.com>
Diffstat (limited to 'extra/sps_errs')
-rw-r--r-- | extra/sps_errs/.gitignore | 1 | ||||
-rw-r--r-- | extra/sps_errs/Makefile | 37 | ||||
-rw-r--r-- | extra/sps_errs/README | 28 | ||||
-rw-r--r-- | extra/sps_errs/prog.c | 447 |
4 files changed, 513 insertions, 0 deletions
diff --git a/extra/sps_errs/.gitignore b/extra/sps_errs/.gitignore new file mode 100644 index 0000000000..ea17491321 --- /dev/null +++ b/extra/sps_errs/.gitignore @@ -0,0 +1 @@ +prog diff --git a/extra/sps_errs/Makefile b/extra/sps_errs/Makefile new file mode 100644 index 0000000000..12224ad803 --- /dev/null +++ b/extra/sps_errs/Makefile @@ -0,0 +1,37 @@ +# Copyright 2015 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. + +# Use your own libmpsse if you want, but we're going to use the files +# that are part of the Chromium OS trunks_client program. +PLATFORM2 = ../../../../platform2 +MPSSE_DIR = $(PLATFORM2)/trunks/ftdi + +PROG = prog +SRCS = prog.c $(MPSSE_DIR)/mpsse.c $(MPSSE_DIR)/support.c + +CFLAGS = \ + -std=gnu99 \ + -g3 \ + -O3 \ + -Wall \ + -Werror \ + -Wpointer-arith \ + -Wcast-align \ + -Wcast-qual \ + -Wundef \ + -Wsign-compare \ + -Wredundant-decls \ + -Wmissing-declarations + +CFLAGS += -I../../include -I${MPSSE_DIR} -I${PLATFORM2} + +CFLAGS += $(shell pkg-config --cflags libusb-1.0 libftdi1) +LIBS += $(shell pkg-config --libs libusb-1.0 libftdi1) + +$(PROG): $(SRCS) Makefile + gcc $(CFLAGS) $(SRCS) $(LDFLAGS) $(LIBS) -o $@ + +.PHONY: clean +clean: + rm -rf $(PROG) diff --git a/extra/sps_errs/README b/extra/sps_errs/README new file mode 100644 index 0000000000..d1fbb6b43f --- /dev/null +++ b/extra/sps_errs/README @@ -0,0 +1,28 @@ +SETUP: + + Attach an EC to the build host using an FTDI USB-to-SPI adapter. + +BUILD: + + make + ./prog + + +USAGE: + + Usage: ./prog [-v] [-c BYTES] + + This sends a EC_CMD_HELLO host command. The -c option can + be used to truncate the exchange early, to see how the EC + deals with the interruption. + +NOTE: + + Ubuntu Trusty uses an ancient version of libftdi. + + If building outside of the Chromium chroot, you'll probably want to grab the + latest libftdi1-1.2.tar.bz2 from + + http://www.intra2net.com/en/developer/libftdi/ + + and install it into /usr instead. diff --git a/extra/sps_errs/prog.c b/extra/sps_errs/prog.c new file mode 100644 index 0000000000..b649199068 --- /dev/null +++ b/extra/sps_errs/prog.c @@ -0,0 +1,447 @@ +/* Copyright 2015 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 <signal.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "mpsse.h" + +#include "ec_commands.h" + +static int opt_verbose; +static size_t stop_after = -1; + +/* Communication handle */ +static struct mpsse_context *mpsse; + +/* enum ec_status meaning */ +static const char *ec_strerr(enum ec_status r) +{ + static const char * const strs[] = { + "SUCCESS", + "INVALID_COMMAND", + "ERROR", + "INVALID_PARAM", + "ACCESS_DENIED", + "INVALID_RESPONSE", + "INVALID_VERSION", + "INVALID_CHECKSUM", + "IN_PROGRESS", + "UNAVAILABLE", + "TIMEOUT", + "OVERFLOW", + "INVALID_HEADER", + "REQUEST_TRUNCATED", + "RESPONSE_TOO_BIG", + "BUS_ERROR", + "BUSY", + }; + if (r >= EC_RES_SUCCESS && r <= EC_RES_BUSY) + return strs[r]; + + return "<undefined result>"; +}; + + +/**************************************************************************** + * Debugging output + */ + +#define LINELEN 16 + +static void showline(uint8_t *buf, int len) +{ + int i; + printf(" "); + for (i = 0; i < len; i++) + printf(" %02x", buf[i]); + for (i = len; i < LINELEN; i++) + printf(" "); + printf(" "); + for (i = 0; i < len; i++) + printf("%c", + (buf[i] >= ' ' && buf[i] <= '~') ? buf[i] : '.'); + printf("\n"); +} + +static void show(const char *fmt, uint8_t *buf, int len) +{ + int i, m, n; + + if (!opt_verbose) + return; + + printf(fmt, len); + + m = len / LINELEN; + n = len % LINELEN; + + for (i = 0; i < m; i++) + showline(buf + i * LINELEN, LINELEN); + if (n) + showline(buf + m * LINELEN, n); +} + +/**************************************************************************** + * Send command & receive result + */ + +/* + * With proto v3, the kernel driver asks the EC for the max param size + * (EC_CMD_GET_PROTOCOL_INFO) at probe time, because it can vary depending on + * the bus and/or the supported commands. + * + * FIXME: For now we'll just hard-code a size. + */ +static uint8_t txbuf[128]; + +/* + * Load the output buffer with a proto v3 request (header, then data, with + * checksum correct in header). + */ +static size_t prepare_request(int cmd, int version, + const uint8_t *data, size_t data_len) +{ + struct ec_host_request *request; + size_t i, total_len; + uint8_t csum = 0; + + total_len = sizeof(*request) + data_len; + if (total_len > sizeof(txbuf)) { + printf("Request too large (%zd > %zd)\n", + total_len, sizeof(txbuf)); + return -1; + } + + /* Header first */ + request = (struct ec_host_request *)txbuf; + request->struct_version = EC_HOST_REQUEST_VERSION; + request->checksum = 0; + request->command = cmd; + request->command_version = version; + request->reserved = 0; + request->data_len = data_len; + + /* Then data */ + memcpy(txbuf + sizeof(*request), data, data_len); + + /* Update checksum */ + for (i = 0; i < total_len; i++) + csum += txbuf[i]; + request->checksum = -csum; + + return total_len; +} + + +/* Timeout flag, so we don't wait forever */ +static int timedout; +static void alarm_handler(int sig) +{ + timedout = 1; +} + +/* + * Send command, wait for result. Return zero if communication succeeded; check + * response to see if the EC liked the command. + */ +static int send_cmd(int cmd, int version, + void *outbuf, + size_t outsize, + struct ec_host_response *hdr, + void *bodydest, + size_t bodylen) +{ + uint8_t *tptr, *hptr = 0, *bptr = 0; + size_t len, i; + uint8_t sum = 0; + int lastone = 0x1111; + int ret = 0; + size_t bytes_left = stop_after; + size_t bytes_sent = 0; + + + /* Load up the txbuf with the stuff to send */ + len = prepare_request(cmd, version, outbuf, outsize); + if (len < 0) + return -1; + + if (MPSSE_OK != Start(mpsse)) { + fprintf(stderr, "Start failed: %s\n", + ErrorString(mpsse)); + return -1; + } + + /* Send the command request */ + if (len > bytes_left) { + printf("len %zd => %zd\n", len, bytes_left); + len = bytes_left; + } + + show("Transfer(%d) =>\n", txbuf, len); + tptr = Transfer(mpsse, txbuf, len); + bytes_left -= len; + bytes_sent += len; + if (!tptr) { + fprintf(stderr, "Transfer failed: %s\n", + ErrorString(mpsse)); + goto out; + } + + show("Transfer(%d) <=\n", tptr, len); + + /* Make sure the EC was listening */ + for (i = 0; i < len; i++) { + switch (tptr[i]) { + case EC_SPI_PAST_END: + case EC_SPI_RX_BAD_DATA: + case EC_SPI_NOT_READY: + ret = tptr[i]; + /* FALLTHROUGH */ + default: + break; + } + if (ret) + break; + } + free(tptr); + if (ret) { + printf("HEY: EC no good (0x%02x)\n", ret); + goto out; + } + + if (!bytes_left) + goto out; + + /* Read until we see the response come along */ + + /* Give up eventually */ + timedout = 0; + if (SIG_ERR == signal(SIGALRM, alarm_handler)) { + perror("Problem with signal handler"); + goto out; + } + alarm(1); + + if (opt_verbose) + printf("Wait:"); + + /* Read a byte at a time until we see the start of the frame. + * This is slow, but still faster than the EC. */ + while (bytes_left) { + uint8_t *ptr = Read(mpsse, 1); + bytes_left--; + bytes_sent++; + if (!ptr) { + fprintf(stderr, "Read failed: %s\n", + ErrorString(mpsse)); + alarm(0); + goto out; + } + if (opt_verbose && lastone != *ptr) { + printf(" %02x", *ptr); + lastone = *ptr; + } + if (*ptr == EC_SPI_FRAME_START) { + free(ptr); + break; + } + free(ptr); + + if (timedout) { + fprintf(stderr, "timed out\n"); + goto out; + } + } + alarm(0); + + if (opt_verbose) + printf("\n"); + + if (!bytes_left) + goto out; + + /* Now read the response header */ + len = sizeof(*hdr); + if (len > bytes_left) { + printf("len %zd => %zd\n", len, bytes_left); + len = bytes_left; + } + + hptr = Read(mpsse, len); + bytes_left -= len; + bytes_sent += len; + if (!hptr) { + fprintf(stderr, "Read failed: %s\n", + ErrorString(mpsse)); + goto out; + } + show("Header(%d):\n", hptr, sizeof(*hdr)); + memcpy(hdr, hptr, sizeof(*hdr)); + + /* Check the header */ + if (hdr->struct_version != EC_HOST_RESPONSE_VERSION) { + printf("HEY: response version %d (should be %d)\n", + hdr->struct_version, + EC_HOST_RESPONSE_VERSION); + goto out; + } + + if (hdr->data_len > bodylen) { + printf("HEY: response data_len %d is > %zd\n", + hdr->data_len, + bodylen); + goto out; + } + + if (!bytes_left) + goto out; + + len = hdr->data_len; + if (len > bytes_left) { + printf("len %zd => %zd\n", len, bytes_left); + len = bytes_left; + } + + /* Read the data */ + if (len) { + bptr = Read(mpsse, len); + bytes_left -= len; + bytes_sent += len; + if (!bptr) { + fprintf(stderr, "Read failed: %s\n", + ErrorString(mpsse)); + goto out; + } + show("Body(%d):\n", bptr, hdr->data_len); + memcpy(bodydest, bptr, hdr->data_len); + } + + /* Verify the checksum */ + for (i = 0; i < sizeof(hdr); i++) + sum += hptr[i]; + for (i = 0; i < hdr->data_len; i++) + sum += bptr[i]; + if (sum) + printf("HEY: Checksum invalid\n"); + +out: + printf("sent %zd bytes\n", bytes_sent); + if (!bytes_left) + printf("hit byte limit\n"); + if (hptr) + free(hptr); + if (bptr) + free(bptr); + + if (MPSSE_OK != Stop(mpsse)) { + fprintf(stderr, "Stop failed: %s\n", + ErrorString(mpsse)); + return -1; + } + + return 0; +} + + +/****************************************************************************/ + +/** + * Try it. + * + * @return zero on success + */ +static int hello(void) +{ + struct ec_params_hello p; + struct ec_host_response resp; + struct ec_response_hello r; + uint32_t expected; + int retval; + + memset(&p, 0, sizeof(p)); + memset(&resp, 0, sizeof(resp)); + memset(&r, 0, sizeof(r)); + + p.in_data = 0xa5a5a5a5; + expected = p.in_data + 0x01020304; + + retval = send_cmd(EC_CMD_HELLO, 0, + &p, sizeof(p), + &resp, + &r, sizeof(r)); + + if (retval) { + printf("Transmission error\n"); + return -1; + } + + if (EC_RES_SUCCESS != resp.result) { + printf("EC result is %d: %s\n", + resp.result, ec_strerr(resp.result)); + return -1; + } + + printf("sent %08x, expected %08x, got %08x => %s\n", + p.in_data, expected, r.out_data, + expected == r.out_data ? "yay" : "boo"); + + return !(expected == r.out_data); +} + +static void usage(char *progname) +{ + printf("\nUsage: %s [-v] [-c BYTES]\n\n", progname); + printf("This sends a EC_CMD_HELLO host command. The -c option can\n"); + printf("be used to truncate the exchange early, to see how the EC\n"); + printf("deals with the interruption.\n\n"); +} + +int main(int argc, char *argv[]) +{ + int retval = 1; + int errorcnt = 0; + int i; + + while ((i = getopt(argc, argv, ":vc:")) != -1) { + switch (i) { + case 'c': + stop_after = atoi(optarg); + printf("stopping after %zd bytes\n", stop_after); + break; + case 'v': + opt_verbose++; + break; + case '?': + printf("unrecognized option: -%c\n", optopt); + errorcnt++; + break; + } + } + if (errorcnt) { + usage(argv[0]); + return 1; + } + + /* Find something to talk to */ + mpsse = MPSSE(SPI0, 2000000, 0); + if (!mpsse) { + printf("Can't find a device to open\n"); + return 1; + } + + if (0 != hello()) + goto out; + + retval = 0; +out: + Close(mpsse); + mpsse = 0; + return retval; +} |