You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
736 lines
18 KiB
736 lines
18 KiB
/* |
|
* Copyright (C) 2014 Andrew Duggan |
|
* Copyright (C) 2014 Synaptics Inc |
|
* |
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
* you may not use this file except in compliance with the License. |
|
* You may obtain a copy of the License at |
|
* |
|
* http://www.apache.org/licenses/LICENSE-2.0 |
|
* |
|
* Unless required by applicable law or agreed to in writing, software |
|
* distributed under the License is distributed on an "AS IS" BASIS, |
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
* See the License for the specific language governing permissions and |
|
* limitations under the License. |
|
*/ |
|
|
|
#include <stdio.h> |
|
#include <sys/types.h> |
|
#include <sys/stat.h> |
|
#include <fcntl.h> |
|
#include <dirent.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, |
|
}; |
|
|
|
enum hid_report_type { |
|
HID_REPORT_TYPE_UNKNOWN = 0x0, |
|
HID_REPORT_TYPE_INPUT = 0x81, |
|
HID_REPORT_TYPE_OUTPUT = 0x91, |
|
HID_REPORT_TYPE_FEATURE = 0xb1, |
|
}; |
|
|
|
#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 |
|
|
|
#define SYNAPTICS_VENDOR_ID 0x06cb |
|
|
|
int HIDDevice::Open(const char * filename) |
|
{ |
|
int rc; |
|
int desc_size; |
|
|
|
if (!filename) |
|
return -EINVAL; |
|
|
|
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; |
|
|
|
if (m_info.vendor != SYNAPTICS_VENDOR_ID) { |
|
errno = -ENODEV; |
|
return -1; |
|
} |
|
|
|
ParseReportSizes(); |
|
|
|
m_inputReport = new unsigned char[m_inputReportSize](); |
|
if (!m_inputReport) { |
|
errno = -ENOMEM; |
|
return -1; |
|
} |
|
|
|
m_outputReport = new unsigned char[m_outputReportSize](); |
|
if (!m_outputReport) { |
|
errno = -ENOMEM; |
|
return -1; |
|
} |
|
|
|
m_readData = new unsigned char[m_inputReportSize](); |
|
if (!m_readData) { |
|
errno = -ENOMEM; |
|
return -1; |
|
} |
|
|
|
m_attnData = new unsigned char[m_inputReportSize](); |
|
if (!m_attnData) { |
|
errno = -ENOMEM; |
|
return -1; |
|
} |
|
|
|
m_deviceOpen = true; |
|
|
|
rc = SetMode(HID_RMI4_MODE_ATTN_REPORTS); |
|
if (rc) |
|
return -1; |
|
|
|
return 0; |
|
} |
|
|
|
void HIDDevice::ParseReportSizes() |
|
{ |
|
bool isVendorSpecific = false; |
|
bool isReport = false; |
|
int totalReportSize = 0; |
|
int reportSize = 0; |
|
int reportCount = 0; |
|
enum hid_report_type hidReportType = HID_REPORT_TYPE_UNKNOWN; |
|
|
|
for (unsigned int i = 0; i < m_rptDesc.size; ++i) { |
|
if (isVendorSpecific) { |
|
if (m_rptDesc.value[i] == 0x85 || m_rptDesc.value[i] == 0xc0) { |
|
if (isReport) { |
|
// finish up data on the previous report |
|
totalReportSize = (reportSize * reportCount) >> 3; |
|
|
|
switch (hidReportType) { |
|
case HID_REPORT_TYPE_INPUT: |
|
m_inputReportSize = totalReportSize + 1; |
|
break; |
|
case HID_REPORT_TYPE_OUTPUT: |
|
m_outputReportSize = totalReportSize + 1; |
|
break; |
|
case HID_REPORT_TYPE_FEATURE: |
|
m_featureReportSize = totalReportSize + 1; |
|
break; |
|
case HID_REPORT_TYPE_UNKNOWN: |
|
default: |
|
break; |
|
} |
|
} |
|
|
|
// reset values for the new report |
|
totalReportSize = 0; |
|
reportSize = 0; |
|
reportCount = 0; |
|
hidReportType = HID_REPORT_TYPE_UNKNOWN; |
|
|
|
if (m_rptDesc.value[i] == 0x85) |
|
isReport = true; |
|
else |
|
isReport = false; |
|
|
|
if (m_rptDesc.value[i] == 0xc0) |
|
isVendorSpecific = false; |
|
} |
|
|
|
if (isReport) { |
|
if (m_rptDesc.value[i] == 0x75) { |
|
if (i + 1 >= m_rptDesc.size) |
|
return; |
|
reportSize = m_rptDesc.value[++i]; |
|
continue; |
|
} |
|
|
|
if (m_rptDesc.value[i] == 0x95) { |
|
if (i + 1 >= m_rptDesc.size) |
|
return; |
|
reportCount = m_rptDesc.value[++i]; |
|
continue; |
|
} |
|
|
|
if (m_rptDesc.value[i] == HID_REPORT_TYPE_INPUT) |
|
hidReportType = HID_REPORT_TYPE_INPUT; |
|
|
|
if (m_rptDesc.value[i] == HID_REPORT_TYPE_OUTPUT) |
|
hidReportType = HID_REPORT_TYPE_OUTPUT; |
|
|
|
if (m_rptDesc.value[i] == HID_REPORT_TYPE_FEATURE) { |
|
hidReportType = HID_REPORT_TYPE_FEATURE; |
|
} |
|
} |
|
} |
|
|
|
if (i + 2 >= m_rptDesc.size) |
|
return; |
|
if (m_rptDesc.value[i] == 0x06 && m_rptDesc.value[i + 1] == 0x00 |
|
&& m_rptDesc.value[i + 2] == 0xFF) { |
|
isVendorSpecific = true; |
|
i += 2; |
|
} |
|
} |
|
} |
|
|
|
int HIDDevice::Read(unsigned short addr, unsigned char *buf, unsigned short len) |
|
{ |
|
ssize_t count; |
|
size_t bytesReadPerRequest; |
|
size_t bytesInDataReport; |
|
size_t totalBytesRead; |
|
size_t bytesPerRequest; |
|
size_t bytesWritten; |
|
size_t bytesToRequest; |
|
int reportId; |
|
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; |
|
|
|
if (m_outputReportSize < HID_RMI4_READ_OUTPUT_COUNT + 2) { |
|
return -1; |
|
} |
|
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(&reportId); |
|
if (rc > 0 && reportId == RMI_READ_DATA_REPORT_ID) { |
|
if (static_cast<ssize_t>(m_inputReportSize) < |
|
std::max(HID_RMI4_READ_INPUT_COUNT, |
|
HID_RMI4_READ_INPUT_DATA)) |
|
return -1; |
|
bytesInDataReport = m_readData[HID_RMI4_READ_INPUT_COUNT]; |
|
if (bytesInDataReport > bytesToRequest |
|
|| bytesReadPerRequest + bytesInDataReport > len) |
|
return -1; |
|
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) |
|
{ |
|
ssize_t count; |
|
|
|
if (!m_deviceOpen) |
|
return -1; |
|
|
|
if (static_cast<ssize_t>(m_outputReportSize) < |
|
HID_RMI4_WRITE_OUTPUT_DATA + len) |
|
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 len; |
|
} |
|
} |
|
|
|
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_attnData; |
|
m_attnData = NULL; |
|
} |
|
|
|
int HIDDevice::WaitForAttention(struct timeval * timeout, unsigned int source_mask) |
|
{ |
|
return GetAttentionReport(timeout, source_mask, NULL, NULL); |
|
} |
|
|
|
int HIDDevice::GetAttentionReport(struct timeval * timeout, unsigned int source_mask, |
|
unsigned char *buf, unsigned int *len) |
|
{ |
|
int rc = 0; |
|
int reportId; |
|
|
|
// Assume the Linux implementation of select with timeout set to the |
|
// time remaining. |
|
while (!timeout || (timeout->tv_sec != 0 || timeout->tv_usec != 0)) { |
|
rc = GetReport(&reportId, timeout); |
|
if (rc > 0) { |
|
if (reportId == RMI_ATTN_REPORT_ID) { |
|
// If a valid buffer is passed in then copy the data from |
|
// the attention report into it. If the buffer is |
|
// too small simply set *len to 0 to indicate nothing |
|
// was copied. Some callers won't care about the contents |
|
// of the report so failing to copy the data should not return |
|
// an error. |
|
if (buf && len) { |
|
if (*len >= m_inputReportSize) { |
|
*len = m_inputReportSize; |
|
memcpy(buf, m_attnData, *len); |
|
} else { |
|
*len = 0; |
|
} |
|
} |
|
|
|
if (m_inputReportSize < HID_RMI4_ATTN_INTERUPT_SOURCES + 1) |
|
return -1; |
|
|
|
if (source_mask & m_attnData[HID_RMI4_ATTN_INTERUPT_SOURCES]) |
|
return rc; |
|
} |
|
} else { |
|
return rc; |
|
} |
|
} |
|
|
|
return rc; |
|
} |
|
|
|
int HIDDevice::GetReport(int *reportId, struct timeval * timeout) |
|
{ |
|
ssize_t count = 0; |
|
fd_set fds; |
|
int rc; |
|
|
|
if (!m_deviceOpen) |
|
return -1; |
|
|
|
if (m_inputReportSize < HID_RMI4_REPORT_ID + 1) |
|
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; |
|
} |
|
count = offset; |
|
} |
|
break; |
|
} |
|
|
|
if (reportId) |
|
*reportId = m_inputReport[HID_RMI4_REPORT_ID]; |
|
|
|
if (m_inputReport[HID_RMI4_REPORT_ID] == RMI_ATTN_REPORT_ID) { |
|
if (static_cast<ssize_t>(m_inputReportSize) < count) |
|
return -1; |
|
memcpy(m_attnData, m_inputReport, count); |
|
} else if (m_inputReport[HID_RMI4_REPORT_ID] == RMI_READ_DATA_REPORT_ID) { |
|
if (static_cast<ssize_t>(m_inputReportSize) < count) |
|
return -1; |
|
memcpy(m_readData, m_inputReport, count); |
|
m_dataBytesRead = count; |
|
} |
|
return 1; |
|
} |
|
|
|
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"); |
|
} |
|
|
|
// Print protocol specific device information |
|
void HIDDevice::PrintDeviceInfo() |
|
{ |
|
fprintf(stdout, "HID device info:\nBus: %s Vendor: 0x%04x Product: 0x%04x\n", |
|
m_info.bustype == BUS_I2C ? "I2C" : "USB", m_info.vendor, m_info.product); |
|
fprintf(stdout, "Report sizes: input: %ld output: %ld\n", (unsigned long)m_inputReportSize, |
|
(unsigned long)m_outputReportSize); |
|
} |
|
|
|
bool WriteDeviceNameToFile(const char * file, const char * str) |
|
{ |
|
int fd; |
|
ssize_t size; |
|
|
|
fd = open(file, O_WRONLY); |
|
if (fd < 0) |
|
return false; |
|
|
|
for (;;) { |
|
size = write(fd, str, strlen(str)); |
|
if (size < 0) { |
|
if (errno == EINTR) |
|
continue; |
|
|
|
return false; |
|
} |
|
break; |
|
} |
|
|
|
return close(fd) == 0 && size == static_cast<ssize_t>(strlen(str)); |
|
} |
|
|
|
void HIDDevice::RebindDriver() |
|
{ |
|
int bus = m_info.bustype; |
|
int vendor = m_info.vendor; |
|
int product = m_info.product; |
|
std::string hidDeviceName; |
|
std::string transportDeviceName; |
|
std::string driverPath; |
|
std::string bindFile; |
|
std::string unbindFile; |
|
std::string hidrawFile; |
|
struct stat stat_buf; |
|
int rc; |
|
int i; |
|
|
|
Close(); |
|
|
|
if (!LookupHidDeviceName(bus, vendor, product, hidDeviceName)) { |
|
fprintf(stderr, "Failed to find HID device name for the specified device: bus (0x%x) vendor: (0x%x) product: (0x%x)\n", |
|
bus, vendor, product); |
|
return; |
|
} |
|
|
|
if (!FindTransportDevice(bus, hidDeviceName, transportDeviceName, driverPath)) { |
|
fprintf(stderr, "Failed to find the transport device / driver for %s\n", hidDeviceName.c_str()); |
|
return; |
|
} |
|
|
|
bindFile = driverPath + "bind"; |
|
unbindFile = driverPath + "unbind"; |
|
|
|
if (!WriteDeviceNameToFile(unbindFile.c_str(), transportDeviceName.c_str())) { |
|
fprintf(stderr, "Failed to unbind HID device %s: %s\n", |
|
transportDeviceName.c_str(), strerror(errno)); |
|
return; |
|
} |
|
|
|
if (!WriteDeviceNameToFile(bindFile.c_str(), transportDeviceName.c_str())) { |
|
fprintf(stderr, "Failed to bind HID device %s: %s\n", |
|
transportDeviceName.c_str(), strerror(errno)); |
|
return; |
|
} |
|
|
|
// The hid device id has changed since this is now a new hid device. Now we have to look up the new name. |
|
if (!LookupHidDeviceName(bus, vendor, product, hidDeviceName)) { |
|
fprintf(stderr, "Failed to find HID device name for the specified device: bus (0x%x) vendor: (0x%x) product: (0x%x)\n", |
|
bus, vendor, product); |
|
return; |
|
} |
|
|
|
if (!FindHidRawFile(hidDeviceName, hidrawFile)) { |
|
fprintf(stderr, "Failed to find the hidraw device file for %s\n", hidDeviceName.c_str()); |
|
return; |
|
} |
|
|
|
for (i = 0; i < 200; i++) { |
|
rc = stat(hidrawFile.c_str(), &stat_buf); |
|
if (!rc) |
|
break; |
|
Sleep(5); |
|
} |
|
|
|
rc = Open(hidrawFile.c_str()); |
|
if (rc) |
|
fprintf(stderr, "Failed to open device (%s) during rebind: %d: errno: %s (%d)\n", |
|
hidrawFile.c_str(), rc, strerror(errno), errno); |
|
} |
|
|
|
bool HIDDevice::FindTransportDevice(int bus, std::string & hidDeviceName, |
|
std::string & transportDeviceName, std::string & driverPath) |
|
{ |
|
std::string devicePrefix = "/sys/bus/"; |
|
std::string devicePath; |
|
struct dirent * devicesDirEntry; |
|
DIR * devicesDir; |
|
struct dirent * devDirEntry; |
|
DIR * devDir; |
|
bool deviceFound = false; |
|
ssize_t sz; |
|
|
|
if (bus == BUS_I2C) { |
|
devicePrefix += "i2c/"; |
|
driverPath = devicePrefix + "drivers/i2c_hid/"; |
|
} else { |
|
devicePrefix += "usb/"; |
|
driverPath = devicePrefix + "drivers/usbhid/"; |
|
} |
|
devicePath = devicePrefix + "devices/"; |
|
|
|
devicesDir = opendir(devicePath.c_str()); |
|
if (!devicesDir) |
|
return false; |
|
|
|
while((devicesDirEntry = readdir(devicesDir)) != NULL) { |
|
if (devicesDirEntry->d_type != DT_LNK) |
|
continue; |
|
|
|
char buf[PATH_MAX]; |
|
|
|
sz = readlinkat(dirfd(devicesDir), devicesDirEntry->d_name, buf, PATH_MAX); |
|
if (sz < 0) |
|
continue; |
|
|
|
buf[sz] = 0; |
|
|
|
std::string fullLinkPath = devicePath + buf; |
|
devDir = opendir(fullLinkPath.c_str()); |
|
if (!devDir) { |
|
fprintf(stdout, "opendir failed\n"); |
|
continue; |
|
} |
|
|
|
while ((devDirEntry = readdir(devDir)) != NULL) { |
|
if (!strcmp(devDirEntry->d_name, hidDeviceName.c_str())) { |
|
transportDeviceName = devicesDirEntry->d_name; |
|
deviceFound = true; |
|
break; |
|
} |
|
} |
|
closedir(devDir); |
|
|
|
if (deviceFound) |
|
break; |
|
} |
|
closedir(devicesDir); |
|
|
|
return deviceFound; |
|
} |
|
|
|
bool HIDDevice::LookupHidDeviceName(int bus, int vendorId, int productId, std::string & deviceName) |
|
{ |
|
bool ret = false; |
|
struct dirent * devDirEntry; |
|
DIR * devDir; |
|
char devicePrefix[15]; |
|
|
|
snprintf(devicePrefix, 15, "%04X:%04X:%04X", bus, vendorId, productId); |
|
|
|
devDir = opendir("/sys/bus/hid/devices"); |
|
if (!devDir) |
|
return false; |
|
|
|
while ((devDirEntry = readdir(devDir)) != NULL) { |
|
if (!strncmp(devDirEntry->d_name, devicePrefix, 14)) { |
|
deviceName = devDirEntry->d_name; |
|
ret = true; |
|
break; |
|
} |
|
} |
|
closedir(devDir); |
|
|
|
return ret; |
|
} |
|
|
|
bool HIDDevice::FindHidRawFile(std::string & deviceName, std::string & hidrawFile) |
|
{ |
|
bool ret = false; |
|
char hidrawDir[PATH_MAX]; |
|
struct dirent * devDirEntry; |
|
DIR * devDir; |
|
|
|
snprintf(hidrawDir, PATH_MAX, "/sys/bus/hid/devices/%s/hidraw", deviceName.c_str()); |
|
|
|
devDir = opendir(hidrawDir); |
|
if (!devDir) |
|
return false; |
|
|
|
while ((devDirEntry = readdir(devDir)) != NULL) { |
|
if (!strncmp(devDirEntry->d_name, "hidraw", 6)) { |
|
hidrawFile = std::string("/dev/") + devDirEntry->d_name; |
|
ret = true; |
|
break; |
|
} |
|
} |
|
closedir(devDir); |
|
|
|
return ret; |
|
}
|
|
|