diff options
Diffstat (limited to 'extra/touchpad_updater/touchpad_updater.c')
-rw-r--r-- | extra/touchpad_updater/touchpad_updater.c | 669 |
1 files changed, 0 insertions, 669 deletions
diff --git a/extra/touchpad_updater/touchpad_updater.c b/extra/touchpad_updater/touchpad_updater.c deleted file mode 100644 index 716ded00f5..0000000000 --- a/extra/touchpad_updater/touchpad_updater.c +++ /dev/null @@ -1,669 +0,0 @@ -/* - * 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 uint8_t extended_i2c_exercise; /* non-zero to exercise */ -static char *firmware_binary = "144.0_2.0.bin"; /* firmware blob */ - -/* Firmware binary blob related */ -#define MAX_FW_PAGE_SIZE 512 -#define MAX_FW_PAGE_COUNT 1024 -#define MAX_FW_SIZE (128 * 1024) - -static uint8_t fw_data[MAX_FW_SIZE]; -int fw_page_count; -int fw_page_size; -int fw_size; -uint8_t ic_type; -int iap_version; - -/* Utility functions */ -static int le_bytes_to_int(uint8_t *buf) -{ - return buf[0] + (int)(buf[1] << 8); -} - -/* Command line parsing related */ -static char *progname; -static char *short_opts = ":f:v:p:e:hd"; -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'}, - {"debug", 0, NULL, 'd'}, - {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" - " -d,--debug Exercise extended read I2C over USB\n" - " and print verbose debug messages.\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) strtoull(optarg, &e, 16); - if (!*optarg || (e && *e)) { - printf("Invalid argument: \"%s\"\n", optarg); - errorcnt++; - } - break; - case 'v': - vid = (uint16_t) strtoull(optarg, &e, 16); - if (!*optarg || (e && *e)) { - printf("Invalid argument: \"%s\"\n", optarg); - errorcnt++; - } - break; - case 'e': - ep_num = (uint8_t) strtoull(optarg, &e, 0); - if (!*optarg || (e && *e)) { - printf("Invalid argument: \"%s\"\n", optarg); - errorcnt++; - } - break; - case 'd': - extended_i2c_exercise = 1; - 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); - } - } - - if (errorcnt) - usage(errorcnt); - -} - -/* USB transfer related */ -static uint8_t rx_buf[1024]; -static uint8_t tx_buf[1024]; - -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 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, strsignal(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 || extended_i2c_exercise) { - printf("\nDumping 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; -} - -#define MAX_USB_PACKET_SIZE 64 -#define PRIMITIVE_READING_SIZE 60 - -static int libusb_single_write_and_read( - const uint8_t *to_write, uint16_t write_length, - uint8_t *to_read, uint16_t read_length) -{ - int r; - int tx_ready; - int remains; - int sent_bytes = 0; - int actual_length = -1; - int offset = read_length > PRIMITIVE_READING_SIZE ? 6 : 4; - tx_transfer = rx_transfer = 0; - - memmove(tx_buf + offset, to_write, write_length); - tx_buf[0] = I2C_PORT_ON_HAMMER | ((write_length >> 8) << 4); - tx_buf[1] = I2C_ADDRESS_ON_HAMMER; - tx_buf[2] = write_length & 0xff; - if (read_length > PRIMITIVE_READING_SIZE) { - tx_buf[3] = (read_length & 0x7f) | (1 << 7); - tx_buf[4] = read_length >> 7; - if (extended_i2c_exercise) { - printf("Triggering extended reading." - "rc:%0x, rc1:%0x\n", - tx_buf[3], tx_buf[4]); - printf("Expecting %d Bytes.\n", - (tx_buf[3] & 0x7f) | (tx_buf[4] << 7)); - } - } else { - tx_buf[3] = read_length; - } - - /* - * TODO: This loop is probably not required as we write the whole block - * in one transaction. - */ - while (sent_bytes < (offset + write_length)) { - tx_ready = remains = (offset + write_length) - sent_bytes; - - r = libusb_bulk_transfer(devh, - (ep_num | LIBUSB_ENDPOINT_OUT), - tx_buf + sent_bytes, tx_ready, - &actual_length, 5000); - if (r == 0 && actual_length == tx_ready) { - r = libusb_bulk_transfer(devh, - (ep_num | LIBUSB_ENDPOINT_IN), - rx_buf, sizeof(rx_buf), - &actual_length, 5000); - } - r = check_read_status( - r, (remains == tx_ready) ? read_length : 0, - actual_length); - if (r) - break; - sent_bytes += tx_ready; - } - return r; -} - -/* Control Elan trackpad I2C over USB */ -#define ETP_I2C_INF_LENGTH 2 - -static int elan_write_and_read( - int reg, uint8_t *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, uint8_t *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); -} - -static int elan_write_cmd(int reg, int cmd) -{ - return elan_write_and_read(reg, rx_buf, 0, 1, cmd); -} - -/* 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 -#define ETP_I2C_OSM_VERSION_CMD 0x0103 - -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 void elan_get_ic_page_count(void) -{ - elan_read_cmd(ETP_I2C_OSM_VERSION_CMD); - - ic_type = rx_buf[5]; - printf("ic_type: %02x\n", ic_type); - - switch (ic_type) { - case 0x09: - fw_page_count = 768; - break; - case 0x0D: - fw_page_count = 896; - break; - case 0x00: - case 0x10: - case 0x14: - fw_page_count = 1024; - break; - default: - request_exit("The IC type is not supported.\n"); - } - - iap_version = elan_get_version(1); - if (ic_type == 0x14 && iap_version >= 2) { - fw_page_count /= 8; - fw_page_size = 512; - } else if (ic_type >= 0x0D && iap_version >= 1) { - fw_page_count /= 2; - fw_page_size = 128; - } else { - fw_page_size = 64; - } -} - -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 uint16_t elan_get_fw_info(void) -{ - int fw_version = -1; - uint16_t iap_checksum = 0xffff; - uint16_t 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); - return fw_checksum; -} - -/* Update preparation */ -#define ETP_I2C_IAP_RESET_CMD 0x0314 -#define ETP_I2C_IAP_RESET 0xF0F0 -#define ETP_I2C_IAP_CTRL_CMD 0x0310 -#define ETP_I2C_MAIN_MODE_ON (1 << 9) -#define ETP_I2C_IAP_CMD 0x0311 -#define ETP_I2C_IAP_PASSWORD 0x1EA5 -#define ETP_I2C_IAP_TYPE_CMD 0x0304 - -static int elan_in_main_mode(void) -{ - elan_read_cmd(ETP_I2C_IAP_CTRL_CMD); - return le_bytes_to_int(rx_buf + 4) & ETP_I2C_MAIN_MODE_ON; -} - -static int elan_read_write_iap_type(void) -{ - for (int retry = 0; retry < 3; ++retry) { - uint16_t val; - - if (elan_write_cmd(ETP_I2C_IAP_TYPE_CMD, - fw_page_size / 2)) - return -1; - - if (elan_read_cmd(ETP_I2C_IAP_TYPE_CMD)) - return -1; - - val = le_bytes_to_int(rx_buf + 4); - if (val == fw_page_size / 2) { - printf("%s: OK\n", __func__); - return 0; - } - - } - return -1; -} - -static void elan_prepare_for_update(void) -{ - printf("%s\n", __func__); - - int initial_mode = elan_in_main_mode(); - if (!initial_mode) { - printf("In IAP mode, reset IC.\n"); - elan_write_cmd(ETP_I2C_IAP_RESET_CMD, ETP_I2C_IAP_RESET); - usleep(30 * 1000); - } - - /* Send the passphrase */ - elan_write_cmd(ETP_I2C_IAP_CMD, ETP_I2C_IAP_PASSWORD); - usleep((initial_mode ? 100 : 30) * 1000); - - /* We should be in the IAP mode now */ - if (elan_in_main_mode()) - request_exit("Failure to enter IAP mode, still in main mode\n"); - - if (ic_type >= 0x0D && iap_version >= 1) { - if (elan_read_write_iap_type()) - request_exit("Failure to set IAP mode\n"); - } - - /* Send the passphrase again */ - elan_write_cmd(ETP_I2C_IAP_CMD, ETP_I2C_IAP_PASSWORD); - usleep(30 * 1000); - - /* Verify the password */ - if (elan_read_cmd(ETP_I2C_IAP_CMD)) - request_exit("cannot read iap password.\n"); - if (le_bytes_to_int(rx_buf + 4) != ETP_I2C_IAP_PASSWORD) - request_exit("Got an unexpected IAP password %4x\n", - le_bytes_to_int(rx_buf + 4)); -} - -/* Firmware block update */ -#define ETP_IAP_START_ADDR 0x0083 - -static uint16_t elan_calc_checksum(uint8_t *data, int length) -{ - uint16_t checksum = 0; - for (int i = 0; i < length; i += 2) - checksum += ((uint16_t)(data[i+1]) << 8) | (data[i]); - return checksum; -} - -static int elan_get_iap_addr(void) -{ - return le_bytes_to_int(fw_data + ETP_IAP_START_ADDR * 2) * 2; -} - -#define ETP_I2C_IAP_REG_L 0x01 -#define ETP_I2C_IAP_REG_H 0x06 - -#define ETP_FW_IAP_PAGE_ERR (1 << 5) -#define ETP_FW_IAP_INTF_ERR (1 << 4) - -static int elan_write_fw_block(uint8_t *raw_data, uint16_t checksum) -{ - uint8_t page_store[MAX_FW_PAGE_SIZE + 4]; - int rv; - - page_store[0] = ETP_I2C_IAP_REG_L; - page_store[1] = ETP_I2C_IAP_REG_H; - memcpy(page_store + 2, raw_data, fw_page_size); - page_store[fw_page_size + 2 + 0] = (checksum >> 0) & 0xff; - page_store[fw_page_size + 2 + 1] = (checksum >> 8) & 0xff; - - rv = libusb_single_write_and_read( - page_store, fw_page_size + 4, rx_buf, 0); - if (rv) - return rv; - usleep((fw_page_size >= 512 ? 50 : 35) * 1000); - elan_read_cmd(ETP_I2C_IAP_CTRL_CMD); - rv = le_bytes_to_int(rx_buf + 4); - if (rv & (ETP_FW_IAP_PAGE_ERR | ETP_FW_IAP_INTF_ERR)) { - printf("IAP reports failed write : %x\n", rv); - return rv; - } - return 0; -} - - -static uint16_t elan_update_firmware(void) -{ - uint16_t checksum = 0, block_checksum; - int rv; - - printf("%s\n", __func__); - - for (int i = elan_get_iap_addr(); i < fw_size; i += fw_page_size) { - printf("\rUpdating page %3d...", i / fw_page_size); - fflush(stdout); - block_checksum = elan_calc_checksum(fw_data + i, fw_page_size); - rv = elan_write_fw_block(fw_data + i, block_checksum); - if (rv) - request_exit("Failed to update.\n"); - checksum += block_checksum; - printf(" Updated, checksum: %d", checksum); - fflush(stdout); - } - return checksum; -} - -static void pretty_print_buffer(uint8_t *buf, int len) -{ - int i; - - printf("Buffer = 0x"); - for (i = 0; i < len; ++i) - printf("%02X", buf[i]); - printf("\n"); -} - -int main(int argc, char *argv[]) -{ - uint16_t local_checksum; - uint16_t remote_checksum; - - parse_cmdline(argc, argv); - init_with_libusb(); - register_sigaction(); - - /* - * Judge IC type and get page count first. - * Then check the FW file. - */ - elan_get_ic_page_count(); - fw_size = fw_page_count * fw_page_size; - printf("FW has %d bytes x %d pages\n", fw_page_size, fw_page_count); - - /* Read the FW file */ - FILE *f = fopen(firmware_binary, "rb"); - if (!f) - request_exit("Cannot find binary: %s\n", firmware_binary); - if (fread(fw_data, 1, fw_size, f) != (unsigned int)fw_size) - request_exit("binary size mismatch, expect %d\n", fw_size); - - /* - * It is possible that you are not able to get firmware info. This - * might due to an incomplete update last time - */ - elan_get_fw_info(); - - /* Trigger an I2C transaction of expecting reading of 633 bytes. */ - if (extended_i2c_exercise) { - tx_buf[0] = 0x05; - tx_buf[1] = 0x00; - tx_buf[2] = 0x3C; - tx_buf[3] = 0x02; - tx_buf[4] = 0x06; - tx_buf[5] = 0x00; - libusb_single_write_and_read(tx_buf, 6, rx_buf, 633); - pretty_print_buffer(rx_buf, 637); - } - - /* Get the trackpad ready for receiving update */ - elan_prepare_for_update(); - - local_checksum = elan_update_firmware(); - /* Wait for a reset */ - usleep(600 * 1000); - remote_checksum = elan_get_checksum(1); - if (remote_checksum != local_checksum) - printf("checksum diff local=[%04X], remote=[%04X]\n", - local_checksum, remote_checksum); - - /* Print the updated firmware information */ - elan_get_fw_info(); - return 0; -} |