diff options
author | Andrew Duggan <aduggan@synaptics.com> | 2014-04-03 15:17:57 -0700 |
---|---|---|
committer | Andrew Duggan <aduggan@synaptics.com> | 2014-04-03 15:17:57 -0700 |
commit | 4e811258783e0f1e0cdaf70e3bdb2069ce4f0465 (patch) | |
tree | fbd10bbcc263069c08b3e2f298b0079e159ab3bc /rmidevice | |
download | rmi4utils-4e811258783e0f1e0cdaf70e3bdb2069ce4f0465.tar.gz |
Initial commit
Diffstat (limited to 'rmidevice')
-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 |
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 |