summaryrefslogtreecommitdiff
path: root/extra
diff options
context:
space:
mode:
authorChun-Ta Lin <itspeter@google.com>2017-06-05 19:30:00 +0800
committerchrome-bot <chrome-bot@chromium.org>2017-06-09 07:10:41 -0700
commit0b5e3aa6428d135ff8ce638bc8a10f76fe9731f5 (patch)
tree7b4290b084a301e13f52e856d46688af80baf484 /extra
parentf40e79b3f18d23c49418a6cbd91430e3d5e116a6 (diff)
downloadchrome-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/Makefile34
-rw-r--r--extra/touchpad_updater/touchpad_updater.c392
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;
+}