diff --git a/usb/Usb.cpp b/usb/Usb.cpp index 51e9713..30f4b60 100755 --- a/usb/Usb.cpp +++ b/usb/Usb.cpp @@ -41,14 +41,9 @@ 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; -static void checkUsbDeviceAutoSuspend(const std::string& devicePath); - static int32_t readFile(const std::string &filename, std::string *contents) { FILE *fp; ssize_t read = 0; @@ -73,7 +68,7 @@ static int32_t readFile(const std::string &filename, std::string *contents) { } static int32_t writeFile(const std::string &filename, - const std::string &contents) { + const std::string &contents) { // Might remove later FILE *fp; int ret; @@ -95,7 +90,7 @@ static int32_t writeFile(const std::string &filename, std::string appendRoleNodeHelper(const std::string &portName, PortRoleType type) { - std::string node("/sys/class/typec/" + portName); + std::string node("/sys/class/dual_role_usb/" + portName); // We have /sys/class/dual_role_usb, not /sys/class/typec switch (type) { case PortRoleType::DATA_ROLE: @@ -103,30 +98,27 @@ std::string appendRoleNodeHelper(const std::string &portName, case PortRoleType::POWER_ROLE: return node + "/power_role"; case PortRoleType::MODE: - return node + "/port_type"; + return node + "/mode"; // We have a separate file for mode default: - return ""; + return node + "/mode"; // Disassembly shows no separate case for non-matches and MODE case could be removed, leaving just default, but let's keep it like that for now in case we need to change something. } } std::string convertRoletoString(PortRole role) { if (role.type == PortRoleType::POWER_ROLE) { - if (role.role == static_cast(PortPowerRole::SOURCE)) - return "source"; - else if (role.role == static_cast(PortPowerRole::SINK)) - return "sink"; + if (role.role == static_cast(PortPowerRole::SOURCE)) return "source"; + else if (role.role == static_cast(PortPowerRole::SINK)) return "sink"; } else if (role.type == PortRoleType::DATA_ROLE) { if (role.role == static_cast(PortDataRole::HOST)) return "host"; - if (role.role == static_cast(PortDataRole::DEVICE)) - return "device"; + if (role.role == static_cast(PortDataRole::DEVICE)) return "device"; } else if (role.type == PortRoleType::MODE) { - if (role.role == static_cast(PortMode_1_1::UFP)) return "sink"; - if (role.role == static_cast(PortMode_1_1::DFP)) return "source"; + if (role.role == static_cast(PortMode_1_1::UFP)) return "ufp"; // We probably call these 'ufp' and 'dfp', as seen in switchToDrp for example + if (role.role == static_cast(PortMode_1_1::DFP)) return "dfp"; } return "none"; } -void extractRole(std::string *roleName) { +void extractRole(std::string *roleName) { // Might remove later. std::size_t first, last; first = roleName->find("["); @@ -145,7 +137,7 @@ void switchToDrp(const std::string &portName) { if (filename != "") { fp = fopen(filename.c_str(), "w"); if (fp != NULL) { - int ret = fputs("dual", fp); + int ret = fputs("dfp", fp); // Change dual -> dfp. Based on disassembly of stock HAL. fclose(fp); if (ret == EOF) ALOGE("Fatal: Error while switching back to drp"); @@ -157,61 +149,6 @@ void switchToDrp(const std::string &portName) { } } -bool switchMode(const hidl_string &portName, - const PortRole &newRole, struct Usb *usb) { - std::string filename = - appendRoleNodeHelper(std::string(portName.c_str()), newRole.type); - std::string written; - FILE *fp; - bool roleSwitch = false; - - if (filename == "") { - ALOGE("Fatal: invalid node type"); - return false; - } - - fp = fopen(filename.c_str(), "w"); - if (fp != NULL) { - // Hold the lock here to prevent loosing connected signals - // as once the file is written the partner added signal - // can arrive anytime. - pthread_mutex_lock(&usb->mPartnerLock); - usb->mPartnerUp = false; - int ret = fputs(convertRoletoString(newRole).c_str(), fp); - fclose(fp); - - if (ret != EOF) { - struct timespec to; - struct timespec now; - -wait_again: - clock_gettime(CLOCK_MONOTONIC, &now); - to.tv_sec = now.tv_sec + PORT_TYPE_TIMEOUT; - to.tv_nsec = now.tv_nsec; - - int err = pthread_cond_timedwait(&usb->mPartnerCV, &usb->mPartnerLock, &to); - // There are no uevent signals which implies role swap timed out. - if (err == ETIMEDOUT) { - ALOGI("uevents wait timedout"); - // Partner check. - } else if (!usb->mPartnerUp) { - goto wait_again; - // Role switch succeeded since usb->mPartnerUp is true. - } else { - roleSwitch = true; - } - } else { - ALOGI("Role switch failed while wrting to file"); - } - pthread_mutex_unlock(&usb->mPartnerLock); - } - - if (!roleSwitch) - switchToDrp(std::string(portName.c_str())); - - return roleSwitch; -} - Usb::Usb() : mLock(PTHREAD_MUTEX_INITIALIZER), mRoleSwitchLock(PTHREAD_MUTEX_INITIALIZER), @@ -252,21 +189,31 @@ Return Usb::switchRole(const hidl_string &portName, pthread_mutex_lock(&mRoleSwitchLock); - ALOGI("filename write: %s role:%s", filename.c_str(), - convertRoletoString(newRole).c_str()); + /* + It looks like we have some type of loop in the stock HAL. + The loop starts at 0x4860 and ends at 0x4D4C in android.hardware.usb@1.1-service-mediatek from V12.0.1.0.QJOEUXM + We break out of the loop only if the value at W24 register is equal to zero. Before loop start, we can see that W24 is set to 0x14 = 20. + We can also see that before deciding to break out or restart, W24 is decremented by 1 and there is an usleep call. + The amount of useconds to sleep is stored in register W19. This register is set to 0xC350 = 50000 before the loop. + + We can conclude that the program attempts to write the role 20 times and waits 50000 useconds before trying again. + */ + for (int i = 0; i < 20; ++i) { + ALOGI("filename write: %s role:%s", filename.c_str(), + convertRoletoString(newRole).c_str()); + + // We don't need to check role type and use a separate way of setting mode, as we simply have a "mode" file for doing that - if (newRole.type == PortRoleType::MODE) { - roleSwitch = switchMode(portName, newRole, this); - } else { fp = fopen(filename.c_str(), "w"); if (fp != NULL) { int ret = fputs(convertRoletoString(newRole).c_str(), fp); fclose(fp); if ((ret != EOF) && !readFile(filename, &written)) { - extractRole(&written); + // extractRole(&written); ALOGI("written: %s", written.c_str()); if (written == convertRoletoString(newRole)) { roleSwitch = true; + break; // Role switched, stop retrying. } else { ALOGE("Role switch failed"); } @@ -276,6 +223,8 @@ Return Usb::switchRole(const hidl_string &portName, } else { ALOGE("fopen failed"); } + + usleep(50000); } pthread_mutex_lock(&mLock); @@ -294,35 +243,23 @@ Return Usb::switchRole(const hidl_string &portName, return Void(); } -Status getAccessoryConnected(const std::string &portName, std::string *accessory) { - std::string filename = - "/sys/class/typec/" + portName + "-partner/accessory_mode"; - - if (readFile(filename, accessory)) { - ALOGE("getAccessoryConnected: Failed to open filesystem node: %s", - filename.c_str()); - return Status::ERROR; - } - - return Status::SUCCESS; -} Status getCurrentRoleHelper(const std::string &portName, bool connected, PortRoleType type, uint32_t *currentRole) { std::string filename; std::string roleName; - std::string accessory; + // Accessory stuff removed - stock HAL doesn't have that // Mode if (type == PortRoleType::POWER_ROLE) { - filename = "/sys/class/typec/" + portName + "/power_role"; + filename = "/sys/class/dual_role_usb/" + portName + "/power_role"; *currentRole = static_cast(PortPowerRole::NONE); } else if (type == PortRoleType::DATA_ROLE) { - filename = "/sys/class/typec/" + portName + "/data_role"; + filename = "/sys/class/dual_role_usb/" + portName + "/data_role"; *currentRole = static_cast(PortDataRole::NONE); } else if (type == PortRoleType::MODE) { - filename = "/sys/class/typec/" + portName + "/data_role"; + filename = "/sys/class/dual_role_usb/" + portName + "/mode"; // We have a separate file for setting mode. *currentRole = static_cast(PortMode_1_1::NONE); } else { return Status::ERROR; @@ -330,40 +267,26 @@ Status getCurrentRoleHelper(const std::string &portName, bool connected, if (!connected) return Status::SUCCESS; - if (type == PortRoleType::MODE) { - if (getAccessoryConnected(portName, &accessory) != Status::SUCCESS) { - return Status::ERROR; - } - if (accessory == "analog_audio") { - *currentRole = static_cast(PortMode_1_1::AUDIO_ACCESSORY); - return Status::SUCCESS; - } else if (accessory == "debug") { - *currentRole = static_cast(PortMode_1_1::DEBUG_ACCESSORY); - return Status::SUCCESS; - } - } - if (readFile(filename, &roleName)) { ALOGE("getCurrentRole: Failed to open filesystem node: %s", filename.c_str()); return Status::ERROR; } - extractRole(&roleName); + // extractRole(&roleName); // Probably don't need to extract. The role names on our device are just single words + // We have simple role names and don't need to make a distinction based on role type if (roleName == "source") { - *currentRole = static_cast(PortPowerRole::SOURCE); + *currentRole = static_cast(PortPowerRole::SOURCE); } else if (roleName == "sink") { - *currentRole = static_cast(PortPowerRole::SINK); + *currentRole = static_cast(PortPowerRole::SINK); } else if (roleName == "host") { - if (type == PortRoleType::DATA_ROLE) *currentRole = static_cast(PortDataRole::HOST); - else + } else if (roleName == "dfp") { *currentRole = static_cast(PortMode_1_1::DFP); } else if (roleName == "device") { - if (type == PortRoleType::DATA_ROLE) *currentRole = static_cast(PortDataRole::DEVICE); - else + } else if (roleName == "ufp") { *currentRole = static_cast(PortMode_1_1::UFP); } else if (roleName != "none") { /* case for none has already been addressed. @@ -378,34 +301,30 @@ Status getCurrentRoleHelper(const std::string &portName, bool connected, Status getTypeCPortNamesHelper(std::unordered_map *names) { DIR *dp; - dp = opendir("/sys/class/typec"); + dp = opendir("/sys/class/dual_role_usb"); if (dp != NULL) { struct dirent *ep; while ((ep = readdir(dp))) { if (ep->d_type == DT_LNK) { - if (std::string::npos == std::string(ep->d_name).find("-partner")) { + // We don't have -partner suffixes std::unordered_map::const_iterator portName = names->find(ep->d_name); if (portName == names->end()) { - names->insert({ep->d_name, false}); + names->insert({ep->d_name, true}); // True by default - otherwise getPortStatusHelper will never say if role can be switched } - } else { - (*names)[std::strtok(ep->d_name, "-")] = true; - } } } closedir(dp); return Status::SUCCESS; } - ALOGE("Failed to open /sys/class/typec"); + ALOGE("Failed to open /sys/class/dual_role_usb"); return Status::ERROR; } bool canSwitchRoleHelper(const std::string &portName, PortRoleType /*type*/) { - std::string filename = - "/sys/class/typec/" + portName + "-partner/supports_usb_power_delivery"; + std::string filename = "/sys/class/tcpc/type_c_port0/pe_ready"; std::string supportsPD; if (!readFile(filename, &supportsPD)) { @@ -466,14 +385,16 @@ Status getPortStatusHelper(hidl_vec *currentPortStatus_1_1, goto done; } - (*currentPortStatus_1_1)[i].status.canChangeMode = true; + // Treat mode change like any type of role change - we have the "mode" file in our port directory. + (*currentPortStatus_1_1)[i].status.canChangeMode = + port.second ? canSwitchRoleHelper(port.first, PortRoleType::MODE) + : false; (*currentPortStatus_1_1)[i].status.canChangeDataRole = port.second ? canSwitchRoleHelper(port.first, PortRoleType::DATA_ROLE) : false; (*currentPortStatus_1_1)[i].status.canChangePowerRole = - port.second - ? canSwitchRoleHelper(port.first, PortRoleType::POWER_ROLE) - : false; + port.second ? canSwitchRoleHelper(port.first, PortRoleType::POWER_ROLE) + : false; ALOGI("connected:%d canChangeMode:%d canChagedata:%d canChangePower:%d", port.second, (*currentPortStatus_1_1)[i].status.canChangeMode, @@ -548,14 +469,8 @@ 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); - payload->usb->mPartnerUp = true; - pthread_cond_signal(&payload->usb->mPartnerCV); - pthread_mutex_unlock(&payload->usb->mPartnerLock); - } else if (!strncmp(cp, "DEVTYPE=typec_", strlen("DEVTYPE=typec_"))) { + // Removed regex matching for -partner, as we don't seem to have that in the stock HAL and in /sys/class/dual_role_usb. + if (!strncmp(cp, "SUBSYSTEM=dual_role_usb", strlen("SUBSYSTEM=dual_role_usb"))) { // Stock HAL looks for dual_role_usb, and it checks the SUBSYSTEM instead of DEVNAME. 0x62D4 in android.hardware.usb@1.1-service-mediatek from V12.0.1.0.QJOEUXM hidl_vec currentPortStatus_1_1; ALOGI("uevent received %s", cp); pthread_mutex_lock(&payload->usb->mLock); @@ -593,9 +508,8 @@ static void uevent_event(uint32_t /*epevents*/, struct data *payload) { //Role switch is not in progress and port is in disconnected state if (!pthread_mutex_trylock(&payload->usb->mRoleSwitchLock)) { for (unsigned long i = 0; i < currentPortStatus_1_1.size(); i++) { - DIR *dp = opendir(std::string("/sys/class/typec/" - + std::string(currentPortStatus_1_1[i].status.portName.c_str()) - + "-partner").c_str()); + DIR *dp = opendir(std::string("/sys/class/dual_role_usb/" + + std::string(currentPortStatus_1_1[i].status.portName.c_str())).c_str()); // Stock HAL did not append -partner. Based on disassembly - 0x65E0 in android.hardware.usb@1.1-service-mediatek from V12.0.1.0.QJOEUXM if (dp == NULL) { //PortRole role = {.role = static_cast(PortMode::UFP)}; switchToDrp(currentPortStatus_1_1[i].status.portName); @@ -606,14 +520,7 @@ 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()); - } - } + } // Removed autosuspend regex match stuff - we don't even have the path that regex was looking for on a live device, and there are no mentions of auto suspend stuff in strings from the stock HAL whatsoever. The functions related to auto suspend afterwards have been nuked too. /* advance to after the next \0 */ while (*cp++) {} @@ -745,44 +652,6 @@ 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