exynos9610: USB: HAL: enable auto suspend for USB headsets
authorYueyao Zhu <yueyao@google.com>
Wed, 28 Jun 2017 19:13:28 +0000 (12:13 -0700)
committerJan Altensen <info@stricted.net>
Sun, 1 Nov 2020 02:06:28 +0000 (03:06 +0100)
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

index 601c70b509a83d006c3bb0cd4076c76adcc2afd0..3fc188e69a412aab0afebfee8508564c1c143d7e 100644 (file)
@@ -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<void> Usb::setCallback(const sp<V1_0::IUsbCallback> &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