aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile9
-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
-rw-r--r--rmidevice/Makefile22
-rw-r--r--rmidevice/hiddevice.cpp373
-rw-r--r--rmidevice/hiddevice.h62
-rw-r--r--rmidevice/rmidevice.cpp261
-rw-r--r--rmidevice/rmidevice.h66
-rw-r--r--rmidevice/rmifunction.cpp26
-rw-r--r--rmidevice/rmifunction.h27
-rw-r--r--rmihidtool/Makefile19
-rw-r--r--rmihidtool/main.cpp198
18 files changed, 1933 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..014409a
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,9 @@
+all:
+ make -C rmidevice all
+ make -C rmi4update all
+ make -C rmihidtool all
+
+clean:
+ make -C rmidevice clean
+ make -C rmi4update clean
+ make -C rmihidtool clean
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
diff --git a/rmidevice/Makefile b/rmidevice/Makefile
new file mode 100644
index 0000000..e98edd9
--- /dev/null
+++ b/rmidevice/Makefile
@@ -0,0 +1,22 @@
+CXX = g++
+LD = $(CXX)
+AR = ar
+RANLIB = ranlib
+INCLUDES = -I../include -I./include
+CXXFLAGS = -fPIC -g -Wall $(INCLUDES)
+RMIDEVICESRC = rmifunction.cpp rmidevice.cpp hiddevice.cpp
+RMIDEVICEOBJ = $(RMIDEVICESRC:.cpp=.o)
+LIBNAME = librmidevice.so
+STATIC_LIBNAME = librmidevice.a
+
+all: $(LIBNAME) $(STATIC_LIBNAME)
+
+$(LIBNAME): $(RMIDEVICEOBJ)
+ $(CXX) -shared -Wl,-soname,$(LIBNAME) $^ -o $@
+
+$(STATIC_LIBNAME): $(RMIDEVICEOBJ)
+ $(AR) crv $(STATIC_LIBNAME) $^
+ $(RANLIB) $(STATIC_LIBNAME)
+
+clean:
+ rm -f $(RMIDEVICEOBJ) $(LIBNAME)* $(STATIC_LIBNAME)*
diff --git a/rmidevice/hiddevice.cpp b/rmidevice/hiddevice.cpp
new file mode 100644
index 0000000..773ca38
--- /dev/null
+++ b/rmidevice/hiddevice.cpp
@@ -0,0 +1,373 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/select.h>
+
+#include <linux/types.h>
+#include <linux/input.h>
+#include <linux/hidraw.h>
+#include <signal.h>
+#include <stdlib.h>
+
+#include "hiddevice.h"
+
+#define RMI_WRITE_REPORT_ID 0x9 // Output Report
+#define RMI_READ_ADDR_REPORT_ID 0xa // Output Report
+#define RMI_READ_DATA_REPORT_ID 0xb // Input Report
+#define RMI_ATTN_REPORT_ID 0xc // Input Report
+#define RMI_SET_RMI_MODE_REPORT_ID 0xf // Feature Report
+
+enum rmi_hid_mode_type {
+ HID_RMI4_MODE_MOUSE = 0,
+ HID_RMI4_MODE_ATTN_REPORTS = 1,
+ HID_RMI4_MODE_NO_PACKED_ATTN_REPORTS = 2,
+};
+
+#define HID_RMI4_REPORT_ID 0
+#define HID_RMI4_READ_INPUT_COUNT 1
+#define HID_RMI4_READ_INPUT_DATA 2
+#define HID_RMI4_READ_OUTPUT_ADDR 2
+#define HID_RMI4_READ_OUTPUT_COUNT 4
+#define HID_RMI4_WRITE_OUTPUT_COUNT 1
+#define HID_RMI4_WRITE_OUTPUT_ADDR 2
+#define HID_RMI4_WRITE_OUTPUT_DATA 4
+#define HID_RMI4_FEATURE_MODE 1
+#define HID_RMI4_ATTN_INTERUPT_SOURCES 1
+#define HID_RMI4_ATTN_DATA 2
+
+int HIDDevice::Open(const char * filename)
+{
+ int rc;
+ int desc_size;
+
+ m_fd = open(filename, O_RDWR);
+ if (m_fd < 0)
+ return -1;
+
+ memset(&m_rptDesc, 0, sizeof(m_rptDesc));
+ memset(&m_info, 0, sizeof(m_info));
+
+ rc = ioctl(m_fd, HIDIOCGRDESCSIZE, &desc_size);
+ if (rc < 0)
+ return rc;
+
+ m_rptDesc.size = desc_size;
+ rc = ioctl(m_fd, HIDIOCGRDESC, &m_rptDesc);
+ if (rc < 0)
+ return rc;
+
+ rc = ioctl(m_fd, HIDIOCGRAWINFO, &m_info);
+ if (rc < 0)
+ return rc;
+
+ /* FIXME: get there from the report descriptor */
+ m_inputReportSize = 30;
+ m_outputReportSize = 25;
+ m_featureReportSize = 2;
+
+ m_inputReport = new unsigned char[m_inputReportSize]();
+ if (!m_inputReport)
+ return -ENOMEM;
+
+ m_outputReport = new unsigned char[m_outputReportSize]();
+ if (!m_outputReport)
+ return -ENOMEM;
+
+ m_readData = new unsigned char[m_inputReportSize]();
+ if (!m_readData)
+ return -ENOMEM;
+
+ m_attnReportQueue = new unsigned char[m_inputReportSize * HID_REPORT_QUEUE_MAX_SIZE]();
+ if (!m_attnReportQueue)
+ return -ENOMEM;
+
+ m_deviceOpen = true;
+
+ rc = SetMode(HID_RMI4_MODE_ATTN_REPORTS);
+ if (rc)
+ return -1;
+
+ return 0;
+}
+
+int HIDDevice::Read(unsigned short addr, unsigned char *buf, unsigned short len)
+{
+ size_t count;
+ size_t bytesReadPerRequest;
+ size_t bytesInDataReport;
+ size_t totalBytesRead;
+ size_t bytesPerRequest;
+ size_t bytesWritten;
+ size_t bytesToRequest;
+ int rc;
+
+ if (!m_deviceOpen)
+ return -1;
+
+ if (m_bytesPerReadRequest)
+ bytesPerRequest = m_bytesPerReadRequest;
+ else
+ bytesPerRequest = len;
+
+ for (totalBytesRead = 0; totalBytesRead < len; totalBytesRead += bytesReadPerRequest) {
+ count = 0;
+ if ((len - totalBytesRead) < bytesPerRequest)
+ bytesToRequest = len % bytesPerRequest;
+ else
+ bytesToRequest = bytesPerRequest;
+
+ m_outputReport[HID_RMI4_REPORT_ID] = RMI_READ_ADDR_REPORT_ID;
+ m_outputReport[1] = 0; /* old 1 byte read count */
+ m_outputReport[HID_RMI4_READ_OUTPUT_ADDR] = addr & 0xFF;
+ m_outputReport[HID_RMI4_READ_OUTPUT_ADDR + 1] = (addr >> 8) & 0xFF;
+ m_outputReport[HID_RMI4_READ_OUTPUT_COUNT] = bytesToRequest & 0xFF;
+ m_outputReport[HID_RMI4_READ_OUTPUT_COUNT + 1] = (bytesToRequest >> 8) & 0xFF;
+
+ m_dataBytesRead = 0;
+
+ for (bytesWritten = 0; bytesWritten < m_outputReportSize; bytesWritten += count) {
+ m_bCancel = false;
+ count = write(m_fd, m_outputReport + bytesWritten,
+ m_outputReportSize - bytesWritten);
+ if (count < 0) {
+ if (errno == EINTR && m_deviceOpen && !m_bCancel)
+ continue;
+ else
+ return count;
+ }
+ break;
+ }
+
+ bytesReadPerRequest = 0;
+ while (bytesReadPerRequest < bytesToRequest) {
+ rc = GetReport(RMI_READ_DATA_REPORT_ID);
+ if (rc > 0) {
+ bytesInDataReport = m_readData[HID_RMI4_READ_INPUT_COUNT];
+ memcpy(buf + bytesReadPerRequest, &m_readData[HID_RMI4_READ_INPUT_DATA],
+ bytesInDataReport);
+ bytesReadPerRequest += bytesInDataReport;
+ m_dataBytesRead = 0;
+ }
+ }
+ addr += bytesPerRequest;
+ }
+
+ return totalBytesRead;
+}
+
+int HIDDevice::Write(unsigned short addr, const unsigned char *buf, unsigned short len)
+{
+ size_t count;
+
+ if (!m_deviceOpen)
+ return -1;
+
+ m_outputReport[HID_RMI4_REPORT_ID] = RMI_WRITE_REPORT_ID;
+ m_outputReport[HID_RMI4_WRITE_OUTPUT_COUNT] = len;
+ m_outputReport[HID_RMI4_WRITE_OUTPUT_ADDR] = addr & 0xFF;
+ m_outputReport[HID_RMI4_WRITE_OUTPUT_ADDR + 1] = (addr >> 8) & 0xFF;
+ memcpy(&m_outputReport[HID_RMI4_WRITE_OUTPUT_DATA], buf, len);
+
+ for (;;) {
+ m_bCancel = false;
+ count = write(m_fd, m_outputReport, m_outputReportSize);
+ if (count < 0) {
+ if (errno == EINTR && m_deviceOpen && !m_bCancel)
+ continue;
+ else
+ return count;
+ }
+ return count;
+ }
+}
+
+int HIDDevice::SetMode(int mode)
+{
+ int rc;
+ char buf[2];
+
+ if (!m_deviceOpen)
+ return -1;
+
+ buf[0] = 0xF;
+ buf[1] = mode;
+ rc = ioctl(m_fd, HIDIOCSFEATURE(2), buf);
+ if (rc < 0) {
+ perror("HIDIOCSFEATURE");
+ return rc;
+ }
+
+ return 0;
+}
+
+void HIDDevice::Close()
+{
+ if (!m_deviceOpen)
+ return;
+
+ SetMode(HID_RMI4_MODE_MOUSE);
+ m_deviceOpen = false;
+ close(m_fd);
+ m_fd = -1;
+
+ delete[] m_inputReport;
+ m_inputReport = NULL;
+ delete[] m_outputReport;
+ m_outputReport = NULL;
+ delete[] m_readData;
+ m_readData = NULL;
+ delete[] m_attnReportQueue;
+ m_attnReportQueue = NULL;
+}
+
+int HIDDevice::WaitForAttention(struct timeval * timeout, int *sources)
+{
+ return GetAttentionReport(timeout, sources, NULL, NULL);
+}
+
+int HIDDevice::GetAttentionReport(struct timeval * timeout, int *sources, unsigned char *buf, int *len)
+{
+ int rc;
+ int interrupt_sources;
+ const unsigned char * queue_report;
+ int bytes = m_inputReportSize;
+
+ if (len && (int)m_inputReportSize < *len) {
+ bytes = *len;
+ *len = m_inputReportSize;
+ }
+
+ rc = GetReport(RMI_ATTN_REPORT_ID, timeout);
+ if (rc > 0) {
+ queue_report = m_attnReportQueue
+ + m_inputReportSize * m_tailIdx;
+ interrupt_sources = queue_report[HID_RMI4_ATTN_INTERUPT_SOURCES];
+ if (buf)
+ memcpy(buf, queue_report, bytes);
+ m_tailIdx = (m_tailIdx + 1) & (HID_REPORT_QUEUE_MAX_SIZE - 1);
+ --m_attnQueueCount;
+ if (sources)
+ *sources = interrupt_sources;
+ return rc;
+ }
+
+ return rc;
+}
+
+int HIDDevice::GetReport(int reportid, struct timeval * timeout)
+{
+ size_t count;
+ unsigned char *queue_report;
+ fd_set fds;
+ int rc;
+ int report_count = 0;
+
+ if (!m_deviceOpen)
+ return -1;
+
+ for (;;) {
+ FD_ZERO(&fds);
+ FD_SET(m_fd, &fds);
+
+ rc = select(m_fd + 1, &fds, NULL, NULL, timeout);
+ if (rc == 0) {
+ return -ETIMEDOUT;
+ } else if (rc < 0) {
+ if (errno == EINTR && m_deviceOpen && !m_bCancel)
+ continue;
+ else
+ return rc;
+ } else if (rc > 0 && FD_ISSET(m_fd, &fds)) {
+ size_t offset = 0;
+ for (;;) {
+ m_bCancel = false;
+ count = read(m_fd, m_inputReport + offset, m_inputReportSize - offset);
+ if (count < 0) {
+ if (errno == EINTR && m_deviceOpen && !m_bCancel)
+ continue;
+ else
+ return count;
+ }
+ offset += count;
+ if (offset == m_inputReportSize)
+ break;
+ }
+ }
+
+ if (m_inputReport[HID_RMI4_REPORT_ID] == RMI_ATTN_REPORT_ID) {
+ queue_report = m_attnReportQueue
+ + m_inputReportSize * m_headIdx;
+ memcpy(queue_report, m_inputReport, count);
+ m_headIdx = (m_headIdx + 1) & (HID_REPORT_QUEUE_MAX_SIZE - 1);
+ ++m_attnQueueCount;
+ } else if (m_inputReport[HID_RMI4_REPORT_ID] == RMI_READ_DATA_REPORT_ID) {
+ memcpy(m_readData, m_inputReport, count);
+ m_dataBytesRead = count;
+ }
+ ++report_count;
+
+ if (m_inputReport[HID_RMI4_REPORT_ID] == reportid)
+ break;
+ }
+ return report_count;
+}
+
+void HIDDevice::PrintReport(const unsigned char *report)
+{
+ int i;
+ int len = 0;
+ const unsigned char * data;
+ int addr = 0;
+
+ switch (report[HID_RMI4_REPORT_ID]) {
+ case RMI_WRITE_REPORT_ID:
+ len = report[HID_RMI4_WRITE_OUTPUT_COUNT];
+ data = &report[HID_RMI4_WRITE_OUTPUT_DATA];
+ addr = (report[HID_RMI4_WRITE_OUTPUT_ADDR] & 0xFF)
+ | ((report[HID_RMI4_WRITE_OUTPUT_ADDR + 1] & 0xFF) << 8);
+ fprintf(stdout, "Write Report:\n");
+ fprintf(stdout, "Address = 0x%02X\n", addr);
+ fprintf(stdout, "Length = 0x%02X\n", len);
+ break;
+ case RMI_READ_ADDR_REPORT_ID:
+ addr = (report[HID_RMI4_READ_OUTPUT_ADDR] & 0xFF)
+ | ((report[HID_RMI4_READ_OUTPUT_ADDR + 1] & 0xFF) << 8);
+ len = (report[HID_RMI4_READ_OUTPUT_COUNT] & 0xFF)
+ | ((report[HID_RMI4_READ_OUTPUT_COUNT + 1] & 0xFF) << 8);
+ fprintf(stdout, "Read Request (Output Report):\n");
+ fprintf(stdout, "Address = 0x%02X\n", addr);
+ fprintf(stdout, "Length = 0x%02X\n", len);
+ return;
+ break;
+ case RMI_READ_DATA_REPORT_ID:
+ len = report[HID_RMI4_READ_INPUT_COUNT];
+ data = &report[HID_RMI4_READ_INPUT_DATA];
+ fprintf(stdout, "Read Data Report:\n");
+ fprintf(stdout, "Length = 0x%02X\n", len);
+ break;
+ case RMI_ATTN_REPORT_ID:
+ fprintf(stdout, "Attention Report:\n");
+ len = 28;
+ data = &report[HID_RMI4_ATTN_DATA];
+ fprintf(stdout, "Interrupt Sources: 0x%02X\n",
+ report[HID_RMI4_ATTN_INTERUPT_SOURCES]);
+ break;
+ default:
+ fprintf(stderr, "Unknown Report: ID 0x%02x\n", report[HID_RMI4_REPORT_ID]);
+ return;
+ }
+
+ fprintf(stdout, "Data:\n");
+ for (i = 0; i < len; ++i) {
+ fprintf(stdout, "0x%02X ", data[i]);
+ if (i % 8 == 7) {
+ fprintf(stdout, "\n");
+ }
+ }
+ fprintf(stdout, "\n\n");
+} \ No newline at end of file
diff --git a/rmidevice/hiddevice.h b/rmidevice/hiddevice.h
new file mode 100644
index 0000000..59f32f9
--- /dev/null
+++ b/rmidevice/hiddevice.h
@@ -0,0 +1,62 @@
+#ifndef _HIDDEVICE_H_
+#define _HIDDEVICE_H_
+
+#include <linux/hidraw.h>
+#include "rmidevice.h"
+
+class HIDDevice : public RMIDevice
+{
+public:
+ HIDDevice(int bytesPerReadRequest = 0) : RMIDevice(bytesPerReadRequest), m_headIdx(0),
+ m_tailIdx(0), m_deviceOpen(false), m_bCancel(false),
+ m_attnQueueCount(0)
+ {}
+ virtual int Open(const char * filename);
+ virtual int Read(unsigned short addr, unsigned char *buf,
+ unsigned short len);
+ virtual int Write(unsigned short addr, const unsigned char *buf,
+ unsigned short len);
+ virtual int SetMode(int mode);
+ virtual int WaitForAttention(struct timeval * timeout = NULL, int *sources = NULL);
+ int GetAttentionReport(struct timeval * timeout, int *sources, unsigned char *buf, int *len);
+ virtual void Close();
+ virtual void Cancel() { m_bCancel = true; }
+ ~HIDDevice() { Close(); }
+
+private:
+ int m_fd;
+
+ struct hidraw_report_descriptor m_rptDesc;
+ struct hidraw_devinfo m_info;
+
+ unsigned char *m_inputReport;
+ unsigned char *m_outputReport;
+
+ unsigned char *m_attnReportQueue;
+ int m_headIdx;
+ int m_tailIdx;
+ unsigned char *m_readData;
+ int m_dataBytesRead;
+
+ size_t m_inputReportSize;
+ size_t m_outputReportSize;
+ size_t m_featureReportSize;
+
+ bool m_deviceOpen;
+ bool m_bCancel;
+
+ int m_attnQueueCount;
+
+ enum mode_type {
+ HID_RMI4_MODE_MOUSE = 0,
+ HID_RMI4_MODE_ATTN_REPORTS = 1,
+ HID_RMI4_MODE_NO_PACKED_ATTN_REPORTS = 2,
+ };
+
+ int GetReport(int reportid, struct timeval * timeout = NULL);
+ void PrintReport(const unsigned char *report);
+
+ static const int HID_REPORT_QUEUE_MAX_SIZE = 8;
+ };
+
+#endif /* _HIDDEVICE_H_ */ \ No newline at end of file
diff --git a/rmidevice/rmidevice.cpp b/rmidevice/rmidevice.cpp
new file mode 100644
index 0000000..93fe043
--- /dev/null
+++ b/rmidevice/rmidevice.cpp
@@ -0,0 +1,261 @@
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+#include <errno.h>
+
+#include "rmidevice.h"
+
+#define RMI_DEVICE_PDT_ENTRY_SIZE 6
+#define RMI_DEVICE_PAGE_SELECT_REGISTER 0xFF
+#define RMI_DEVICE_MAX_PAGE 0xFF
+#define RMI_DEVICE_PAGE_SIZE 0x100
+#define RMI_DEVICE_PAGE_SCAN_START 0x00e9
+#define RMI_DEVICE_PAGE_SCAN_END 0x0005
+#define RMI_DEVICE_F01_BASIC_QUERY_LEN 21
+#define RMI_DEVICE_F01_PRODUCTINFO_MASK 0x7f
+#define RMI_DEVICE_F01_QRY5_YEAR_MASK 0x1f
+#define RMI_DEVICE_F01_QRY6_MONTH_MASK 0x0f
+#define RMI_DEVICE_F01_QRY7_DAY_MASK 0x1f
+
+#define RMI_DEVICE_F01_QRY1_HAS_LTS (1 << 2)
+#define RMI_DEVICE_F01_QRY1_HAS_SENSOR_ID (1 << 3)
+#define RMI_DEVICE_F01_QRY1_HAS_CHARGER_INP (1 << 4)
+#define RMI_DEVICE_F01_QRY1_HAS_ADJ_DOZE (1 << 5)
+#define RMI_DEVICE_F01_QRY1_HAS_ADJ_DOZE_HOFF (1 << 6)
+#define RMI_DEVICE_F01_QRY1_HAS_PROPS_2 (1 << 7)
+
+#define RMI_DEVICE_F01_LTS_RESERVED_SIZE 19
+
+#define RMI_DEVICE_F01_QRY42_DS4_QUERIES (1 << 0)
+#define RMI_DEVICE_F01_QRY42_MULTI_PHYS (1 << 1)
+
+#define RMI_DEVICE_F01_QRY43_01_PACKAGE_ID (1 << 0)
+#define RMI_DEVICE_F01_QRY43_01_BUILD_ID (1 << 1)
+
+#define PACKAGE_ID_BYTES 4
+#define BUILD_ID_BYTES 3
+
+#define RMI_F01_CMD_DEVICE_RESET 1
+#define RMI_F01_DEFAULT_RESET_DELAY_MS 100
+
+int RMIDevice::SetRMIPage(unsigned char page)
+{
+ return Write(RMI_DEVICE_PAGE_SELECT_REGISTER, &page, 1);
+}
+
+int RMIDevice::QueryBasicProperties()
+{
+ int rc;
+ unsigned char basicQuery[RMI_DEVICE_F01_BASIC_QUERY_LEN];
+ unsigned short queryAddr;
+ unsigned char infoBuf[4];
+ unsigned short prodInfoAddr;
+ RMIFunction f01;
+
+ if (GetFunction(f01, 1)) {
+ queryAddr = f01.GetQueryBase();
+
+ rc = Read(queryAddr, basicQuery, RMI_DEVICE_F01_BASIC_QUERY_LEN);
+ if (rc < 0) {
+ fprintf(stderr, "Failed to read the basic query: %s\n", strerror(errno));
+ return rc;
+ }
+ m_manufacturerID = basicQuery[0];
+ m_hasLTS = basicQuery[1] & RMI_DEVICE_F01_QRY1_HAS_LTS;
+ m_hasSensorID = basicQuery[1] & RMI_DEVICE_F01_QRY1_HAS_SENSOR_ID;
+ m_hasAdjustableDoze = basicQuery[1] & RMI_DEVICE_F01_QRY1_HAS_ADJ_DOZE;
+ m_hasAdjustableDozeHoldoff = basicQuery[1] & RMI_DEVICE_F01_QRY1_HAS_ADJ_DOZE_HOFF;
+ m_hasQuery42 = basicQuery[1] & RMI_DEVICE_F01_QRY1_HAS_PROPS_2;
+ m_productInfo = ((basicQuery[2] & RMI_DEVICE_F01_PRODUCTINFO_MASK) << 7) |
+ (basicQuery[3] & RMI_DEVICE_F01_PRODUCTINFO_MASK);
+
+ snprintf(m_dom, sizeof(m_dom), "20%02d/%02d/%02d",
+ basicQuery[5] & RMI_DEVICE_F01_QRY5_YEAR_MASK,
+ basicQuery[6] & RMI_DEVICE_F01_QRY6_MONTH_MASK,
+ basicQuery[7] & RMI_DEVICE_F01_QRY7_DAY_MASK);
+
+ memcpy(m_productID, &basicQuery[11], RMI_PRODUCT_ID_LENGTH);
+ m_productID[RMI_PRODUCT_ID_LENGTH] = '\0';
+
+ queryAddr += 11;
+ prodInfoAddr = queryAddr + 6;
+ queryAddr += 10;
+
+ if (m_hasLTS)
+ ++queryAddr;
+
+ if (m_hasSensorID) {
+ rc = Read(queryAddr++, &m_sensorID, 1);
+ if (rc < 0) {
+ fprintf(stderr, "Failed to read sensor id: %s\n", strerror(errno));
+ return rc;
+ }
+ }
+
+ if (m_hasLTS)
+ queryAddr += RMI_DEVICE_F01_LTS_RESERVED_SIZE;
+
+ if (m_hasQuery42) {
+ rc = Read(queryAddr++, infoBuf, 1);
+ if (rc < 0) {
+ fprintf(stderr, "Failed to read query 42: %s\n", strerror(errno));
+ return rc;
+ }
+
+ m_hasDS4Queries = infoBuf[0] & RMI_DEVICE_F01_QRY42_DS4_QUERIES;
+ m_hasMultiPhysical = infoBuf[0] & RMI_DEVICE_F01_QRY42_MULTI_PHYS;
+ }
+
+ if (m_hasDS4Queries) {
+ rc = Read(queryAddr++, &m_ds4QueryLength, 1);
+ if (rc < 0) {
+ fprintf(stderr, "Failed to read DS4 query length: %s\n", strerror(errno));
+ return rc;
+ }
+ }
+
+ for (int i = 1; i <= m_ds4QueryLength; ++i) {
+ unsigned char val;
+ rc = Read(queryAddr++, &val, 1);
+ if (rc < 0) {
+ fprintf(stderr, "Failed to read F01 Query43.%02d: %s\n", i, strerror(errno));
+ continue;
+ }
+
+ switch(i) {
+ case 1:
+ m_hasPackageIDQuery = val & RMI_DEVICE_F01_QRY43_01_PACKAGE_ID;
+ m_hasBuildIDQuery = val & RMI_DEVICE_F01_QRY43_01_BUILD_ID;
+ break;
+ case 2:
+ case 3:
+ default:
+ break;
+ }
+ }
+
+ if (m_hasPackageIDQuery) {
+ rc = Read(prodInfoAddr++, infoBuf, PACKAGE_ID_BYTES);
+ if (rc > 0) {
+ unsigned short *val = (unsigned short *)infoBuf;
+ m_packageID = *val;
+ val = (unsigned short *)(infoBuf + 2);
+ m_packageRev = *val;
+ }
+ }
+
+ if (m_hasBuildIDQuery) {
+ rc = Read(prodInfoAddr, infoBuf, BUILD_ID_BYTES);
+ if (rc > 0) {
+ unsigned short *val = (unsigned short *)infoBuf;
+ m_buildID = *val;
+ m_buildID += infoBuf[2] * 65536;
+ }
+ }
+ }
+ return 0;
+}
+
+void RMIDevice::PrintProperties()
+{
+ fprintf(stdout, "manufacturerID:\t\t%d\n", m_manufacturerID);
+ fprintf(stdout, "Has LTS?:\t\t%d\n", m_hasLTS);
+ fprintf(stdout, "Has Sensor ID?:\t\t%d\n", m_hasSensorID);
+ fprintf(stdout, "Has Adjustable Doze?:\t%d\n", m_hasAdjustableDoze);
+ fprintf(stdout, "Has Query 42?:\t\t%d\n", m_hasQuery42);
+ fprintf(stdout, "Date of Manufacturer:\t%s\n", m_dom);
+ fprintf(stdout, "Product ID:\t\t%s\n", m_productID);
+ fprintf(stdout, "Product Info:\t\t%d\n", m_productInfo);
+ fprintf(stdout, "Package ID:\t\t%d\n", m_packageID);
+ fprintf(stdout, "Package Rev:\t\t%d\n", m_packageRev);
+ fprintf(stdout, "Build ID:\t\t%ld\n", m_buildID);
+ fprintf(stdout, "Sensor ID:\t\t%d\n", m_sensorID);
+ fprintf(stdout, "Has DS4 Queries?:\t%d\n", m_hasDS4Queries);
+ fprintf(stdout, "Has Multi Phys?:\t%d\n", m_hasMultiPhysical);
+ fprintf(stdout, "\n");
+}
+
+int RMIDevice::Reset()
+{
+ int rc;
+ RMIFunction f01;
+ struct timespec ts;
+ struct timespec rem;
+ const unsigned char deviceReset = RMI_F01_CMD_DEVICE_RESET;
+
+ if (!GetFunction(f01, 1))
+ return -1;
+
+ fprintf(stdout, "Resetting...\n");
+ rc = Write(f01.GetCommandBase(), &deviceReset, 1);
+ if (rc < 0)
+ return rc;
+
+ ts.tv_sec = RMI_F01_DEFAULT_RESET_DELAY_MS / 1000;
+ ts.tv_nsec = (RMI_F01_DEFAULT_RESET_DELAY_MS % 1000) * 1000 * 1000;
+ for (;;) {
+ if (nanosleep(&ts, &rem) == 0) {
+ break;
+ } else {
+ if (errno == EINTR) {
+ ts = rem;
+ continue;
+ }
+ return -1;
+ }
+ }
+ fprintf(stdout, "Reset completed.\n");
+ return 0;
+}
+
+bool RMIDevice::GetFunction(RMIFunction &func, int functionNumber)
+{
+ std::vector<RMIFunction>::iterator funcIter;
+
+ for (funcIter = m_functionList.begin(); funcIter != m_functionList.end(); ++funcIter) {
+ if (funcIter->GetFunctionNumber() == functionNumber) {
+ func = *funcIter;
+ return true;
+ }
+ }
+ return false;
+}
+
+int RMIDevice::ScanPDT()
+{
+ int rc;
+ unsigned int page;
+ unsigned int addr;
+ unsigned char entry[RMI_DEVICE_PDT_ENTRY_SIZE];
+
+ m_functionList.clear();
+
+ for (page = 0; page < RMI_DEVICE_MAX_PAGE; ++page) {
+ unsigned int page_start = RMI_DEVICE_PAGE_SIZE * page;
+ unsigned int pdt_start = page_start + RMI_DEVICE_PAGE_SCAN_START;
+ unsigned int pdt_end = page_start + RMI_DEVICE_PAGE_SCAN_END;
+ bool found = false;
+
+ SetRMIPage(page);
+
+ for (addr = pdt_start; addr >= pdt_end; addr -= RMI_DEVICE_PDT_ENTRY_SIZE) {
+ rc = Read(addr, entry, RMI_DEVICE_PDT_ENTRY_SIZE);
+ if (rc < 0) {
+ fprintf(stderr, "Failed to read PDT entry at address (0x%04x)\n", addr);
+ return rc;
+ }
+
+ RMIFunction func(entry);
+ if (func.GetFunctionNumber() == 0)
+ break;
+
+ m_functionList.push_back(func);
+ found = true;
+ }
+
+ if (!found)
+ break;
+ }
+
+ return 0;
+} \ No newline at end of file
diff --git a/rmidevice/rmidevice.h b/rmidevice/rmidevice.h
new file mode 100644
index 0000000..ca97b48
--- /dev/null
+++ b/rmidevice/rmidevice.h
@@ -0,0 +1,66 @@
+#ifndef _RMIDEVICE_H_
+#define _RMIDEVICE_H_
+
+#include <cstddef>
+#include <vector>
+
+#include "rmifunction.h"
+
+#define RMI_PRODUCT_ID_LENGTH 10
+
+class RMIDevice
+{
+public:
+ RMIDevice(int bytesPerReadRequest = 0) : m_bytesPerReadRequest(bytesPerReadRequest)
+ {}
+ virtual int Open(const char * filename) = 0;
+ virtual int Read(unsigned short addr, unsigned char *data,
+ unsigned short len) = 0;
+ virtual int Write(unsigned short addr, const unsigned char *data,
+ unsigned short len) = 0;
+ virtual int SetMode(int mode) = 0;
+ virtual int WaitForAttention(struct timeval * timeout = NULL, int *sources = NULL) = 0;
+ virtual void Close() = 0;
+ virtual void Cancel() = 0;
+
+ virtual unsigned long GetFirmwareID() { return m_buildID; }
+ virtual int QueryBasicProperties();
+
+ int SetRMIPage(unsigned char page);
+
+ int ScanPDT();
+ void PrintProperties();
+ int Reset();
+
+ bool GetFunction(RMIFunction &func, int functionNumber);
+
+ void SetBytesPerReadRequest(int bytes) { m_bytesPerReadRequest = bytes; }
+
+protected:
+ std::vector<RMIFunction> m_functionList;
+ unsigned char m_manufacturerID;
+ bool m_hasLTS;
+ bool m_hasSensorID;
+ bool m_hasAdjustableDoze;
+ bool m_hasAdjustableDozeHoldoff;
+ bool m_hasQuery42;
+ char m_dom[11];
+ unsigned char m_productID[RMI_PRODUCT_ID_LENGTH + 1];
+ unsigned short m_productInfo;
+ unsigned short m_packageID;
+ unsigned short m_packageRev;
+ unsigned long m_buildID;
+ unsigned char m_sensorID;
+
+ bool m_hasDS4Queries;
+ bool m_hasMultiPhysical;
+
+ unsigned char m_ds4QueryLength;
+
+ bool m_hasPackageIDQuery;
+ bool m_hasBuildIDQuery;
+
+ int m_bytesPerReadRequest;
+ };
+
+#endif /* _RMIDEVICE_H_ */ \ No newline at end of file
diff --git a/rmidevice/rmifunction.cpp b/rmidevice/rmifunction.cpp
new file mode 100644
index 0000000..2a7ccbb
--- /dev/null
+++ b/rmidevice/rmifunction.cpp
@@ -0,0 +1,26 @@
+#include "rmifunction.h"
+
+#define RMI_FUNCTION_QUERY_OFFSET 0
+#define RMI_FUNCTION_COMMAND_OFFSET 1
+#define RMI_FUNCTION_CONTROL_OFFSET 2
+#define RMI_FUNCTION_DATA_OFFSET 3
+#define RMI_FUNCTION_INTERRUPT_SOURCES_OFFSET 4
+#define RMI_FUNCTION_NUMBER 5
+
+#define RMI_FUNCTION_VERSION_MASK 0x60
+#define RMI_FUNCTION_INTERRUPT_SOURCES_MASK 0x7
+
+RMIFunction::RMIFunction(const unsigned char * pdtEntry)
+{
+ if (pdtEntry) {
+ m_queryBase = pdtEntry[RMI_FUNCTION_QUERY_OFFSET];
+ m_commandBase = pdtEntry[RMI_FUNCTION_COMMAND_OFFSET];
+ m_controlBase = pdtEntry[RMI_FUNCTION_CONTROL_OFFSET];
+ m_dataBase = pdtEntry[RMI_FUNCTION_DATA_OFFSET];
+ m_interruptSourceCount = pdtEntry[RMI_FUNCTION_INTERRUPT_SOURCES_OFFSET]
+ & RMI_FUNCTION_INTERRUPT_SOURCES_MASK;
+ m_functionNumber = pdtEntry[RMI_FUNCTION_NUMBER];
+ m_functionVersion = (pdtEntry[RMI_FUNCTION_INTERRUPT_SOURCES_OFFSET]
+ & RMI_FUNCTION_VERSION_MASK) >> 5;
+ }
+} \ No newline at end of file
diff --git a/rmidevice/rmifunction.h b/rmidevice/rmifunction.h
new file mode 100644
index 0000000..85be803
--- /dev/null
+++ b/rmidevice/rmifunction.h
@@ -0,0 +1,27 @@
+#ifndef _RMIFUNCTION_H_
+#define _RMIFUNCTION_H_
+
+class RMIFunction
+{
+public:
+ RMIFunction() {}
+ RMIFunction(const unsigned char * pdtEntry);
+ unsigned short GetQueryBase() { return m_queryBase; }
+ unsigned short GetCommandBase() { return m_commandBase; }
+ unsigned short GetControlBase() { return m_controlBase; }
+ unsigned short GetDataBase() { return m_dataBase; }
+ unsigned char GetInterruptSourceCount() { return m_interruptSourceCount; }
+ unsigned char GetFunctionNumber() { return m_functionNumber; }
+ unsigned char GetFunctionVersion() { return m_functionVersion; }
+
+private:
+ unsigned short m_queryBase;
+ unsigned short m_commandBase;
+ unsigned short m_controlBase;
+ unsigned short m_dataBase;
+ unsigned char m_interruptSourceCount;
+ unsigned char m_functionNumber;
+ unsigned char m_functionVersion;
+};
+
+#endif // _RMIFUNCTION_H_ \ No newline at end of file
diff --git a/rmihidtool/Makefile b/rmihidtool/Makefile
new file mode 100644
index 0000000..3b8ada6
--- /dev/null
+++ b/rmihidtool/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
+RMIHIDTOOLSRC = main.cpp
+RMIHIDTOOLOBJ = $(RMIHIDTOOLSRC:.cpp=.o)
+PROGNAME = rmihidtool
+
+all: $(PROGNAME)
+
+$(PROGNAME): $(RMIHIDTOOLOBJ)
+ $(LD) $(LDFLAGS) $(RMIHIDTOOLOBJ) -L$(LIBDIR) $(LIBS) -o $(PROGNAME)
+
+clean:
+ rm -f $(RMIHIDTOOLOBJ) $(PROGNAME) \ No newline at end of file
diff --git a/rmihidtool/main.cpp b/rmihidtool/main.cpp
new file mode 100644
index 0000000..128d67e
--- /dev/null
+++ b/rmihidtool/main.cpp
@@ -0,0 +1,198 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/select.h>
+
+#include <linux/types.h>
+#include <linux/input.h>
+#include <linux/hidraw.h>
+#include <signal.h>
+#include <stdlib.h>
+
+#include "hiddevice.h"
+
+static int report_attn = 0;
+static HIDDevice * g_device = NULL;
+
+void print_help()
+{
+ fprintf(stdout, "Commands:\n");
+ fprintf(stdout, "s [0,1,2]: Set RMIMode\n");
+ fprintf(stdout, "r address size: read size bytes from address\n");
+ fprintf(stdout, "w address { values }: write bytes to address\n");
+ fprintf(stdout, "a: Wait for attention\n");
+ fprintf(stdout, "q: quit\n");
+}
+
+int find_token(char * input, char * result, char ** endpp)
+{
+ int i = 0;
+ char * start = input;
+ char * end;
+
+ while (input[i] == ' ') {
+ ++start;
+ ++i;
+ }
+
+ while (input[i] != '\0') {
+ ++i;
+ if (input[i] == ' ') {
+ break;
+ }
+ }
+ end = &input[i];
+
+ if (start == end) {
+ return 0;
+ }
+
+ *endpp = end;
+ strncpy(result, start, end - start);
+ result[end - start + 1] = '\0';
+
+ return 1;
+}
+
+int process(HIDDevice * device, char * input)
+{
+ unsigned char report[256];
+ char token[256];
+ char * start;
+ char * end;
+ int rc;
+
+ memset(token, 0, 256);
+
+ if (input[0] == 's') {
+ start = input + 2;
+ find_token(start, token, &end);
+ int mode = strtol(token, NULL, 0);
+ if (mode >= 0 && mode <= 2) {
+ if (device->SetMode(mode)) {
+ fprintf(stderr, "Set RMI Mode to: %d\n", mode);
+ } else {
+ fprintf(stderr, "Set RMI Mode FAILED!\n");
+ return -1;
+ }
+ }
+ } else if (input[0] == 'r') {
+ start = input + 2;
+ find_token(start, token, &end);
+ start = end + 1;
+ int addr = strtol(token, NULL, 0);
+ find_token(start, token, &end);
+ start = end + 1;
+ int len = strtol(token, NULL, 0);
+ fprintf(stdout, "Address = 0x%02x Length = %d\n", addr, len);
+
+ memset(report, 0, sizeof(report));
+ rc = device->Read(addr, report, len);
+ if (rc < 0)
+ fprintf(stderr, "Failed to read report: %d\n", rc);
+
+ fprintf(stdout, "Data:\n");
+ for (int i = 0; i < len; ++i) {
+ fprintf(stdout, "0x%02X ", report[i]);
+ if (i % 8 == 7) {
+ fprintf(stdout, "\n");
+ }
+ }
+ fprintf(stdout, "\n");
+ } else if (input[0] == 'w') {
+ int index = 0;
+ start = input + 2;
+ find_token(start, token, &end);
+ start = end + 1;
+ int addr = strtol(token, NULL, 0);
+ int len = 0;
+
+ while (find_token(start, token, &end)) {
+ start = end + 1;
+ report[index++] = (unsigned char)strtol(token, NULL, 0);
+ ++len;
+ }
+
+ memset(report, 0, sizeof(report));
+
+ if (device->Write(addr, report, len) < 0) {
+ fprintf(stderr, "Failed to Write Report\n");
+ return -1;
+ }
+ } else if (input[0] == 'a') {
+ report_attn = 1;
+ while(report_attn) {
+ int bytes = 256;
+ device->GetAttentionReport(NULL, NULL, report, &bytes);
+ fprintf(stdout, "Data:\n");
+ for (int i = 0; i < bytes; ++i) {
+ fprintf(stdout, "0x%02X ", report[i]);
+ if (i % 8 == 7) {
+ fprintf(stdout, "\n");
+ }
+ }
+ fprintf(stdout, "\n\n");
+ }
+ } else if (input[0] == 'q') {
+ return 1;
+ } else {
+ print_help();
+ }
+ return 0;
+}
+
+static void cleanup(int status)
+{
+ if (report_attn) {
+ report_attn = 0;
+ if (g_device)
+ g_device->Cancel();
+ } else {
+ exit(0);
+ }
+}
+
+int main(int argc, char ** argv)
+{
+ int rc;
+ struct sigaction sig_cleanup_action;
+
+ memset(&sig_cleanup_action, 0, sizeof(struct sigaction));
+ sig_cleanup_action.sa_handler = cleanup;
+ sig_cleanup_action.sa_flags = SA_RESTART;
+ sigaction(SIGINT, &sig_cleanup_action, NULL);
+
+ HIDDevice device;
+ rc = device.Open(argv[1]);
+ if (rc) {
+ fprintf(stderr, "%s: failed to initialize rmi device (%d)\n", argv[0], rc);
+ return 1;
+ }
+
+ device.ScanPDT();
+ device.QueryBasicProperties();
+ device.PrintProperties();
+
+ g_device = &device;
+
+ fprintf(stdout, "\n");
+ for (;;) {
+ fprintf(stdout, "\n");
+ print_help();
+ char buf[256];
+
+ if (fgets(buf, 256, stdin)) {
+ if (process(&device, buf) == 1)
+ break;
+ }
+ }
+
+ device.Close();
+
+ return 0;
+} \ No newline at end of file