Skip to content

Commit

Permalink
USB: HAL: enable auto suspend for USB headsets
Browse files Browse the repository at this point in the history
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
  • Loading branch information
blackhogz authored and Andrew Chant committed Jul 26, 2017
1 parent f5a9953 commit b9cd118
Showing 1 changed file with 75 additions and 2 deletions.
77 changes: 75 additions & 2 deletions usb/Usb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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++) {}
}
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit b9cd118

Please sign in to comment.