| /****************************************************************************** |
| * |
| * Copyright 2011, Cypress Semiconductor Corporation. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * 3. Neither the name of the Cypress Semiconductor Corporation, nor the names |
| * of its contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY Cypress Semiconductor Corporation ''AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Cypress Semiconductor Corporation |
| * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
| * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
| * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
| * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| * The views and conclusions contained in the software and documentation are |
| * those of the authors and should not be interpreted as representing official |
| * policies, either expressed or implied, of Cypress Semiconductor Corporation. |
| * |
| ******************************************************************************* |
| * |
| * Authors: |
| * Dudley Du <dudl@cypress.com> |
| * Usage: |
| * Used to update Cypress Trackpad device firmware image from .iic or |
| * .bin files. |
| * e.g., update Cypress Trackpad device with new "new_image.iic" file: |
| * $ sudo ./cyapa_fw_update new_image.iic -f |
| * Version: |
| * 1.0.0 2011/07/26 Initial release of cyapa_fw_update utility tool. |
| */ |
| |
| #include <ctype.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <getopt.h> |
| #include <libgen.h> |
| #include <linux/limits.h> |
| #include <linux/reboot.h> |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/ioctl.h> |
| #include <sys/reboot.h> |
| #include <sys/stat.h> |
| #include <sys/time.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| #include "cyapa.h" |
| |
| #ifdef DBG |
| #define prt_info(fmt, ...) printf(fmt, ##__VA_ARGS__) |
| #define prt_warn(fmt, ...) printf("WARNING: " fmt, ##__VA_ARGS__) |
| #define prt_err(fmt, ...) printf("ERROR: " fmt, ##__VA_ARGS__) |
| #define prt_dbg(fmt, ...) printf("DEBUG: %d: " fmt, __LINE__, ##__VA_ARGS__) |
| #define DBG_DUMP_DATA_BLOCK 1 |
| #else |
| #define prt_info(fmt, ...) printf(fmt, ##__VA_ARGS__) |
| #define prt_warn(fmt, ...) printf("WARNING: " fmt, ##__VA_ARGS__) |
| #define prt_err(fmt, ...) printf("ERROR: " fmt, ##__VA_ARGS__) |
| #define prt_dbg(fmt, ...) |
| #define DBG_DUMP_DATA_BLOCK 0 |
| #endif |
| |
| #define CYAPA_FW_UPDATE_VER "1.0.0" |
| |
| #define DEFAULT_PROGRAM_NAME "cyapa_fw_update" |
| #define DEFAULT_FW_BAK_FOLDER "/tmp/cypress" |
| #define DEFAULT_FW_BAK_IMAGE_NAME "/tmp/cypress/cyapa_bak_firmware.bin" |
| |
| #define FILE_TYPE_IIC 0 |
| #define FILE_TYPE_BIN 1 |
| |
| #define CYAPA_FW_OFFSET_START 0x0780 |
| #define CYAPA_FW_CHECKSUM_END 0x07FF |
| #define CYAPA_FW_OFFSET_END 0x7FFF |
| #define CYAPA_BAK_READ_BLOCK_LEN 16 |
| #define CYAPA_UPDATE_BLOCK_LEN 64 |
| |
| #define CYAPA_IIC_CMD_OP_NONE 0 |
| #define CYAPA_IIC_CMD_OP_READ 1 |
| #define CYAPA_IIC_CMD_OP_WRITE 2 |
| #define CYAPA_IIC_CMD_OP_DELAY 3 |
| #define CYAPA_IIC_CMD_OP_tries_WRITE 4 |
| |
| |
| struct args { |
| /* the value should be FILE_TYPE_IIC or FILE_TYPE_BIN. */ |
| int file_type; |
| bool backup_fw; |
| bool force; |
| |
| char *new_fw_image; |
| char *bak_fw_image; |
| |
| int fd_dev; |
| int fd_new_fw; |
| int fd_bak_fw; |
| }; |
| |
| /* |
| * command format: |
| * write data: |
| * byte 0 : operation. CYAPA_IIC_CMD_OP_WRITE |
| * byte 1 : length of behind real command data, from byte 2 to byte n.q |
| * byte 2 - byte n : real command as show in .iic file. |
| * byte 2 : I2C address, 0x67 for trackpad. |
| * byte 3 : I2C registers offset where byte 4 -byte n should be written. |
| * usually 0x00 for trackpad. |
| * byte 4 - byte n : command data to be written to trackpad. |
| * byte 4 : index, 0x00, 0x10, 0x20, 0x30, 0x40. |
| * |
| * read status: |
| * byte 0 : operation. CYAPA_IIC_CMD_OP_READ |
| * byte 1 : read length. |
| * |
| * delay: |
| * byte 0 : operation. CYAPA_IIC_CMD_OP_DELAY |
| * byte 1 - byte 4 : delay milliseconds (int). |
| */ |
| struct cmds_update_block { |
| int valid_cmds; |
| unsigned char cmds[8][24]; |
| }; |
| |
| |
| #if DBG_DUMP_DATA_BLOCK |
| void cyapa_dump_data_block(const char *str, unsigned char *buf, |
| unsigned int offset, int length) |
| { |
| char strbuf[2048]; |
| unsigned int rest_len = sizeof(strbuf); |
| char *p = strbuf; |
| int i, len; |
| |
| memset(strbuf, 0, sizeof(strbuf)); |
| len = snprintf(p, rest_len, "%s: offset 0x%04X, %d bytes:", |
| str, offset, length); |
| rest_len -= len; |
| p += len; |
| for (i = 0; i < length && rest_len; i++, p += len, rest_len -= len) { |
| len = snprintf(p, rest_len, " %02X", |
| *(unsigned char *)(buf + i)); |
| } |
| |
| prt_info("%s\n", strbuf); |
| } |
| |
| void cyapa_dump_delay_time(const char *str, int delay) |
| { |
| prt_info("%s: delay [%d] ms\n", str, delay); |
| } |
| #else |
| void cyapa_dump_data_block(const char *str, unsigned char *buf, |
| unsigned int offset, int length) {} |
| void cyapa_dump_delay_time(const char *str, int delay) {} |
| #endif |
| |
| void show_usage(char *name) |
| { |
| printf("Usage: %s <new-firmware-image> [options]\n", |
| name ? name : DEFAULT_PROGRAM_NAME); |
| printf("Options:\n"); |
| printf("\t -b\n"); |
| printf("\t Backup current firmware before updating" |
| " to new firmware.\n"); |
| printf("\t If -o option is not set, the default back up" |
| " path is \"/tmp/cypress\".\n"); |
| printf("\t If -o option is set, use the path specified in" |
| "-o <path>.\n"); |
| printf("\t -o path\n"); |
| printf("\t Specifies full path of the output file for where to\n"); |
| printf("\t back up a copy of the current trackpad firmware.\n"); |
| printf("\t Only valid when option -b is set and\n"); |
| printf("\t filename must have a \'bin\' extension.\n"); |
| printf("\t By default, without -o option, the back up path is:\n"); |
| printf("\t %s\n", DEFAULT_FW_BAK_IMAGE_NAME); |
| printf("\t If the requested update fails, this backed up copy\n"); |
| printf("\t will be rewritten to the trackpad.\n"); |
| printf("\t -f\n"); |
| printf("\t Force new firmware to be updated to trackpad device\n"); |
| printf("\t and suppress any prompt information.\n"); |
| printf("\t After update, the system will be rebooted" |
| " immediately\n"); |
| printf("\t if required, so be careful when using this option.\n"); |
| printf("\t -v, -V\n"); |
| printf("\t Print version information and exit.\n"); |
| printf("\t -h, -H, --help\n"); |
| printf("\t Show this help information.\n"); |
| printf("NOTE:\n"); |
| printf(" This program must be executed as root\n"); |
| printf(" After new firmware is updated a system" |
| " system reboot may required.\n"); |
| printf(" so make sure that you have closed and stored" |
| " all opened files before updating.\n"); |
| printf(" cyapa_fw_update program release version: %s\n\n", |
| CYAPA_FW_UPDATE_VER); |
| } |
| |
| void show_version_info(char *name) |
| { |
| printf("Cypress utility: %s %s\n\n", name, CYAPA_FW_UPDATE_VER); |
| printf("Copyright (C) 2011 Cypress Semiconductor Corporation.\n"); |
| printf("License BSD 2-Clause License or later.\n"); |
| printf("This is free software: you are free to change and" |
| " redistribute it.\n"); |
| printf("There is NO WARRANTY, to the extent permitted by law.\n\n"); |
| } |
| |
| /** |
| * return value: |
| * 0 - parse and get valid input parameters. |
| * 1 - show program usage information. |
| * 2 - show program version information. |
| * < 0 - failed to parse and check input parameters. |
| */ |
| int check_input_args(int argc, char **argv, struct args *args) |
| { |
| int i; |
| int ret = 0; |
| int fd = 0; |
| char *filename = NULL; |
| char *extstr = NULL; |
| char *temp_path = NULL; |
| |
| memset(args, 0, sizeof(struct args)); |
| |
| /* parse options for this program. */ |
| for (i = 0; i < argc; i++) { |
| if (!strcmp(argv[i], "-b")) { |
| args->backup_fw = true; |
| } else if (!strcmp(argv[i], "-o")) { |
| i++; |
| if (argv[i] == NULL) { |
| ret = -1; |
| goto error; |
| } |
| |
| extstr = strrchr(argv[i], '.'); |
| if ((extstr == NULL) || |
| (strcmp(extstr, ".bin") != 0)) { |
| ret = -2; |
| goto error; |
| } |
| |
| if (access(argv[i], F_OK)) |
| remove(argv[i]); |
| args->bak_fw_image = calloc(1, strlen(argv[i]) + 1); |
| if (args->bak_fw_image == NULL) { |
| ret = -3; |
| goto error; |
| } |
| strcpy(args->bak_fw_image, argv[i]); |
| } else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-H") |
| || !strcmp(argv[i], "--help")) { |
| ret = 1; |
| goto error; |
| } else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "-V")) { |
| ret = 2; |
| goto error; |
| } else if (!strcmp(argv[i], "-f")) { |
| args->force = true; |
| } else { |
| /* parse new firmware file. */ |
| if (argv[i] == NULL || access(argv[i], R_OK)) { |
| ret = -4; |
| goto error; |
| } |
| |
| args->new_fw_image = calloc(1, strlen(argv[i]) + 1); |
| if (args->new_fw_image == NULL) { |
| ret = -5; |
| goto error; |
| } |
| strcpy(args->new_fw_image, argv[i]); |
| } |
| } |
| |
| if (args->new_fw_image != NULL) { |
| temp_path = calloc(1, PATH_MAX); |
| if (temp_path == NULL) { |
| ret = -6; |
| goto error; |
| } |
| strcpy(temp_path, args->new_fw_image); |
| |
| filename = basename(temp_path); |
| extstr = strrchr(filename, '.'); |
| if (extstr && !strcmp(extstr, ".iic")) |
| args->file_type = FILE_TYPE_IIC; |
| else if (extstr && !strcmp(extstr, ".bin")) |
| args->file_type = FILE_TYPE_BIN; |
| else { |
| ret = -7; |
| goto error; |
| } |
| } |
| |
| if (args->backup_fw && args->bak_fw_image == NULL) { |
| args->bak_fw_image = |
| calloc(1, strlen(DEFAULT_FW_BAK_IMAGE_NAME) + 1); |
| if (args->bak_fw_image == NULL) { |
| ret = -8; |
| goto error; |
| } |
| |
| strcpy(args->bak_fw_image, DEFAULT_FW_BAK_IMAGE_NAME); |
| mkdir(DEFAULT_FW_BAK_FOLDER, 0777); |
| remove(args->bak_fw_image); |
| fd = creat(args->bak_fw_image, O_CREAT | |
| O_TRUNC | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); |
| if (fd < 0) { |
| ret = -9; |
| goto error; |
| } |
| close(fd); |
| } |
| |
| if (args->new_fw_image) |
| prt_info("Update new firmware image from: %s\n", |
| args->new_fw_image); |
| if (args->backup_fw && args->backup_fw) |
| prt_info("Backup firmware image from trackpad device to: %s\n", |
| args->bak_fw_image); |
| |
| free(temp_path); |
| return 0; |
| |
| error: |
| if (temp_path == NULL) |
| temp_path = malloc(PATH_MAX); |
| if (temp_path == NULL || args->new_fw_image == NULL) { |
| filename = NULL; |
| } else { |
| memset(temp_path, 0, PATH_MAX); |
| strcpy(temp_path, args->new_fw_image); |
| filename = basename(temp_path); |
| } |
| if (ret == 2) |
| show_version_info(filename); |
| else |
| show_usage(filename); |
| |
| free(temp_path); |
| free(args->new_fw_image); |
| free(args->bak_fw_image); |
| return ret; |
| } |
| |
| #define MAX_PROGRESS_LENGTH 110 |
| #define DEFAULT_TERM_WIDTH 80 |
| int get_terminal_width(void) |
| { |
| int ret; |
| struct winsize sz; |
| |
| ret = ioctl(0, TIOCGWINSZ, &sz); |
| if (ret < 0) |
| return DEFAULT_TERM_WIDTH; |
| else |
| return (sz.ws_col > MAX_PROGRESS_LENGTH) ? |
| MAX_PROGRESS_LENGTH : |
| ((sz.ws_col < 20) ? DEFAULT_TERM_WIDTH : sz.ws_col); |
| } |
| |
| /** |
| * Output progress format: |
| * |----------------------------- | ddd% | |
| */ |
| #define SHOW_PROGRESS_INIT 0 |
| #define SHOW_PROGRESS_CONT 1 |
| #define SHOW_PROGRESS_EXIT 2 |
| void show_progress(int percent, int state, char *str) |
| { |
| int i; |
| int term_width; |
| int progress_count; |
| char buf[MAX_PROGRESS_LENGTH]; |
| static int last_str_len; |
| |
| #if DBG_DUMP_DATA_BLOCK |
| /* when enable dumping data, disable show progress. */ |
| return; |
| #endif |
| |
| if (state == SHOW_PROGRESS_INIT) { |
| prt_info("\r"); |
| if (str != NULL) |
| prt_info("%s", str); |
| last_str_len = 0; |
| |
| return; |
| } else if (state == SHOW_PROGRESS_EXIT) { |
| prt_info("\n"); |
| if (str != NULL) |
| prt_info("%s", str); |
| last_str_len = 0; |
| |
| return; |
| } |
| |
| if (state != SHOW_PROGRESS_CONT) |
| return; |
| |
| fflush(stdout); |
| for (i = 0; i < last_str_len; i++) |
| prt_info("\b"); |
| |
| percent = (percent < 0) ? 0 : ((percent > 100) ? 100 : percent); |
| term_width = get_terminal_width(); |
| progress_count = percent * (term_width - 10) / 100; |
| memset(buf, ' ', term_width); |
| buf[0] = '|'; |
| memset(&buf[1], '-', progress_count); |
| buf[term_width - 9] = '|'; |
| sprintf(&buf[term_width - 7], "%3d", percent); |
| buf[term_width - 4] = '%'; |
| buf[term_width - 2] = '|'; |
| buf[term_width - 1] = '\0'; |
| |
| prt_info("%s", buf); |
| last_str_len = strlen(buf); |
| fflush(stdout); |
| } |
| |
| /* |
| * delay "usec" milliseconds. |
| */ |
| void wait(int usec) |
| { |
| struct timeval tv_start; |
| struct timeval tv_stop; |
| time_t seconds = (time_t)(usec / 1000); |
| suseconds_t microseconds = (suseconds_t)((usec % 1000) * 1000); |
| |
| gettimeofday(&tv_start, NULL); |
| do { |
| gettimeofday(&tv_stop, NULL); |
| if ((tv_stop.tv_sec - tv_start.tv_sec > seconds) || |
| ((tv_stop.tv_sec - tv_start.tv_sec == seconds) && |
| ((tv_stop.tv_usec - tv_start.tv_usec) |
| > microseconds))) { |
| break; |
| } |
| } while (1); |
| } |
| |
| #define TIMER_START 0 |
| #define TIMER_STOP 1 |
| void calculate_duration_time(int start_stop, char *str) |
| { |
| static time_t last_second; |
| static suseconds_t last_usec; |
| struct timeval tv; |
| suseconds_t duration_usecs; |
| char output_srt[] = "Duration time"; |
| |
| gettimeofday(&tv, NULL); |
| |
| if (start_stop == TIMER_START) { |
| last_second = tv.tv_sec; |
| last_usec = tv.tv_usec; |
| |
| return; |
| } |
| |
| if (start_stop != TIMER_STOP) |
| return; |
| |
| duration_usecs = (tv.tv_usec >= last_usec) ? (tv.tv_usec - last_usec) : |
| (1000000 - last_usec + tv.tv_usec); |
| prt_info("%s: %ld.%ld seconds\n", |
| (str == NULL) ? output_srt : str, |
| (long)(tv.tv_sec - last_second), |
| (long)duration_usecs); |
| last_second = tv.tv_sec; |
| last_usec = tv.tv_usec; |
| } |
| |
| int system_reboot_check(struct args *args, |
| int new_protocol, int old_protocol) |
| { |
| int input; |
| |
| /* protocol not changed, no reboot required. */ |
| if (new_protocol == old_protocol) |
| return 0; |
| |
| /* |
| * both old and new firmware support MT-B protocol, no reboot required. |
| */ |
| if (old_protocol > CYAPA_GEN2 && new_protocol > CYAPA_GEN2) |
| return 0; |
| |
| /* |
| * both old and new firmware support MT-B protocol, no reboot required. |
| */ |
| if (old_protocol < CYAPA_GEN3 && new_protocol < CYAPA_GEN3) |
| return 0; |
| |
| /* |
| * the protocol has been changed from MT-A to MT-B or |
| * from MT-B to MT-A, so system reboot is required before |
| * new updated firmware can be applied. |
| */ |
| if (!args->force) { |
| prt_warn("System reboot is required before" |
| " applying new firmware.\n"); |
| prt_warn("Please save and close all opened files.\n"); |
| prt_info("Do you want to reboot system immediately, <Y|N>: "); |
| while (!args->force && ((input = getchar()) != EOF)) { |
| if (toupper(input) == 'Y') { |
| prt_info("Reboot system immediately ...\n"); |
| reboot(LINUX_REBOOT_CMD_RESTART); |
| break; |
| } else if (toupper(input) == 'N') { |
| prt_info("Reboot system later," |
| " firmware update done.\n"); |
| break; |
| } |
| prt_info("\nDo you want to reboot system immediately," |
| " <Y|N>: "); |
| } |
| } else { |
| prt_warn("System reboot is required before" |
| " applying new firmware.\n"); |
| prt_warn("Reboot system ...\n"); |
| reboot(LINUX_REBOOT_CMD_RESTART); |
| } |
| |
| return 0; |
| } |
| |
| unsigned short cyapa_calculate_checksum(unsigned char *buf, int count) |
| { |
| int i; |
| int checksum = 0; |
| |
| for (i = 0; i < count; i++) |
| checksum = ((checksum + buf[i]) & 0xFFFF); |
| |
| return (unsigned short)checksum; |
| } |
| |
| int cyapa_read_reg(int fd, unsigned int offset, int bytes, unsigned char *buf) |
| { |
| int ret; |
| int tries = 3; |
| |
| do { |
| ret = (int)lseek(fd, (off_t)offset, SEEK_SET); |
| if (ret < 0) |
| continue; |
| |
| ret = (int)read(fd, buf, (ssize_t)bytes); |
| if (ret == bytes) |
| break; |
| } while (tries--); |
| |
| if (tries < 0) |
| return -1; |
| |
| return ret; |
| } |
| |
| int cyapa_write_reg(int fd, unsigned char *buf, unsigned int offset, int bytes) |
| { |
| int ret; |
| int tries = 3; |
| |
| do { |
| ret = (int)lseek(fd, (off_t)offset, SEEK_SET); |
| if (ret < 0) |
| continue; |
| |
| ret = (int)write(fd, buf, (ssize_t)bytes); |
| if (ret == bytes) |
| break; |
| } while (tries--); |
| |
| if (tries < 0) |
| return -1; |
| |
| return ret; |
| } |
| |
| int open_trackpad_dev(struct args *args) |
| { |
| char dev_name[64]; |
| unsigned int offset = 0; |
| |
| sprintf(dev_name, "/dev/%s", CYAPA_MISC_NAME); |
| args->fd_dev = open(dev_name, O_RDWR); |
| if (args->fd_dev < 0) { |
| prt_err("Open trackpad interface device %s failed, %d.\n", |
| dev_name, args->fd_dev); |
| return -EFAULT; |
| } |
| |
| if (lseek(args->fd_dev, (off_t)offset, SEEK_SET) < 0) { |
| prt_err("Access trackpad device failed, %d\n", -errno); |
| close(args->fd_dev); |
| return -errno; |
| } |
| |
| return args->fd_dev; |
| } |
| |
| int open_new_fw_image(struct args *args) |
| { |
| unsigned int offset = 0; |
| |
| args->fd_new_fw = open(args->new_fw_image, O_RDONLY); |
| if (args->fd_new_fw < 0) { |
| prt_err("Open new firmware image file failed.\n"); |
| return -EFAULT; |
| } |
| |
| if (lseek(args->fd_new_fw, (off_t)offset, SEEK_SET) < 0) { |
| prt_err("Access new firmware image file failed, %d\n", -errno); |
| close(args->fd_new_fw); |
| return -errno; |
| } |
| |
| return args->fd_new_fw; |
| } |
| |
| int open_bak_fw_image(struct args *args) |
| { |
| unsigned int offset = 0; |
| |
| args->fd_bak_fw = |
| open(args->bak_fw_image, O_RDWR | O_CREAT | O_TRUNC); |
| if (args->fd_bak_fw < 0) { |
| prt_err("Open firmware backup image file failed.\n"); |
| return -EFAULT; |
| } |
| |
| if (lseek(args->fd_bak_fw, (off_t)offset, SEEK_SET) < 0) { |
| prt_err("Access firmware backup image failed, %d\n", -errno); |
| close(args->fd_bak_fw); |
| return -errno; |
| } |
| |
| return args->fd_bak_fw; |
| } |
| |
| int cyapa_fw_image_check(struct args *args) |
| { |
| int ret; |
| int tries = 3; |
| int ret_bytes; |
| unsigned int offset = 0; |
| unsigned char buf[64]; |
| int fd = args->fd_new_fw; |
| const char iic_identify[] = "00 00 FF 38 00 01 02 03 04 05 06 07"; |
| |
| ret_bytes = 0; |
| while ((ret_bytes != 64) && (tries-- > 0)) { |
| ret = (int)lseek(fd, (off_t)offset, SEEK_SET); |
| if (ret != (int)offset) |
| continue; |
| |
| memset(buf, 0, sizeof(buf)); |
| ret_bytes = (int)read(fd, buf, 64); |
| } |
| if (tries < 0) |
| return -1; |
| |
| if (args->file_type == FILE_TYPE_BIN) { |
| if (buf[0x28] != 0xC0 || buf[0x29] != 0xC1 || buf[0x2A] != 0xC2) |
| return -2; |
| } else { |
| if (strncmp(iic_identify, (const char *)&buf[5], |
| strlen(iic_identify))) |
| return -3; |
| } |
| |
| /* reset file cursor to the start of the image file. */ |
| tries = 3; |
| do { |
| if (lseek(args->fd_new_fw, (off_t)offset, SEEK_SET) < 0) |
| continue; |
| else |
| break; |
| } while (tries--); |
| if (tries < 0) |
| return -4; |
| |
| return 0; |
| } |
| |
| int cyapa_get_trackpad_run_mode(int fd, |
| struct cyapa_trackpad_run_mode *run_mode) |
| { |
| struct cyapa_misc_ioctl_data ioctl_data; |
| |
| memset(&ioctl_data, 0, sizeof(struct cyapa_misc_ioctl_data)); |
| ioctl_data.buf = (__u8 *)run_mode; |
| ioctl_data.len = sizeof(struct cyapa_trackpad_run_mode); |
| |
| if (ioctl(fd, CYAPA_GET_TRACKPAD_RUN_MODE, &ioctl_data) < 0) { |
| prt_dbg("Failed to get trackpad run mode state, %d\n", -errno); |
| return -errno; |
| } |
| |
| prt_dbg("GET: run_mode = %d, bootloader_state = %d\n", |
| run_mode->run_mode, run_mode->bootloader_state); |
| |
| return 0; |
| } |
| |
| int cyapa_set_trackpad_run_mode(int fd, |
| struct cyapa_trackpad_run_mode *run_mode) |
| { |
| struct cyapa_misc_ioctl_data ioctl_data; |
| |
| memset(&ioctl_data, 0, sizeof(struct cyapa_misc_ioctl_data)); |
| ioctl_data.buf = (__u8 *)run_mode; |
| ioctl_data.len = sizeof(struct cyapa_trackpad_run_mode); |
| |
| if (ioctl(fd, CYAYA_SEND_MODE_SWITCH_CMD, &ioctl_data) < 0) { |
| prt_dbg("failed to set trackpad device run mode state, %d\n", |
| --errno); |
| return -errno; |
| } |
| |
| prt_dbg("SET: run_mode = %d, bootloader_state = %d\n", |
| run_mode->run_mode, run_mode->bootloader_state); |
| |
| return 0; |
| } |
| |
| void cyapa_recovery_bootload_header(struct args *args) |
| { |
| unsigned char cmd[] = {0x00, 0xFF, 0x3C, |
| 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, |
| 0x07, 0x80}; |
| |
| cyapa_write_reg(args->fd_dev, cmd, 0, sizeof(cmd)); |
| wait(10); |
| } |
| |
| int cyapa_set_bootloader_idle_mode(struct args *args) |
| { |
| int tries = 3; |
| int fd = args->fd_dev; |
| struct cyapa_trackpad_run_mode run_mode; |
| |
| while (tries-- > 0) { |
| memset(&run_mode, 0, sizeof(struct cyapa_trackpad_run_mode)); |
| if (cyapa_get_trackpad_run_mode(fd, &run_mode) < 0) |
| continue; |
| |
| if ((run_mode.bootloader_state == CYAPA_BOOTLOADER_IDLE_STATE) |
| && (run_mode.run_mode == CYAPA_BOOTLOADER_MODE)) |
| break; /* already in correct state. */ |
| |
| if (run_mode.run_mode == CYAPA_OPERATIONAL_MODE) { |
| run_mode.rev_cmd = CYAPA_CMD_APP_TO_IDLE; |
| if (cyapa_set_trackpad_run_mode(fd, &run_mode) < 0) |
| continue; |
| |
| wait(300); |
| } else if ((run_mode.run_mode == CYAPA_BOOTLOADER_MODE) |
| && (run_mode.bootloader_state == |
| CYAPA_BOOTLOADER_ACTIVE_STATE)) { |
| run_mode.rev_cmd = CYAPA_CMD_ACTIVE_TO_IDLE; |
| if (cyapa_set_trackpad_run_mode(fd, &run_mode) < 0) |
| continue; |
| |
| wait(300); |
| } else { |
| /* |
| * unknown trackpad states. |
| * try to recovery bootloader header registers. |
| */ |
| cyapa_recovery_bootload_header(args); |
| continue; |
| } |
| } |
| |
| if (tries < 0) |
| return -1; |
| |
| return 0; |
| } |
| |
| int cyapa_set_bootloader_active_mode(struct args *args) |
| { |
| int tries = 3; |
| int fd = args->fd_dev; |
| struct cyapa_trackpad_run_mode run_mode; |
| |
| while (tries-- > 0) { |
| memset(&run_mode, 0, sizeof(struct cyapa_trackpad_run_mode)); |
| if (cyapa_get_trackpad_run_mode(fd, &run_mode) < 0) |
| continue; |
| |
| if ((run_mode.bootloader_state == CYAPA_BOOTLOADER_ACTIVE_STATE) |
| && (run_mode.run_mode == CYAPA_BOOTLOADER_MODE)) |
| break; /* already in correct state. */ |
| |
| if (run_mode.run_mode == CYAPA_OPERATIONAL_MODE) { |
| if (cyapa_set_bootloader_idle_mode(args) < 0) |
| continue; |
| |
| } else if ((run_mode.run_mode == CYAPA_BOOTLOADER_MODE) |
| && (run_mode.bootloader_state == |
| CYAPA_BOOTLOADER_IDLE_STATE)) { |
| run_mode.rev_cmd = CYAPA_CMD_IDLE_TO_ACTIVE; |
| if (cyapa_set_trackpad_run_mode(fd, &run_mode) < 0) |
| continue; |
| |
| wait(12000); |
| break; |
| } else { |
| /* |
| * unknown trackpad states. |
| * try to recovery bootloader header registers. |
| */ |
| cyapa_recovery_bootload_header(args); |
| continue; |
| } |
| } |
| |
| if (tries < 0) |
| return -1; |
| |
| return 0; |
| } |
| |
| int cyapa_set_app_operational_mode(struct args *args) |
| { |
| int tries = 3; |
| int fd = args->fd_dev; |
| struct cyapa_trackpad_run_mode run_mode; |
| |
| |
| while (tries-- > 0) { |
| memset(&run_mode, 0, sizeof(struct cyapa_trackpad_run_mode)); |
| if (cyapa_get_trackpad_run_mode(fd, &run_mode) < 0) |
| continue; |
| |
| if (run_mode.run_mode == CYAPA_OPERATIONAL_MODE) |
| break; /* already in correct state. */ |
| |
| if ((run_mode.run_mode == CYAPA_BOOTLOADER_MODE) |
| && (run_mode.bootloader_state == |
| CYAPA_BOOTLOADER_IDLE_STATE)) { |
| run_mode.rev_cmd = CYAPA_CMD_IDLE_TO_APP; |
| if (cyapa_set_trackpad_run_mode(fd, &run_mode) < 0) |
| continue; |
| |
| wait(300); |
| } else if ((run_mode.run_mode == CYAPA_BOOTLOADER_MODE) |
| && (run_mode.bootloader_state == |
| CYAPA_BOOTLOADER_ACTIVE_STATE)) { |
| run_mode.rev_cmd = CYAPA_CMD_ACTIVE_TO_IDLE; |
| if (cyapa_set_trackpad_run_mode(fd, &run_mode) < 0) |
| continue; |
| |
| wait(300); |
| } else { |
| /* |
| * unknown trackpad states. |
| * try to recovery bootloader header registers. |
| */ |
| cyapa_recovery_bootload_header(args); |
| continue; |
| } |
| } |
| |
| if (tries < 0) |
| return -1; |
| |
| return 0; |
| } |
| |
| int cyapa_get_firmware_version(struct args *args, |
| struct cyapa_firmware_ver *fw_version) |
| { |
| int ret = 0; |
| int tries = 3; |
| int fd = args->fd_dev; |
| struct cyapa_misc_ioctl_data ioctl_data; |
| struct cyapa_trackpad_run_mode run_mode; |
| unsigned char buf[32]; |
| |
| do { |
| memset(&run_mode, 0, sizeof(struct cyapa_trackpad_run_mode)); |
| ret = cyapa_get_trackpad_run_mode(fd, &run_mode); |
| if (ret == 0) |
| break; |
| |
| cyapa_recovery_bootload_header(args); |
| } while (tries--); |
| |
| if (ret < 0) |
| goto error; |
| |
| if (run_mode.run_mode == CYAPA_OPERATIONAL_MODE) { |
| memset(&ioctl_data, 0, sizeof(struct cyapa_misc_ioctl_data)); |
| ioctl_data.buf = (__u8 *)fw_version; |
| ioctl_data.len = sizeof(struct cyapa_firmware_ver); |
| |
| if (ioctl(args->fd_dev, |
| CYAPA_GET_FIRMWARE_VER, &ioctl_data) < 0) |
| goto error; |
| |
| return 0; |
| } |
| |
| /* |
| * firmware working in bootload mode. |
| * try to get firmware version from bootload header. |
| */ |
| memset(buf, 0, sizeof(buf)); |
| ret = cyapa_read_reg(fd, 0, 16, buf); |
| if ((ret < 0) || ((buf[0x01] & 0x10) != 0x10)) |
| goto error; |
| |
| /* have valid bootload head and firmware version. */ |
| if ((buf[0x0D] == 0xC0) && (buf[0x0E] == 0xC1) && (buf[0x0F] == 0xC2)) { |
| fw_version->major_ver = buf[0x0B]; |
| fw_version->minor_ver = buf[0x0C]; |
| } else |
| goto error; |
| |
| return 0; |
| |
| error: |
| fw_version->major_ver = 0; |
| fw_version->minor_ver = 0; |
| prt_warn("Unknown trackpad device firmware version.\n"); |
| |
| return ret; |
| } |
| |
| int cyapa_get_protocol_version(struct args *args) |
| { |
| struct cyapa_misc_ioctl_data ioctl_data; |
| struct cyapa_protocol_ver protocol_gen; |
| |
| memset(&ioctl_data, 0, sizeof(struct cyapa_misc_ioctl_data)); |
| ioctl_data.buf = (__u8 *)&protocol_gen; |
| ioctl_data.len = sizeof(struct cyapa_protocol_ver); |
| |
| if (ioctl(args->fd_dev, CYAPA_GET_PROTOCOL_VER, &ioctl_data) < 0) |
| return 0; |
| |
| return (int)protocol_gen.protocol_gen; |
| } |
| |
| /* |
| * routines for dumping and restoring firmware image from trackpad firmware |
| * to a specific output file. |
| */ |
| int cyapa_read_trackpad_fw_image(int fd, unsigned short offset, |
| unsigned char *buf, int len) |
| { |
| |
| int i; |
| int tries = 3; |
| int read_cmd_len; |
| unsigned char cmd_str[] = {0x00, 0xFF, 0x3C, |
| 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}; |
| unsigned char read_cmd[32]; |
| unsigned char read_buf[32]; |
| unsigned char verify_buf[32]; |
| |
| if (len > 16) { |
| prt_err("Can't read more then 16 bytes firmware each time.\n"); |
| return -1; |
| } |
| |
| memset(read_cmd, 0, sizeof(read_cmd)); |
| for (i = 0; i < sizeof(cmd_str); i++) |
| read_cmd[i] = cmd_str[i]; |
| /* offset is sent big endian. */ |
| read_cmd[i++] = offset >> 8; |
| read_cmd[i++] = offset; |
| read_cmd_len = sizeof(cmd_str) + 2; |
| |
| /* |
| * reset IDAC values blocks read from trackpad device, |
| * because IDAC values are dynamic generated in running time, |
| * and will be restored in flash, so we do not want this values. |
| */ |
| if ((offset >= 0x7A00) && (offset < 0x7A80)) { |
| /* reset 128 bytes consecutive IDAC values. */ |
| for (i = 0; i < 16; i++) |
| buf[i] = 0x08; |
| return 16; |
| } |
| if ((offset >= 0x7B00) && (offset < 0x7C00)) { |
| /* reset 256 bytes consecutive IDAC values. */ |
| for (i = 0; i < 16; i++) |
| buf[i] = 0x90; |
| return 16; |
| } |
| |
| cyapa_dump_data_block("TP: read cmd", read_cmd, 0, read_cmd_len); |
| |
| while (tries-- > 0) { |
| memset(read_buf, 0, sizeof(read_buf)); |
| memset(verify_buf, 0, sizeof(verify_buf)); |
| |
| cyapa_write_reg(fd, read_cmd, 0, read_cmd_len); |
| wait(10); |
| cyapa_read_reg(fd, 0, (16 + len), read_buf); |
| |
| cyapa_write_reg(fd, read_cmd, 0, read_cmd_len); |
| wait(10); |
| cyapa_read_reg(fd, 0, (16 + len), verify_buf); |
| |
| /* |
| * because trackpad doesn't supply read CRC data, |
| * so verify data by reading twice. |
| */ |
| for (i = 16; i < (16 + len); i++) { |
| if (read_buf[i] != verify_buf[i]) |
| break; |
| } |
| |
| if (i == (16 + len)) /* read and verified successfully. */ |
| break; |
| } |
| |
| if (tries < 0) |
| return -2; |
| |
| /* return 16 bytes firmware data. */ |
| for (i = 0; i < 16; i++) |
| buf[i] = read_buf[i+16]; |
| |
| return 16; |
| } |
| |
| int cyapa_write_tp_fw_image_to_file(int fd, unsigned short offset, |
| unsigned char *buf, int len) |
| { |
| int i; |
| int ret; |
| int ret_bytes = 0; |
| int tries = 3; |
| int verify_count = 3; |
| unsigned char buf_read[128]; |
| |
| do { |
| /* write data to output file. */ |
| while ((ret_bytes != len) && (tries-- > 0)) { |
| ret = (int)lseek(fd, (off_t)offset, SEEK_SET); |
| if (ret != (int)offset) |
| continue; |
| |
| ret_bytes = (int)write(fd, buf, (ssize_t)len); |
| } |
| if (tries < 0) |
| return -1; |
| |
| /* sync data to disk. */ |
| tries = 3; |
| while ((fsync(fd) != 0) && (tries-- > 0)) |
| ; |
| if (tries < 0) |
| return -2; |
| |
| /* read data back from disk. */ |
| tries = 3; |
| ret_bytes = 0; |
| while ((ret_bytes != len) && (tries-- > 0)) { |
| ret = (int)lseek(fd, (off_t)offset, SEEK_SET); |
| if (ret != (int)offset) |
| continue; |
| |
| memset(buf_read, 0, sizeof(buf_read)); |
| ret_bytes = (int)read(fd, buf_read, (ssize_t)len); |
| } |
| if (tries < 0) |
| return -3; |
| |
| /* verify data. */ |
| for (i = 0; (i < len) && (buf_read[i] == buf[i]); i++) |
| ; |
| if (i == len) |
| break; |
| else |
| continue; /* not consistent, tries. */ |
| } while (verify_count--); |
| |
| if (verify_count < 0) |
| return -4; |
| |
| return len; |
| } |
| |
| int cyapa_backup_fw_from_trackpad(struct args *args) |
| { |
| int i = 0; |
| int ret = 0; |
| int total_blocks = 0; |
| unsigned char buf[64]; |
| unsigned short offset; |
| unsigned short bak_offset; |
| |
| ret = cyapa_set_bootloader_idle_mode(args); |
| if (ret < 0) { |
| prt_err("Failed to reset trackpad device, unable switch to " |
| "firmware bootloader idle state, %d.\n", |
| ret); |
| return -1; |
| } |
| |
| calculate_duration_time(TIMER_START, NULL); |
| total_blocks = (CYAPA_FW_OFFSET_END - |
| CYAPA_FW_OFFSET_START + 1) / CYAPA_BAK_READ_BLOCK_LEN; |
| show_progress(0, SHOW_PROGRESS_INIT, |
| "Backup firmware from trackpad device in progress:\n"); |
| show_progress(0, SHOW_PROGRESS_CONT, NULL); |
| |
| bak_offset = 0; |
| offset = CYAPA_FW_OFFSET_START + i * CYAPA_BAK_READ_BLOCK_LEN; |
| while ((offset + CYAPA_BAK_READ_BLOCK_LEN - 1) <= CYAPA_FW_OFFSET_END) { |
| /* read 16 bytes from trackpad device each time. */ |
| memset(buf, 0, sizeof(buf)); |
| ret = cyapa_read_trackpad_fw_image(args->fd_dev, |
| offset, buf, CYAPA_BAK_READ_BLOCK_LEN); |
| if (ret < 0) { |
| prt_err("Failed to read firmware image, %d.\n", ret); |
| return -2; |
| } |
| |
| cyapa_dump_data_block("TP: read data", buf, offset, |
| CYAPA_BAK_READ_BLOCK_LEN); |
| |
| /* store read data to output file. */ |
| ret = cyapa_write_tp_fw_image_to_file(args->fd_bak_fw, |
| bak_offset, buf, CYAPA_BAK_READ_BLOCK_LEN); |
| if (ret < 0) { |
| prt_err("Failed to output firmware image to" |
| " backup file, %d.\n", ret); |
| return -3; |
| } |
| |
| /* update pointers. */ |
| i++; |
| bak_offset = i * CYAPA_BAK_READ_BLOCK_LEN; |
| offset = CYAPA_FW_OFFSET_START + i * CYAPA_BAK_READ_BLOCK_LEN; |
| |
| /* show backup progress information. */ |
| show_progress((i * 100 / total_blocks), |
| SHOW_PROGRESS_CONT, NULL); |
| } |
| |
| show_progress(0, SHOW_PROGRESS_EXIT, |
| "Backup firmware from trackpad device done.\n"); |
| calculate_duration_time(TIMER_STOP, |
| "Backup firmware form trackpad device duration time"); |
| |
| return i * CYAPA_BAK_READ_BLOCK_LEN; |
| } |
| |
| /* |
| * routines for updating trackpad firmware from .iic firmware image file. |
| */ |
| int string_to_hex(const char *str) |
| { |
| int i, j; |
| int len = strlen(str); |
| char c = 0; |
| int d = 0; |
| int hex = 0; |
| |
| for (j = 0, i = len - 1; i >= 0; j++, i--) { |
| c = tolower(*(str + i)); |
| if ((c >= '0') && (c <= '9')) |
| d = c - '0'; |
| else |
| d = c - 'a' + 10; |
| hex += (d << 4 * j); |
| } |
| |
| return hex; |
| } |
| |
| int cyapa_iic_cmd_to_data(char *iic_buf, int *cmd_op, |
| unsigned char *outbuf, int *cmd_data_len) |
| { |
| int i = 0; |
| int j = 0; |
| char *p = NULL; |
| char *tmpp = NULL; |
| char hexstr[4]; |
| char buf[128]; |
| |
| memset(buf, 0, sizeof(buf)); |
| strcpy(buf, iic_buf); |
| p = buf; |
| |
| /* skip white-space if exists for the head of the string. */ |
| while (*p == ' ') |
| p++; |
| |
| /* delay specific time command. */ |
| if (*p == '[' && !strncmp((p + 1), "delay=", 6)) { |
| *cmd_op = CYAPA_IIC_CMD_OP_DELAY; |
| |
| tmpp = strrchr(p, ']'); |
| *tmpp = '\0'; |
| p = strchr(p, '=') + 1; |
| *(int *)outbuf = atoi(p); |
| *cmd_data_len = sizeof(int); |
| |
| return 0; |
| } |
| |
| if (*p == 'r') |
| *cmd_op = CYAPA_IIC_CMD_OP_READ; |
| else if (*p == 'w') |
| *cmd_op = CYAPA_IIC_CMD_OP_WRITE; |
| else { |
| /* |
| * this iis command line doesn't contain valid command data. |
| * skip this command line. |
| */ |
| *cmd_op = CYAPA_IIC_CMD_OP_NONE; |
| *cmd_data_len = 0; |
| return 0; |
| } |
| /* skip white-space. */ |
| while (*(++p) == ' ') |
| ; |
| |
| /* parse command data. */ |
| while (*p != 'p') { |
| if (*p == ' ') { |
| p++; |
| continue; |
| } |
| |
| i = 0; |
| memset(hexstr, 0, sizeof(hexstr)); |
| while (*p != ' ') |
| hexstr[i++] = *(p++); |
| if (!strcmp(hexstr, "x")) |
| j++; |
| else |
| outbuf[j++] = string_to_hex(hexstr); |
| } |
| *cmd_data_len = j; |
| |
| return 0; |
| } |
| |
| int cyapa_send_update_cmds(struct args *args, |
| struct cmds_update_block *iic_cmds) |
| { |
| int i; |
| int ret = 0; |
| int tries = 3; |
| int fd = args->fd_dev; |
| int cmd_op; |
| unsigned int offset; |
| int cmd_len; |
| int delay = 100; |
| unsigned char *cmd_buf = NULL; |
| unsigned char status[32]; |
| |
| if (iic_cmds->valid_cmds <= 0) |
| return 0; |
| |
| do { |
| for (i = 0; i < iic_cmds->valid_cmds; i++) { |
| /* byte 0: cmd_op, read/write/delay*/ |
| cmd_op = iic_cmds->cmds[i][0]; |
| |
| if (cmd_op == CYAPA_IIC_CMD_OP_WRITE) { |
| /* |
| * command format for read/write: |
| * byte 0: cmd_op, read/write; |
| * byte 1: total cmd len; |
| * byte 2: i2c device addr; |
| * byte 3: i2c register map offset; |
| * byte 4 - ... : cmd data. |
| */ |
| cmd_len = iic_cmds->cmds[i][1] - 4; |
| offset = iic_cmds->cmds[i][3]; |
| cmd_buf = &iic_cmds->cmds[i][4]; |
| cyapa_dump_data_block("CYAPA_IIC_CMD_OP_WRITE", |
| cmd_buf, offset, cmd_len); |
| ret = cyapa_write_reg(fd, cmd_buf, |
| offset, cmd_len); |
| if (ret < 0) |
| break; |
| } else if (cmd_op == CYAPA_IIC_CMD_OP_DELAY) { |
| /* |
| * byte 0: cmd_op, delay; |
| * byte 1: total cmd len; |
| * byte 2 - byte 5: time in milliseconds, int. |
| */ |
| delay = *(int *)&iic_cmds->cmds[i][2]; |
| cyapa_dump_delay_time("CYAPA_IIC_CMD_OP_DELAY", |
| delay); |
| wait(delay); |
| } else if (cmd_op == CYAPA_IIC_CMD_OP_READ) { |
| /* |
| * byte 0: cmd_op, read; |
| * byte 1: total cmd len; |
| * byte 2: i2c device addr. |
| * byte 3 - ... : total bytes number indicates |
| * how many bytes should be read. |
| */ |
| memset(status, 0, sizeof(status)); |
| cmd_len = iic_cmds->cmds[i][1] - 3; |
| ret = cyapa_read_reg(fd, 0, cmd_len, status); |
| cyapa_dump_data_block("CYAPA_IIC_CMD_OP_READ", |
| status, 0, cmd_len); |
| if ((ret < 0) || (status[2] != 0x20)) |
| break; /* tries to write this block. */ |
| } |
| } |
| |
| if (i == iic_cmds->valid_cmds) |
| break; |
| } while (tries--); |
| |
| if (tries < 0) |
| return -1; |
| |
| return 0; |
| } |
| |
| int is_bootloader_terminal_cmd(struct cmds_update_block *iic_cmds) |
| { |
| int i; |
| unsigned char terminal_bootloader_cmd[] = {0x00, 0xFF, 0x3B, |
| 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}; |
| |
| if (iic_cmds->valid_cmds != 1) |
| return 0; |
| |
| for (i = 0; i < sizeof(terminal_bootloader_cmd); i++) { |
| if (iic_cmds->cmds[0][i+4] != terminal_bootloader_cmd[i]) |
| break; |
| } |
| |
| return (i == sizeof(terminal_bootloader_cmd)) ? 1 : 0; |
| } |
| |
| #define IIC_CHECKSUM_START_BLOCK 0x001E |
| #define IIC_CHECKSUM_END_BLOCK 0x001F |
| #define IIC_APP_START_BLOCK 0x0020 |
| #define IIC_APP_END_BLOCK 0x01FF |
| int cyapa_update_fw_image_from_iic(struct args *args) |
| { |
| int ret; |
| int block_index; |
| int total_blocks; |
| int fd = args->fd_new_fw; |
| char iic_buf[128]; |
| int cmd_op; |
| unsigned char cmd_buf[CYAPA_UPDATE_BLOCK_LEN]; |
| int cmd_data_len; |
| FILE *fp = fdopen(fd, "r"); |
| char *retp = NULL; |
| struct cmds_update_block iic_cmds; |
| |
| if (fp == NULL) { |
| fp = fopen(args->new_fw_image, "r"); |
| if (fp == NULL) { |
| prt_err("Failed to open new firmware image file, %d\n", |
| -errno); |
| ret = -errno; |
| goto error; |
| } |
| } |
| |
| calculate_duration_time(TIMER_START, NULL); |
| total_blocks = (IIC_APP_END_BLOCK - IIC_APP_START_BLOCK + 1) + |
| (IIC_CHECKSUM_END_BLOCK - IIC_CHECKSUM_START_BLOCK + 1); |
| show_progress(0, SHOW_PROGRESS_INIT, |
| "Update trackpad firmware from .iic file in progress:\n"); |
| show_progress(0, SHOW_PROGRESS_CONT, NULL); |
| |
| /* updating firmware. */ |
| memset(&iic_cmds, 0, sizeof(struct cmds_update_block)); |
| while (1) { |
| /* read a command line from .iic file. */ |
| memset(iic_buf, 0, sizeof(iic_buf)); |
| retp = fgets(iic_buf, sizeof(iic_buf), fp); |
| /* TODO(djkurtz): Why forcing errno to 0?!? */ |
| errno = 0; |
| if (retp == NULL) { |
| if (errno == 0) { |
| show_progress(0, SHOW_PROGRESS_EXIT, |
| "Update firmware from .iic" |
| " file done.\n"); |
| calculate_duration_time(TIMER_STOP, |
| "Update firmware from .iic file" |
| " duration time"); |
| ret = 0; |
| break; |
| } else { |
| prt_err("Failed to read next command line" |
| " from .iic file, %d.\n", -errno); |
| ret = -errno; |
| goto error; |
| } |
| } |
| |
| /* convert .iic command line to i2c data. */ |
| memset(cmd_buf, 0, sizeof(cmd_buf)); |
| cyapa_iic_cmd_to_data(iic_buf, &cmd_op, cmd_buf, &cmd_data_len); |
| |
| if (cmd_op == CYAPA_IIC_CMD_OP_NONE) |
| continue; |
| |
| iic_cmds.cmds[iic_cmds.valid_cmds][0] = (unsigned char)cmd_op; |
| iic_cmds.cmds[iic_cmds.valid_cmds][1] = |
| (unsigned char)(cmd_data_len + 2); |
| memcpy(&iic_cmds.cmds[iic_cmds.valid_cmds][2], |
| cmd_buf, (cmd_data_len + 2)); |
| iic_cmds.valid_cmds++; |
| |
| /* update new firmware block data to trackpad device. */ |
| if ((cmd_op == CYAPA_IIC_CMD_OP_READ) |
| || (is_bootloader_terminal_cmd(&iic_cmds))) { |
| ret = cyapa_send_update_cmds(args, &iic_cmds); |
| if (ret < 0) { |
| prt_err("Failed to write new firmware's .iic" |
| " block data, %d.\n", ret); |
| goto error; |
| } |
| |
| /* show new firmware update progress. */ |
| if (iic_cmds.valid_cmds > 5) { |
| block_index = iic_cmds.cmds[0][15] << 8 | |
| iic_cmds.cmds[0][16]; |
| if (block_index == IIC_CHECKSUM_START_BLOCK) |
| block_index = IIC_APP_END_BLOCK + 1; |
| else if (block_index == IIC_CHECKSUM_END_BLOCK) |
| block_index = IIC_APP_END_BLOCK + 2; |
| |
| show_progress(((block_index - |
| IIC_APP_START_BLOCK + 1) * 100 / |
| total_blocks), |
| SHOW_PROGRESS_CONT, |
| NULL); |
| } |
| |
| memset(&iic_cmds, 0, sizeof(struct cmds_update_block)); |
| } |
| } |
| |
| error: |
| fclose(fp); |
| |
| return ret; |
| } |
| |
| /* |
| * routines for updating trackpad firmware from .bin firmware image file. |
| */ |
| int cyapa_read_bin_fw_image(struct args *args, |
| unsigned short offset, unsigned char *buf, int len) |
| { |
| int ret = 0; |
| int read_bytes = 0; |
| int fd = args->fd_new_fw; |
| unsigned short read_offset; |
| int tries = 3; |
| |
| if (args->file_type != FILE_TYPE_BIN) |
| return -1; |
| |
| read_offset = offset - CYAPA_FW_OFFSET_START; |
| while ((read_bytes != len) && (tries-- > 0)) { |
| ret = (int)lseek(fd, (off_t)read_offset, SEEK_SET); |
| if (ret != (int)read_offset) |
| continue; |
| |
| read_bytes = (int)read(fd, buf, (ssize_t)len); |
| } |
| |
| if (tries < 0) |
| return -2; |
| |
| return 0; |
| } |
| |
| int cyapa_write_fw_image_block(struct args *args, |
| unsigned short offset, unsigned char *buf, int len) |
| { |
| int ret = 0; |
| int tries = 3; |
| int fd = args->fd_dev; |
| unsigned char status[3]; |
| unsigned char cmd_buf[32]; |
| unsigned char block_offset; |
| unsigned char block_buf[78]; |
| unsigned char *p; |
| int left_len; |
| int cmd_len; |
| unsigned short block_index; |
| |
| memset(block_buf, 0, sizeof(block_buf)); |
| /* set write command and security key bytes. */ |
| block_buf[0] = 0xFF; |
| block_buf[1] = 0x39; |
| block_buf[2] = 0x00; |
| block_buf[3] = 0x01; |
| block_buf[4] = 0x02; |
| block_buf[5] = 0x03; |
| block_buf[6] = 0x04; |
| block_buf[7] = 0x05; |
| block_buf[8] = 0x06; |
| block_buf[9] = 0x07; |
| /* block index is sent big endian. */ |
| block_index = (unsigned short)(offset / CYAPA_UPDATE_BLOCK_LEN); |
| block_buf[10] = block_index >> 8; |
| block_buf[11] = block_index; |
| memcpy(&block_buf[12], buf, len); |
| /* checksum for block data and whole command. */ |
| block_buf[76] = |
| (unsigned char)cyapa_calculate_checksum(&block_buf[12], len); |
| block_buf[77] = |
| (unsigned char)cyapa_calculate_checksum(&block_buf[0], 77); |
| |
| do { |
| p = block_buf; |
| left_len = sizeof(block_buf); |
| block_offset = 0; |
| while (left_len > 0) { |
| cmd_len = (left_len >= 16) ? 16 : left_len; |
| |
| memset(cmd_buf, 0, sizeof(cmd_buf)); |
| cmd_buf[0] = block_offset; |
| memcpy(&cmd_buf[1], p, cmd_len); |
| |
| p += cmd_len; |
| block_offset += cmd_len; |
| left_len -= cmd_len; |
| |
| /* write block data to trackpad device. */ |
| cyapa_dump_data_block("CYAPA_WRITE_BLOCK_DATA", |
| cmd_buf, offset, (cmd_len + 1)); |
| if (cyapa_write_reg(fd, cmd_buf, 0, (cmd_len + 1)) < 0) |
| break; |
| } |
| |
| /* wait write command finished by trackpad device. */ |
| wait(100); |
| cyapa_dump_delay_time("CYAPA_WRITE_BLOCK_DELAY", 100); |
| |
| if (left_len > 0) /* write failed, tries again. */ |
| continue; |
| |
| /* reset device pointer to offset 0. */ |
| memset(cmd_buf, 0, sizeof(cmd_buf)); |
| if (cyapa_write_reg(fd, cmd_buf, 0, 0) < 0) |
| prt_warn("Failed to reset trackpad device pointer.\n"); |
| |
| /* check block write command result status. */ |
| memset(status, 0, sizeof(status)); |
| ret = cyapa_read_reg(fd, 0, 3, status); |
| cyapa_dump_data_block("CYAPA_WRITE_STATUS_STATUS", |
| status, 0, 3); |
| if ((ret < 0) || (status[2] != 0x20)) |
| continue; |
| |
| /* block data written successfully. */ |
| break; |
| } while (tries--); |
| |
| if (tries < 0) |
| return -1; |
| |
| return 0; |
| } |
| |
| int cyapa_update_part_fw_image(struct args *args, |
| unsigned short start, unsigned end, int *blocks_written) |
| { |
| int blocks; |
| int ret = 0; |
| int total_blocks; |
| unsigned short offset; |
| unsigned char buf[CYAPA_UPDATE_BLOCK_LEN]; |
| |
| total_blocks = (CYAPA_FW_OFFSET_END - CYAPA_FW_OFFSET_START + 1) / |
| CYAPA_UPDATE_BLOCK_LEN; |
| blocks = 0; |
| offset = start + blocks * CYAPA_UPDATE_BLOCK_LEN; |
| while ((offset + CYAPA_UPDATE_BLOCK_LEN - 1) <= end) { |
| /* read data block from firmware image file. */ |
| memset(buf, 0, sizeof(buf)); |
| ret = cyapa_read_bin_fw_image(args, offset, |
| buf, CYAPA_UPDATE_BLOCK_LEN); |
| if (ret < 0) { |
| prt_err("Failed to read %d bytes block data at 0x%04x" |
| " from .bin firmware image file, %d\n", |
| CYAPA_UPDATE_BLOCK_LEN, offset, ret); |
| return ret; |
| } |
| |
| /* write firmware data block to trackpad device. */ |
| ret = cyapa_write_fw_image_block(args, offset, |
| buf, CYAPA_UPDATE_BLOCK_LEN); |
| if (ret < 0) { |
| prt_err("Failed to write an image block data" |
| " to trackpad device, %d\n", ret); |
| return ret; |
| } |
| |
| /* update pointers. */ |
| blocks++; |
| offset = start + blocks * CYAPA_UPDATE_BLOCK_LEN; |
| |
| ++(*blocks_written); |
| show_progress(((*blocks_written) * 100 / total_blocks), |
| SHOW_PROGRESS_CONT, NULL); |
| } |
| |
| return blocks; |
| } |
| |
| int cyapa_update_fw_image_from_bin(struct args *args) |
| { |
| int ret; |
| int blocks_written = 0; |
| |
| calculate_duration_time(TIMER_START, NULL); |
| show_progress(0, SHOW_PROGRESS_INIT, |
| "Update trackpad firmware from .bin file in progress:\n"); |
| show_progress(0, SHOW_PROGRESS_CONT, NULL); |
| |
| /* switch firmware working state to bootloader active state. */ |
| ret = cyapa_set_bootloader_active_mode(args); |
| if (ret < 0) { |
| prt_err("Failed switching firmware working state" |
| " to bootloader active state.\n"); |
| goto error; |
| } |
| |
| /* |
| * update firmware image to trackpad device. |
| * by default, should burn checksum blocks in the last step. |
| */ |
| ret = cyapa_update_part_fw_image(args, (CYAPA_FW_CHECKSUM_END + 1), |
| CYAPA_FW_OFFSET_END, &blocks_written); |
| if (ret < 0) { |
| prt_err("Failed to write firmware image blocks to" |
| " trackpad device.\n"); |
| goto error; |
| } |
| |
| ret = cyapa_update_part_fw_image(args, CYAPA_FW_OFFSET_START, |
| CYAPA_FW_CHECKSUM_END, &blocks_written); |
| if (ret < 0) { |
| prt_err("Failed to write checksum data blocks" |
| " to trackpad device.\n"); |
| goto error; |
| } |
| |
| show_progress(0, SHOW_PROGRESS_EXIT, |
| "Update firmware from .bin image file done.\n"); |
| calculate_duration_time(TIMER_STOP, |
| "Update firmware from .bin image file duration time"); |
| error: |
| /* set firmware back to bootloader idle mode working state. */ |
| if (cyapa_set_bootloader_idle_mode(args) < 0) { |
| prt_warn("Failed to switch firmware working state" |
| " to bootloader idle state.\n"); |
| } |
| |
| return ret; |
| } |
| |
| int cyapa_update_firmware(struct args *args) |
| { |
| int ret; |
| |
| if (args->file_type == FILE_TYPE_IIC) |
| ret = cyapa_update_fw_image_from_iic(args); |
| else |
| ret = cyapa_update_fw_image_from_bin(args); |
| |
| return ret; |
| } |
| |
| int check_run_as_root(char *name) |
| { |
| const char *program; |
| |
| if (getuid() == 0) |
| return 0; |
| |
| program = basename(name) ?: DEFAULT_PROGRAM_NAME; |
| |
| prt_warn("\"%s\" must be run as root.\n", program); |
| prt_warn("e.g.:\n"); |
| prt_warn(" sudo %s new-firmware [options]\n", program); |
| prt_warn("Please run \"%s -h\" for more information.\n", program); |
| |
| return -1; |
| } |
| |
| int main(int argc, char **argv) |
| { |
| int err_code = 0; |
| int input; |
| int fd_temp = 0; |
| struct args args; |
| struct cyapa_firmware_ver fw_version; |
| int old_protocol = -1; |
| int new_protocol = -1; |
| |
| prt_info("\n"); |
| /* only root priority user can execute this program. */ |
| if (check_run_as_root(argv[0]) < 0) |
| return -1; |
| |
| /* parse input parameters. */ |
| memset(&args, 0, sizeof(struct args)); |
| if (check_input_args(argc, argv, &args) != 0) |
| return -2; |
| |
| /* show warning info about reboot system after firmware updated. */ |
| if (!args.force) { |
| prt_warn("System may need to be rebooted after" |
| " updated new firmware.\n"); |
| prt_warn("Please close and store all your opened files before" |
| " update new firmware.\n"); |
| prt_info("Have you closed and store all your opened files and" |
| " continue firmware update, <Y|N>: "); |
| while ((input = getchar()) != EOF) { |
| if (toupper(input) == 'Y') { |
| prt_info("Start to update new Trackpad" |
| " firmware ...\n"); |
| break; |
| } else if (toupper(input) == 'N') { |
| prt_info("Exist without new Trackpad firmware" |
| " updated.\n\n"); |
| return -1; |
| } |
| prt_info("\nHave you closed and store all your opened" |
| " files and continue firmware update, <Y|N>: "); |
| } |
| } |
| |
| if (open_trackpad_dev(&args) < 0) { |
| prt_err("unable to open trackpad device, exit.\n"); |
| err_code = -3; |
| goto error; |
| } |
| |
| /* open device and firmware image files. */ |
| if (!args.new_fw_image || (open_new_fw_image(&args) < 0)) { |
| prt_err("unable to open new firmware file, exit.\n"); |
| err_code = -4; |
| goto error; |
| } |
| |
| if (args.backup_fw) { |
| if (open_bak_fw_image(&args) < 0) { |
| prt_warn("Failed to open firmware" |
| " backup image file.\n"); |
| prt_info("Do you want to continue update trackpad" |
| " firmware without firmware backup? <Y/N>: "); |
| while ((input = getchar()) != EOF) { |
| if (toupper(input) == 'Y') { |
| args.backup_fw = false; |
| prt_info("Continue update trackpad" |
| " firmware ...\n"); |
| break; |
| } else if (toupper(input) == 'N') { |
| err_code = -5; |
| prt_info("Firmware update has been" |
| " canceled, exit.\n"); |
| goto error; |
| } |
| prt_info("\nDo you want to continue update" |
| " trackpad firmware without current" |
| " firmware backed up? <Y/N>: "); |
| } |
| } |
| } |
| |
| if (cyapa_fw_image_check(&args) < 0) { |
| prt_err("New firmware image file \"%s\" is invlaid.\n", |
| args.new_fw_image); |
| prt_info("Please specify a valid new trackpad" |
| " firmare image file.\n"); |
| goto error; |
| } |
| |
| /* show firmware version before firmware updated. */ |
| memset(&fw_version, 0, sizeof(struct cyapa_firmware_ver)); |
| if (cyapa_get_firmware_version(&args, &fw_version) < 0) { |
| err_code = -6; |
| goto error; |
| } |
| old_protocol = cyapa_get_protocol_version(&args); |
| prt_info("Firmware version before updated is: <%02d.%02d>," |
| " protocol version is: GEN%d.\n\n", |
| fw_version.major_ver, fw_version.minor_ver, old_protocol); |
| |
| /* backup firmware image for trackpad device. */ |
| if (args.backup_fw) { |
| if (cyapa_backup_fw_from_trackpad(&args) < 0) { |
| prt_warn("Backup firmware image from trackpad" |
| " device failed.\n"); |
| prt_info("Do you want to continue updating, <Y|N>: "); |
| while ((input = getchar()) != EOF) { |
| if (toupper(input) == 'Y') { |
| args.backup_fw = false; |
| prt_info("Continue update trackpad" |
| " firmware ...\n"); |
| break; |
| } else if (toupper(input) == 'N') { |
| err_code = -7; |
| prt_info("Firmware update has been" |
| " canceled, exit.\n"); |
| goto error; |
| } |
| prt_info("Do you want to continue updating," |
| " <Y|N>: "); |
| } |
| } else { |
| prt_info("Backup firmware image from trackpad device" |
| " to \"%s\" successfully.\n\n", |
| args.bak_fw_image); |
| } |
| } |
| |
| /* reset firmware working state to bootloader idle state. */ |
| if (cyapa_set_bootloader_idle_mode(&args) < 0) { |
| prt_err("Failed to reset and switch firmware working" |
| " state to bootloader idle state.\n"); |
| err_code = -8; |
| goto error; |
| } |
| |
| /* update new firmware to trackpad device. */ |
| if (cyapa_update_firmware(&args) < 0) { |
| prt_err("Update new firmware image %s failed.\n", |
| args.new_fw_image); |
| err_code = -9; |
| |
| if (args.backup_fw) { |
| prt_info("Tries to recovery trackpad device to" |
| " previously backed up firmware image.\n"); |
| fd_temp = args.fd_new_fw; |
| args.fd_new_fw = args.fd_bak_fw; |
| args.file_type = FILE_TYPE_BIN; |
| if (cyapa_update_firmware(&args) < 0) { |
| prt_err("Trackpad firmware recovery failed.\n"); |
| err_code = -5; |
| } else { |
| err_code = 0; |
| prt_info("Successfully restored backed up" |
| " trackpad firmware\n"); |
| } |
| args.fd_new_fw = fd_temp; |
| } |
| |
| if (err_code < 0) |
| goto error; |
| } |
| prt_info("Successfully updated trackpad to new firmware image %s\n\n", |
| args.new_fw_image); |
| |
| error: |
| /* reset trackpad device to operational mode. */ |
| if (cyapa_set_app_operational_mode(&args) < 0) { |
| prt_info("Failed to reset trackpad device to" |
| " operational mode.\n"); |
| prt_info("Need to reboot system to recover trackpad device.\n"); |
| } |
| |
| /* show firmware version after firmware updated. */ |
| new_protocol = cyapa_get_protocol_version(&args); |
| memset(&fw_version, 0, sizeof(struct cyapa_firmware_ver)); |
| if ((cyapa_get_firmware_version(&args, &fw_version) >= 0) |
| && (err_code == 0)) |
| prt_info("Firmware version after update is: v%02d.%02d," |
| " new protocol is: GEN%d\n", |
| fw_version.major_ver, |
| fw_version.minor_ver, |
| new_protocol); |
| |
| if (args.fd_dev > 0) |
| close(args.fd_dev); |
| if (args.fd_new_fw > 0) |
| close(args.fd_new_fw); |
| if (args.fd_bak_fw > 0) |
| close(args.fd_bak_fw); |
| free(args.new_fw_image); |
| free(args.bak_fw_image); |
| |
| prt_info("\n"); |
| |
| system_reboot_check(&args, new_protocol, old_protocol); |
| return err_code; |
| } |