diff options
author | Andrew Duggan <aduggan@synaptics.com> | 2014-07-31 11:22:01 -0700 |
---|---|---|
committer | Andrew Duggan <aduggan@synaptics.com> | 2014-07-31 11:22:01 -0700 |
commit | 64f3a0c0192fb883513d949114cb2f8147a0af83 (patch) | |
tree | 7cc568205ed4e1e588dea46c7b812cf738b01e7b | |
parent | 18cc3b3343a6518f22de111134900b1e535aeea4 (diff) | |
download | platform_external_rmi4utils-64f3a0c0192fb883513d949114cb2f8147a0af83.tar.gz platform_external_rmi4utils-64f3a0c0192fb883513d949114cb2f8147a0af83.tar.bz2 platform_external_rmi4utils-64f3a0c0192fb883513d949114cb2f8147a0af83.zip |
Unbind and rebind the driver to the device after firmware update so that the driver reinitializes
based on the updated values and ensure the device is powered on.
-rw-r--r-- | rmi4update/main.cpp | 123 | ||||
-rw-r--r-- | rmi4update/updateutil.cpp | 14 | ||||
-rw-r--r-- | rmi4update/updateutil.h | 1 |
3 files changed, 135 insertions, 3 deletions
diff --git a/rmi4update/main.cpp b/rmi4update/main.cpp index b2b5fcb..a807f43 100644 --- a/rmi4update/main.cpp +++ b/rmi4update/main.cpp @@ -20,7 +20,11 @@ #include <errno.h> #include <getopt.h> #include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> #include <dirent.h> +#include <unistd.h> +#include <time.h> #include "hiddevice.h" #include "rmi4update.h" @@ -52,6 +56,116 @@ int UpdateDevice(FirmwareImage & image, bool force, const char * deviceFile) return rc; } +int WriteDeviceNameToFile(const char * file, const char * str) +{ + int fd; + ssize_t size; + + fd = open(file, O_WRONLY); + if (fd < 0) + return UPDATE_FAIL; + + for (;;) { + size = write(fd, str, 19); + if (size < 0) { + if (errno == EINTR) + continue; + + return UPDATE_FAIL; + } + break; + } + + close(fd); + + return UPDATE_SUCCESS; +} + +/* + * We need to rebind the driver to the device after firmware update for two reasons + * a) The parameters of the device may have changed in the new firmware so the + * driver should redo the initialization to read the new values. + * b) Kernel commit 0f5a24c6 will now power down the device once we close the + * file descriptor for the hidraw device file. Reloading the driver powers the + * device back on. + */ +void RebindDriver(const char * hidraw) +{ + int rc; + ssize_t size; + char bindFile[PATH_MAX]; + char unbindFile[PATH_MAX]; + char deviceLink[PATH_MAX]; + char driverName[PATH_MAX]; + char driverLink[PATH_MAX]; + char linkBuf[PATH_MAX]; + char hidDeviceString[20]; + int i; + + snprintf(unbindFile, PATH_MAX, "/sys/class/hidraw/%s/device/driver/unbind", hidraw); + snprintf(deviceLink, PATH_MAX, "/sys/class/hidraw/%s/device", hidraw); + + size = readlink(deviceLink, linkBuf, PATH_MAX); + if (size < 0) { + fprintf(stderr, "Failed to find the HID string for this device: %s\n", + hidraw); + return; + } + linkBuf[size] = 0; + + strncpy(hidDeviceString, StripPath(linkBuf, size), 20); + + snprintf(driverLink, PATH_MAX, "/sys/class/hidraw/%s/device/driver", hidraw); + + size = readlink(driverLink, linkBuf, PATH_MAX); + if (size < 0) { + fprintf(stderr, "Failed to find the HID string for this device: %s\n", + hidraw); + return; + } + linkBuf[size] = 0; + + strncpy(driverName, StripPath(linkBuf, size), PATH_MAX); + + snprintf(bindFile, PATH_MAX, "/sys/bus/hid/drivers/%s/bind", driverName); + + rc = WriteDeviceNameToFile(unbindFile, hidDeviceString); + if (rc != UPDATE_SUCCESS) { + fprintf(stderr, "Failed to unbind HID device %s: %s\n", + hidDeviceString, strerror(errno)); + return; + } + + for (i = 0;; ++i) { + struct timespec req; + struct timespec rem; + + rc = WriteDeviceNameToFile(bindFile, hidDeviceString); + if (rc == UPDATE_SUCCESS) + return; + + if (i <= 4) + break; + + /* device might not be ready yet to bind to */ + req.tv_sec = 0; + req.tv_nsec = 100 * 1000 * 1000; /* 100 ms */ + for (;;) { + rc = nanosleep(&req, &rem); + if (rc < 0) { + if (errno == EINTR) { + req = rem; + continue; + } + } + break; + } + } + + fprintf(stderr, "Failed to bind HID device %s: %s\n", + hidDeviceString, strerror(errno)); +} + int main(int argc, char **argv) { int rc; @@ -103,7 +217,8 @@ int main(int argc, char **argv) if (deviceName) { return UpdateDevice(image, force, deviceName); } else { - char buf[PATH_MAX]; + char rawDevice[PATH_MAX]; + char deviceFile[PATH_MAX]; bool found = false; devDir = opendir("/dev"); @@ -112,12 +227,14 @@ int main(int argc, char **argv) while ((devDirEntry = readdir(devDir)) != NULL) { if (strstr(devDirEntry->d_name, "hidraw")) { - snprintf(buf, PATH_MAX, "/dev/%s", devDirEntry->d_name); - rc = UpdateDevice(image, force, buf); + strncpy(rawDevice, devDirEntry->d_name, PATH_MAX); + snprintf(deviceFile, PATH_MAX, "/dev/%s", devDirEntry->d_name); + rc = UpdateDevice(image, force, deviceFile); if (rc != 0) { continue; } else { found = true; + RebindDriver(rawDevice); break; } } diff --git a/rmi4update/updateutil.cpp b/rmi4update/updateutil.cpp index 7950e4c..5b27e71 100644 --- a/rmi4update/updateutil.cpp +++ b/rmi4update/updateutil.cpp @@ -15,6 +15,8 @@ * limitations under the License. */ +#include <sys/types.h> + #include "updateutil.h" const char *update_error_str[] = { @@ -68,4 +70,16 @@ unsigned short extract_short(const unsigned char *data) { return (unsigned long)data [0] + (unsigned long)data [1] * 0x100; +} + +const char * StripPath(char * path, ssize_t size) +{ + int i; + char * str; + + for (i = size - 1, str = &path[size - 1]; i > 0; --i, --str) + if (path[i - 1] == '/') + break; + + return str; }
\ No newline at end of file diff --git a/rmi4update/updateutil.h b/rmi4update/updateutil.h index 92c934d..70fc8a0 100644 --- a/rmi4update/updateutil.h +++ b/rmi4update/updateutil.h @@ -56,5 +56,6 @@ const char * update_err_to_string(int err); unsigned long extract_long(const unsigned char *data); unsigned short extract_short(const unsigned char *data); +const char * StripPath(char * path, ssize_t size); #endif // _UPDATEUTIL_H_
\ No newline at end of file |