aboutsummaryrefslogtreecommitdiff
path: root/rmi4update
diff options
context:
space:
mode:
authorAndrew Duggan <aduggan@synaptics.com>2014-04-03 15:17:57 -0700
committerAndrew Duggan <aduggan@synaptics.com>2014-04-03 15:17:57 -0700
commit4e811258783e0f1e0cdaf70e3bdb2069ce4f0465 (patch)
treefbd10bbcc263069c08b3e2f298b0079e159ab3bc /rmi4update
downloadrmi4utils-4e811258783e0f1e0cdaf70e3bdb2069ce4f0465.tar.gz
Initial commit
Diffstat (limited to 'rmi4update')
-rw-r--r--rmi4update/Makefile19
-rw-r--r--rmi4update/firmware_image.cpp146
-rw-r--r--rmi4update/firmware_image.h74
-rw-r--r--rmi4update/main.cpp76
-rw-r--r--rmi4update/rmi4update.cpp400
-rw-r--r--rmi4update/rmi4update.h58
-rw-r--r--rmi4update/updateutil.cpp54
-rw-r--r--rmi4update/updateutil.h43
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