aboutsummaryrefslogtreecommitdiff
path: root/rmidevice
diff options
context:
space:
mode:
authorAndrew Duggan <aduggan@synaptics.com>2014-04-03 15:17:57 -0700
committerAndrew Duggan <aduggan@synaptics.com>2014-04-03 15:17:57 -0700
commit4e811258783e0f1e0cdaf70e3bdb2069ce4f0465 (patch)
treefbd10bbcc263069c08b3e2f298b0079e159ab3bc /rmidevice
downloadrmi4utils-4e811258783e0f1e0cdaf70e3bdb2069ce4f0465.tar.gz
Initial commit
Diffstat (limited to 'rmidevice')
-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
7 files changed, 837 insertions, 0 deletions
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