diff options
author | Chun-Ta Lin <itspeter@google.com> | 2017-06-05 19:30:00 +0800 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2017-06-09 07:10:41 -0700 |
commit | 0b5e3aa6428d135ff8ce638bc8a10f76fe9731f5 (patch) | |
tree | 7b4290b084a301e13f52e856d46688af80baf484 /extra | |
parent | f40e79b3f18d23c49418a6cbd91430e3d5e116a6 (diff) | |
download | chrome-ec-0b5e3aa6428d135ff8ce638bc8a10f76fe9731f5.tar.gz |
hammer: initial commit for trackpad firmware updater
Tool to update the trackpad firmware over USB. Primarily borrowed from
extra/usb_console/usb_console.c
BRANCH=none
BUG=b:35587174
TEST=Manually executed with hammer
Change-Id: Icad951d2478a3e231f293e11fb461eaae20e5042
Reviewed-on: https://chromium-review.googlesource.com/525192
Commit-Ready: Chun-ta Lin <itspeter@chromium.org>
Tested-by: Chun-ta Lin <itspeter@chromium.org>
Reviewed-by: Nicolas Boichat <drinkcat@chromium.org>
Diffstat (limited to 'extra')
-rw-r--r-- | extra/touchpad_updater/Makefile | 34 | ||||
-rw-r--r-- | extra/touchpad_updater/touchpad_updater.c | 392 |
2 files changed, 426 insertions, 0 deletions
diff --git a/extra/touchpad_updater/Makefile b/extra/touchpad_updater/Makefile new file mode 100644 index 0000000000..fd858a152d --- /dev/null +++ b/extra/touchpad_updater/Makefile @@ -0,0 +1,34 @@ +# Copyright 2017 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. + +PROGRAM := touchpad_updater +SOURCE := $(PROGRAM).c +LIBS := +LFLAGS := +CFLAGS := -std=gnu99 \ + -g3 \ + -O3 \ + -Wall \ + -Werror \ + -Wpointer-arith \ + -Wcast-align \ + -Wcast-qual \ + -Wundef \ + -Wsign-compare \ + -Wredundant-decls \ + -Wmissing-declarations + +# +# Add libusb-1.0 required flags +# +LIBS += $(shell pkg-config --libs libusb-1.0) +CFLAGS += $(shell pkg-config --cflags libusb-1.0) + +$(PROGRAM): $(SOURCE) Makefile + gcc $(CFLAGS) $(SOURCE) $(LFLAGS) $(LIBS) -o $@ + +.PHONY: clean + +clean: + rm -rf $(PROGRAM) *~ diff --git a/extra/touchpad_updater/touchpad_updater.c b/extra/touchpad_updater/touchpad_updater.c new file mode 100644 index 0000000000..369a56ba5c --- /dev/null +++ b/extra/touchpad_updater/touchpad_updater.c @@ -0,0 +1,392 @@ +/* + * Copyright 2017 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 <errno.h> +#include <getopt.h> +#include <poll.h> +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/select.h> +#include <unistd.h> + +#include <libusb.h> + +/* Command line options */ +static uint16_t vid = 0x18d1; /* Google */ +static uint16_t pid = 0x5022; /* Hammer */ +static uint8_t ep_num = 4; /* console endpoint */ +static char *firmware_binary = "144.0_2.0.bin"; /* firmware blob */ + +/* Firmware binary blob related */ +#define FW_PAGE_SIZE 64 +#define FW_PAGE_COUNT 768 +#define FW_SIZE (FW_PAGE_SIZE*FW_PAGE_COUNT) + +static unsigned char fw_data[FW_SIZE]; + +/* Utility functions */ +static int le_bytes_to_int(unsigned char *buf) +{ + return buf[0] + (int)(buf[1] << 8); +} + +/* Command line parsing related */ +static char *progname; +static char *short_opts = ":f:v:p:e:h"; +static const struct option long_opts[] = { + /* name hasarg *flag val */ + {"file", 1, NULL, 'f'}, + {"vid", 1, NULL, 'v'}, + {"pid", 1, NULL, 'p'}, + {"ep", 1, NULL, 'e'}, + {"help", 0, NULL, 'h'}, + {NULL, 0, NULL, 0}, +}; + +static void usage(int errs) +{ + printf("\nUsage: %s [options]\n" + "\n" + "Firmware updater over USB for trackpad under hammer\n" + "\n" + "Options:\n" + "\n" + " -f,--file STR Firmware binary (default %s)\n" + " -v,--vid HEXVAL Vendor ID (default %04x)\n" + " -p,--pid HEXVAL Product ID (default %04x)\n" + " -e,--ep NUM Endpoint (default %d)\n" + " -h,--help Show this message\n" + "\n", progname, firmware_binary, vid, pid, ep_num); + + exit(!!errs); +} + +static void parse_cmdline(int argc, char *argv[]) +{ + char *e = 0; + int i, errorcnt = 0; + progname = strrchr(argv[0], '/'); + if (progname) + progname++; + else + progname = argv[0]; + + opterr = 0; /* quiet, you */ + while ((i = getopt_long(argc, argv, short_opts, long_opts, 0)) != -1) { + switch (i) { + case 'f': + firmware_binary = optarg; + break; + case 'p': + pid = (uint16_t) strtoul(optarg, &e, 16); + if (!*optarg || (e && *e)) { + printf("Invalid argument: \"%s\"\n", optarg); + errorcnt++; + } + break; + case 'v': + vid = (uint16_t) strtoul(optarg, &e, 16); + if (!*optarg || (e && *e)) { + printf("Invalid argument: \"%s\"\n", optarg); + errorcnt++; + } + break; + case 'e': + ep_num = (uint8_t) strtoul(optarg, &e, 0); + if (!*optarg || (e && *e)) { + printf("Invalid argument: \"%s\"\n", optarg); + errorcnt++; + } + break; + case 'h': + usage(errorcnt); + break; + case 0: /* auto-handled option */ + break; + case '?': + if (optopt) + printf("Unrecognized option: -%c\n", optopt); + else + printf("Unrecognized option: %s\n", + argv[optind - 1]); + errorcnt++; + break; + case ':': + printf("Missing argument to %s\n", argv[optind - 1]); + errorcnt++; + break; + default: + printf("Internal error at %s:%d\n", __FILE__, __LINE__); + exit(1); + } + } + + /* Read the FW file */ + FILE *f = fopen(firmware_binary, "rb"); + if (!(f && fread(fw_data, 1, FW_SIZE, f) == FW_SIZE)) { + printf("Failed to read firmware from %s\n", firmware_binary); + errorcnt++; + } + + if (errorcnt) + usage(errorcnt); + +} + +/* USB transfer related */ +static unsigned char rx_buf[128]; +static unsigned char tx_buf[128]; + +static struct libusb_device_handle *devh; +static struct libusb_transfer *rx_transfer; +static struct libusb_transfer *tx_transfer; + +static int claimed_iface; +static int iface_num = -1; +static int tx_ready; +static int do_exit; + +static void request_exit(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + do_exit++; /* Why need this ? */ + + if (tx_transfer) + libusb_free_transfer(tx_transfer); + if (rx_transfer) + libusb_free_transfer(rx_transfer); + if (devh) { + if (claimed_iface) + libusb_release_interface(devh, iface_num); + libusb_close(devh); + } + libusb_exit(NULL); + exit(1); +} + +#define DIE(msg, r) \ + request_exit("%s: line %d, %s\n", msg, __LINE__, \ + libusb_error_name(r)) + +static void sighandler(int signum) +{ + request_exit("caught signal %d: %s\n", signum, sys_siglist[signum]); +} + +static int find_interface_with_endpoint(int want_ep_num) +{ + int iface_num = -1; + int r, i, j, k; + struct libusb_device *dev; + struct libusb_config_descriptor *conf = 0; + const struct libusb_interface *iface0; + const struct libusb_interface_descriptor *iface; + const struct libusb_endpoint_descriptor *ep; + + dev = libusb_get_device(devh); + r = libusb_get_active_config_descriptor(dev, &conf); + if (r < 0) { + DIE("get_active_config", r); + return -1; + } + + for (i = 0; i < conf->bNumInterfaces; i++) { + iface0 = &conf->interface[i]; + for (j = 0; j < iface0->num_altsetting; j++) { + iface = &iface0->altsetting[j]; + for (k = 0; k < iface->bNumEndpoints; k++) { + ep = &iface->endpoint[k]; + if (ep->bEndpointAddress == want_ep_num) { + iface_num = i; + break; + } + } + } + } + + libusb_free_config_descriptor(conf); + return iface_num; +} + +static void init_with_libusb(void) +{ + int r = 1; + + printf("init usb interface\n"); + r = libusb_init(NULL); + if (r < 0) + DIE("init", r); + + printf("open_device %04x:%04x\n", vid, pid); + devh = libusb_open_device_with_vid_pid(NULL, vid, pid); + if (!devh) + request_exit("can't find device\n"); + + iface_num = find_interface_with_endpoint(ep_num); + if (iface_num < 0) + request_exit("can't find interface owning EP %d\n", ep_num); + + printf("claim_interface %d to use endpoint %d\n", iface_num, ep_num); + r = libusb_claim_interface(devh, iface_num); + if (r < 0) + DIE("claim interface", r); + claimed_iface = 1; +} + +static void register_sigaction(void) +{ + struct sigaction sigact; + sigact.sa_handler = sighandler; + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + sigaction(SIGINT, &sigact, NULL); + sigaction(SIGTERM, &sigact, NULL); + sigaction(SIGQUIT, &sigact, NULL); +} + +/* Transfer over libusb */ +#define I2C_PORT_ON_HAMMER 0x00 +#define I2C_ADDRESS_ON_HAMMER 0x15 + +static int check_read_status(int r, int expected, int actual) +{ + int i; + if (r) + printf("Warning: libusb_bulk_transfer return error : %d\n", r); + if (actual != (expected + 4)) { + printf("Warning: Not reading back %d bytes.\n", expected); + r = 1; + } + + /* Check transaction status as defined in usb_i2c.h */ + for (i = 0; i < 4; ++i) + if (rx_buf[i] != 0) + break; + + if (i != 4) { + r = le_bytes_to_int(rx_buf); + printf("Warning: Defined error code (%d) returned.\n", r); + } + + if (r) { + printf("Dumping the receive buffer:\n"); + printf(" Recv %d bytes from USB hosts.\n", actual); + for (i = 0; i < actual; ++i) + printf(" [%2d]bytes: 0x%0x\n", i, rx_buf[i]); + } + return r; +} + +static int libusb_single_write_and_read( + uint8_t *to_write, uint16_t write_length, + uint8_t *to_read, uint8_t read_length) +{ + int r; + int actual_length = -1; + tx_transfer = rx_transfer = 0; + + memmove(tx_buf + 4, to_write, write_length); + tx_buf[0] = I2C_PORT_ON_HAMMER; + tx_buf[1] = I2C_ADDRESS_ON_HAMMER; + tx_buf[2] = write_length; + tx_buf[3] = read_length; + tx_ready = 4 + write_length; + + r = libusb_bulk_transfer(devh, + (ep_num | LIBUSB_ENDPOINT_OUT), + tx_buf, tx_ready, &actual_length, 0); + if (r == 0 && actual_length == tx_ready) { + r = libusb_bulk_transfer(devh, + (ep_num | LIBUSB_ENDPOINT_IN), + rx_buf, 1024, &actual_length, 0); + } + return check_read_status(r, read_length, actual_length); +} + +/* Control Elan trackpad I2C over USB */ +#define ETP_I2C_INF_LENGTH 2 + +static int elan_write_and_read( + int reg, unsigned char *buf, int read_length, + int with_cmd, int cmd) +{ + + tx_buf[0] = (reg >> 0) & 0xff; + tx_buf[1] = (reg >> 8) & 0xff; + if (with_cmd) { + tx_buf[2] = (cmd >> 0) & 0xff; + tx_buf[3] = (cmd >> 8) & 0xff; + } + return libusb_single_write_and_read( + tx_buf, with_cmd ? 4 : 2, rx_buf, read_length); +} + +static int elan_read_block(int reg, unsigned char *buf, int read_length) + + return elan_write_and_read(reg, buf, read_length, 0, 0); +} + +static int elan_read_cmd(int reg) +{ + return elan_read_block(reg, rx_buf, ETP_I2C_INF_LENGTH); +} + +/* Elan trackpad firmware information related */ +#define ETP_I2C_IAP_VERSION_CMD 0x0110 +#define ETP_I2C_FW_VERSION_CMD 0x0102 +#define ETP_I2C_IAP_CHECKSUM_CMD 0x0315 +#define ETP_I2C_FW_CHECKSUM_CMD 0x030F + +static int elan_get_version(int is_iap) +{ + elan_read_cmd( + is_iap ? ETP_I2C_IAP_VERSION_CMD : ETP_I2C_FW_VERSION_CMD); + return le_bytes_to_int(rx_buf + 4); +} + +static int elan_get_checksum(int is_iap) +{ + elan_read_cmd( + is_iap ? ETP_I2C_IAP_CHECKSUM_CMD : ETP_I2C_FW_CHECKSUM_CMD); + return le_bytes_to_int(rx_buf + 4); +} + +static void get_fw_info(void) +{ + int iap_version = -1; + int fw_version = -1; + unsigned int iap_checksum = 0xffff; + unsigned int fw_checksum = 0xffff; + + printf("Querying device info...\n"); + fw_checksum = elan_get_checksum(0); + iap_checksum = elan_get_checksum(1); + fw_version = elan_get_version(0); + iap_version = elan_get_version(1); + printf("IAP version: %4x, FW version: %4x\n", + iap_version, fw_version); + printf("IAP checksum: %4x, FW checksum: %4x\n", + iap_checksum, fw_checksum); +} + +int main(int argc, char *argv[]) +{ + parse_cmdline(argc, argv); + init_with_libusb(); + register_sigaction(); + get_fw_info(); + /* TODO(itspeter): Update the firmware */ + + /* Print the updated firmware information */ + get_fw_info(); + return 0; +} |