From ee70a3ea715e17c777f3e66593233dfea87029db Mon Sep 17 00:00:00 2001 From: Yueyao Zhu Date: Wed, 28 Jun 2017 12:13:28 -0700 Subject: [PATCH] exynos9610: USB: HAL: enable auto suspend for USB headsets Adds a thread that handles add uevents of USB devices, and enables auto suspend on that USB device (i.e. set power/control to auto) if the device idProduct/idVendor is whitelisted. The android kernel will already autosuspend audio devices, however this enables autosuspend for the Google USB-C to 3.5mm adapter, which presents an HID-only interface when no 3.5mm headset is connected. Test: with the selinux and .rc changes for access permission - MIR without headset: power/control set to auto - MIR with headset: power/control set to auto - regular mouse: power/control set to on Bug: 38352281 Change-Id: I81572584ea02f6bdc814e70ab3439ab86c34a50a --- hidl/usb/Usb.cpp | 77 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 2 deletions(-) diff --git a/hidl/usb/Usb.cpp b/hidl/usb/Usb.cpp index 601c70b..3fc188e 100644 --- a/hidl/usb/Usb.cpp +++ b/hidl/usb/Usb.cpp @@ -41,10 +41,15 @@ namespace usb { namespace V1_1 { namespace implementation { +const char GOOGLE_USB_VENDOR_ID_STR[] = "18d1"; +const char GOOGLE_USBC_35_ADAPTER_UNPLUGGED_ID_STR[] = "5029"; + // Set by the signal handler to destroy the thread volatile bool destroyThread; -int32_t readFile(const std::string &filename, std::string *contents) { +static void checkUsbDeviceAutoSuspend(const std::string& devicePath); + +static int32_t readFile(const std::string &filename, std::string *contents) { FILE *fp; ssize_t read = 0; char *line = NULL; @@ -61,7 +66,28 @@ int32_t readFile(const std::string &filename, std::string *contents) { fclose(fp); return 0; } else { - ALOGE("fopen failed"); + ALOGE("fopen failed in readFile %s, errno=%d", filename.c_str(), errno); + } + + return -1; +} + +static int32_t writeFile(const std::string &filename, + const std::string &contents) { + FILE *fp; + int ret; + + fp = fopen(filename.c_str(), "w"); + if (fp != NULL) { + ret = fputs(contents.c_str(), fp); + fclose(fp); + if (ret == EOF) { + ALOGE("fputs failed in writeFile %s", filename.c_str()); + return -1; + } + return 0; + } else { + ALOGE("fopen failed in writeFile %s, errno=%d", filename.c_str(), errno); } return -1; @@ -501,6 +527,7 @@ static void uevent_event(uint32_t /*epevents*/, struct data *payload) { cp = msg; while (*cp) { + std::cmatch match; if (std::regex_match(cp, std::regex("(add)(.*)(-partner)"))) { ALOGI("partner added"); pthread_mutex_lock(&payload->usb->mPartnerLock); @@ -558,7 +585,15 @@ static void uevent_event(uint32_t /*epevents*/, struct data *payload) { pthread_mutex_unlock(&payload->usb->mRoleSwitchLock); } break; + } else if (std::regex_match(cp, match, + std::regex("add@(/devices/soc/a800000\\.ssusb/a800000\\.dwc3/xhci-hcd\\.0\\.auto/" + "usb\\d/\\d-\\d)/.*"))) { + if (match.size() == 2) { + std::csub_match submatch = match[1]; + checkUsbDeviceAutoSuspend("/sys" + submatch.str()); + } } + /* advance to after the next \0 */ while (*cp++) {} } @@ -689,6 +724,44 @@ Return Usb::setCallback(const sp &callback) { return Void(); } +/* + * whitelisting USB device idProduct and idVendor to allow auto suspend. + */ +static bool canProductAutoSuspend(const std::string &deviceIdVendor, + const std::string &deviceIdProduct) { + if (deviceIdVendor == GOOGLE_USB_VENDOR_ID_STR && + deviceIdProduct == GOOGLE_USBC_35_ADAPTER_UNPLUGGED_ID_STR) { + return true; + } + return false; +} + +static bool canUsbDeviceAutoSuspend(const std::string &devicePath) { + std::string deviceIdVendor; + std::string deviceIdProduct; + readFile(devicePath + "/idVendor", &deviceIdVendor); + readFile(devicePath + "/idProduct", &deviceIdProduct); + + // deviceIdVendor and deviceIdProduct will be empty strings if readFile fails + return canProductAutoSuspend(deviceIdVendor, deviceIdProduct); +} + +/* + * function to consume USB device plugin events (on receiving a + * USB device path string), and enable autosupend on the USB device if + * necessary. + */ +void checkUsbDeviceAutoSuspend(const std::string& devicePath) { + /* + * Currently we only actively enable devices that should be autosuspended, and leave others + * to the defualt. + */ + if (canUsbDeviceAutoSuspend(devicePath)) { + ALOGI("auto suspend usb device %s", devicePath.c_str()); + writeFile(devicePath + "/power/control", "auto"); + } +} + } // namespace implementation } // namespace V1_0 } // namespace usb -- 2.20.1