From adf891674519dcfce4abaab639085250cedc5bad Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Mon, 31 Jan 2022 22:15:15 -0800 Subject: [PATCH] Migrate IUsb implementation to AIDL This change migrates IUsb implementation to AIDL. Also, IUsb and IUsbGadget now run in its own processes to improve stability and isolation. Port of 3c97a6bb68f6418ee41026f3bd0024405c97cf0c Bug: 200993386 Change-Id: I681c1ba9c4f547e3cf5dc0fa7c9ee1aaf4fd603c --- device-redfin.mk | 5 +- usb/Usb.cpp | 840 ------------------ usb/Usb.h | 103 --- usb/{ => gadget}/Android.bp | 13 +- usb/{ => gadget}/UsbGadget.cpp | 0 usb/{ => gadget}/UsbGadget.h | 0 ...oid.hardware.usb.gadget-service.redfin.rc} | 2 +- ...hardware.usb.gadget@1.1-service.redfin.xml | 0 .../service_gadget.cpp} | 22 +- usb/usb/Android.bp | 50 ++ usb/usb/Usb.cpp | 835 +++++++++++++++++ usb/usb/Usb.h | 90 ++ usb/usb/android.hardware.usb-service.rc | 4 + .../android.hardware.usb-service.xml} | 6 +- usb/usb/service.cpp | 37 + 15 files changed, 1032 insertions(+), 975 deletions(-) delete mode 100644 usb/Usb.cpp delete mode 100644 usb/Usb.h rename usb/{ => gadget}/Android.bp (71%) rename usb/{ => gadget}/UsbGadget.cpp (100%) rename usb/{ => gadget}/UsbGadget.h (100%) rename usb/{android.hardware.usb@1.3-service.redfin.rc => gadget/android.hardware.usb.gadget-service.redfin.rc} (90%) rename usb/{ => gadget}/android.hardware.usb.gadget@1.1-service.redfin.xml (100%) rename usb/{service.cpp => gadget/service_gadget.cpp} (70%) create mode 100644 usb/usb/Android.bp create mode 100644 usb/usb/Usb.cpp create mode 100644 usb/usb/Usb.h create mode 100644 usb/usb/android.hardware.usb-service.rc rename usb/{android.hardware.usb@1.3-service.redfin.xml => usb/android.hardware.usb-service.xml} (69%) create mode 100644 usb/usb/service.cpp diff --git a/device-redfin.mk b/device-redfin.mk index fc6f260..fa45916 100644 --- a/device-redfin.mk +++ b/device-redfin.mk @@ -130,8 +130,11 @@ ifeq ($(wildcard vendor/google_devices/redfin/proprietary/device-vendor-redfin.m BUILD_WITHOUT_VENDOR := true endif +# USB HAL PRODUCT_PACKAGES += \ - android.hardware.usb@1.3-service.redfin + android.hardware.usb-service.redfin +PRODUCT_PACKAGES += \ + android.hardware.usb.gadget-service.redfin # Vibrator HAL PRODUCT_PACKAGES += \ diff --git a/usb/Usb.cpp b/usb/Usb.cpp deleted file mode 100644 index dde01ce..0000000 --- a/usb/Usb.cpp +++ /dev/null @@ -1,840 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "android.hardware.usb@1.3-service.redfin" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "Usb.h" - -using android::base::GetProperty; - -namespace android { -namespace hardware { -namespace usb { -namespace V1_3 { -namespace implementation { - -Return Usb::enableUsbDataSignal(bool enable) { - bool result = true; - - ALOGI("Userspace turn %s USB data signaling", enable ? "on" : "off"); - - if (enable) { - if (!WriteStringToFile("1", USB_DATA_PATH)) { - ALOGE("Not able to turn on usb connection notification"); - result = false; - } - - if (!WriteStringToFile(kGadgetName, PULLUP_PATH)) { - ALOGE("Gadget cannot be pulled up"); - result = false; - } - } else { - if (!WriteStringToFile("1", ID_PATH)) { - ALOGE("Not able to turn off host mode"); - result = false; - } - - if (!WriteStringToFile("0", VBUS_PATH)) { - ALOGE("Not able to set Vbus state"); - result = false; - } - - if (!WriteStringToFile("0", USB_DATA_PATH)) { - ALOGE("Not able to turn on usb connection notification"); - result = false; - } - - if (!WriteStringToFile("none", PULLUP_PATH)) { - ALOGE("Gadget cannot be pulled down"); - result = false; - } - } - - return result; -} - -// Set by the signal handler to destroy the thread -volatile bool destroyThread; - -constexpr char kEnabledPath[] = "/sys/class/power_supply/usb/moisture_detection_enabled"; -constexpr char kDetectedPath[] = "/sys/class/power_supply/usb/moisture_detected"; -constexpr char kConsole[] = "init.svc.console"; -constexpr char kDisableContatminantDetection[] = "vendor.usb.contaminantdisable"; - -void queryVersionHelper(implementation::Usb *usb, hidl_vec *currentPortStatus_1_2); - -int32_t readFile(const std::string &filename, std::string *contents) { - FILE *fp; - ssize_t read = 0; - char *line = NULL; - size_t len = 0; - - fp = fopen(filename.c_str(), "r"); - if (fp != NULL) { - if ((read = getline(&line, &len, fp)) != -1) { - char *pos; - if ((pos = strchr(line, '\n')) != NULL) - *pos = '\0'; - *contents = line; - } - free(line); - fclose(fp); - return 0; - } else { - ALOGE("fopen failed"); - } - - return -1; -} - -int32_t writeFile(const std::string &filename, const std::string &contents) { - FILE *fp; - std::string written; - - fp = fopen(filename.c_str(), "w"); - if (fp != NULL) { - // FAILURE RETRY - int ret = fputs(contents.c_str(), fp); - fclose(fp); - if ((ret != EOF) && !readFile(filename, &written) && written == contents) - return 0; - } - return -1; -} - -Status queryMoistureDetectionStatus(hidl_vec *currentPortStatus_1_2) { - std::string enabled, status; - - if (currentPortStatus_1_2 == NULL || currentPortStatus_1_2->size() == 0) { - ALOGE("currentPortStatus_1_2 is not available"); - return Status::ERROR; - } - - (*currentPortStatus_1_2)[0].supportedContaminantProtectionModes = 0; - (*currentPortStatus_1_2)[0].supportedContaminantProtectionModes |= - V1_2::ContaminantProtectionMode::FORCE_SINK; - (*currentPortStatus_1_2)[0].contaminantProtectionStatus = V1_2::ContaminantProtectionStatus::NONE; - (*currentPortStatus_1_2)[0].contaminantDetectionStatus = V1_2::ContaminantDetectionStatus::DISABLED; - (*currentPortStatus_1_2)[0].supportsEnableContaminantPresenceDetection = true; - (*currentPortStatus_1_2)[0].supportsEnableContaminantPresenceProtection = false; - - if (readFile(kEnabledPath, &enabled)) { - ALOGE("Failed to open moisture_detection_enabled"); - return Status::ERROR; - } - - if (enabled == "1") { - if (readFile(kDetectedPath, &status)) { - ALOGE("Failed to open moisture_detected"); - return Status::ERROR; - } - if (status == "1") { - (*currentPortStatus_1_2)[0].contaminantDetectionStatus = - V1_2::ContaminantDetectionStatus::DETECTED; - (*currentPortStatus_1_2)[0].contaminantProtectionStatus = - V1_2::ContaminantProtectionStatus::FORCE_SINK; - } else - (*currentPortStatus_1_2)[0].contaminantDetectionStatus = - V1_2::ContaminantDetectionStatus::NOT_DETECTED; - } - - ALOGI("ContaminantDetectionStatus:%d ContaminantProtectionStatus:%d", - (*currentPortStatus_1_2)[0].contaminantDetectionStatus, - (*currentPortStatus_1_2)[0].contaminantProtectionStatus); - - return Status::SUCCESS; -} - -Return Usb::enableContaminantPresenceDetection(const hidl_string & /*portName*/, - bool enable) { - - std::string status = GetProperty(kConsole, ""); - std::string disable = GetProperty(kDisableContatminantDetection, ""); - - if (status != "running" && disable != "true") - writeFile(kEnabledPath, enable ? "1" : "0"); - - hidl_vec currentPortStatus_1_2; - - queryVersionHelper(this, ¤tPortStatus_1_2); - return Void(); -} - -Return Usb::enableContaminantPresenceProtection(const hidl_string & /*portName*/, - bool /*enable*/) { - hidl_vec currentPortStatus_1_2; - - queryVersionHelper(this, ¤tPortStatus_1_2); - return Void(); -} - -std::string appendRoleNodeHelper(const std::string &portName, PortRoleType type) { - std::string node("/sys/class/typec/" + portName); - - switch (type) { - case PortRoleType::DATA_ROLE: - return node + "/data_role"; - case PortRoleType::POWER_ROLE: - return node + "/power_role"; - case PortRoleType::MODE: - return node + "/port_type"; - default: - return ""; - } -} - -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"; - } 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"; - } 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"; - } - return "none"; -} - -void extractRole(std::string *roleName) { - std::size_t first, last; - - first = roleName->find("["); - last = roleName->find("]"); - - if (first != std::string::npos && last != std::string::npos) { - *roleName = roleName->substr(first + 1, last - first - 1); - } -} - -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 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"); - // 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; -} - -Usb::Usb() - : mLock(PTHREAD_MUTEX_INITIALIZER), - mRoleSwitchLock(PTHREAD_MUTEX_INITIALIZER), - mPartnerLock(PTHREAD_MUTEX_INITIALIZER), - mPartnerUp(false) { - pthread_condattr_t attr; - if (pthread_condattr_init(&attr)) { - ALOGE("pthread_condattr_init failed: %s", strerror(errno)); - abort(); - } - if (pthread_condattr_setclock(&attr, CLOCK_MONOTONIC)) { - ALOGE("pthread_condattr_setclock failed: %s", strerror(errno)); - abort(); - } - if (pthread_cond_init(&mPartnerCV, &attr)) { - ALOGE("pthread_cond_init failed: %s", strerror(errno)); - abort(); - } - if (pthread_condattr_destroy(&attr)) { - ALOGE("pthread_condattr_destroy failed: %s", strerror(errno)); - abort(); - } -} - -Return Usb::switchRole(const hidl_string &portName, const V1_0::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()); - - 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("Role switch failed"); - } - } else { - ALOGE("failed to update the new role"); - } - } else { - ALOGE("fopen failed"); - } - } - - pthread_mutex_lock(&mLock); - if (mCallback_1_0 != NULL) { - Return ret = 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(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; - - // Mode - - if (type == PortRoleType::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 + "/data_role"; - *currentRole = static_cast(PortDataRole::NONE); - } else if (type == PortRoleType::MODE) { - filename = "/sys/class/typec/" + portName + "/data_role"; - *currentRole = static_cast(PortMode_1_1::NONE); - } else { - return Status::ERROR; - } - - 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); - - if (roleName == "source") { - *currentRole = static_cast(PortPowerRole::SOURCE); - } else if (roleName == "sink") { - *currentRole = static_cast(PortPowerRole::SINK); - } else if (roleName == "host") { - if (type == PortRoleType::DATA_ROLE) - *currentRole = static_cast(PortDataRole::HOST); - else - *currentRole = static_cast(PortMode_1_1::DFP); - } else if (roleName == "device") { - if (type == PortRoleType::DATA_ROLE) - *currentRole = static_cast(PortDataRole::DEVICE); - else - *currentRole = static_cast(PortMode_1_1::UFP); - } else if (roleName != "none") { - /* case for none has already been addressed. - * so we check if the role isnt none. - */ - return Status::UNRECOGNIZED_ROLE; - } - - return Status::SUCCESS; -} - -Status getTypeCPortNamesHelper(std::unordered_map *names) { - DIR *dp; - - dp = opendir("/sys/class/typec"); - 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")) { - std::unordered_map::const_iterator portName = - names->find(ep->d_name); - if (portName == names->end()) { - names->insert({ep->d_name, false}); - } - } else { - (*names)[std::strtok(ep->d_name, "-")] = true; - } - } - } - closedir(dp); - return Status::SUCCESS; - } - - ALOGE("Failed to open /sys/class/typec"); - 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 supportsPD; - - if (!readFile(filename, &supportsPD)) { - if (supportsPD == "yes") { - return true; - } - } - - return false; -} - -/* - * 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_2, HALVersion version) { - std::unordered_map names; - Status result = getTypeCPortNamesHelper(&names); - int i = -1; - - if (result == Status::SUCCESS) { - currentPortStatus_1_2->resize(names.size()); - for (std::pair port : names) { - i++; - ALOGI("%s", port.first.c_str()); - (*currentPortStatus_1_2)[i].status_1_1.status.portName = port.first; - - uint32_t currentRole; - if (getCurrentRoleHelper(port.first, port.second, PortRoleType::POWER_ROLE, - ¤tRole) == Status::SUCCESS) { - (*currentPortStatus_1_2)[i].status_1_1.status.currentPowerRole = - static_cast(currentRole); - } else { - ALOGE("Error while retreiving portNames"); - goto done; - } - - if (getCurrentRoleHelper(port.first, port.second, PortRoleType::DATA_ROLE, - ¤tRole) == Status::SUCCESS) { - (*currentPortStatus_1_2)[i].status_1_1.status.currentDataRole = - static_cast(currentRole); - } else { - ALOGE("Error while retreiving current port role"); - goto done; - } - - if (getCurrentRoleHelper(port.first, port.second, PortRoleType::MODE, ¤tRole) == - Status::SUCCESS) { - (*currentPortStatus_1_2)[i].status_1_1.currentMode = - static_cast(currentRole); - (*currentPortStatus_1_2)[i].status_1_1.status.currentMode = - static_cast(currentRole); - } else { - ALOGE("Error while retreiving current data role"); - goto done; - } - - (*currentPortStatus_1_2)[i].status_1_1.status.canChangeMode = true; - (*currentPortStatus_1_2)[i].status_1_1.status.canChangeDataRole = - port.second ? canSwitchRoleHelper(port.first, PortRoleType::DATA_ROLE) : false; - (*currentPortStatus_1_2)[i].status_1_1.status.canChangePowerRole = - port.second ? canSwitchRoleHelper(port.first, PortRoleType::POWER_ROLE) : false; - - if (version == HALVersion::V1_0) { - ALOGI("HAL version V1_0"); - (*currentPortStatus_1_2)[i].status_1_1.status.supportedModes = V1_0::PortMode::DRP; - } else { - if (version == HALVersion::V1_1) - ALOGI("HAL version V1_1"); - else - ALOGI("HAL version V1_2"); - (*currentPortStatus_1_2)[i].status_1_1.supportedModes = 0 | PortMode_1_1::DRP; - (*currentPortStatus_1_2)[i].status_1_1.status.supportedModes = V1_0::PortMode::NONE; - (*currentPortStatus_1_2)[i].status_1_1.status.currentMode = V1_0::PortMode::NONE; - } - - ALOGI( - "%d:%s connected:%d canChangeMode:%d canChagedata:%d canChangePower:%d " - "supportedModes:%d", - i, port.first.c_str(), port.second, - (*currentPortStatus_1_2)[i].status_1_1.status.canChangeMode, - (*currentPortStatus_1_2)[i].status_1_1.status.canChangeDataRole, - (*currentPortStatus_1_2)[i].status_1_1.status.canChangePowerRole, - (*currentPortStatus_1_2)[i].status_1_1.supportedModes); - } - return Status::SUCCESS; - } -done: - return Status::ERROR; -} - -void queryVersionHelper(implementation::Usb *usb, hidl_vec *currentPortStatus_1_2) { - hidl_vec currentPortStatus_1_1; - hidl_vec currentPortStatus; - Status status; - sp callback_V1_1 = V1_1::IUsbCallback::castFrom(usb->mCallback_1_0); - sp callback_V1_2 = IUsbCallback::castFrom(usb->mCallback_1_0); - - pthread_mutex_lock(&usb->mLock); - if (usb->mCallback_1_0 != NULL) { - if (callback_V1_2 != NULL) { - status = getPortStatusHelper(currentPortStatus_1_2, HALVersion::V1_2); - if (status == Status::SUCCESS) - queryMoistureDetectionStatus(currentPortStatus_1_2); - } else if (callback_V1_1 != NULL) { - status = getPortStatusHelper(currentPortStatus_1_2, HALVersion::V1_1); - currentPortStatus_1_1.resize(currentPortStatus_1_2->size()); - for (unsigned long i = 0; i < currentPortStatus_1_2->size(); i++) - currentPortStatus_1_1[i] = (*currentPortStatus_1_2)[i].status_1_1; - } else { - status = getPortStatusHelper(currentPortStatus_1_2, HALVersion::V1_0); - currentPortStatus.resize(currentPortStatus_1_2->size()); - for (unsigned long i = 0; i < currentPortStatus_1_2->size(); i++) - currentPortStatus[i] = (*currentPortStatus_1_2)[i].status_1_1.status; - } - - Return ret; - - if (callback_V1_2 != NULL) - ret = callback_V1_2->notifyPortStatusChange_1_2(*currentPortStatus_1_2, status); - else if (callback_V1_1 != NULL) - ret = callback_V1_1->notifyPortStatusChange_1_1(currentPortStatus_1_1, status); - else - ret = usb->mCallback_1_0->notifyPortStatusChange(currentPortStatus, status); - - if (!ret.isOk()) - ALOGE("queryPortStatus_1_2 error %s", ret.description().c_str()); - } else { - ALOGI("Notifying userspace skipped. Callback is NULL"); - } - pthread_mutex_unlock(&usb->mLock); -} - -Return Usb::queryPortStatus() { - hidl_vec currentPortStatus_1_2; - - queryVersionHelper(this, ¤tPortStatus_1_2); - return Void(); -} - -struct data { - int uevent_fd; - android::hardware::usb::V1_3::implementation::Usb *usb; -}; - -static void uevent_event(uint32_t /*epevents*/, struct data *payload) { - char msg[UEVENT_MSG_LEN + 2]; - char *cp; - int n; - - n = uevent_kernel_multicast_recv(payload->uevent_fd, msg, UEVENT_MSG_LEN); - if (n <= 0) - return; - if (n >= UEVENT_MSG_LEN) /* overflow -- discard */ - return; - - msg[n] = '\0'; - msg[n + 1] = '\0'; - cp = msg; - - while (*cp) { - 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_")) || - !strncmp(cp, "POWER_SUPPLY_MOISTURE_DETECTED", - strlen("POWER_SUPPLY_MOISTURE_DETECTED"))) { - hidl_vec currentPortStatus_1_2; - queryVersionHelper(payload->usb, ¤tPortStatus_1_2); - - // 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_2.size(); i++) { - DIR *dp = - opendir(std::string("/sys/class/typec/" + - std::string(currentPortStatus_1_2[i] - .status_1_1.status.portName.c_str()) + - "-partner") - .c_str()); - if (dp == NULL) { - // PortRole role = {.role = static_cast(PortMode::UFP)}; - switchToDrp(currentPortStatus_1_2[i].status_1_1.status.portName); - } else { - closedir(dp); - } - } - pthread_mutex_unlock(&payload->usb->mRoleSwitchLock); - } - break; - } - /* advance to after the next \0 */ - while (*cp++) { - } - } -} - -void *work(void *param) { - int epoll_fd, uevent_fd; - struct epoll_event ev; - int nevents = 0; - struct data payload; - - ALOGE("creating thread"); - - uevent_fd = uevent_open_socket(64 * 1024, true); - - if (uevent_fd < 0) { - ALOGE("uevent_init: uevent_open_socket failed\n"); - return NULL; - } - - payload.uevent_fd = uevent_fd; - payload.usb = (android::hardware::usb::V1_3::implementation::Usb *)param; - - fcntl(uevent_fd, F_SETFL, O_NONBLOCK); - - ev.events = EPOLLIN; - ev.data.ptr = (void *)uevent_event; - - epoll_fd = epoll_create(64); - if (epoll_fd == -1) { - ALOGE("epoll_create failed; errno=%d", errno); - goto error; - } - - if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, uevent_fd, &ev) == -1) { - ALOGE("epoll_ctl failed; errno=%d", errno); - goto error; - } - - while (!destroyThread) { - struct epoll_event events[64]; - - nevents = epoll_wait(epoll_fd, events, 64, -1); - if (nevents == -1) { - if (errno == EINTR) - continue; - ALOGE("usb epoll_wait failed; errno=%d", errno); - break; - } - - for (int n = 0; n < nevents; ++n) { - if (events[n].data.ptr) - (*(void (*)(int, struct data *payload))events[n].data.ptr)(events[n].events, - &payload); - } - } - - ALOGI("exiting worker thread"); -error: - close(uevent_fd); - - if (epoll_fd >= 0) - close(epoll_fd); - - return NULL; -} - -void sighandler(int sig) { - if (sig == SIGUSR1) { - destroyThread = true; - ALOGI("destroy set"); - return; - } - signal(SIGUSR1, sighandler); -} - -Return Usb::setCallback(const sp &callback) { - sp callback_V1_1 = V1_1::IUsbCallback::castFrom(callback); - sp callback_V1_2 = IUsbCallback::castFrom(callback); - - if (callback != NULL) { - if (callback_V1_2 != NULL) - ALOGI("Registering 1.2 callback"); - else if (callback_V1_1 != NULL) - ALOGI("Registering 1.1 callback"); - } - - pthread_mutex_lock(&mLock); - /* - * When both the old callback and new callback values are NULL, - * there is no need to spin off the worker thread. - * When both the values are not NULL, we would already have a - * worker thread running, so updating the callback object would - * be suffice. - */ - 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_1_0 = callback; - ALOGI("registering callback"); - - // Kill the worker thread if the new callback is NULL. - if (mCallback_1_0 == NULL) { - pthread_mutex_unlock(&mLock); - if (!pthread_kill(mPoll, SIGUSR1)) { - pthread_join(mPoll, NULL); - ALOGI("pthread destroyed"); - } - return Void(); - } - - destroyThread = false; - signal(SIGUSR1, sighandler); - - /* - * Create a background thread if the old callback value is NULL - * and being updated with a new value. - */ - if (pthread_create(&mPoll, NULL, work, this)) { - ALOGE("pthread creation failed %d", errno); - mCallback_1_0 = NULL; - } - - pthread_mutex_unlock(&mLock); - return Void(); -} - -} // namespace implementation -} // namespace V1_3 -} // namespace usb -} // namespace hardware -} // namespace android diff --git a/usb/Usb.h b/usb/Usb.h deleted file mode 100644 index 5ff110d..0000000 --- a/usb/Usb.h +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -#pragma once - -#include -#include -#include -#include -#include -#include -#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 { -namespace usb { -namespace V1_3 { -namespace implementation { - -using ::android::sp; -using ::android::base::WriteStringToFile; -using ::android::hardware::hidl_array; -using ::android::hardware::hidl_memory; -using ::android::hardware::hidl_string; -using ::android::hardware::hidl_vec; -using ::android::hardware::Return; -using ::android::hardware::Void; -using ::android::hardware::usb::V1_0::PortDataRole; -using ::android::hardware::usb::V1_0::PortPowerRole; -using ::android::hardware::usb::V1_0::PortRole; -using ::android::hardware::usb::V1_0::PortRoleType; -using ::android::hardware::usb::V1_0::Status; -using ::android::hardware::usb::V1_1::PortMode_1_1; -using ::android::hardware::usb::V1_1::PortStatus_1_1; -using ::android::hardware::usb::V1_2::PortStatus; -using ::android::hardware::usb::V1_2::IUsbCallback; -using ::android::hardware::usb::V1_3::IUsb; -using ::android::hidl::base::V1_0::DebugInfo; -using ::android::hidl::base::V1_0::IBase; - -enum class HALVersion{ - V1_0, - V1_1, - V1_2, - V1_3 -}; - -constexpr char kGadgetName[] = "a600000.dwc3"; -#define SOC_PATH "/sys/devices/platform/soc/a600000.ssusb/" -#define ID_PATH SOC_PATH "id" -#define VBUS_PATH SOC_PATH "b_sess" -#define USB_DATA_PATH SOC_PATH "usb_data_enabled" - -struct Usb : public IUsb { - Usb(); - - Return switchRole(const hidl_string &portName, const V1_0::PortRole &role) override; - Return setCallback(const sp &callback) override; - Return queryPortStatus() override; - Return enableContaminantPresenceDetection(const hidl_string& portName, bool enable); - Return enableContaminantPresenceProtection(const hidl_string& portName, bool enable); - Return enableUsbDataSignal(bool enable) override; - - sp mCallback_1_0; - // Protects mCallback variable - pthread_mutex_t mLock; - // Protects roleSwitch operation - pthread_mutex_t mRoleSwitchLock; - // Threads waiting for the partner to come back wait here - pthread_cond_t mPartnerCV; - // lock protecting mPartnerCV - pthread_mutex_t mPartnerLock; - // Variable to signal partner coming back online after type switch - bool mPartnerUp; - - private: - pthread_t mPoll; -}; - -} // namespace implementation -} // namespace V1_3 -} // namespace usb -} // namespace hardware -} // namespace android diff --git a/usb/Android.bp b/usb/gadget/Android.bp similarity index 71% rename from usb/Android.bp rename to usb/gadget/Android.bp index 03b0c92..756b9eb 100644 --- a/usb/Android.bp +++ b/usb/gadget/Android.bp @@ -1,5 +1,5 @@ // -// Copyright (C) 2018 The Android Open Source Project +// Copyright (C) 2021 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -18,19 +18,14 @@ package { } cc_binary { - name: "android.hardware.usb@1.3-service.redfin", + name: "android.hardware.usb.gadget-service.redfin", relative_install_path: "hw", - init_rc: ["android.hardware.usb@1.3-service.redfin.rc"], + init_rc: ["android.hardware.usb.gadget-service.redfin.rc"], vintf_fragments: [ - "android.hardware.usb@1.3-service.redfin.xml", "android.hardware.usb.gadget@1.1-service.redfin.xml", ], - srcs: ["service.cpp", "Usb.cpp", "UsbGadget.cpp"], + srcs: ["service_gadget.cpp", "UsbGadget.cpp"], shared_libs: [ - "android.hardware.usb@1.0", - "android.hardware.usb@1.1", - "android.hardware.usb@1.2", - "android.hardware.usb@1.3", "android.hardware.usb.gadget@1.0", "android.hardware.usb.gadget@1.1", "libbase", diff --git a/usb/UsbGadget.cpp b/usb/gadget/UsbGadget.cpp similarity index 100% rename from usb/UsbGadget.cpp rename to usb/gadget/UsbGadget.cpp diff --git a/usb/UsbGadget.h b/usb/gadget/UsbGadget.h similarity index 100% rename from usb/UsbGadget.h rename to usb/gadget/UsbGadget.h diff --git a/usb/android.hardware.usb@1.3-service.redfin.rc b/usb/gadget/android.hardware.usb.gadget-service.redfin.rc similarity index 90% rename from usb/android.hardware.usb@1.3-service.redfin.rc rename to usb/gadget/android.hardware.usb.gadget-service.redfin.rc index fffa93a..5e61e87 100644 --- a/usb/android.hardware.usb@1.3-service.redfin.rc +++ b/usb/gadget/android.hardware.usb.gadget-service.redfin.rc @@ -1,4 +1,4 @@ -service vendor.usb-hal-1-3 /vendor/bin/hw/android.hardware.usb@1.3-service.redfin +service vendor.usb-gadget-hal-1-1 /vendor/bin/hw/android.hardware.usb.gadget-service.redfin class hal user system group system shell mtp diff --git a/usb/android.hardware.usb.gadget@1.1-service.redfin.xml b/usb/gadget/android.hardware.usb.gadget@1.1-service.redfin.xml similarity index 100% rename from usb/android.hardware.usb.gadget@1.1-service.redfin.xml rename to usb/gadget/android.hardware.usb.gadget@1.1-service.redfin.xml diff --git a/usb/service.cpp b/usb/gadget/service_gadget.cpp similarity index 70% rename from usb/service.cpp rename to usb/gadget/service_gadget.cpp index 570beef..159e8f2 100644 --- a/usb/service.cpp +++ b/usb/gadget/service_gadget.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,10 +14,9 @@ * limitations under the License. */ -#define LOG_TAG "android.hardware.usb@1.3-service.redfin" +#define LOG_TAG "android.hardware.usb.gadget-service.redfin" #include -#include "Usb.h" #include "UsbGadget.h" using android::sp; @@ -29,34 +28,23 @@ using android::hardware::joinRpcThreadpool; // Generated HIDL files using android::hardware::usb::gadget::V1_1::IUsbGadget; using android::hardware::usb::gadget::V1_1::implementation::UsbGadget; -using android::hardware::usb::V1_3::IUsb; -using android::hardware::usb::V1_3::implementation::Usb; using android::OK; using android::status_t; int main() { - android::sp service = new Usb(); - android::sp service2 = new UsbGadget(); - + android::sp service = new UsbGadget(); configureRpcThreadpool(2, true /*callerWillJoin*/); status_t status = service->registerAsService(); - if (status != OK) { - ALOGE("Cannot register USB HAL service"); - return 1; - } - - status = service2->registerAsService(); - if (status != OK) { ALOGE("Cannot register USB Gadget HAL service"); return 1; } - ALOGI("USB HAL Ready."); + ALOGI("USB gadget HAL Ready."); joinRpcThreadpool(); // Under noraml cases, execution will not reach this line. - ALOGI("USB HAL failed to join thread pool."); + ALOGI("USB gadget HAL failed to join thread pool."); return 1; } diff --git a/usb/usb/Android.bp b/usb/usb/Android.bp new file mode 100644 index 0000000..4f5d599 --- /dev/null +++ b/usb/usb/Android.bp @@ -0,0 +1,50 @@ +// +// Copyright (C) 2021 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +cc_binary { + name: "android.hardware.usb-service.redfin", + relative_install_path: "hw", + init_rc: ["android.hardware.usb-service.rc"], + vintf_fragments: ["android.hardware.usb-service.xml"], + vendor: true, + srcs: [ + "service.cpp", + "Usb.cpp", + ], + shared_libs: [ + "libbase", + "libbinder", + "libhidlbase", + "liblog", + "libutils", + "libhardware", + "android.hardware.usb.gadget@1.0", + "android.hardware.usb-V1-ndk", + "libcutils", + "android.frameworks.stats-V1-ndk", + "pixelatoms-cpp", + "libbinder_ndk", + + ], + static_libs: [ + "libpixelusb", + "libpixelstats", + ], + export_shared_lib_headers: [ + "android.frameworks.stats-V1-ndk", + "pixelatoms-cpp", + ], +} diff --git a/usb/usb/Usb.cpp b/usb/usb/Usb.cpp new file mode 100644 index 0000000..8d4cb47 --- /dev/null +++ b/usb/usb/Usb.cpp @@ -0,0 +1,835 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "android.hardware.usb.aidl-service" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "Usb.h" + +using android::base::GetProperty; +using android::base::Trim; + +namespace aidl { +namespace android { +namespace hardware { +namespace usb { + +// Set by the signal handler to destroy the thread +volatile bool destroyThread; + +constexpr char kConsole[] = "init.svc.console"; +constexpr char kDetectedPath[] = "/sys/class/power_supply/usb/moisture_detected"; +constexpr char kDisableContatminantDetection[] = "vendor.usb.contaminantdisable"; +constexpr char kEnabledPath[] = "/sys/class/power_supply/usb/moisture_detection_enabled"; +constexpr char kTypecPath[] = "/sys/class/typec"; + +void queryVersionHelper(android::hardware::usb::Usb *usb, + std::vector *currentPortStatus); + +ScopedAStatus Usb::enableUsbData(const string& in_portName, bool in_enable, + int64_t in_transactionId) { + bool result = true; + std::vector currentPortStatus; + + ALOGI("Userspace turn %s USB data signaling. opID:%ld", in_enable ? "on" : "off", + in_transactionId); + + if (in_enable) { + if (!WriteStringToFile("1", USB_DATA_PATH)) { + ALOGE("Not able to turn on usb connection notification"); + result = false; + } + + if (!WriteStringToFile(kGadgetName, PULLUP_PATH)) { + ALOGE("Gadget cannot be pulled up"); + result = false; + } + } else { + if (!WriteStringToFile("1", ID_PATH)) { + ALOGE("Not able to turn off host mode"); + result = false; + } + + if (!WriteStringToFile("0", VBUS_PATH)) { + ALOGE("Not able to set Vbus state"); + result = false; + } + + if (!WriteStringToFile("0", USB_DATA_PATH)) { + ALOGE("Not able to turn on usb connection notification"); + result = false; + } + + if (!WriteStringToFile("none", PULLUP_PATH)) { + ALOGE("Gadget cannot be pulled down"); + result = false; + } + } + + if (result) { + mUsbDataEnabled = in_enable; + } + pthread_mutex_lock(&mLock); + if (mCallback != NULL) { + ScopedAStatus ret = mCallback->notifyEnableUsbDataStatus( + in_portName, in_enable, result ? Status::SUCCESS : Status::ERROR, in_transactionId); + if (!ret.isOk()) + ALOGE("notifyEnableUsbDataStatus error %s", ret.getDescription().c_str()); + } else { + ALOGE("Not notifying the userspace. Callback is not set"); + } + pthread_mutex_unlock(&mLock); + queryVersionHelper(this, ¤tPortStatus); + + return ScopedAStatus::ok(); +} + +ScopedAStatus Usb::enableUsbDataWhileDocked(const string& in_portName, + int64_t in_transactionId) { + std::vector currentPortStatus; + + ALOGI("Userspace enableUsbDataWhileDocked opID:%ld", in_transactionId); + + pthread_mutex_lock(&mLock); + if (mCallback != NULL) { + ScopedAStatus ret = mCallback->notifyEnableUsbDataWhileDockedStatus( + in_portName, Status::NOT_SUPPORTED, in_transactionId); + if (!ret.isOk()) + ALOGE("notifyEnableUsbDataStatus error %s", ret.getDescription().c_str()); + } else { + ALOGE("Not notifying the userspace. Callback is not set"); + } + pthread_mutex_unlock(&mLock); + queryVersionHelper(this, ¤tPortStatus); + + return ScopedAStatus::ok(); +} + +ScopedAStatus Usb::resetUsbPort(const std::string& in_portName, int64_t in_transactionId) { + bool result = true; + std::vector currentPortStatus; + + ALOGI("Userspace reset USB Port. opID:%ld", in_transactionId); + + if (!WriteStringToFile("none", PULLUP_PATH)) { + ALOGI("Gadget cannot be pulled down"); + result = false; + } + + pthread_mutex_lock(&mLock); + if (mCallback != NULL) { + ::ndk::ScopedAStatus ret = mCallback->notifyResetUsbPortStatus( + in_portName, result ? Status::SUCCESS : Status::ERROR, in_transactionId); + if (!ret.isOk()) + ALOGE("notifyTransactionStatus error %s", ret.getDescription().c_str()); + } else { + ALOGE("Not notifying the userspace. Callback is not set"); + } + pthread_mutex_unlock(&mLock); + + return ::ndk::ScopedAStatus::ok(); +} + +ScopedAStatus Usb::limitPowerTransfer(const string& in_portName, bool in_limit, + int64_t in_transactionId) { + std::vector currentPortStatus; + + ALOGI("limitPowerTransfer limit:%c opId:%ld", in_limit ? 'y' : 'n', in_transactionId); + pthread_mutex_lock(&mLock); + if (mCallback != NULL && in_transactionId >= 0) { + ScopedAStatus ret = mCallback->notifyLimitPowerTransferStatus( + in_portName, in_limit, Status::NOT_SUPPORTED, in_transactionId); + if (!ret.isOk()) + ALOGE("limitPowerTransfer error %s", ret.getDescription().c_str()); + } else { + ALOGE("Not notifying the userspace. Callback is not set"); + } + + pthread_mutex_unlock(&mLock); + queryVersionHelper(this, ¤tPortStatus); + + return ScopedAStatus::ok(); +} + +Status queryMoistureDetectionStatus(std::vector *currentPortStatus) { + string enabled, status, path, DetectedPath; + + (*currentPortStatus)[0].supportedContaminantProtectionModes + .push_back(ContaminantProtectionMode::FORCE_DISABLE); + (*currentPortStatus)[0].contaminantProtectionStatus = ContaminantProtectionStatus::NONE; + (*currentPortStatus)[0].contaminantDetectionStatus = ContaminantDetectionStatus::DISABLED; + (*currentPortStatus)[0].supportsEnableContaminantPresenceDetection = true; + (*currentPortStatus)[0].supportsEnableContaminantPresenceProtection = false; + + if (!ReadFileToString(kEnabledPath, &enabled)) { + ALOGE("Failed to open moisture_detection_enabled"); + return Status::ERROR; + } + + enabled = Trim(enabled); + if (enabled == "1") { + if (!ReadFileToString(kDetectedPath, &status)) { + ALOGE("Failed to open moisture_detected"); + return Status::ERROR; + } + status = Trim(status); + if (status == "1") { + (*currentPortStatus)[0].contaminantDetectionStatus = + ContaminantDetectionStatus::DETECTED; + (*currentPortStatus)[0].contaminantProtectionStatus = + ContaminantProtectionStatus::FORCE_DISABLE; + } else { + (*currentPortStatus)[0].contaminantDetectionStatus = + ContaminantDetectionStatus::NOT_DETECTED; + } + } + + ALOGI("ContaminantDetectionStatus:%d ContaminantProtectionStatus:%d", + (*currentPortStatus)[0].contaminantDetectionStatus, + (*currentPortStatus)[0].contaminantProtectionStatus); + + return Status::SUCCESS; +} + +string appendRoleNodeHelper(const string &portName, PortRole::Tag tag) { + string node("/sys/class/typec/" + portName); + + switch (tag) { + case PortRole::dataRole: + return node + "/data_role"; + case PortRole::powerRole: + return node + "/power_role"; + case PortRole::mode: + return node + "/port_type"; + default: + return ""; + } +} + +string convertRoletoString(PortRole role) { + if (role.getTag() == PortRole::powerRole) { + if (role.get() == PortPowerRole::SOURCE) + return "source"; + else if (role.get() == PortPowerRole::SINK) + return "sink"; + } else if (role.getTag() == PortRole::dataRole) { + if (role.get() == PortDataRole::HOST) + return "host"; + if (role.get() == PortDataRole::DEVICE) + return "device"; + } else if (role.getTag() == PortRole::mode) { + if (role.get() == PortMode::UFP) + return "sink"; + if (role.get() == PortMode::DFP) + return "source"; + } + return "none"; +} + +void extractRole(string *roleName) { + std::size_t first, last; + + first = roleName->find("["); + last = roleName->find("]"); + + if (first != string::npos && last != string::npos) { + *roleName = roleName->substr(first + 1, last - first - 1); + } +} + +void switchToDrp(const string &portName) { + string filename = appendRoleNodeHelper(string(portName.c_str()), PortRole::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 string &portName, const PortRole &in_role, struct Usb *usb) { + string filename = appendRoleNodeHelper(string(portName.c_str()), in_role.getTag()); + 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(in_role).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"); + // Validity 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(string(portName.c_str())); + + return roleSwitch; +} + +Usb::Usb() + : mLock(PTHREAD_MUTEX_INITIALIZER), + mRoleSwitchLock(PTHREAD_MUTEX_INITIALIZER), + mPartnerLock(PTHREAD_MUTEX_INITIALIZER), + mPartnerUp(false) { + pthread_condattr_t attr; + if (pthread_condattr_init(&attr)) { + ALOGE("pthread_condattr_init failed: %s", strerror(errno)); + abort(); + } + if (pthread_condattr_setclock(&attr, CLOCK_MONOTONIC)) { + ALOGE("pthread_condattr_setclock failed: %s", strerror(errno)); + abort(); + } + if (pthread_cond_init(&mPartnerCV, &attr)) { + ALOGE("pthread_cond_init failed: %s", strerror(errno)); + abort(); + } + if (pthread_condattr_destroy(&attr)) { + ALOGE("pthread_condattr_destroy failed: %s", strerror(errno)); + abort(); + } +} + +ScopedAStatus Usb::switchRole(const string& in_portName, const PortRole& in_role, + int64_t in_transactionId) { + string filename = appendRoleNodeHelper(string(in_portName.c_str()), in_role.getTag()); + string written; + FILE *fp; + bool roleSwitch = false; + + if (filename == "") { + ALOGE("Fatal: invalid node type"); + return ScopedAStatus::ok(); + } + + pthread_mutex_lock(&mRoleSwitchLock); + + ALOGI("filename write: %s role:%s", filename.c_str(), convertRoletoString(in_role).c_str()); + + if (in_role.getTag() == PortRole::mode) { + roleSwitch = switchMode(in_portName, in_role, this); + } else { + fp = fopen(filename.c_str(), "w"); + if (fp != NULL) { + int ret = fputs(convertRoletoString(in_role).c_str(), fp); + fclose(fp); + if ((ret != EOF) && ReadFileToString(filename, &written)) { + written = Trim(written); + extractRole(&written); + ALOGI("written: %s", written.c_str()); + if (written == convertRoletoString(in_role)) { + roleSwitch = true; + } else { + ALOGE("Role switch failed"); + } + } else { + ALOGE("failed to update the new role"); + } + } else { + ALOGE("fopen failed"); + } + } + + pthread_mutex_lock(&mLock); + if (mCallback != NULL) { + ScopedAStatus ret = mCallback->notifyRoleSwitchStatus( + in_portName, in_role, roleSwitch ? Status::SUCCESS : Status::ERROR, in_transactionId); + if (!ret.isOk()) + ALOGE("RoleSwitchStatus error %s", ret.getDescription().c_str()); + } else { + ALOGE("Not notifying the userspace. Callback is not set"); + } + pthread_mutex_unlock(&mLock); + pthread_mutex_unlock(&mRoleSwitchLock); + + return ScopedAStatus::ok(); +} + +Status getAccessoryConnected(const string &portName, string *accessory) { + string filename = "/sys/class/typec/" + portName + "-partner/accessory_mode"; + + if (!ReadFileToString(filename, accessory)) { + ALOGE("getAccessoryConnected: Failed to open filesystem node: %s", filename.c_str()); + return Status::ERROR; + } + *accessory = Trim(*accessory); + + return Status::SUCCESS; +} + +Status getCurrentRoleHelper(const string &portName, bool connected, PortRole *currentRole) { + string filename; + string roleName; + string accessory; + + if (currentRole->getTag() == PortRole::powerRole) { + filename = "/sys/class/typec/" + portName + "/power_role"; + currentRole->set(PortPowerRole::NONE); + } else if (currentRole->getTag() == PortRole::dataRole) { + filename = "/sys/class/typec/" + portName + "/data_role"; + currentRole->set(PortDataRole::NONE); + } else if (currentRole->getTag() == PortRole::mode) { + filename = "/sys/class/typec/" + portName + "/data_role"; + currentRole->set(PortMode::NONE); + } else { + return Status::ERROR; + } + + if (!connected) + return Status::SUCCESS; + + if (currentRole->getTag() == PortRole::mode) { + if (getAccessoryConnected(portName, &accessory) != Status::SUCCESS) { + return Status::ERROR; + } + if (accessory == "analog_audio") { + currentRole->set(PortMode::AUDIO_ACCESSORY); + return Status::SUCCESS; + } else if (accessory == "debug") { + currentRole->set(PortMode::DEBUG_ACCESSORY); + return Status::SUCCESS; + } + } + + if (!ReadFileToString(filename, &roleName)) { + ALOGE("getCurrentRole: Failed to open filesystem node: %s", filename.c_str()); + return Status::ERROR; + } + + roleName = Trim(roleName); + extractRole(&roleName); + + if (roleName == "source") { + currentRole->set(PortPowerRole::SOURCE); + } else if (roleName == "sink") { + currentRole->set(PortPowerRole::SINK); + } else if (roleName == "host") { + if (currentRole->getTag() == PortRole::dataRole) + currentRole->set(PortDataRole::HOST); + else + currentRole->set(PortMode::DFP); + } else if (roleName == "device") { + if (currentRole->getTag() == PortRole::dataRole) + currentRole->set(PortDataRole::DEVICE); + else + currentRole->set(PortMode::UFP); + } else if (roleName != "none") { + /* case for none has already been addressed. + * so we check if the role isn't none. + */ + return Status::UNRECOGNIZED_ROLE; + } + return Status::SUCCESS; +} + +Status getTypeCPortNamesHelper(std::unordered_map *names) { + DIR *dp; + + dp = opendir(kTypecPath); + if (dp != NULL) { + struct dirent *ep; + + while ((ep = readdir(dp))) { + if (ep->d_type == DT_LNK) { + if (string::npos == string(ep->d_name).find("-partner")) { + std::unordered_map::const_iterator portName = + names->find(ep->d_name); + if (portName == names->end()) { + names->insert({ep->d_name, false}); + } + } else { + (*names)[std::strtok(ep->d_name, "-")] = true; + } + } + } + closedir(dp); + return Status::SUCCESS; + } + + ALOGE("Failed to open /sys/class/typec"); + return Status::ERROR; +} + +bool canSwitchRoleHelper(const string &portName) { + string filename = "/sys/class/typec/" + portName + "-partner/supports_usb_power_delivery"; + string supportsPD; + + if (ReadFileToString(filename, &supportsPD)) { + supportsPD = Trim(supportsPD); + if (supportsPD == "yes") { + return true; + } + } + + return false; +} + +Status getPortStatusHelper(android::hardware::usb::Usb *usb, + std::vector *currentPortStatus) { + std::unordered_map names; + Status result = getTypeCPortNamesHelper(&names); + int i = -1; + + if (result == Status::SUCCESS) { + currentPortStatus->resize(names.size()); + for (std::pair port : names) { + i++; + ALOGI("%s", port.first.c_str()); + (*currentPortStatus)[i].portName = port.first; + + PortRole currentRole; + currentRole.set(PortPowerRole::NONE); + if (getCurrentRoleHelper(port.first, port.second, ¤tRole) == Status::SUCCESS){ + (*currentPortStatus)[i].currentPowerRole = currentRole.get(); + } else { + ALOGE("Error while retrieving portNames"); + goto done; + } + + currentRole.set(PortDataRole::NONE); + if (getCurrentRoleHelper(port.first, port.second, ¤tRole) == Status::SUCCESS) { + (*currentPortStatus)[i].currentDataRole = currentRole.get(); + } else { + ALOGE("Error while retrieving current port role"); + goto done; + } + + currentRole.set(PortMode::NONE); + if (getCurrentRoleHelper(port.first, port.second, ¤tRole) == Status::SUCCESS) { + (*currentPortStatus)[i].currentMode = currentRole.get(); + } else { + ALOGE("Error while retrieving current data role"); + goto done; + } + + (*currentPortStatus)[i].canChangeMode = true; + (*currentPortStatus)[i].canChangeDataRole = + port.second ? canSwitchRoleHelper(port.first) : false; + (*currentPortStatus)[i].canChangePowerRole = + port.second ? canSwitchRoleHelper(port.first) : false; + + (*currentPortStatus)[i].supportedModes.push_back(PortMode::DRP); + + if (!usb->mUsbDataEnabled) { + (*currentPortStatus)[i].usbDataStatus.push_back(UsbDataStatus::DISABLED_FORCE); + } else { + (*currentPortStatus)[i].usbDataStatus.push_back(UsbDataStatus::ENABLED); + } + (*currentPortStatus)[i].powerBrickStatus = PowerBrickStatus::UNKNOWN; + + ALOGI("%d:%s connected:%d canChangeMode:%d canChagedata:%d canChangePower:%d " + "usbDataEnabled:%d", + i, port.first.c_str(), port.second, + (*currentPortStatus)[i].canChangeMode, + (*currentPortStatus)[i].canChangeDataRole, + (*currentPortStatus)[i].canChangePowerRole, + usb->mUsbDataEnabled ? 1 : 0); + } + return Status::SUCCESS; + } +done: + return Status::ERROR; +} + +void queryVersionHelper(android::hardware::usb::Usb *usb, + std::vector *currentPortStatus) { + Status status; + pthread_mutex_lock(&usb->mLock); + status = getPortStatusHelper(usb, currentPortStatus); + queryMoistureDetectionStatus(currentPortStatus); + if (usb->mCallback != NULL) { + ScopedAStatus ret = usb->mCallback->notifyPortStatusChange(*currentPortStatus, + status); + if (!ret.isOk()) + ALOGE("queryPortStatus error %s", ret.getDescription().c_str()); + } else { + ALOGI("Notifying userspace skipped. Callback is NULL"); + } + pthread_mutex_unlock(&usb->mLock); +} + +ScopedAStatus Usb::queryPortStatus(int64_t in_transactionId) { + std::vector currentPortStatus; + + queryVersionHelper(this, ¤tPortStatus); + pthread_mutex_lock(&mLock); + if (mCallback != NULL) { + ScopedAStatus ret = mCallback->notifyQueryPortStatus( + "all", Status::SUCCESS, in_transactionId); + if (!ret.isOk()) + ALOGE("notifyQueryPortStatus error %s", ret.getDescription().c_str()); + } else { + ALOGE("Not notifying the userspace. Callback is not set"); + } + pthread_mutex_unlock(&mLock); + + return ScopedAStatus::ok(); +} + +ScopedAStatus Usb::enableContaminantPresenceDetection(const string& in_portName, + bool in_enable, int64_t in_transactionId) { + string disable = GetProperty(kDisableContatminantDetection, ""); + std::string status = GetProperty(kConsole, ""); + std::vector currentPortStatus; + bool success = true; + + if (status != "running" && disable != "true") + success = WriteStringToFile(in_enable ? "1" : "0", kEnabledPath); + + pthread_mutex_lock(&mLock); + if (mCallback != NULL) { + ScopedAStatus ret = mCallback->notifyContaminantEnabledStatus( + in_portName, in_enable, success ? Status::SUCCESS : Status::ERROR, in_transactionId); + if (!ret.isOk()) + ALOGE("notifyContaminantEnabledStatus error %s", ret.getDescription().c_str()); + } else { + ALOGE("Not notifying the userspace. Callback is not set"); + } + pthread_mutex_unlock(&mLock); + + queryVersionHelper(this, ¤tPortStatus); + return ScopedAStatus::ok(); +} + +struct data { + int uevent_fd; + ::aidl::android::hardware::usb::Usb *usb; +}; + +static void uevent_event(uint32_t /*epevents*/, struct data *payload) { + char msg[UEVENT_MSG_LEN + 2]; + char *cp; + int n; + + n = uevent_kernel_multicast_recv(payload->uevent_fd, msg, UEVENT_MSG_LEN); + if (n <= 0) + return; + if (n >= UEVENT_MSG_LEN) /* overflow -- discard */ + return; + + msg[n] = '\0'; + msg[n + 1] = '\0'; + cp = msg; + + while (*cp) { + 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_")) || + !strncmp(cp, "POWER_SUPPLY_MOISTURE_DETECTED", + strlen("POWER_SUPPLY_MOISTURE_DETECTED"))) { + std::vector currentPortStatus; + queryVersionHelper(payload->usb, ¤tPortStatus); + + // 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.size(); i++) { + DIR *dp = + opendir(string("/sys/class/typec/" + + string(currentPortStatus[i].portName.c_str()) + + "-partner").c_str()); + if (dp == NULL) { + switchToDrp(currentPortStatus[i].portName); + } else { + closedir(dp); + } + } + pthread_mutex_unlock(&payload->usb->mRoleSwitchLock); + } + break; + } + /* advance to after the next \0 */ + while (*cp++) { + } + } +} + +void *work(void *param) { + int epoll_fd, uevent_fd; + struct epoll_event ev; + int nevents = 0; + struct data payload; + + ALOGE("creating thread"); + + uevent_fd = uevent_open_socket(64 * 1024, true); + + if (uevent_fd < 0) { + ALOGE("uevent_init: uevent_open_socket failed\n"); + return NULL; + } + + payload.uevent_fd = uevent_fd; + payload.usb = (::aidl::android::hardware::usb::Usb *)param; + + fcntl(uevent_fd, F_SETFL, O_NONBLOCK); + + ev.events = EPOLLIN; + ev.data.ptr = (void *)uevent_event; + + epoll_fd = epoll_create(64); + if (epoll_fd == -1) { + ALOGE("epoll_create failed; errno=%d", errno); + goto error; + } + + if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, uevent_fd, &ev) == -1) { + ALOGE("epoll_ctl failed; errno=%d", errno); + goto error; + } + + while (!destroyThread) { + struct epoll_event events[64]; + + nevents = epoll_wait(epoll_fd, events, 64, -1); + if (nevents == -1) { + if (errno == EINTR) + continue; + ALOGE("usb epoll_wait failed; errno=%d", errno); + break; + } + + for (int n = 0; n < nevents; ++n) { + if (events[n].data.ptr) + (*(void (*)(int, struct data *payload))events[n].data.ptr)(events[n].events, + &payload); + } + } + + ALOGI("exiting worker thread"); +error: + close(uevent_fd); + + if (epoll_fd >= 0) + close(epoll_fd); + + return NULL; +} + +void sighandler(int sig) { + if (sig == SIGUSR1) { + destroyThread = true; + ALOGI("destroy set"); + return; + } + signal(SIGUSR1, sighandler); +} + +ScopedAStatus Usb::setCallback(const shared_ptr& in_callback) { + pthread_mutex_lock(&mLock); + if ((mCallback == NULL && in_callback == NULL) || + (mCallback != NULL && in_callback != NULL)) { + mCallback = in_callback; + pthread_mutex_unlock(&mLock); + return ScopedAStatus::ok(); + } + + mCallback = in_callback; + ALOGI("registering callback"); + + if (mCallback == NULL) { + if (!pthread_kill(mPoll, SIGUSR1)) { + pthread_join(mPoll, NULL); + ALOGI("pthread destroyed"); + } + pthread_mutex_unlock(&mLock); + return ScopedAStatus::ok(); + } + + destroyThread = false; + signal(SIGUSR1, sighandler); + + /* + * Create a background thread if the old callback value is NULL + * and being updated with a new value. + */ + if (pthread_create(&mPoll, NULL, work, this)) { + ALOGE("pthread creation failed %d", errno); + mCallback = NULL; + } + + pthread_mutex_unlock(&mLock); + return ScopedAStatus::ok(); +} + +} // namespace usb +} // namespace hardware +} // namespace android +} // aidl diff --git a/usb/usb/Usb.h b/usb/usb/Usb.h new file mode 100644 index 0000000..0cd9699 --- /dev/null +++ b/usb/usb/Usb.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#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 aidl { +namespace android { +namespace hardware { +namespace usb { + +using ::aidl::android::hardware::usb::IUsbCallback; +using ::aidl::android::hardware::usb::PortRole; +using ::android::base::ReadFileToString; +using ::android::base::WriteStringToFile; +using ::android::sp; +using ::ndk::ScopedAStatus; +using ::std::shared_ptr; +using ::std::string; + +constexpr char kGadgetName[] = "a600000.dwc3"; +#define ID_PATH SOC_PATH "id" +#define PULLUP_PATH "/config/usb_gadget/g1/UDC" +#define SOC_PATH "/sys/devices/platform/soc/a600000.ssusb/" +#define USB_DATA_PATH SOC_PATH "usb_data_enabled" +#define VBUS_PATH SOC_PATH "b_sess" + +struct Usb : public BnUsb { + Usb(); + + ScopedAStatus enableContaminantPresenceDetection(const std::string& in_portName, + bool in_enable, int64_t in_transactionId) override; + ScopedAStatus queryPortStatus(int64_t in_transactionId) override; + ScopedAStatus setCallback(const shared_ptr& in_callback) override; + ScopedAStatus switchRole(const string& in_portName, const PortRole& in_role, + int64_t in_transactionId) override; + ScopedAStatus enableUsbData(const string& in_portName, bool in_enable, + int64_t in_transactionId) override; + ScopedAStatus enableUsbDataWhileDocked(const string& in_portName, + int64_t in_transactionId) override; + ScopedAStatus limitPowerTransfer(const string& in_portName, bool in_limit, + int64_t in_transactionId) override; + ScopedAStatus resetUsbPort(const string& in_portName, int64_t in_transactionId) override; + + std::shared_ptr<::aidl::android::hardware::usb::IUsbCallback> mCallback; + // Protects mCallback variable + pthread_mutex_t mLock; + // Protects roleSwitch operation + pthread_mutex_t mRoleSwitchLock; + // Threads waiting for the partner to come back wait here + pthread_cond_t mPartnerCV; + // lock protecting mPartnerCV + pthread_mutex_t mPartnerLock; + // Variable to signal partner coming back online after type switch + bool mPartnerUp; + // Usb Data status + bool mUsbDataEnabled; + + private: + pthread_t mPoll; +}; + +} // namespace usb +} // namespace hardware +} // namespace android +} // aidl diff --git a/usb/usb/android.hardware.usb-service.rc b/usb/usb/android.hardware.usb-service.rc new file mode 100644 index 0000000..ec21fce --- /dev/null +++ b/usb/usb/android.hardware.usb-service.rc @@ -0,0 +1,4 @@ +service vendor.usb /vendor/bin/hw/android.hardware.usb-service.redfin + class hal + user system + group system shell diff --git a/usb/android.hardware.usb@1.3-service.redfin.xml b/usb/usb/android.hardware.usb-service.xml similarity index 69% rename from usb/android.hardware.usb@1.3-service.redfin.xml rename to usb/usb/android.hardware.usb-service.xml index cd54268..6088194 100644 --- a/usb/android.hardware.usb@1.3-service.redfin.xml +++ b/usb/usb/android.hardware.usb-service.xml @@ -1,12 +1,10 @@ - + android.hardware.usb - hwbinder - 1.3 + 1 IUsb default - diff --git a/usb/usb/service.cpp b/usb/usb/service.cpp new file mode 100644 index 0000000..2c0a596 --- /dev/null +++ b/usb/usb/service.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include "Usb.h" + +using ::aidl::android::hardware::usb::Usb; + +int main() { + ABinderProcess_setThreadPoolMaxThreadCount(0); + std::shared_ptr usb = ndk::SharedRefBase::make(); + + const std::string instance = std::string() + Usb::descriptor + "/default"; + binder_status_t status = AServiceManager_addService(usb->asBinder().get(), instance.c_str()); + CHECK(status == STATUS_OK); + + ALOGV("AIDL USB HAL about to start"); + ABinderProcess_joinThreadPool(); + return -1; // Should never be reached +}