blob: 28cd3f09efffb01430756f3101297c5217333776 [file] [log] [blame]
/******************************************************************************
*
* 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;
const char *new_fw_image;
const 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, --backup\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, --output <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, --force\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, --version\n");
printf("\t Print version information and exit.\n");
printf("\t -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");
}
static struct option options[] = {
{"backup", no_argument, NULL, 'b'},
{"force", no_argument, NULL, 'f'},
{"help", no_argument, NULL, 'h'},
{"output", required_argument, NULL, 'o'},
{"version", no_argument, NULL, 'v'},
{0, 0, 0, 0}
};
bool ends_with(const char* name, const char* ext)
{
size_t nlen = strlen(name);
size_t elen = strlen(ext);
return (nlen >= elen && !strcmp(&name[nlen-elen], ext));
}
/**
* 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 c;
while (1) {
int index = 0;
c = getopt_long(argc, argv, "bfho:v", options, &index);
if (c == -1)
break;
switch (c) {
case 'b':
args->backup_fw = true;
break;
case 'f':
args->force = true;
break;
case 'o':
args->bak_fw_image = optarg;
break;
case 'v':
return 2;
default:
return 1;
}
}
/* The last option should be the path of a new firmware image */
if (optind < argc)
args->new_fw_image = argv[optind++];
if (args->new_fw_image == NULL)
return 1;
if (ends_with(args->new_fw_image, ".iic"))
args->file_type = FILE_TYPE_IIC;
else if (ends_with(args->new_fw_image, ".bin"))
args->file_type = FILE_TYPE_BIN;
else
return 1;
if (args->backup_fw && !args->bak_fw_image) {
int fd;
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)
return 1;
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);
return 0;
}
#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 = { 0 };
struct cyapa_firmware_ver fw_version;
int old_protocol = -1;
int new_protocol = -1;
/* only root priority user can execute this program. */
if (check_run_as_root(argv[0]) < 0)
return -1;
/* parse input parameters. */
err_code = check_input_args(argc, argv, &args);
if (err_code == 2) {
show_version_info(basename(argv[0]));
exit(0);
} else if (err_code == 1) {
show_usage(basename(argv[0]));
exit(0);
}
/* 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);
prt_info("\n");
system_reboot_check(&args, new_protocol, old_protocol);
return err_code;
}