diff options
Diffstat (limited to 'rmidevice/hiddevice.cpp')
-rwxr-xr-x[-rw-r--r--] | rmidevice/hiddevice.cpp | 446 |
1 files changed, 355 insertions, 91 deletions
diff --git a/rmidevice/hiddevice.cpp b/rmidevice/hiddevice.cpp index 7ebf5fd..709559d 100644..100755 --- a/rmidevice/hiddevice.cpp +++ b/rmidevice/hiddevice.cpp @@ -31,6 +31,7 @@ #include <linux/hidraw.h> #include <signal.h> #include <stdlib.h> +#include <sys/inotify.h> #include "hiddevice.h" @@ -40,12 +41,6 @@ #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, @@ -71,6 +66,8 @@ int HIDDevice::Open(const char * filename) { int rc; int desc_size; + std::string hidDeviceName; + std::string hidDriverName; if (!filename) return -EINVAL; @@ -84,58 +81,80 @@ int HIDDevice::Open(const char * filename) rc = ioctl(m_fd, HIDIOCGRDESCSIZE, &desc_size); if (rc < 0) - return rc; + goto error; m_rptDesc.size = desc_size; rc = ioctl(m_fd, HIDIOCGRDESC, &m_rptDesc); if (rc < 0) - return rc; + goto error; rc = ioctl(m_fd, HIDIOCGRAWINFO, &m_info); if (rc < 0) - return rc; + goto error; if (m_info.vendor != SYNAPTICS_VENDOR_ID) { errno = -ENODEV; - return -1; + rc = -1; + goto error; } - ParseReportSizes(); + ParseReportDescriptor(); m_inputReport = new unsigned char[m_inputReportSize](); if (!m_inputReport) { errno = -ENOMEM; - return -1; + rc = -1; + goto error; } m_outputReport = new unsigned char[m_outputReportSize](); if (!m_outputReport) { errno = -ENOMEM; - return -1; + rc = -1; + goto error; } m_readData = new unsigned char[m_inputReportSize](); if (!m_readData) { errno = -ENOMEM; - return -1; + rc = -1; + goto error; } m_attnData = new unsigned char[m_inputReportSize](); if (!m_attnData) { errno = -ENOMEM; - return -1; + rc = -1; + goto error; } m_deviceOpen = true; - rc = SetMode(HID_RMI4_MODE_ATTN_REPORTS); - if (rc) - return -1; + // Determine which mode the device is currently running in based on the current HID driver + // hid-rmi indicated RMI Mode 1 all others would be Mode 0 + if (LookupHidDeviceName(m_info.bustype, m_info.vendor, m_info.product, hidDeviceName)) { + if (LookupHidDriverName(hidDeviceName, hidDriverName)) { + if (hidDriverName == "hid-rmi") + m_initialMode = HID_RMI4_MODE_ATTN_REPORTS; + } + } + + if (m_initialMode != m_mode) { + rc = SetMode(m_mode); + if (rc) { + rc = -1; + goto error; + } + } return 0; + +error: + Close(); + return rc; } -void HIDDevice::ParseReportSizes() +void HIDDevice::ParseReportDescriptor() { bool isVendorSpecific = false; bool isReport = false; @@ -143,10 +162,18 @@ void HIDDevice::ParseReportSizes() int reportSize = 0; int reportCount = 0; enum hid_report_type hidReportType = HID_REPORT_TYPE_UNKNOWN; + bool inCollection = false; for (unsigned int i = 0; i < m_rptDesc.size; ++i) { + if (m_rptDesc.value[i] == 0xc0) { + inCollection = false; + isVendorSpecific = false; + isReport = false; + continue; + } + if (isVendorSpecific) { - if (m_rptDesc.value[i] == 0x85 || m_rptDesc.value[i] == 0xc0) { + if (m_rptDesc.value[i] == 0x85) { if (isReport) { // finish up data on the previous report totalReportSize = (reportSize * reportCount) >> 3; @@ -173,13 +200,7 @@ void HIDDevice::ParseReportSizes() 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; + isReport = true; } if (isReport) { @@ -209,12 +230,52 @@ void HIDDevice::ParseReportSizes() } } - 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; + if (!inCollection) { + switch (m_rptDesc.value[i]) { + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + inCollection = true; + break; + case 0x05: + inCollection = true; + + if (i + 3 >= m_rptDesc.size) + break; + + // touchscreens with active pen have a Generic Mouse collection + // so stop searching if we have already found the touchscreen digitizer + // usage. + if (m_deviceType == RMI_DEVICE_TYPE_TOUCHSCREEN) + break; + + if (m_rptDesc.value[i + 1] == 0x01) { + if (m_rptDesc.value[i + 2] == 0x09 && m_rptDesc.value[i + 3] == 0x02) + m_deviceType = RMI_DEVICE_TYPE_TOUCHPAD; + } else if (m_rptDesc.value[i + 1] == 0x0d) { + if (m_rptDesc.value[i + 2] == 0x09 && m_rptDesc.value[i + 3] == 0x04) + m_deviceType = RMI_DEVICE_TYPE_TOUCHSCREEN; + // for Precision Touch Pad + else if (m_rptDesc.value[i + 2] == 0x09 && m_rptDesc.value[i + 3] == 0x05) + m_deviceType = RMI_DEVICE_TYPE_TOUCHPAD; + } + i += 3; + break; + case 0x06: + inCollection = true; + if (i + 2 >= m_rptDesc.size) + break; + + if (m_rptDesc.value[i + 1] == 0x00 && m_rptDesc.value[i + 2] == 0xFF) + isVendorSpecific = true; + i += 2; + break; + default: + break; + + } } } } @@ -277,12 +338,14 @@ int HIDDevice::Read(unsigned short addr, unsigned char *buf, unsigned short len) 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)) + HID_RMI4_READ_INPUT_DATA)){ return -1; + } bytesInDataReport = m_readData[HID_RMI4_READ_INPUT_COUNT]; if (bytesInDataReport > bytesToRequest - || bytesReadPerRequest + bytesInDataReport > len) + || bytesReadPerRequest + bytesInDataReport > len){ return -1; + } memcpy(buf + bytesReadPerRequest, &m_readData[HID_RMI4_READ_INPUT_DATA], bytesInDataReport); bytesReadPerRequest += bytesInDataReport; @@ -345,10 +408,14 @@ int HIDDevice::SetMode(int mode) void HIDDevice::Close() { + RMIDevice::Close(); + if (!m_deviceOpen) return; - SetMode(HID_RMI4_MODE_MOUSE); + if (m_initialMode != m_mode) + SetMode(m_initialMode); + m_deviceOpen = false; close(m_fd); m_fd = -1; @@ -527,10 +594,15 @@ void HIDDevice::PrintReport(const unsigned char *report) // Print protocol specific device information void HIDDevice::PrintDeviceInfo() { + enum RMIDeviceType deviceType = GetDeviceType(); + 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); + if (deviceType) + fprintf(stdout, "device type: %s\n", deviceType == RMI_DEVICE_TYPE_TOUCHSCREEN ? + "touchscreen" : "touchpad"); } bool WriteDeviceNameToFile(const char * file, const char * str) @@ -555,76 +627,159 @@ bool WriteDeviceNameToFile(const char * file, const char * str) return close(fd) == 0 && size == static_cast<ssize_t>(strlen(str)); } +static const char * const absval[6] = { "Value", "Min ", "Max ", "Fuzz ", "Flat ", "Resolution "}; +#define KEY_MAX 0x2ff +#define EV_MAX 0x1f +#define BITS_PER_LONG (sizeof(long) * 8) +#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) +#define OFF(x) ((x)%BITS_PER_LONG) +#define BIT(x) (1UL<<OFF(x)) +#define LONG(x) ((x)/BITS_PER_LONG) +#define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1) +#define DEV_INPUT_EVENT "/dev/input" +#define EVENT_DEV_NAME "event" +/** + * Filter for the AutoDevProbe scandir on /dev/input. + * + * @param dir The current directory entry provided by scandir. + * + * @return Non-zero if the given directory entry starts with "event", or zero + * otherwise. + */ +static int is_event_device(const struct dirent *dir) { + return strncmp(EVENT_DEV_NAME, dir->d_name, 5) == 0; +} + +bool HIDDevice::CheckABSEvent() +{ + int fd=-1; + unsigned int type; + int abs[6] = {0}; + int k; + struct dirent **namelist; + int i, ndev, devnum, match; + char *filename; + int max_device = 0; + char input_event_name[PATH_MAX]; + unsigned long bit[EV_MAX][NBITS(KEY_MAX)]; + + +#ifdef __BIONIC__ + // Android's libc doesn't have the GNU versionsort extension. + ndev = scandir(DEV_INPUT_EVENT, &namelist, is_event_device, alphasort); +#else + ndev = scandir(DEV_INPUT_EVENT, &namelist, is_event_device, versionsort); +#endif + if (ndev <= 0) + return false; + for (i = 0; i < ndev; i++) + { + char fname[64]; + int fd = -1; + char name[256] = "???"; + + snprintf(fname, sizeof(fname), + "%s/%s", DEV_INPUT_EVENT, namelist[i]->d_name); + fd = open(fname, O_RDONLY); + if (fd < 0) + continue; + ioctl(fd, EVIOCGNAME(sizeof(name)), name); + //fprintf(stderr, "%s: %s\n", fname, name); + close(fd); + if(strstr(name, m_transportDeviceName.c_str()+4)) + { + snprintf(input_event_name, sizeof(fname), "%s", fname); + } + free(namelist[i]); + } + + if ((fd = open(input_event_name, O_RDONLY)) < 0) { + if (errno == EACCES && getuid() != 0) + fprintf(stderr, "No access right \n"); + } + memset(bit, 0, sizeof(bit)); + ioctl(fd, EVIOCGBIT(0, EV_MAX), bit[0]); + for (type = 0; type < EV_MAX; type++) { + if (test_bit(type, bit[0]) && type == EV_ABS) { + ioctl(fd, EVIOCGBIT(type, KEY_MAX), bit[type]); + if (test_bit(ABS_X, bit[type])) { + ioctl(fd, EVIOCGABS(ABS_X), abs); + if(abs[2] == 0) //maximum + { + Sleep(1000); + return false; + } + } + } + } + return true; +} 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 notifyFd; + int wd; 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); + notifyFd = inotify_init(); + if (notifyFd < 0) { + fprintf(stderr, "Failed to initialize inotify\n"); return; } - if (!FindTransportDevice(bus, hidDeviceName, transportDeviceName, driverPath)) { - fprintf(stderr, "Failed to find the transport device / driver for %s\n", hidDeviceName.c_str()); + wd = inotify_add_watch(notifyFd, "/dev", IN_CREATE); + if (wd < 0) { + fprintf(stderr, "Failed to add watcher for /dev\n"); return; } - bindFile = driverPath + "bind"; - unbindFile = driverPath + "unbind"; + if (m_transportDeviceName == "") { + 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 (!WriteDeviceNameToFile(unbindFile.c_str(), transportDeviceName.c_str())) { - fprintf(stderr, "Failed to unbind HID device %s: %s\n", - transportDeviceName.c_str(), strerror(errno)); - return; - } + if (!FindTransportDevice(bus, hidDeviceName, m_transportDeviceName, m_driverPath)) { + fprintf(stderr, "Failed to find the transport device / driver for %s\n", hidDeviceName.c_str()); + 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; } + + bindFile = m_driverPath + "bind"; + unbindFile = m_driverPath + "unbind"; - // 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); + Sleep(500); + if (!WriteDeviceNameToFile(unbindFile.c_str(), m_transportDeviceName.c_str())) { + fprintf(stderr, "Failed to unbind HID device %s: %s\n", + m_transportDeviceName.c_str(), strerror(errno)); return; } - - if (!FindHidRawFile(hidDeviceName, hidrawFile)) { - fprintf(stderr, "Failed to find the hidraw device file for %s\n", hidDeviceName.c_str()); + Sleep(500); + if (!WriteDeviceNameToFile(bindFile.c_str(), m_transportDeviceName.c_str())) { + fprintf(stderr, "Failed to bind HID device %s: %s\n", + m_transportDeviceName.c_str(), strerror(errno)); return; } - for (i = 0; i < 200; i++) { - rc = stat(hidrawFile.c_str(), &stat_buf); - if (!rc) - break; - Sleep(5); + if (WaitForHidRawDevice(notifyFd, hidrawFile)) { + 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); } - - 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, +bool HIDDevice::FindTransportDevice(uint32_t bus, std::string & hidDeviceName, std::string & transportDeviceName, std::string & driverPath) { std::string devicePrefix = "/sys/bus/"; @@ -638,7 +793,15 @@ bool HIDDevice::FindTransportDevice(int bus, std::string & hidDeviceName, if (bus == BUS_I2C) { devicePrefix += "i2c/"; - driverPath = devicePrefix + "drivers/i2c_hid/"; + // From new patch released on 2020/11, i2c_hid would be renamed as i2c_hid_acpi, + // and also need backward compatible. + std::string driverPathTemp = devicePrefix + "drivers/i2c_hid/"; + DIR *driverPathtest = opendir(driverPathTemp.c_str()); + if(!driverPathtest) { + driverPath = devicePrefix + "drivers/i2c_hid_acpi/"; + } else { + driverPath = devicePrefix + "drivers/i2c_hid/"; + } } else { devicePrefix += "usb/"; driverPath = devicePrefix + "drivers/usbhid/"; @@ -685,14 +848,14 @@ bool HIDDevice::FindTransportDevice(int bus, std::string & hidDeviceName, return deviceFound; } -bool HIDDevice::LookupHidDeviceName(int bus, int vendorId, int productId, std::string & deviceName) +bool HIDDevice::LookupHidDeviceName(uint32_t bus, int16_t vendorId, int16_t productId, std::string & deviceName) { bool ret = false; struct dirent * devDirEntry; DIR * devDir; char devicePrefix[15]; - snprintf(devicePrefix, 15, "%04X:%04X:%04X", bus, vendorId, productId); + snprintf(devicePrefix, 15, "%04X:%04X:%04X", bus, (vendorId & 0xFFFF), (productId & 0xFFFF)); devDir = opendir("/sys/bus/hid/devices"); if (!devDir) @@ -710,27 +873,128 @@ bool HIDDevice::LookupHidDeviceName(int bus, int vendorId, int productId, std::s return ret; } -bool HIDDevice::FindHidRawFile(std::string & deviceName, std::string & hidrawFile) +bool HIDDevice::LookupHidDriverName(std::string &deviceName, std::string &driverName) { bool ret = false; - char hidrawDir[PATH_MAX]; - struct dirent * devDirEntry; - DIR * devDir; + ssize_t sz; + char link[PATH_MAX]; + std::string driverLink = "/sys/bus/hid/devices/" + deviceName + "/driver"; + + sz = readlink(driverLink.c_str(), link, PATH_MAX); + if (sz == -1) + return ret; - snprintf(hidrawDir, PATH_MAX, "/sys/bus/hid/devices/%s/hidraw", deviceName.c_str()); + link[sz] = 0; - devDir = opendir(hidrawDir); + driverName = std::string(StripPath(link, PATH_MAX)); + + return true; +} + +bool HIDDevice::WaitForHidRawDevice(int notifyFd, std::string & hidrawFile) +{ + struct timeval timeout; + fd_set fds; + int rc; + ssize_t eventBytesRead; + int eventBytesAvailable; + size_t sz; + char link[PATH_MAX]; + std::string transportDeviceName; + std::string driverPath; + std::string hidDeviceName; + int offset = 0; + + for (;;) { + FD_ZERO(&fds); + FD_SET(notifyFd, &fds); + + timeout.tv_sec = 20; + timeout.tv_usec = 0; + + rc = select(notifyFd + 1, &fds, NULL, NULL, &timeout); + if (rc < 0) { + if (errno == -EINTR) + continue; + + return false; + } + + if (rc == 0) { + return false; + } + + if (FD_ISSET(notifyFd, &fds)) { + struct inotify_event * event; + + rc = ioctl(notifyFd, FIONREAD, &eventBytesAvailable); + if (rc < 0) { + continue; + } + + char buf[eventBytesAvailable]; + + eventBytesRead = read(notifyFd, buf, eventBytesAvailable); + if (eventBytesRead < 0) { + continue; + } + + while (offset < eventBytesRead) { + event = (struct inotify_event *)&buf[offset]; + + if (!strncmp(event->name, "hidraw", 6)) { + std::string classPath = std::string("/sys/class/hidraw/") + + event->name + "/device"; + sz = readlink(classPath.c_str(), link, PATH_MAX); + link[sz] = 0; + + hidDeviceName = std::string(link).substr(9, 19); + + if (!FindTransportDevice(m_info.bustype, hidDeviceName, transportDeviceName, driverPath)) { + fprintf(stderr, "Failed to find the transport device / driver for %s\n", hidDeviceName.c_str()); + continue; + } + + if (transportDeviceName == m_transportDeviceName) { + hidrawFile = std::string("/dev/") + event->name; + return true; + } + } + + offset += sizeof(struct inotify_event) + event->len; + } + } + } +} + +bool HIDDevice::FindDevice(enum RMIDeviceType type) +{ + DIR * devDir; + struct dirent * devDirEntry; + char deviceFile[PATH_MAX]; + bool found = false; + int rc; + devDir = opendir("/dev"); if (!devDir) - return false; + return -1; while ((devDirEntry = readdir(devDir)) != NULL) { - if (!strncmp(devDirEntry->d_name, "hidraw", 6)) { - hidrawFile = std::string("/dev/") + devDirEntry->d_name; - ret = true; - break; + if (strstr(devDirEntry->d_name, "hidraw")) { + snprintf(deviceFile, PATH_MAX, "/dev/%s", devDirEntry->d_name); + fprintf(stdout, "Got device : /dev/%s\n", devDirEntry->d_name); + rc = Open(deviceFile); + if (rc != 0) { + continue; + } else if (type != RMI_DEVICE_TYPE_ANY && GetDeviceType() != type) { + Close(); + continue; + } else { + found = true; + break; + } } } closedir(devDir); - - return ret; + + return found; } |