diff options
Diffstat (limited to 'extra')
-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; +} |