diff options
author | Andrew Duggan <aduggan@synaptics.com> | 2014-04-03 15:17:57 -0700 |
---|---|---|
committer | Andrew Duggan <aduggan@synaptics.com> | 2014-04-03 15:17:57 -0700 |
commit | 4e811258783e0f1e0cdaf70e3bdb2069ce4f0465 (patch) | |
tree | fbd10bbcc263069c08b3e2f298b0079e159ab3bc /rmi4update | |
download | rmi4utils-4e811258783e0f1e0cdaf70e3bdb2069ce4f0465.tar.gz |
Initial commit
Diffstat (limited to 'rmi4update')
-rw-r--r-- | rmi4update/Makefile | 19 | ||||
-rw-r--r-- | rmi4update/firmware_image.cpp | 146 | ||||
-rw-r--r-- | rmi4update/firmware_image.h | 74 | ||||
-rw-r--r-- | rmi4update/main.cpp | 76 | ||||
-rw-r--r-- | rmi4update/rmi4update.cpp | 400 | ||||
-rw-r--r-- | rmi4update/rmi4update.h | 58 | ||||
-rw-r--r-- | rmi4update/updateutil.cpp | 54 | ||||
-rw-r--r-- | rmi4update/updateutil.h | 43 |
8 files changed, 870 insertions, 0 deletions
diff --git a/rmi4update/Makefile b/rmi4update/Makefile new file mode 100644 index 0000000..53d0881 --- /dev/null +++ b/rmi4update/Makefile @@ -0,0 +1,19 @@ +CXX = g++ +LD = $(CXX) +INCLUDES = -I../include -I./include -I../rmidevice +CXXFLAGS = -g -Wall $(INCLUDES) +LDFLAGS = -static -L. +LIBS = -lrmidevice +LIBDIR = ../rmidevice +LIBNAME = librmidevice.a +RMI4UPDATESRC = main.cpp firmware_image.cpp rmi4update.cpp updateutil.cpp +RMI4UPDATEOBJ = $(RMI4UPDATESRC:.cpp=.o) +PROGNAME = rmi4update + +all: $(PROGNAME) + +$(PROGNAME): $(RMI4UPDATEOBJ) + $(LD) $(LDFLAGS) $(RMI4UPDATEOBJ) -L$(LIBDIR) $(LIBS) -o $(PROGNAME) + +clean: + rm -f $(RMI4UPDATEOBJ) $(PROGNAME)
\ No newline at end of file diff --git a/rmi4update/firmware_image.cpp b/rmi4update/firmware_image.cpp new file mode 100644 index 0000000..010ab10 --- /dev/null +++ b/rmi4update/firmware_image.cpp @@ -0,0 +1,146 @@ +//=========================================================================== +// Copyright (c) 2012 Synaptics Incorporated. All rights reserved. +//=========================================================================== + +#include <iostream> +#include <fstream> +#include <string.h> +#include <stdint.h> + +#include "firmware_image.h" + +using namespace std; + +unsigned long FirmwareImage::Checksum(unsigned short * data, unsigned long len) +{ + unsigned long checksum = 0xFFFFFFFF; + unsigned long lsw = checksum & 0xFFFF; + unsigned long msw = checksum >> 16; + + while (len--) { + lsw += *data++; + msw += lsw; + lsw = (lsw & 0xffff) + (lsw >> 16); + msw = (msw & 0xffff) + (msw >> 16); + } + + checksum = msw << 16 | lsw; + + return checksum; +} + +int FirmwareImage::Initialize(const char * filename) +{ + int rc; + + if (!filename) + return UPDATE_FAIL_INVALID_PARAMETER; + + ifstream ifsFile(filename, ios::in|ios::binary|ios::ate); + if (!ifsFile) + return UPDATE_FAIL_OPEN_FIRMWARE_IMAGE; + + m_imageSize = (unsigned long)ifsFile.tellg(); + m_memBlock = new unsigned char[m_imageSize]; + ifsFile.seekg(0, ios::beg); + ifsFile.read((char*)m_memBlock, m_imageSize); + + rc = ExtractHeader(); + if (rc != UPDATE_SUCCESS) + return rc; + + fprintf(stdout, "Firmware Header:\n"); + PrintHeaderInfo(); + + return UPDATE_SUCCESS; +} + +int FirmwareImage::ExtractHeader() +{ + m_checksum = extract_long(&m_memBlock[RMI_IMG_CHECKSUM_OFFSET]); + m_io = m_memBlock[RMI_IMG_IO_OFFSET]; + m_bootloaderVersion = m_memBlock[RMI_IMG_BOOTLOADER_VERSION_OFFSET]; + m_firmwareSize = extract_long(&m_memBlock[RMI_IMG_IMAGE_SIZE_OFFSET]); + m_configSize = extract_long(&m_memBlock[RMI_IMG_CONFIG_SIZE_OFFSET]); + if (m_io == 1) { + m_firmwareBuildID = extract_long(&m_memBlock[RMI_IMG_FW_BUILD_ID_OFFSET]); + m_packageID = extract_long(&m_memBlock[RMI_IMG_PACKAGE_ID_OFFSET]); + } + memcpy(m_productID, &m_memBlock[RMI_IMG_PRODUCT_ID_OFFSET], RMI_PRODUCT_ID_LENGTH); + m_productID[RMI_PRODUCT_ID_LENGTH] = 0; + memcpy(m_productInfo, &m_memBlock[RMI_IMG_PRODUCT_INFO_OFFSET], RMI_IMG_PRODUCT_INFO_LENGTH); + + m_firmwareData = &m_memBlock[RMI_IMG_FW_OFFSET]; + m_configData = &m_memBlock[RMI_IMG_FW_OFFSET + m_firmwareSize]; + + switch (m_bootloaderVersion) { + case 2: + m_lockdownSize = RMI_IMG_LOCKDOWN_V2_SIZE; + m_lockdownData = &m_memBlock[RMI_IMG_LOCKDOWN_V2_OFFSET]; + break; + case 3: + case 4: + m_lockdownSize = RMI_IMG_LOCKDOWN_V3_SIZE; + m_lockdownData = &m_memBlock[RMI_IMG_LOCKDOWN_V3_OFFSET]; + break; + case 5: + m_lockdownSize = RMI_IMG_LOCKDOWN_V5_SIZE; + m_lockdownData = &m_memBlock[RMI_IMG_LOCKDOWN_V5_OFFSET]; + break; + case 6: + // TODO: Add support for V6 + default: + return UPDATE_FAIL_UNSUPPORTED_IMAGE_VERSION; + } + + return UPDATE_SUCCESS; +} + +void FirmwareImage::PrintHeaderInfo() +{ + fprintf(stdout, "Checksum:\t\t0x%lx\n", m_checksum); + fprintf(stdout, "Firmware Size:\t\t%ld\n", m_firmwareSize); + fprintf(stdout, "Config Size:\t\t%ld\n", m_configSize); + fprintf(stdout, "Lockdown Size:\t\t%ld\n", m_lockdownSize); + fprintf(stdout, "Firmware Build ID:\t%ld\n", m_firmwareBuildID); + fprintf(stdout, "Package ID:\t\t%d\n", *((unsigned short*)&m_packageID)); + fprintf(stdout, "Bootloader Version:\t%d\n", m_bootloaderVersion); + fprintf(stdout, "Product ID:\t\t%s\n", m_productID); + fprintf(stdout, "Product Info:\t\t%d\n", *((unsigned short*)&m_productInfo)); + fprintf(stdout, "\n"); +} + +int FirmwareImage::VerifyImage(unsigned short deviceFirmwareSize, unsigned short deviceConfigSize) +{ + if (m_imageSize < 0x100) + return UPDATE_FAIL_VERIFY_IMAGE; + + unsigned long calculated_checksum = Checksum((uint16_t *)&(m_memBlock[4]), + (unsigned short)(m_imageSize - 4) >> 1); + + if (m_checksum != calculated_checksum) { + fprintf(stderr, "Firmware image checksum verification failed, saw 0x%08lX, calculated 0x%08lX\n", + m_checksum, calculated_checksum); + return UPDATE_FAIL_VERIFY_CHECKSUM; + } + + if (m_firmwareSize != deviceFirmwareSize) { + fprintf(stderr, "Firmware image size verfication failed, size in image %ld did " + "not match device size %d\n", m_firmwareSize, deviceFirmwareSize); + return UPDATE_FAIL_VERIFY_FIRMWARE_SIZE; + } + + if (m_configSize != deviceConfigSize) { + fprintf(stderr, "Firmware image size verfication failed, size in image %ld did " + "not match device size %d\n", m_firmwareSize, deviceConfigSize); + return UPDATE_FAIL_VERIFY_CONFIG_SIZE; + } + + return UPDATE_SUCCESS; +} + +FirmwareImage::~FirmwareImage() +{ + delete [] m_memBlock; + m_memBlock = NULL; +} diff --git a/rmi4update/firmware_image.h b/rmi4update/firmware_image.h new file mode 100644 index 0000000..0d88ad9 --- /dev/null +++ b/rmi4update/firmware_image.h @@ -0,0 +1,74 @@ +//=========================================================================== +// Copyright (c) 2012 - 2014 Synaptics Incorporated. All rights reserved. +//=========================================================================== + +#ifndef _FIRMWAREIMAGE_H_ +#define _FIRMWAREIMAGE_H_ + +#include "rmidevice.h" +#include "updateutil.h" + +#define RMI_IMG_CHECKSUM_OFFSET 0 +#define RMI_IMG_IO_OFFSET 0x06 +#define RMI_IMG_BOOTLOADER_VERSION_OFFSET 0x07 +#define RMI_IMG_IMAGE_SIZE_OFFSET 0x08 +#define RMI_IMG_CONFIG_SIZE_OFFSET 0x0C +#define RMI_IMG_PACKAGE_ID_OFFSET 0x1A +#define RMI_IMG_FW_BUILD_ID_OFFSET 0x50 + +#define RMI_IMG_PRODUCT_INFO_LENGTH 2 + +#define RMI_IMG_PRODUCT_ID_OFFSET 0x10 +#define RMI_IMG_PRODUCT_INFO_OFFSET 0x1E + +#define RMI_IMG_FW_OFFSET 0x100 + +#define RMI_IMG_LOCKDOWN_V2_OFFSET 0xD0 +#define RMI_IMG_LOCKDOWN_V2_SIZE 0x30 + +#define RMI_IMG_LOCKDOWN_V3_OFFSET 0xC0 +#define RMI_IMG_LOCKDOWN_V3_SIZE 0x40 + +#define RMI_IMG_LOCKDOWN_V5_OFFSET 0xB0 +#define RMI_IMG_LOCKDOWN_V5_SIZE 0x50 + +class FirmwareImage +{ +public: + int Initialize(const char * filename); + int VerifyImage(unsigned short deviceFirmwareSize, unsigned short deviceConfigSize); + unsigned char * GetFirmwareData() { return m_firmwareData; } + unsigned char * GetConfigData() { return m_configData; } + unsigned char * GetLockdownData() { return m_lockdownData; } + unsigned long GetFirmwareSize() { return m_firmwareSize; } + unsigned long GetConfigSize() { return m_configSize; } + unsigned long GetLockdownSize() { return m_lockdownSize; } + unsigned long GetFirmwareID() { return m_firmwareBuildID; } + bool HasIO() { return m_io; } + ~FirmwareImage(); + +private: + unsigned long Checksum(unsigned short * data, unsigned long len); + int ExtractHeader(); + void PrintHeaderInfo(); + +private: + unsigned long m_checksum; + unsigned long m_firmwareSize; + unsigned long m_configSize; + unsigned long m_lockdownSize; + unsigned long m_imageSize; + unsigned long m_firmwareBuildID; + unsigned short m_packageID; + unsigned char m_bootloaderVersion; + unsigned char m_io; + char m_productID[RMI_PRODUCT_ID_LENGTH + 1]; + char m_productInfo[RMI_IMG_PRODUCT_INFO_LENGTH]; + + unsigned char * m_firmwareData; + unsigned char * m_configData; + unsigned char * m_lockdownData; + unsigned char * m_memBlock; +}; + +#endif // _FIRMWAREIMAGE_H_ diff --git a/rmi4update/main.cpp b/rmi4update/main.cpp new file mode 100644 index 0000000..a589d7b --- /dev/null +++ b/rmi4update/main.cpp @@ -0,0 +1,76 @@ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <getopt.h> + +#include "hiddevice.h" +#include "rmi4update.h" + +#define RMI4UPDATE_GETOPTS "hf" + +void printHelp(const char *prog_name) +{ + fprintf(stdout, "Usage: %s [OPTIONS] DEVICEFILE FIRMWAREFILE\n", prog_name); + fprintf(stdout, "\t-h, --help\tPrint this message\n"); + fprintf(stdout, "\t-f, --force\tForce updating firmware even it the image provided is older\n\t\t\tthen the current firmware on the device.\n"); +} + +int main(int argc, char **argv) +{ + int rc; + HIDDevice rmidevice; + FirmwareImage image; + int opt; + int index; + const char *deviceName; + const char *firmwareName; + bool force = false; + static struct option long_options[] = { + {"help", 0, NULL, 'h'}, + {"force", 0, NULL, 'f'}, + {0, 0, 0, 0}, + }; + + while ((opt = getopt_long(argc, argv, RMI4UPDATE_GETOPTS, long_options, &index)) != -1) { + switch (opt) { + case 'h': + printHelp(argv[0]); + return 0; + case 'f': + force = true; + break; + default: + break; + + } + } + + if (optind < argc) + deviceName = argv[optind++]; + + if (optind < argc) + firmwareName = argv[optind]; + + rc = rmidevice.Open(deviceName); + if (rc) { + fprintf(stderr, "Failed to open rmi device: %s\n", strerror(errno)); + return rc; + } + + rc = image.Initialize(firmwareName); + if (rc != UPDATE_SUCCESS) { + fprintf(stderr, "Failed to initialize the firmware image: %s\n", update_err_to_string(rc)); + return 1; + } + + + + RMI4Update update(rmidevice, image); + rc = update.UpdateFirmware(force); + if (rc != UPDATE_SUCCESS) { + fprintf(stderr, "Firmware update failed because: %s\n", update_err_to_string(rc)); + return 1; + } + + return 0; +} diff --git a/rmi4update/rmi4update.cpp b/rmi4update/rmi4update.cpp new file mode 100644 index 0000000..f4b9bcb --- /dev/null +++ b/rmi4update/rmi4update.cpp @@ -0,0 +1,400 @@ +#include <alloca.h> +#include <time.h> +#include <stdint.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> + +#include "rmi4update.h" + +#define RMI_F34_QUERY_SIZE 7 +#define RMI_F34_HAS_NEW_REG_MAP (1 << 0) +#define RMI_F34_IS_UNLOCKED (1 << 1) +#define RMI_F34_HAS_CONFIG_ID (1 << 2) +#define RMI_F34_BLOCK_SIZE_OFFSET 1 +#define RMI_F34_FW_BLOCKS_OFFSET 3 +#define RMI_F34_CONFIG_BLOCKS_OFFSET 5 + +#define RMI_F34_BLOCK_DATA_OFFSET 2 + +#define RMI_F34_COMMAND_MASK 0x0F +#define RMI_F34_STATUS_MASK 0x07 +#define RMI_F34_STATUS_SHIFT 4 +#define RMI_F34_ENABLED_MASK 0x80 + +#define RMI_F34_WRITE_FW_BLOCK 0x02 +#define RMI_F34_ERASE_ALL 0x03 +#define RMI_F34_WRITE_LOCKDOWN_BLOCK 0x04 +#define RMI_F34_WRITE_CONFIG_BLOCK 0x06 +#define RMI_F34_ENABLE_FLASH_PROG 0x0f + +#define RMI_F34_ENABLE_WAIT_MS 300 +#define RMI_F34_ERASE_WAIT_MS (5 * 1000) +#define RMI_F34_IDLE_WAIT_MS 500 + +/* Most recent device status event */ +#define RMI_F01_STATUS_CODE(status) ((status) & 0x0f) +/* Indicates that flash programming is enabled (bootloader mode). */ +#define RMI_F01_STATUS_BOOTLOADER(status) (!!((status) & 0x40)) +/* The device has lost its configuration for some reason. */ +#define RMI_F01_STATUS_UNCONFIGURED(status) (!!((status) & 0x80)) + +/* + * Sleep mode controls power management on the device and affects all + * functions of the device. + */ +#define RMI_F01_CTRL0_SLEEP_MODE_MASK 0x03 + +#define RMI_SLEEP_MODE_NORMAL 0x00 +#define RMI_SLEEP_MODE_SENSOR_SLEEP 0x01 +#define RMI_SLEEP_MODE_RESERVED0 0x02 +#define RMI_SLEEP_MODE_RESERVED1 0x03 + +/* + * This bit disables whatever sleep mode may be selected by the sleep_mode + * field and forces the device to run at full power without sleeping. + */ +#define RMI_F01_CRTL0_NOSLEEP_BIT (1 << 2) + +int RMI4Update::UpdateFirmware(bool force) +{ + struct timespec start; + struct timespec end; + int64_t duration_ns = 0; + int rc; + const unsigned char eraseAll = RMI_F34_ERASE_ALL; + + rc = FindUpdateFunctions(); + if (rc != UPDATE_SUCCESS) + return rc; + + rc = m_device.QueryBasicProperties(); + if (rc < 0) + return UPDATE_FAIL_QUERY_BASIC_PROPERTIES; + + fprintf(stdout, "Device Properties:\n"); + m_device.PrintProperties(); + + rc = ReadF34Queries(); + if (rc != UPDATE_SUCCESS) + return rc; + + rc = m_firmwareImage.VerifyImage(GetFirmwareSize(), GetConfigSize()); + if (rc != UPDATE_SUCCESS) + return rc; + + rc = EnterFlashProgramming(); + if (rc != UPDATE_SUCCESS) { + fprintf(stderr, "%s: %s\n", __func__, update_err_to_string(rc)); + return rc; + } + + if (!force && m_firmwareImage.HasIO()) { + if (m_firmwareImage.GetFirmwareID() <= m_device.GetFirmwareID()) { + m_device.Reset(); + return UPDATE_FAIL_FIRMWARE_IMAGE_IS_OLDER; + } + } + + if (m_unlocked) { + if (m_firmwareImage.GetLockdownData()) { + fprintf(stdout, "Writing lockdown...\n"); + clock_gettime(CLOCK_MONOTONIC, &start); + rc = WriteBlocks(m_firmwareImage.GetLockdownData(), + m_firmwareImage.GetLockdownSize() / 0x10, + RMI_F34_WRITE_LOCKDOWN_BLOCK); + if (rc != UPDATE_SUCCESS) { + fprintf(stderr, "%s: %s\n", __func__, update_err_to_string(rc)); + return rc; + } + clock_gettime(CLOCK_MONOTONIC, &end); +#if 0 // TODO: convert to userspace + duration_ns = timespec_to_ns(&end) - timespec_to_ns(&start); +#endif + fprintf(stdout, "Done writing lockdown, time: %ld ns.\n", duration_ns); + } + + rc = EnterFlashProgramming(); + if (rc != UPDATE_SUCCESS) { + fprintf(stderr, "%s: %s\n", __func__, update_err_to_string(rc)); + return rc; + } + + } + + rc = WriteBootloaderID(); + if (rc != UPDATE_SUCCESS) { + fprintf(stderr, "%s: %s\n", __func__, update_err_to_string(rc)); + return rc; + } + + fprintf(stdout, "Erasing FW...\n"); + clock_gettime(CLOCK_MONOTONIC, &start); + rc = m_device.Write(m_f34StatusAddr, &eraseAll, 1); + if (rc < 0) { + fprintf(stderr, "%s: %s\n", __func__, update_err_to_string(UPDATE_FAIL_ERASE_ALL)); + return UPDATE_FAIL_ERASE_ALL; + } + + rc = WaitForIdle(RMI_F34_ERASE_WAIT_MS); + if (rc != UPDATE_SUCCESS) { + fprintf(stderr, "%s: %s\n", __func__, update_err_to_string(rc)); + return rc; + } + clock_gettime(CLOCK_MONOTONIC, &end); +#if 0 // TODO: convert to userspace + duration_ns = timespec_to_ns(&end) - timespec_to_ns(&start); +#endif + fprintf(stdout, "Erase complete, time: %ld ns.\n", duration_ns); + + if (m_firmwareImage.GetFirmwareData()) { + fprintf(stdout, "Writing firmware...\n"); + clock_gettime(CLOCK_MONOTONIC, &start); + rc = WriteBlocks(m_firmwareImage.GetFirmwareData(), m_fwBlockCount, + RMI_F34_WRITE_FW_BLOCK); + if (rc != UPDATE_SUCCESS) { + fprintf(stderr, "%s: %s\n", __func__, update_err_to_string(rc)); + return rc; + } + clock_gettime(CLOCK_MONOTONIC, &end); +#if 0 // TODO: convert to userspace + duration_ns = timespec_to_ns(&end) - timespec_to_ns(&start); +#endif + fprintf(stdout, "Done writing FW, time: %ld ns.\n", duration_ns); + } + + if (m_firmwareImage.GetConfigData()) { + fprintf(stdout, "Writing configuration...\n"); + clock_gettime(CLOCK_MONOTONIC, &start); + rc = WriteBlocks(m_firmwareImage.GetConfigData(), m_configBlockCount, + RMI_F34_WRITE_CONFIG_BLOCK); + if (rc != UPDATE_SUCCESS) { + fprintf(stderr, "%s: %s\n", __func__, update_err_to_string(rc)); + return rc; + } + clock_gettime(CLOCK_MONOTONIC, &end); +#if 0 // TODO: convert to userspace + duration_ns = timespec_to_ns(&end) - timespec_to_ns(&start); +#endif + fprintf(stdout, "Done writing config, time: %ld ns.\n", duration_ns); + } + m_device.Reset(); + + return UPDATE_SUCCESS; + +} + +int RMI4Update::FindUpdateFunctions() +{ + if (0 > m_device.ScanPDT()) + return UPDATE_FAIL_SCAN_PDT; + + if (!m_device.GetFunction(m_f01, 0x01)) + return UPDATE_FAIL_NO_FUNCTION_01; + + if (!m_device.GetFunction(m_f34, 0x34)) + return UPDATE_FAIL_NO_FUNCTION_34; + + return UPDATE_SUCCESS; +} + +int RMI4Update::ReadF34Queries() +{ + int rc; + unsigned char idStr[3]; + unsigned char buf[RMI_F34_QUERY_SIZE]; + unsigned short queryAddr = m_f34.GetQueryBase(); + + rc = m_device.Read(queryAddr, m_bootloaderID, RMI_BOOTLOADER_ID_SIZE); + if (rc < 0) + return UPDATE_FAIL_READ_BOOTLOADER_ID; + + queryAddr += RMI_BOOTLOADER_ID_SIZE; + + rc = m_device.Read(queryAddr, buf, RMI_F34_QUERY_SIZE); + if (rc < 0) + return UPDATE_FAIL_READ_F34_QUERIES; + + m_hasNewRegmap = buf[0] & RMI_F34_HAS_NEW_REG_MAP; + m_unlocked = buf[0] & RMI_F34_IS_UNLOCKED;; + m_hasConfigID = buf[0] & RMI_F34_HAS_CONFIG_ID; + m_blockSize = extract_short(buf + RMI_F34_BLOCK_SIZE_OFFSET); + m_fwBlockCount = extract_short(buf + RMI_F34_FW_BLOCKS_OFFSET); + m_configBlockCount = extract_short(buf + RMI_F34_CONFIG_BLOCKS_OFFSET); + + idStr[0] = m_bootloaderID[0]; + idStr[1] = m_bootloaderID[1]; + idStr[2] = 0; + + fprintf(stdout, "F34 bootloader id: %s (%#04x %#04x)\n", idStr, m_bootloaderID[0], + m_bootloaderID[1]); + fprintf(stdout, "F34 has config id: %d\n", m_hasConfigID); + fprintf(stdout, "F34 unlocked: %d\n", m_unlocked); + fprintf(stdout, "F34 new reg map: %d\n", m_hasNewRegmap); + fprintf(stdout, "F34 block size: %d\n", m_blockSize); + fprintf(stdout, "F34 fw blocks: %d\n", m_fwBlockCount); + fprintf(stdout, "F34 config blocks: %d\n", m_configBlockCount); + fprintf(stdout, "\n"); + + m_f34StatusAddr = m_f34.GetDataBase() + RMI_F34_BLOCK_DATA_OFFSET + m_blockSize; + + return UPDATE_SUCCESS; +} + +int RMI4Update::ReadF34Controls() +{ + int rc; + unsigned char buf; + + rc = m_device.Read(m_f34StatusAddr, &buf, 1); + if (rc < 0) + return UPDATE_FAIL_READ_F34_CONTROLS; + + m_f34Command = buf & RMI_F34_COMMAND_MASK; + m_f34Status = (buf >> RMI_F34_STATUS_SHIFT) & RMI_F34_STATUS_MASK; + m_programEnabled = !!(buf & RMI_F34_ENABLED_MASK); + + return UPDATE_SUCCESS; +} + +int RMI4Update::WriteBootloaderID() +{ + int rc; + + rc = m_device.Write(m_f34.GetDataBase() + RMI_F34_BLOCK_DATA_OFFSET, + m_bootloaderID, RMI_BOOTLOADER_ID_SIZE); + if (rc < 0) + return UPDATE_FAIL_WRITE_BOOTLOADER_ID; + + return UPDATE_SUCCESS; +} + +int RMI4Update::EnterFlashProgramming() +{ + int rc; + unsigned char f01Control_0; + const unsigned char enableProg = RMI_F34_ENABLE_FLASH_PROG; + + rc = WriteBootloaderID(); + if (rc != UPDATE_SUCCESS) + return rc; + + fprintf(stdout, "Enabling flash programming.\n"); + rc = m_device.Write(m_f34StatusAddr, &enableProg, 1); + if (rc < 0) + return UPDATE_FAIL_ENABLE_FLASH_PROGRAMMING; + + rc = WaitForIdle(RMI_F34_ENABLE_WAIT_MS); + if (rc != UPDATE_SUCCESS) + return UPDATE_FAIL_NOT_IN_IDLE_STATE; + + if (!m_programEnabled) + return UPDATE_FAIL_PROGRAMMING_NOT_ENABLED; + + fprintf(stdout, "HOORAY! Programming is enabled!\n"); + rc = FindUpdateFunctions(); + if (rc != UPDATE_SUCCESS) + return rc; + + rc = m_device.Read(m_f01.GetDataBase(), &m_deviceStatus, 1); + if (rc < 0) + return UPDATE_FAIL_READ_DEVICE_STATUS; + + if (!RMI_F01_STATUS_BOOTLOADER(m_deviceStatus)) + return UPDATE_FAIL_DEVICE_NOT_IN_BOOTLOADER; + + rc = ReadF34Queries(); + if (rc != UPDATE_SUCCESS) + return rc; + + rc = m_device.Read(m_f01.GetControlBase(), &f01Control_0, 1); + if (rc < 0) + return UPDATE_FAIL_READ_F01_CONTROL_0; + + f01Control_0 |= RMI_F01_CRTL0_NOSLEEP_BIT; + f01Control_0 = (f01Control_0 & ~RMI_F01_CTRL0_SLEEP_MODE_MASK) | RMI_SLEEP_MODE_NORMAL; + + rc = m_device.Write(m_f01.GetControlBase(), &f01Control_0, 1); + if (rc < 0) + return UPDATE_FAIL_WRITE_F01_CONTROL_0; + + return UPDATE_SUCCESS; +} + +int RMI4Update::WriteBlocks(unsigned char *block, unsigned short count, unsigned char cmd) +{ + int blockNum; + unsigned char zeros[] = { 0, 0 }; + int rc; + unsigned short addr = m_f34.GetDataBase() + RMI_F34_BLOCK_DATA_OFFSET; + + rc = m_device.Write(m_f34.GetDataBase(), zeros, 2); + if (rc < 0) + return UPDATE_FAIL_WRITE_INITIAL_ZEROS; + + for (blockNum = 0; blockNum < count; ++blockNum) { + rc = m_device.Write(addr, block, m_blockSize); + if (rc < 0) { + fprintf(stderr, "failed to write block %d\n", blockNum); + return UPDATE_FAIL_WRITE_BLOCK; + } + + + rc = m_device.Write(m_f34StatusAddr, &cmd, 1); + if (rc < 0) { + fprintf(stderr, "failed to write command for block %d\n", blockNum); + return UPDATE_FAIL_WRITE_FLASH_COMMAND; + } + + rc = WaitForIdle(RMI_F34_IDLE_WAIT_MS); + if (rc != UPDATE_SUCCESS) { + fprintf(stderr, "failed to go into idle after writing block %d\n", blockNum); + return UPDATE_FAIL_NOT_IN_IDLE_STATE; + } + + block += m_blockSize; + } + + return UPDATE_SUCCESS; +} + +/* + * This is a limited implementation of WaitForIdle which assumes WaitForAttention is supported + * this will be true for HID, but other protocols will need to revert polling. Polling + * is not implemented yet. + */ +int RMI4Update::WaitForIdle(int timeout_ms) +{ + int rc; + struct timeval tv; + + tv.tv_sec = timeout_ms / 1000; + tv.tv_usec = (timeout_ms % 1000) * 1000; + + rc = m_device.WaitForAttention(&tv); + if (rc > 0) { + rc = ReadF34Controls(); + if (rc != UPDATE_SUCCESS) + return rc; + + if (!m_f34Status && !m_f34Command) { + if (!m_programEnabled) { + fprintf(stderr, "Bootloader is idle but program_enabled bit isn't set.\n"); + return UPDATE_FAIL_PROGRAMMING_NOT_ENABLED; + } else { + return UPDATE_SUCCESS; + } + } + + fprintf(stderr, "ERROR: Waiting for idle status.\n"); + fprintf(stderr, "Command: %#04x\n", m_f34Command); + fprintf(stderr, "Status: %#04x\n", m_f34Status); + fprintf(stderr, "Enabled: %d\n", m_programEnabled); + fprintf(stderr, "Idle: %d\n", !m_f34Command && !m_f34Status); + + return UPDATE_FAIL_NOT_IN_IDLE_STATE; + } + + return UPDATE_FAIL_TIMEOUT; +}
\ No newline at end of file diff --git a/rmi4update/rmi4update.h b/rmi4update/rmi4update.h new file mode 100644 index 0000000..e59f2f6 --- /dev/null +++ b/rmi4update/rmi4update.h @@ -0,0 +1,58 @@ +//=========================================================================== +// Copyright (c) 2012 - 2014 Synaptics Incorporated. All rights reserved. +//=========================================================================== + +#ifndef _RMI4UPDATE_H_ +#define _RMI4UPDATE_H_ + +#include "rmidevice.h" +#include "firmware_image.h" + +#define RMI_BOOTLOADER_ID_SIZE 2 + +class RMI4Update +{ +public: + RMI4Update(RMIDevice & device, FirmwareImage & firmwareImage) : m_device(device), + m_firmwareImage(firmwareImage) + {} + int UpdateFirmware(bool force = false); + +private: + int FindUpdateFunctions(); + int ReadF34Queries(); + int ReadF34Controls(); + int WriteBootloaderID(); + int EnterFlashProgramming(); + int WriteBlocks(unsigned char *block, unsigned short count, unsigned char cmd); + int WaitForIdle(int timeout_ms); + int GetFirmwareSize() { return m_blockSize * m_fwBlockCount; } + int GetConfigSize() { return m_blockSize * m_configBlockCount; } + +private: + RMIDevice & m_device; + FirmwareImage & m_firmwareImage; + + RMIFunction m_f01; + RMIFunction m_f34; + + unsigned char m_deviceStatus; + unsigned char m_bootloaderID[RMI_BOOTLOADER_ID_SIZE]; + + /* F34 Controls */ + unsigned char m_f34Command; + unsigned char m_f34Status; + bool m_programEnabled; + + /* F34 Query */ + bool m_hasNewRegmap; + bool m_unlocked; + bool m_hasConfigID; + unsigned short m_blockSize; + unsigned short m_fwBlockCount; + unsigned short m_configBlockCount; + + unsigned short m_f34StatusAddr; +}; + +#endif // _RMI4UPDATE_H_ diff --git a/rmi4update/updateutil.cpp b/rmi4update/updateutil.cpp new file mode 100644 index 0000000..33330f2 --- /dev/null +++ b/rmi4update/updateutil.cpp @@ -0,0 +1,54 @@ +#include "updateutil.h" + +const char *update_error_str[] = { + "success", // UPDATE_SUCCESS + "failed", // UPDATE_FAIL + "timeout", // UPDATE_FAIL_TIMEOUT + "invalid firmware image", // UPDATE_FAIL_VERIFY_IMAGE + "checksum does not match image", // UPDATE_FAIL_VERIFY_CHECKSUM + "image firmware size does not match device", // UPDATE_FAIL_VERIFY_FIRMWARE_SIZE + "image config size does not match device", // UPDATE_FAIL_VERIFY_CONFIG_SIZE + "image version is unsupported", // UPDATE_FAIL_UNSUPPORTED_IMAGE_VERSION + "failed to find F01 on device", // UPDATE_FAIL_NO_FUNCTION_01 + "failed to find F34 on device", // UPDATE_FAIL_NO_FUNCTION_34 + "failed to query the basic properties in F01", // UPDATE_FAIL_QUERY_BASIC_PROPERTIES + "failed to read F34 query registers", // UPDATE_FAIL_READ_F34_QUERIES + "failed to read the bootloader id", // UPDATE_FAIL_READ_BOOTLOADER_ID + "failed to read F34 control registers", // UPDATE_FAIL_READ_F34_CONTROLS + "failed to write the bootloader id", // UPDATE_FAIL_WRITE_BOOTLOADER_ID + "failed to enable flash programming", // UPDATE_FAIL_ENABLE_FLASH_PROGRAMMING + "failed to reach idle state", // UPDATE_FAIL_NOT_IN_IDLE_STATE + "programming is not enabled", // UPDATE_FAIL_PROGRAMMING_NOT_ENABLED + "failed to scan the PDT", // UPDATE_FAIL_SCAN_PDT + "failed to read the device status", // UPDATE_FAIL_READ_DEVICE_STATUS + "device not in the bootloader after enabling programming", // UPDATE_FAIL_DEVICE_NOT_IN_BOOTLOADER + "failed to read F01 control 0 register", // UPDATE_FAIL_READ_F01_CONTROL_0 + "failed to write F01 control 0 register", // UPDATE_FAIL_WRITE_F01_CONTROL_0 + "failed to write initial zeros", // UPDATE_FAIL_WRITE_INITIAL_ZEROS + "failed to write block", // UPDATE_FAIL_WRITE_BLOCK + "failed to write the flash command", // UPDATE_FAIL_WRITE_FLASH_COMMAND + "timeout waiting for idle", // UPDATE_FAIL_TIMEOUT_WAITING_FOR_IDLE + "failed to write erase all command", // UPDATE_FAIL_ERASE_ALL + "the firmware image is older then the firmware on the device", // UPDATE_FAIL_FIRMWARE_IMAGE_IS_OLDER + "invalid parameter", // UPDATE_FAIL_INVALID_PARAMETER + "failed to open firmware image file", // UPDATE_FAIL_OPEN_FIRMWARE_IMAGE +}; + +const char * update_err_to_string(int err) +{ + return update_error_str[err]; +} + +unsigned long extract_long(const unsigned char *data) +{ + return (unsigned long)data [0] + + (unsigned long)data [1] * 0x100 + + (unsigned long)data [2] * 0x10000 + + (unsigned long)data [3] * 0x1000000; +} + +unsigned short extract_short(const unsigned char *data) +{ + return (unsigned long)data [0] + + (unsigned long)data [1] * 0x100; +}
\ No newline at end of file diff --git a/rmi4update/updateutil.h b/rmi4update/updateutil.h new file mode 100644 index 0000000..c8beadd --- /dev/null +++ b/rmi4update/updateutil.h @@ -0,0 +1,43 @@ +#ifndef _UPDATEUTIL_H_ +#define _UPDATEUTIL_H_ + +enum update_error { + UPDATE_SUCCESS = 0, + UPDATE_FAIL, + UPDATE_FAIL_TIMEOUT, + UPDATE_FAIL_VERIFY_IMAGE, + UPDATE_FAIL_VERIFY_CHECKSUM, + UPDATE_FAIL_VERIFY_FIRMWARE_SIZE, + UPDATE_FAIL_VERIFY_CONFIG_SIZE, + UPDATE_FAIL_UNSUPPORTED_IMAGE_VERSION, + UPDATE_FAIL_NO_FUNCTION_01, + UPDATE_FAIL_NO_FUNCTION_34, + UPDATE_FAIL_QUERY_BASIC_PROPERTIES, + UPDATE_FAIL_READ_F34_QUERIES, + UPDATE_FAIL_READ_BOOTLOADER_ID, + UPDATE_FAIL_READ_F34_CONTROLS, + UPDATE_FAIL_WRITE_BOOTLOADER_ID, + UPDATE_FAIL_ENABLE_FLASH_PROGRAMMING, + UPDATE_FAIL_NOT_IN_IDLE_STATE, + UPDATE_FAIL_PROGRAMMING_NOT_ENABLED, + UPDATE_FAIL_SCAN_PDT, + UPDATE_FAIL_READ_DEVICE_STATUS, + UPDATE_FAIL_DEVICE_NOT_IN_BOOTLOADER, + UPDATE_FAIL_READ_F01_CONTROL_0, + UPDATE_FAIL_WRITE_F01_CONTROL_0, + UPDATE_FAIL_WRITE_INITIAL_ZEROS, + UPDATE_FAIL_WRITE_BLOCK, + UPDATE_FAIL_WRITE_FLASH_COMMAND, + UPDATE_FAIL_TIMEOUT_WAITING_FOR_IDLE, + UPDATE_FAIL_ERASE_ALL, + UPDATE_FAIL_FIRMWARE_IMAGE_IS_OLDER, + UPDATE_FAIL_INVALID_PARAMETER, + UPDATE_FAIL_OPEN_FIRMWARE_IMAGE, +}; + +const char * update_err_to_string(int err); + +unsigned long extract_long(const unsigned char *data); +unsigned short extract_short(const unsigned char *data); + +#endif // _UPDATEUTIL_H_
\ No newline at end of file |