From 80fa0c820f935972a61e6776e536447486ec30da Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Tue, 13 Jun 2017 10:52:16 -0700 Subject: [PATCH] exynos9610: Usb: HAL: Update wahoo HAL This CL rebases wahoo HAL based on the kernel change to update sysfs interface. https://partner-android-review.googlesource.com/#/c/836088/ Bug: 62272992 Test: Verify USB dialog on wahoo Change-Id: I8bc8f494176c7648abeb9783fbd18dc837793bda --- hidl/usb/Usb.cpp | 290 ++++++++++++++---- hidl/usb/Usb.h | 18 +- .../android.hardware.usb@1.1-service.wahoo.rc | 10 +- 3 files changed, 247 insertions(+), 71 deletions(-) diff --git a/hidl/usb/Usb.cpp b/hidl/usb/Usb.cpp index 0d472bd..601c70b 100644 --- a/hidl/usb/Usb.cpp +++ b/hidl/usb/Usb.cpp @@ -18,10 +18,13 @@ #include #include +#include #include #include +#include #include #include +#include #include #include @@ -41,7 +44,7 @@ namespace implementation { // Set by the signal handler to destroy the thread volatile bool destroyThread; -int32_t readFile(std::string filename, std::string *contents) { +int32_t readFile(const std::string &filename, std::string *contents) { FILE *fp; ssize_t read = 0; char *line = NULL; @@ -64,17 +67,19 @@ int32_t readFile(std::string filename, std::string *contents) { return -1; } -std::string appendRoleNodeHelper(const std::string portName, +std::string appendRoleNodeHelper(const std::string &portName, PortRoleType type) { std::string node("/sys/class/typec/" + portName); switch (type) { case PortRoleType::DATA_ROLE: - return node + "/current_data_role"; + return node + "/data_role"; case PortRoleType::POWER_ROLE: - return node + "/current_power_role"; + return node + "/power_role"; + case PortRoleType::MODE: + return node + "/port_type"; default: - return node + "/mode"; + return ""; } } @@ -89,8 +94,8 @@ std::string convertRoletoString(PortRole role) { 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 "ufp"; - if (role.role == static_cast(PortMode_1_1::DFP)) return "dfp"; + if (role.role == static_cast(PortMode_1_1::UFP)) return "sink"; + if (role.role == static_cast(PortMode_1_1::DFP)) return "source"; } return "none"; } @@ -106,60 +111,141 @@ void extractRole(std::string *roleName) { } } +void switchToDrp(const std::string &portName) { + std::string filename = + appendRoleNodeHelper(std::string(portName.c_str()), PortRoleType::MODE); + FILE *fp; + + if (filename != "") { + fp = fopen(filename.c_str(), "w"); + if (fp != NULL) { + int ret = fputs("dual", fp); + fclose(fp); + if (ret == EOF) + ALOGE("Fatal: Error while switching back to drp"); + } else { + ALOGE("Fatal: Cannot open file to switch back to drp"); + } + } else { + ALOGE("Fatal: invalid node type"); + } +} + +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 timeval tp; + +wait_again: + gettimeofday(&tp, NULL); + to.tv_sec = tp.tv_sec + PORT_TYPE_TIMEOUT; + to.tv_nsec = tp.tv_usec * 1000;; + + 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"); + // Sanity 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; +} + + + Return Usb::switchRole(const hidl_string &portName, const PortRole &newRole) { 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 Void(); + } + + pthread_mutex_lock(&mRoleSwitchLock); ALOGI("filename write: %s role:%s", filename.c_str(), convertRoletoString(newRole).c_str()); - 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); - ALOGI("written: %s", written.c_str()); - if (written == convertRoletoString(newRole)) { - pthread_mutex_lock(&mLock); - if (mCallback != NULL) { - Return ret = mCallback->notifyRoleSwitchStatus( - portName, newRole, Status::SUCCESS); - if (!ret.isOk()) - ALOGE("RoleSwitch transaction error %s", ret.description().c_str()); + 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); + ALOGI("written: %s", written.c_str()); + if (written == convertRoletoString(newRole)) { + roleSwitch = true; } else { - ALOGE("Not notifying the userspace. Callback is not set"); + ALOGE("Role switch failed"); } - pthread_mutex_unlock(&mLock); - return Void(); } else { - ALOGE("Role switch failed"); + ALOGE("failed to update the new role"); } } else { - ALOGE("failed to update the new role"); + ALOGE("fopen failed"); } - } else { - ALOGE("fopen failed"); } pthread_mutex_lock(&mLock); - if (mCallback != NULL) { + if (mCallback_1_0 != NULL) { Return ret = - mCallback->notifyRoleSwitchStatus(portName, newRole, Status::ERROR); + mCallback_1_0->notifyRoleSwitchStatus(portName, newRole, + roleSwitch ? Status::SUCCESS : Status::ERROR); if (!ret.isOk()) ALOGE("RoleSwitchStatus error %s", ret.description().c_str()); } else { ALOGE("Not notifying the userspace. Callback is not set"); } pthread_mutex_unlock(&mLock); + pthread_mutex_unlock(&mRoleSwitchLock); return Void(); } -Status getAccessoryConnected(std::string portName, std::string *accessory) { +Status getAccessoryConnected(const std::string &portName, std::string *accessory) { std::string filename = "/sys/class/typec/" + portName + "-partner/accessory_mode"; @@ -172,7 +258,7 @@ Status getAccessoryConnected(std::string portName, std::string *accessory) { return Status::SUCCESS; } -Status getCurrentRoleHelper(std::string portName, bool connected, +Status getCurrentRoleHelper(const std::string &portName, bool connected, PortRoleType type, uint32_t *currentRole) { std::string filename; std::string roleName; @@ -181,13 +267,13 @@ Status getCurrentRoleHelper(std::string portName, bool connected, // Mode if (type == PortRoleType::POWER_ROLE) { - filename = "/sys/class/typec/" + portName + "/current_power_role"; + filename = "/sys/class/typec/" + portName + "/power_role"; *currentRole = static_cast(PortPowerRole::NONE); } else if (type == PortRoleType::DATA_ROLE) { - filename = "/sys/class/typec/" + portName + "/current_data_role"; + filename = "/sys/class/typec/" + portName + "/data_role"; *currentRole = static_cast(PortDataRole::NONE); } else if (type == PortRoleType::MODE) { - filename = "/sys/class/typec/" + portName + "/current_data_role"; + filename = "/sys/class/typec/" + portName + "/data_role"; *currentRole = static_cast(PortMode_1_1::NONE); } else { return Status::ERROR; @@ -199,10 +285,10 @@ Status getCurrentRoleHelper(std::string portName, bool connected, if (getAccessoryConnected(portName, &accessory) != Status::SUCCESS) { return Status::ERROR; } - if (accessory == "Audio Adapter Accessory Mode") { + if (accessory == "analog_audio") { *currentRole = static_cast(PortMode_1_1::AUDIO_ACCESSORY); return Status::SUCCESS; - } else if (accessory == "Debug Accessory Mode") { + } else if (accessory == "debug") { *currentRole = static_cast(PortMode_1_1::DEBUG_ACCESSORY); return Status::SUCCESS; } @@ -270,13 +356,13 @@ Status getTypeCPortNamesHelper(std::unordered_map *names) { return Status::ERROR; } -bool canSwitchRoleHelper(const std::string portName, PortRoleType /*type*/) { +bool canSwitchRoleHelper(const std::string &portName, PortRoleType /*type*/) { std::string filename = "/sys/class/typec/" + portName + "-partner/supports_usb_power_delivery"; std::string supportsPD; if (!readFile(filename, &supportsPD)) { - if (supportsPD == "1") { + if (supportsPD == "yes") { return true; } } @@ -284,7 +370,13 @@ bool canSwitchRoleHelper(const std::string portName, PortRoleType /*type*/) { return false; } -Status getPortStatus_1_1Helper(hidl_vec *currentPortStatus_1_1) { +/* + * Reuse the same method for both V1_0 and V1_1 callback objects. + * The caller of this method would reconstruct the V1_0::PortStatus + * object if required. + */ +Status getPortStatusHelper(hidl_vec *currentPortStatus_1_1, + bool V1_0) { std::unordered_map names; Status result = getTypeCPortNamesHelper(&names); int i = -1; @@ -320,12 +412,14 @@ Status getPortStatus_1_1Helper(hidl_vec *currentPortStatus_1_1) ¤tRole) == Status::SUCCESS) { (*currentPortStatus_1_1)[i].currentMode = static_cast(currentRole); + (*currentPortStatus_1_1)[i].status.currentMode = + static_cast(currentRole); } else { ALOGE("Error while retreiving current data role"); goto done; } - (*currentPortStatus_1_1)[i].status.canChangeMode = false; + (*currentPortStatus_1_1)[i].status.canChangeMode = true; (*currentPortStatus_1_1)[i].status.canChangeDataRole = port.second ? canSwitchRoleHelper(port.first, PortRoleType::DATA_ROLE) : false; @@ -334,15 +428,18 @@ Status getPortStatus_1_1Helper(hidl_vec *currentPortStatus_1_1) ? canSwitchRoleHelper(port.first, PortRoleType::POWER_ROLE) : false; - ALOGI("connected:%d canChangeMode: %d canChagedata: %d canChangePower:%d", + ALOGI("connected:%d canChangeMode:%d canChagedata:%d canChangePower:%d", port.second, (*currentPortStatus_1_1)[i].status.canChangeMode, (*currentPortStatus_1_1)[i].status.canChangeDataRole, (*currentPortStatus_1_1)[i].status.canChangePowerRole); - (*currentPortStatus_1_1)[i].supportedModes = PortMode_1_1::UFP | PortMode_1_1::DFP; - - (*currentPortStatus_1_1)[i].status.supportedModes = V1_0::PortMode::NONE; - (*currentPortStatus_1_1)[i].status.currentMode = V1_0::PortMode::NONE; + if (V1_0) { + (*currentPortStatus_1_1)[i].status.supportedModes = V1_0::PortMode::DFP; + } else { + (*currentPortStatus_1_1)[i].supportedModes = PortMode_1_1::UFP | PortMode_1_1::DFP; + (*currentPortStatus_1_1)[i].status.supportedModes = V1_0::PortMode::NONE; + (*currentPortStatus_1_1)[i].status.currentMode = V1_0::PortMode::NONE; + } } return Status::SUCCESS; } @@ -352,14 +449,28 @@ done: Return Usb::queryPortStatus() { hidl_vec currentPortStatus_1_1; + hidl_vec currentPortStatus; Status status; - - status = getPortStatus_1_1Helper(¤tPortStatus_1_1); + sp callback_V1_1 = IUsbCallback::castFrom(mCallback_1_0); pthread_mutex_lock(&mLock); - if (mCallback != NULL) { - Return ret = - mCallback->notifyPortStatusChange_1_1(currentPortStatus_1_1, status); + if (mCallback_1_0 != NULL) { + if (callback_V1_1 != NULL) { + status = getPortStatusHelper(¤tPortStatus_1_1, false); + } else { + status = getPortStatusHelper(¤tPortStatus_1_1, true); + currentPortStatus.resize(currentPortStatus_1_1.size()); + for (unsigned long i = 0; i < currentPortStatus_1_1.size(); i++) + currentPortStatus[i] = currentPortStatus_1_1[i].status; + } + + Return ret; + + if (callback_V1_1 != NULL) + ret = callback_V1_1->notifyPortStatusChange_1_1(currentPortStatus_1_1, status); + else + ret = mCallback_1_0->notifyPortStatusChange(currentPortStatus, status); + if (!ret.isOk()) ALOGE("queryPortStatus_1_1 error %s", ret.description().c_str()); } else { @@ -369,6 +480,7 @@ Return Usb::queryPortStatus() { return Void(); } + struct data { int uevent_fd; android::hardware::usb::V1_1::implementation::Usb *usb; @@ -389,19 +501,62 @@ static void uevent_event(uint32_t /*epevents*/, struct data *payload) { cp = msg; while (*cp) { - if (!strncmp(cp, "DEVTYPE=typec_", strlen("DEVTYPE=typec_"))) { + 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_"))) { + hidl_vec currentPortStatus_1_1; ALOGI("uevent received %s", cp); pthread_mutex_lock(&payload->usb->mLock); - if (payload->usb->mCallback != NULL) { - hidl_vec currentPortStatus_1_1; - Status status = getPortStatus_1_1Helper(¤tPortStatus_1_1); - Return ret = payload->usb->mCallback->notifyPortStatusChange_1_1( - currentPortStatus_1_1, status); + if (payload->usb->mCallback_1_0 != NULL) { + sp callback_V1_1 = IUsbCallback::castFrom(payload->usb->mCallback_1_0); + Return ret; + + // V1_1 callback + if (callback_V1_1 != NULL) { + Status status = getPortStatusHelper(¤tPortStatus_1_1, false); + ret = callback_V1_1->notifyPortStatusChange_1_1( + currentPortStatus_1_1, status); + } else { // V1_0 callback + Status status = getPortStatusHelper(¤tPortStatus_1_1, true); + + /* + * Copying the result from getPortStatusHelper + * into V1_0::PortStatus to pass back through + * the V1_0 callback object. + */ + hidl_vec currentPortStatus; + currentPortStatus.resize(currentPortStatus_1_1.size()); + for (unsigned long i = 0; i < currentPortStatus_1_1.size(); i++) + currentPortStatus[i] = currentPortStatus_1_1[i].status; + + ret = payload->usb->mCallback_1_0->notifyPortStatusChange( + currentPortStatus, status); + } if (!ret.isOk()) ALOGE("error %s", ret.description().c_str()); } else { ALOGI("Notifying userspace skipped. Callback is NULL"); } pthread_mutex_unlock(&payload->usb->mLock); + + //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()); + if (dp == NULL) { + //PortRole role = {.role = static_cast(PortMode::UFP)}; + switchToDrp(currentPortStatus_1_1[i].status.portName); + } else { + closedir(dp); + } + } + pthread_mutex_unlock(&payload->usb->mRoleSwitchLock); + } break; } /* advance to after the next \0 */ @@ -478,12 +633,13 @@ void sighandler(int sig) { signal(SIGUSR1, sighandler); } -Return Usb::setCallback(const sp<::android::hardware::usb::V1_0::IUsbCallback> &callback) { +Return Usb::setCallback(const sp &callback) { sp callback_V1_1 = IUsbCallback::castFrom(callback); if (callback != NULL) - CHECK(callback_V1_1 != NULL); + if (callback_V1_1 == NULL) + ALOGI("Registering 1.0 callback"); pthread_mutex_lock(&mLock); /* @@ -493,18 +649,22 @@ Return Usb::setCallback(const sp<::android::hardware::usb::V1_0::IUsbCallb * worker thread running, so updating the callback object would * be suffice. */ - if ((mCallback == NULL && callback == NULL) || - (mCallback != NULL && callback != NULL)) { - mCallback = callback_V1_1; + if ((mCallback_1_0 == NULL && callback == NULL) || + (mCallback_1_0 != NULL && callback != NULL)) { + /* + * Always store as V1_0 callback object. Type cast to V1_1 + * when the callback is actually invoked. + */ + mCallback_1_0 = callback; pthread_mutex_unlock(&mLock); return Void(); } - mCallback = callback_V1_1; + mCallback_1_0 = callback; ALOGI("registering callback"); // Kill the worker thread if the new callback is NULL. - if (mCallback == NULL) { + if (mCallback_1_0 == NULL) { pthread_mutex_unlock(&mLock); if (!pthread_kill(mPoll, SIGUSR1)) { pthread_join(mPoll, NULL); @@ -522,7 +682,7 @@ Return Usb::setCallback(const sp<::android::hardware::usb::V1_0::IUsbCallb */ if (pthread_create(&mPoll, NULL, work, this)) { ALOGE("pthread creation failed %d", errno); - mCallback = NULL; + mCallback_1_0 = NULL; } pthread_mutex_unlock(&mLock); diff --git a/hidl/usb/Usb.h b/hidl/usb/Usb.h index 11c18ae..bfa63ae 100644 --- a/hidl/usb/Usb.h +++ b/hidl/usb/Usb.h @@ -8,6 +8,11 @@ #include #define UEVENT_MSG_LEN 2048 +// The type-c stack waits for 4.5 - 5.5 secs before declaring a port non-pd. +// The -partner directory would not be created until this is done. +// Having a margin of ~3 secs for the directory and other related bookeeping +// structures created and uvent fired. +#define PORT_TYPE_TIMEOUT 8 namespace android { namespace hardware { @@ -36,12 +41,21 @@ using ::android::sp; struct Usb : public IUsb { Return switchRole(const hidl_string& portName, const PortRole& role) override; - Return setCallback(const sp<::android::hardware::usb::V1_0::IUsbCallback>& callback) override; + Return setCallback(const sp& callback) override; Return queryPortStatus() override; - sp mCallback; + sp mCallback_1_0; + // Protects mCallback variable pthread_mutex_t mLock = PTHREAD_MUTEX_INITIALIZER; + // Protects roleSwitch operation + pthread_mutex_t mRoleSwitchLock = PTHREAD_MUTEX_INITIALIZER; + // Threads waiting for the partner to come back wait here + pthread_cond_t mPartnerCV = PTHREAD_COND_INITIALIZER; + // lock protecting mPartnerCV + pthread_mutex_t mPartnerLock = PTHREAD_MUTEX_INITIALIZER; + // Variable to signal partner coming back online after type switch + bool mPartnerUp; private: pthread_t mPoll; diff --git a/hidl/usb/android.hardware.usb@1.1-service.wahoo.rc b/hidl/usb/android.hardware.usb@1.1-service.wahoo.rc index e51a870..bc1101d 100644 --- a/hidl/usb/android.hardware.usb@1.1-service.wahoo.rc +++ b/hidl/usb/android.hardware.usb@1.1-service.wahoo.rc @@ -4,7 +4,9 @@ service usb-hal-1-1 /vendor/bin/hw/android.hardware.usb@1.1-service.wahoo group system on boot - chown root system /sys/class/typec/usbc0/current_power_role - chown root system /sys/class/typec/usbc0/current_data_role - chmod 664 /sys/class/typec/usbc0/current_power_role - chmod 664 /sys/class/typec/usbc0/current_data_role + chown root system /sys/class/typec/port0/power_role + chown root system /sys/class/typec/port0/data_role + chown root system /sys/class/typec/port0/port_type + chmod 664 /sys/class/typec/port0/power_role + chmod 664 /sys/class/typec/port0/data_role + chmod 664 /sys/class/typec/port0/port_type -- 2.20.1