aboutsummaryrefslogtreecommitdiff
path: root/rmidevice
diff options
context:
space:
mode:
authorAndrew Duggan <aduggan@synaptics.com>2015-05-06 17:45:48 -0700
committerAndrew Duggan <aduggan@synaptics.com>2015-05-06 17:50:14 -0700
commitbef9c2dd3bfbe71b75f59c0dba08402414e008a2 (patch)
tree1211eb6e9d01ca93abd7af06a4ae72af2b7f148e /rmidevice
parentbc1e37b101aa71b785e0c78abb1c2ee546506b60 (diff)
downloadrmi4utils-bef9c2dd3bfbe71b75f59c0dba08402414e008a2.tar.gz
Allow rebinding of the transport device to force a reload of the HID descriptors
In some cases during firmware update the size of the input reports can change this commit allows for the unbinding and rebinding of the transport HID device to force a reload of the HID descriptors so that the new size if read by the HID transport drivers.
Diffstat (limited to 'rmidevice')
-rw-r--r--rmidevice/hiddevice.cpp191
-rw-r--r--rmidevice/hiddevice.h8
-rw-r--r--rmidevice/rmidevice.h1
3 files changed, 200 insertions, 0 deletions
diff --git a/rmidevice/hiddevice.cpp b/rmidevice/hiddevice.cpp
index f756660..54d5465 100644
--- a/rmidevice/hiddevice.cpp
+++ b/rmidevice/hiddevice.cpp
@@ -19,6 +19,7 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
+#include <dirent.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
@@ -485,3 +486,193 @@ void HIDDevice::PrintReport(const unsigned char *report)
}
fprintf(stdout, "\n\n");
}
+
+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, 19);
+ if (size < 0) {
+ if (errno == EINTR)
+ continue;
+
+ return false;
+ }
+ break;
+ }
+ close(fd);
+
+ 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;
+
+ 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 (!FindTransportDriver(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 this is now a new hid device so 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;
+ }
+
+ Open(hidrawFile.c_str());
+}
+
+bool HIDDevice::FindTransportDriver(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;
+} \ No newline at end of file
diff --git a/rmidevice/hiddevice.h b/rmidevice/hiddevice.h
index ba2d942..ac7439d 100644
--- a/rmidevice/hiddevice.h
+++ b/rmidevice/hiddevice.h
@@ -19,6 +19,7 @@
#define _HIDDEVICE_H_
#include <linux/hidraw.h>
+#include <string>
#include "rmidevice.h"
class HIDDevice : public RMIDevice
@@ -38,6 +39,7 @@ public:
virtual int GetAttentionReport(struct timeval * timeout, unsigned int source_mask,
unsigned char *buf, unsigned int *len);
virtual void Close();
+ virtual void RebindDriver();
~HIDDevice() { Close(); }
private:
@@ -68,6 +70,12 @@ private:
int GetReport(int *reportId, struct timeval * timeout = NULL);
void PrintReport(const unsigned char *report);
void ParseReportSizes();
+
+ // static HID utility functions
+ static bool LookupHidDeviceName(int bus, int vendorId, int productId, std::string &deviceName);
+ static bool FindTransportDriver(int bus, std::string & hidDeviceName,
+ std::string & transportDeviceName, std::string & driverPath);
+ static bool FindHidRawFile(std::string & hidDeviceName, std::string & hidrawFile);
};
#endif /* _HIDDEVICE_H_ */
diff --git a/rmidevice/rmidevice.h b/rmidevice/rmidevice.h
index c6d83e5..d213168 100644
--- a/rmidevice/rmidevice.h
+++ b/rmidevice/rmidevice.h
@@ -45,6 +45,7 @@ public:
{ return -1; /* Unsupported */ }
virtual void Close() = 0;
virtual void Cancel() { m_bCancel = true; }
+ virtual void RebindDriver() = 0;
unsigned long GetFirmwareID() { return m_buildID; }
int GetFirmwareVersionMajor() { return m_firmwareVersionMajor; }