diff options
-rw-r--r-- | Makefile | 9 | ||||
-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 | ||||
-rw-r--r-- | rmidevice/Makefile | 22 | ||||
-rw-r--r-- | rmidevice/hiddevice.cpp | 373 | ||||
-rw-r--r-- | rmidevice/hiddevice.h | 62 | ||||
-rw-r--r-- | rmidevice/rmidevice.cpp | 261 | ||||
-rw-r--r-- | rmidevice/rmidevice.h | 66 | ||||
-rw-r--r-- | rmidevice/rmifunction.cpp | 26 | ||||
-rw-r--r-- | rmidevice/rmifunction.h | 27 | ||||
-rw-r--r-- | rmihidtool/Makefile | 19 | ||||
-rw-r--r-- | rmihidtool/main.cpp | 198 |
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 |