aboutsummaryrefslogtreecommitdiff
path: root/rmidevice/hiddevice.cpp
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/hiddevice.cpp
downloadrmi4utils-4e811258783e0f1e0cdaf70e3bdb2069ce4f0465.tar.gz
Initial commit
Diffstat (limited to 'rmidevice/hiddevice.cpp')
-rw-r--r--rmidevice/hiddevice.cpp373
1 files changed, 373 insertions, 0 deletions
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