From 955422ab8786e8fdf4b30ac1cda5489b0f847e9a Mon Sep 17 00:00:00 2001 From: Tim Zimmermann Date: Sat, 12 Feb 2022 20:22:42 +0100 Subject: [PATCH] hidl: usb: Import gs101 USB HAL * From device/google/gs101 @ android-12.0.0_r29 Change-Id: I76009358c627e2f4104cb96fda52557229397cc1 --- hidl/usb/Android.bp | 67 ++ hidl/usb/Usb.cpp | 938 ++++++++++++++++++ hidl/usb/Usb.h | 117 +++ hidl/usb/UsbGadget.cpp | 399 ++++++++ hidl/usb/UsbGadget.h | 111 +++ ....hardware.usb.gadget@1.2-service.gs101.xml | 11 + .../android.hardware.usb@1.3-service.gs101.rc | 99 ++ ...android.hardware.usb@1.3-service.gs101.xml | 12 + hidl/usb/service.cpp | 62 ++ 9 files changed, 1816 insertions(+) create mode 100644 hidl/usb/Android.bp create mode 100644 hidl/usb/Usb.cpp create mode 100644 hidl/usb/Usb.h create mode 100644 hidl/usb/UsbGadget.cpp create mode 100644 hidl/usb/UsbGadget.h create mode 100644 hidl/usb/android.hardware.usb.gadget@1.2-service.gs101.xml create mode 100644 hidl/usb/android.hardware.usb@1.3-service.gs101.rc create mode 100644 hidl/usb/android.hardware.usb@1.3-service.gs101.xml create mode 100644 hidl/usb/service.cpp diff --git a/hidl/usb/Android.bp b/hidl/usb/Android.bp new file mode 100644 index 00000000..40454afd --- /dev/null +++ b/hidl/usb/Android.bp @@ -0,0 +1,67 @@ +// +// Copyright (C) 2017 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. + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "//device/google/gs101:device_google_gs101_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: [ + "//device/google/gs101:device_google_gs101_license", + ], +} + +cc_binary { + name: "android.hardware.usb@1.3-service.gs101", + relative_install_path: "hw", + init_rc: ["android.hardware.usb@1.3-service.gs101.rc"], + vintf_fragments: [ + "android.hardware.usb@1.3-service.gs101.xml", + "android.hardware.usb.gadget@1.2-service.gs101.xml", + ], + srcs: ["service.cpp", "Usb.cpp", "UsbGadget.cpp"], + cflags: ["-Wall", "-Werror"], + shared_libs: [ + "libbase", + "libbinder", + "libhidlbase", + "liblog", + "libutils", + "libhardware", + "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", + "android.hardware.usb.gadget@1.2", + "android.hardware.thermal@1.0", + "android.hardware.thermal@2.0", + "libcutils", + "android.frameworks.stats-V1-ndk_platform", + "pixelatoms-cpp", + "libbinder_ndk", + ], + static_libs: [ + "libpixelusb", + "libpixelstats", + ], + export_shared_lib_headers: [ + "android.frameworks.stats-V1-ndk_platform", + "pixelatoms-cpp", + ], + proprietary: true, +} diff --git a/hidl/usb/Usb.cpp b/hidl/usb/Usb.cpp new file mode 100644 index 00000000..caf4f57b --- /dev/null +++ b/hidl/usb/Usb.cpp @@ -0,0 +1,938 @@ +/* + * Copyright (C) 2020 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.gs101" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "Usb.h" +#include "UsbGadget.h" + +#include +#include + +using aidl::android::frameworks::stats::IStats; +using android::base::GetProperty; +using android::hardware::google::pixel::getStatsService; +using android::hardware::google::pixel::PixelAtoms::VendorUsbPortOverheat; +using android::hardware::google::pixel::reportUsbPortOverheat; + +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 off 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; + +std::string enabledPath; +constexpr char kHsi2cPath[] = "/sys/devices/platform/10d50000.hsi2c"; +constexpr char kI2CPath[] = "/sys/devices/platform/10d50000.hsi2c/i2c-"; +constexpr char kContaminantDetectionPath[] = "i2c-max77759tcpc/contaminant_detection"; +constexpr char kStatusPath[] = "i2c-max77759tcpc/contaminant_detection_status"; +constexpr char kTypecPath[] = "/sys/class/typec"; +constexpr char kDisableContatminantDetection[] = "vendor.usb.contaminantdisable"; +constexpr char kOverheatStatsPath[] = "/sys/devices/platform/google,usbc_port_cooling_dev/"; +constexpr char kOverheatStatsDev[] = "DRIVER=google,usbc_port_cooling_dev"; +constexpr char kThermalZoneForTrip[] = "VIRTUAL-USB-THROTTLING"; +constexpr char kThermalZoneForTempReadPrimary[] = "usb_pwr_therm2"; +constexpr char kThermalZoneForTempReadSecondary1[] = "usb_pwr_therm"; +constexpr char kThermalZoneForTempReadSecondary2[] = "qi_therm"; +constexpr int kSamplingIntervalSec = 5; + +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 getContaminantDetectionNamesHelper(std::string *name) { + DIR *dp; + + dp = opendir(kHsi2cPath); + if (dp != NULL) { + struct dirent *ep; + + while ((ep = readdir(dp))) { + if (ep->d_type == DT_DIR) { + if (std::string::npos != std::string(ep->d_name).find("i2c-")) { + std::strtok(ep->d_name, "-"); + *name = std::strtok(NULL, "-"); + } + } + } + closedir(dp); + return Status::SUCCESS; + } + + ALOGE("Failed to open %s", kHsi2cPath); + return Status::ERROR; +} + +Status queryMoistureDetectionStatus(hidl_vec *currentPortStatus_1_2) { + std::string enabled, status, path, DetectedPath; + + 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; + + getContaminantDetectionNamesHelper(&path); + enabledPath = kI2CPath + path + "/" + kContaminantDetectionPath; + if (readFile(enabledPath, &enabled)) { + ALOGE("Failed to open moisture_detection_enabled"); + return Status::ERROR; + } + + if (enabled == "1") { + DetectedPath = kI2CPath + path + "/" + kStatusPath; + if (readFile(DetectedPath, &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; +} + +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"); + // 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(std::string(portName.c_str())); + + return roleSwitch; +} + +Usb::Usb() + : mLock(PTHREAD_MUTEX_INITIALIZER), + mRoleSwitchLock(PTHREAD_MUTEX_INITIALIZER), + mPartnerLock(PTHREAD_MUTEX_INITIALIZER), + mPartnerUp(false), + mOverheat(ZoneInfo(TemperatureType::USB_PORT, kThermalZoneForTrip, + ThrottlingSeverity::CRITICAL), + {ZoneInfo(TemperatureType::UNKNOWN, kThermalZoneForTempReadPrimary, + ThrottlingSeverity::NONE), + ZoneInfo(TemperatureType::UNKNOWN, kThermalZoneForTempReadSecondary1, + ThrottlingSeverity::NONE), + ZoneInfo(TemperatureType::UNKNOWN, kThermalZoneForTempReadSecondary2, + ThrottlingSeverity::NONE)}, kSamplingIntervalSec) { + 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 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 (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, + android::hardware::usb::V1_3::implementation::Usb *usb) { + 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 retrieving 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 retrieving 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 retrieving 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; + } + + // Query temperature for the first connect + if (port.second && !usb->mPluggedTemperatureCelsius) { + usb->mOverheat.getCurrentTemperature(kThermalZoneForTempReadPrimary, + &usb->mPluggedTemperatureCelsius); + ALOGV("USB Initial temperature: %f", usb->mPluggedTemperatureCelsius); + } + 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(android::hardware::usb::V1_3::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, usb); + if (status == Status::SUCCESS) + queryMoistureDetectionStatus(currentPortStatus_1_2); + } else if (callback_V1_1 != NULL) { + status = getPortStatusHelper(currentPortStatus_1_2, HALVersion::V1_1, usb); + 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, usb); + 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(); +} + +Return Usb::enableContaminantPresenceDetection(const hidl_string & /*portName*/, + bool enable) { + + std::string disable = GetProperty(kDisableContatminantDetection, ""); + + if (disable != "true") + writeFile(enabledPath, 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(); +} + +void report_overheat_event(android::hardware::usb::V1_3::implementation::Usb *usb) { + VendorUsbPortOverheat overheat_info; + std::string contents; + + overheat_info.set_plug_temperature_deci_c(usb->mPluggedTemperatureCelsius * 10); + overheat_info.set_max_temperature_deci_c(usb->mOverheat.getMaxOverheatTemperature() * 10); + if (ReadFileToString(std::string(kOverheatStatsPath) + "trip_time", &contents)) { + overheat_info.set_time_to_overheat_secs(stoi(contents)); + } else { + ALOGE("Unable to read trip_time"); + return; + } + if (ReadFileToString(std::string(kOverheatStatsPath) + "hysteresis_time", &contents)) { + overheat_info.set_time_to_hysteresis_secs(stoi(contents)); + } else { + ALOGE("Unable to read hysteresis_time"); + return; + } + if (ReadFileToString(std::string(kOverheatStatsPath) + "cleared_time", &contents)) { + overheat_info.set_time_to_inactive_secs(stoi(contents)); + } else { + ALOGE("Unable to read cleared_time"); + return; + } + + const std::shared_ptr stats_client = getStatsService(); + if (!stats_client) { + ALOGE("Unable to get AIDL Stats service"); + } else { + reportUsbPortOverheat(stats_client, overheat_info); + } +} + +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); + // Update Plugged temperature + payload->usb->mOverheat.getCurrentTemperature(kThermalZoneForTempReadPrimary, + &payload->usb->mPluggedTemperatureCelsius); + ALOGI("Usb Plugged temp: %f", payload->usb->mPluggedTemperatureCelsius); + } else if (!strncmp(cp, "DEVTYPE=typec_", strlen("DEVTYPE=typec_")) || + !strncmp(cp, "DRIVER=max77759tcpc", + strlen("DRIVER=max77759tcpc"))) { + 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; + } else if (!strncmp(cp, kOverheatStatsDev, strlen(kOverheatStatsDev))) { + ALOGV("Overheat Cooling device suez update"); + report_overheat_event(payload->usb); + } + /* 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/hidl/usb/Usb.h b/hidl/usb/Usb.h new file mode 100644 index 00000000..23ed0111 --- /dev/null +++ b/hidl/usb/Usb.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2020 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::base::WriteStringToFile; +using ::android::base::ReadFileToString; +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::google::pixel::usb::UsbOverheatEvent; +using ::android::hardware::google::pixel::usb::ZoneInfo; +using ::android::hardware::thermal::V2_0::TemperatureType; +using ::android::hardware::thermal::V2_0::ThrottlingSeverity; +using ::android::hardware::usb::V1_0::PortRole; +using ::android::hardware::usb::V1_0::PortRoleType; +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_3::IUsb; +using ::android::hardware::usb::V1_2::IUsbCallback; +using ::android::hardware::usb::V1_2::PortStatus; +using ::android::hardware::usb::V1_1::PortMode_1_1; +using ::android::hardware::usb::V1_1::PortStatus_1_1; +using ::android::hidl::base::V1_0::DebugInfo; +using ::android::hidl::base::V1_0::IBase; +using ::android::sp; + +enum class HALVersion{ + V1_0, + V1_1, + V1_2, + V1_3 +}; + +constexpr char kGadgetName[] = "11110000.dwc3"; +#define NEW_UDC_PATH "/sys/devices/platform/11110000.usb/" + +#define ID_PATH NEW_UDC_PATH "dwc3_exynos_otg_id" +#define VBUS_PATH NEW_UDC_PATH "dwc3_exynos_otg_b_sess" +#define USB_DATA_PATH NEW_UDC_PATH "usb_data_enabled" + +struct Usb : public IUsb { + Usb(); + + Return switchRole(const hidl_string &portName, const 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; + + // Usb Overheat object for push suez event + UsbOverheatEvent mOverheat; + // Temperature when connected + float mPluggedTemperatureCelsius; + + private: + pthread_t mPoll; +}; + +} // namespace implementation +} // namespace V1_3 +} // namespace usb +} // namespace hardware +} // namespace android diff --git a/hidl/usb/UsbGadget.cpp b/hidl/usb/UsbGadget.cpp new file mode 100644 index 00000000..0a6cea31 --- /dev/null +++ b/hidl/usb/UsbGadget.cpp @@ -0,0 +1,399 @@ +/* + * Copyright (C) 2020 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.gadget@1.2-service.gs101" + +#include "UsbGadget.h" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace android { +namespace hardware { +namespace usb { +namespace gadget { +namespace V1_2 { +namespace implementation { + +UsbGadget::UsbGadget() { + if (access(OS_DESC_PATH, R_OK) != 0) { + ALOGE("configfs setup not done yet"); + abort(); + } +} + +void currentFunctionsAppliedCallback(bool functionsApplied, void *payload) { + UsbGadget *gadget = (UsbGadget *)payload; + gadget->mCurrentUsbFunctionsApplied = functionsApplied; +} + +Return UsbGadget::getCurrentUsbFunctions(const sp &callback) { + Return ret = callback->getCurrentUsbFunctionsCb( + mCurrentUsbFunctions, + mCurrentUsbFunctionsApplied ? Status::FUNCTIONS_APPLIED : Status::FUNCTIONS_NOT_APPLIED); + if (!ret.isOk()) + ALOGE("Call to getCurrentUsbFunctionsCb failed %s", ret.description().c_str()); + + return Void(); +} + +Return UsbGadget::getUsbSpeed(const sp &callback) { + std::string current_speed; + if (ReadFileToString(SPEED_PATH, ¤t_speed)) { + current_speed = Trim(current_speed); + ALOGI("current USB speed is %s", current_speed.c_str()); + if (current_speed == "low-speed") + mUsbSpeed = UsbSpeed::LOWSPEED; + else if (current_speed == "full-speed") + mUsbSpeed = UsbSpeed::FULLSPEED; + else if (current_speed == "high-speed") + mUsbSpeed = UsbSpeed::HIGHSPEED; + else if (current_speed == "super-speed") + mUsbSpeed = UsbSpeed::SUPERSPEED; + else if (current_speed == "super-speed-plus") + mUsbSpeed = UsbSpeed::SUPERSPEED_10Gb; + else if (current_speed == "UNKNOWN") + mUsbSpeed = UsbSpeed::UNKNOWN; + else + mUsbSpeed = UsbSpeed::RESERVED_SPEED; + } else { + ALOGE("Fail to read current speed"); + mUsbSpeed = UsbSpeed::UNKNOWN; + } + + if (callback) { + Return ret = callback->getUsbSpeedCb(mUsbSpeed); + + if (!ret.isOk()) + ALOGE("Call to getUsbSpeedCb failed %s", ret.description().c_str()); + } + + return Void(); +} + +V1_0::Status UsbGadget::tearDownGadget() { + if (resetGadget() != Status::SUCCESS) + return Status::ERROR; + + if (monitorFfs.isMonitorRunning()) { + monitorFfs.reset(); + } else { + ALOGI("mMonitor not running"); + } + return Status::SUCCESS; +} + +static V1_0::Status validateAndSetVidPid(uint64_t functions) { + V1_0::Status ret = Status::SUCCESS; + std::string vendorFunctions = getVendorFunctions(); + + switch (functions) { + case static_cast(GadgetFunction::MTP): + if (!(vendorFunctions == "user" || vendorFunctions == "")) { + ALOGE("Invalid vendorFunctions set: %s", vendorFunctions.c_str()); + ret = Status::CONFIGURATION_NOT_SUPPORTED; + } else { + ret = setVidPid("0x18d1", "0x4ee1"); + } + break; + case GadgetFunction::ADB | GadgetFunction::MTP: + if (!(vendorFunctions == "user" || vendorFunctions == "")) { + ALOGE("Invalid vendorFunctions set: %s", vendorFunctions.c_str()); + ret = Status::CONFIGURATION_NOT_SUPPORTED; + } else { + ret = setVidPid("0x18d1", "0x4ee2"); + } + break; + case static_cast(GadgetFunction::RNDIS): + case GadgetFunction::RNDIS | GadgetFunction::NCM: + if (!(vendorFunctions == "user" || vendorFunctions == "")) { + ALOGE("Invalid vendorFunctions set: %s", vendorFunctions.c_str()); + ret = Status::CONFIGURATION_NOT_SUPPORTED; + } else { + ret = setVidPid("0x18d1", "0x4ee3"); + } + break; + case GadgetFunction::ADB | GadgetFunction::RNDIS: + case GadgetFunction::ADB | GadgetFunction::RNDIS | GadgetFunction::NCM: + if (vendorFunctions == "dm") { + ret = setVidPid("0x04e8", "0x6862"); + } else { + if (!(vendorFunctions == "user" || vendorFunctions == "")) { + ALOGE("Invalid vendorFunctions set: %s", vendorFunctions.c_str()); + ret = Status::CONFIGURATION_NOT_SUPPORTED; + } else { + ret = setVidPid("0x18d1", "0x4ee4"); + } + } + break; + case static_cast(GadgetFunction::PTP): + if (!(vendorFunctions == "user" || vendorFunctions == "")) { + ALOGE("Invalid vendorFunctions set: %s", vendorFunctions.c_str()); + ret = Status::CONFIGURATION_NOT_SUPPORTED; + } else { + ret = setVidPid("0x18d1", "0x4ee5"); + } + break; + case GadgetFunction::ADB | GadgetFunction::PTP: + if (!(vendorFunctions == "user" || vendorFunctions == "")) { + ALOGE("Invalid vendorFunctions set: %s", vendorFunctions.c_str()); + ret = Status::CONFIGURATION_NOT_SUPPORTED; + } else { + ret = setVidPid("0x18d1", "0x4ee6"); + } + break; + case static_cast(GadgetFunction::ADB): + if (vendorFunctions == "dm") { + ret = setVidPid("0x04e8", "0x6862"); + } else if (vendorFunctions == "etr_miu") { + ret = setVidPid("0x18d1", "0x4ee2"); + } else if (vendorFunctions == "uwb_acm"){ + ret = setVidPid("0x18d1", "0x4ee2"); + } else { + if (!(vendorFunctions == "user" || vendorFunctions == "")) { + ALOGE("Invalid vendorFunctions set: %s", vendorFunctions.c_str()); + ret = Status::CONFIGURATION_NOT_SUPPORTED; + } else { + ret = setVidPid("0x18d1", "0x4ee7"); + } + } + break; + case static_cast(GadgetFunction::MIDI): + if (!(vendorFunctions == "user" || vendorFunctions == "")) { + ALOGE("Invalid vendorFunctions set: %s", vendorFunctions.c_str()); + ret = Status::CONFIGURATION_NOT_SUPPORTED; + } else { + ret = setVidPid("0x18d1", "0x4ee8"); + } + break; + case GadgetFunction::ADB | GadgetFunction::MIDI: + if (!(vendorFunctions == "user" || vendorFunctions == "")) { + ALOGE("Invalid vendorFunctions set: %s", vendorFunctions.c_str()); + ret = Status::CONFIGURATION_NOT_SUPPORTED; + } else { + ret = setVidPid("0x18d1", "0x4ee9"); + } + break; + case static_cast(GadgetFunction::ACCESSORY): + if (!(vendorFunctions == "user" || vendorFunctions == "")) + ALOGE("Invalid vendorFunctions set: %s", vendorFunctions.c_str()); + ret = setVidPid("0x18d1", "0x2d00"); + break; + case GadgetFunction::ADB | GadgetFunction::ACCESSORY: + if (!(vendorFunctions == "user" || vendorFunctions == "")) + ALOGE("Invalid vendorFunctions set: %s", vendorFunctions.c_str()); + ret = setVidPid("0x18d1", "0x2d01"); + break; + case static_cast(GadgetFunction::AUDIO_SOURCE): + if (!(vendorFunctions == "user" || vendorFunctions == "")) + ALOGE("Invalid vendorFunctions set: %s", vendorFunctions.c_str()); + ret = setVidPid("0x18d1", "0x2d02"); + break; + case GadgetFunction::ADB | GadgetFunction::AUDIO_SOURCE: + if (!(vendorFunctions == "user" || vendorFunctions == "")) + ALOGE("Invalid vendorFunctions set: %s", vendorFunctions.c_str()); + ret = setVidPid("0x18d1", "0x2d03"); + break; + case GadgetFunction::ACCESSORY | GadgetFunction::AUDIO_SOURCE: + if (!(vendorFunctions == "user" || vendorFunctions == "")) + ALOGE("Invalid vendorFunctions set: %s", vendorFunctions.c_str()); + ret = setVidPid("0x18d1", "0x2d04"); + break; + case GadgetFunction::ADB | GadgetFunction::ACCESSORY | GadgetFunction::AUDIO_SOURCE: + if (!(vendorFunctions == "user" || vendorFunctions == "")) + ALOGE("Invalid vendorFunctions set: %s", vendorFunctions.c_str()); + ret = setVidPid("0x18d1", "0x2d05"); + break; + case static_cast(GadgetFunction::NCM): + if (!(vendorFunctions == "user" || vendorFunctions == "")) + ALOGE("Invalid vendorFunctions set: %s", vendorFunctions.c_str()); + ret = setVidPid("0x18d1", "0x4eeb"); + break; + case GadgetFunction::ADB | GadgetFunction::NCM: + if (!(vendorFunctions == "user" || vendorFunctions == "")) + ALOGE("Invalid vendorFunctions set: %s", vendorFunctions.c_str()); + ret = setVidPid("0x18d1", "0x4eec"); + break; + default: + ALOGE("Combination not supported"); + ret = Status::CONFIGURATION_NOT_SUPPORTED; + } + return ret; +} + +Return UsbGadget::reset() { + ALOGI("USB Gadget reset"); + + if (!WriteStringToFile("none", PULLUP_PATH)) { + ALOGI("Gadget cannot be pulled down"); + return Status::ERROR; + } + + usleep(kDisconnectWaitUs); + + if (!WriteStringToFile(kGadgetName, PULLUP_PATH)) { + ALOGI("Gadget cannot be pulled up"); + return Status::ERROR; + } + + return Status::SUCCESS; +} + +V1_0::Status UsbGadget::setupFunctions(uint64_t functions, + const sp &callback, + uint64_t timeout) { + bool ffsEnabled = false; + int i = 0; + + if (addGenericAndroidFunctions(&monitorFfs, functions, &ffsEnabled, &i) != + Status::SUCCESS) + return Status::ERROR; + + std::string vendorFunctions = getVendorFunctions(); + + if (vendorFunctions == "dm") { + ALOGI("enable usbradio debug functions"); + if ((functions & GadgetFunction::RNDIS) != 0) { + if (linkFunction("acm.gs6", i++)) + return Status::ERROR; + if (linkFunction("dm.gs7", i++)) + return Status::ERROR; + } else { + if (linkFunction("dm.gs7", i++)) + return Status::ERROR; + if (linkFunction("acm.gs6", i++)) + return Status::ERROR; + } + } else if (vendorFunctions == "etr_miu") { + ALOGI("enable etr_miu functions"); + if (linkFunction("etr_miu.gs11", i++)) + return Status::ERROR; + } else if (vendorFunctions == "uwb_acm") { + ALOGI("enable uwb acm function"); + if (linkFunction("acm.uwb0", i++)) + return Status::ERROR; + } + + if ((functions & GadgetFunction::ADB) != 0) { + ffsEnabled = true; + if (addAdb(&monitorFfs, &i) != Status::SUCCESS) + return Status::ERROR; + } + + if ((functions & GadgetFunction::NCM) != 0) { + ALOGI("setCurrentUsbFunctions ncm"); + if (linkFunction("ncm.gs9", i++)) + return Status::ERROR; + } + + // Pull up the gadget right away when there are no ffs functions. + if (!ffsEnabled) { + if (!WriteStringToFile(kGadgetName, PULLUP_PATH)) + return Status::ERROR; + mCurrentUsbFunctionsApplied = true; + if (callback) + callback->setCurrentUsbFunctionsCb(functions, Status::SUCCESS); + return Status::SUCCESS; + } + + monitorFfs.registerFunctionsAppliedCallback(¤tFunctionsAppliedCallback, this); + // Monitors the ffs paths to pull up the gadget when descriptors are written. + // Also takes of the pulling up the gadget again if the userspace process + // dies and restarts. + monitorFfs.startMonitor(); + + if (kDebug) + ALOGI("Mainthread in Cv"); + + if (callback) { + bool pullup = monitorFfs.waitForPullUp(timeout); + Return ret = callback->setCurrentUsbFunctionsCb( + functions, pullup ? Status::SUCCESS : Status::ERROR); + if (!ret.isOk()) + ALOGE("setCurrentUsbFunctionsCb error %s", ret.description().c_str()); + } + + return Status::SUCCESS; +} + +Return UsbGadget::setCurrentUsbFunctions(uint64_t functions, + const sp &callback, + uint64_t timeout) { + std::unique_lock lk(mLockSetCurrentFunction); + + mCurrentUsbFunctions = functions; + mCurrentUsbFunctionsApplied = false; + + // Unlink the gadget and stop the monitor if running. + V1_0::Status status = tearDownGadget(); + if (status != Status::SUCCESS) { + goto error; + } + + ALOGI("Returned from tearDown gadget"); + + // Leave the gadget pulled down to give time for the host to sense disconnect. + usleep(kDisconnectWaitUs); + + if (functions == static_cast(GadgetFunction::NONE)) { + if (callback == NULL) + return Void(); + Return ret = callback->setCurrentUsbFunctionsCb(functions, Status::SUCCESS); + if (!ret.isOk()) + ALOGE("Error while calling setCurrentUsbFunctionsCb %s", ret.description().c_str()); + return Void(); + } + + status = validateAndSetVidPid(functions); + + if (status != Status::SUCCESS) { + goto error; + } + + status = setupFunctions(functions, callback, timeout); + if (status != Status::SUCCESS) { + goto error; + } + + if (functions & GadgetFunction::NCM) { + SetProperty("vendor.usb.dwc3_irq", "big"); + } else { + SetProperty("vendor.usb.dwc3_irq", "medium"); + } + + ALOGI("Usb Gadget setcurrent functions called successfully"); + return Void(); + +error: + ALOGI("Usb Gadget setcurrent functions failed"); + if (callback == NULL) + return Void(); + Return ret = callback->setCurrentUsbFunctionsCb(functions, status); + if (!ret.isOk()) + ALOGE("Error while calling setCurrentUsbFunctionsCb %s", ret.description().c_str()); + return Void(); +} +} // namespace implementation +} // namespace V1_2 +} // namespace gadget +} // namespace usb +} // namespace hardware +} // namespace android diff --git a/hidl/usb/UsbGadget.h b/hidl/usb/UsbGadget.h new file mode 100644 index 00000000..2669f8da --- /dev/null +++ b/hidl/usb/UsbGadget.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2020 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace android { +namespace hardware { +namespace usb { +namespace gadget { +namespace V1_2 { +namespace implementation { + +using ::android::sp; +using ::android::base::GetProperty; +using ::android::base::SetProperty; +using ::android::base::unique_fd; +using ::android::base::ReadFileToString; +using ::android::base::Trim; +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::google::pixel::usb::addAdb; +using ::android::hardware::google::pixel::usb::addEpollFd; +using ::android::hardware::google::pixel::usb::getVendorFunctions; +using ::android::hardware::google::pixel::usb::kDebug; +using ::android::hardware::google::pixel::usb::kDisconnectWaitUs; +using ::android::hardware::google::pixel::usb::linkFunction; +using ::android::hardware::google::pixel::usb::MonitorFfs; +using ::android::hardware::google::pixel::usb::resetGadget; +using ::android::hardware::google::pixel::usb::setVidPid; +using ::android::hardware::google::pixel::usb::unlinkFunctions; +using ::android::hardware::usb::gadget::V1_0::Status; +using ::android::hardware::usb::gadget::V1_0::IUsbGadgetCallback; +using ::android::hardware::usb::gadget::V1_2::IUsbGadget; +using ::android::hardware::usb::gadget::V1_2::GadgetFunction; +using ::std::string; + +constexpr char kGadgetName[] = "11110000.dwc3"; +#ifndef UDC_PATH +#define UDC_PATH "/sys/class/udc/11110000.dwc3/" +#endif +static MonitorFfs monitorFfs(kGadgetName); + +#define SPEED_PATH UDC_PATH "current_speed" + +struct UsbGadget : public IUsbGadget { + UsbGadget(); + + // Makes sure that only one request is processed at a time. + std::mutex mLockSetCurrentFunction; + uint64_t mCurrentUsbFunctions; + bool mCurrentUsbFunctionsApplied; + UsbSpeed mUsbSpeed; + + Return setCurrentUsbFunctions(uint64_t functions, + const sp &callback, + uint64_t timeout) override; + + Return getCurrentUsbFunctions(const sp &callback) override; + + Return reset() override; + + Return getUsbSpeed(const sp &callback) override; + + private: + Status tearDownGadget(); + Status setupFunctions(uint64_t functions, const sp &callback, + uint64_t timeout); +}; + +} // namespace implementation +} // namespace V1_2 +} // namespace gadget +} // namespace usb +} // namespace hardware +} // namespace android diff --git a/hidl/usb/android.hardware.usb.gadget@1.2-service.gs101.xml b/hidl/usb/android.hardware.usb.gadget@1.2-service.gs101.xml new file mode 100644 index 00000000..8557f6ff --- /dev/null +++ b/hidl/usb/android.hardware.usb.gadget@1.2-service.gs101.xml @@ -0,0 +1,11 @@ + + + android.hardware.usb.gadget + hwbinder + 1.2 + + IUsbGadget + default + + + diff --git a/hidl/usb/android.hardware.usb@1.3-service.gs101.rc b/hidl/usb/android.hardware.usb@1.3-service.gs101.rc new file mode 100644 index 00000000..4112b4ec --- /dev/null +++ b/hidl/usb/android.hardware.usb@1.3-service.gs101.rc @@ -0,0 +1,99 @@ +service vendor.usb-hal-1-3 /vendor/bin/hw/android.hardware.usb@1.3-service.gs101 + class hal + user system + group system shell mtp wakelock + capabilities WAKE_ALARM BLOCK_SUSPEND + +on post-fs + 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 + chown root system /sys/devices/platform/10d50000.hsi2c/i2c-5/i2c-max77759tcpc/contaminant_detection + chown root system /sys/devices/platform/10d50000.hsi2c/i2c-6/i2c-max77759tcpc/contaminant_detection + chown root system /sys/devices/platform/11110000.usb/dwc3_exynos_otg_b_sess + chown root system /sys/devices/platform/11110000.usb/dwc3_exynos_otg_id + chown root system /sys/devices/platform/11110000.usb/usb_data_enabled + chmod 664 /sys/class/typec/port0/power_role + chmod 664 /sys/class/typec/port0/data_role + chmod 664 /sys/class/typec/port0/port_type + chmod 664 /sys/devices/platform/11110000.usb/dwc3_exynos_otg_b_sess + chmod 664 /sys/devices/platform/11110000.usb/dwc3_exynos_otg_id + chmod 664 /sys/devices/platform/11110000.usb/usb_data_enabled + +on property:vendor.usb.functions.ready=1 + chown system system /config/usb_gadget/ + chown system system /config/usb_gadget/g1 + chown system system /config/usb_gadget/g1/UDC + chown system system /config/usb_gadget/g1/bDeviceClass + chown system system /config/usb_gadget/g1/bDeviceProtocol + chown system system /config/usb_gadget/g1/bDeviceSubClass + chown system system /config/usb_gadget/g1/bMaxPacketSize0 + chown system system /config/usb_gadget/g1/bcdDevice + chown system system /config/usb_gadget/g1/bcdUSB + chown system system /config/usb_gadget/g1/configs + chown system system /config/usb_gadget/g1/configs/b.1 + chown system system /config/usb_gadget/g1/configs/b.1/MaxPower + chown system system /config/usb_gadget/g1/configs/b.1/bmAttributes + chown system system /config/usb_gadget/g1/configs/b.1/strings + chown system system /config/usb_gadget/g1/functions + chown system system /config/usb_gadget/g1/functions/accessory.gs2 + chown system system /config/usb_gadget/g1/functions/acm.gs6 + chown system system /config/usb_gadget/g1/functions/acm.gs6/port_num + chown system system /config/usb_gadget/g1/functions/acm.uwb0 + chown system system /config/usb_gadget/g1/functions/acm.uwb0/port_num + chown system system /config/usb_gadget/g1/functions/audio_source.gs3 + chown system system /config/usb_gadget/g1/functions/dm.gs7 + chown system system /config/usb_gadget/g1/functions/ffs.adb + chown system system /config/usb_gadget/g1/functions/ffs.mtp + chown system system /config/usb_gadget/g1/functions/ffs.ptp + chown system system /config/usb_gadget/g1/functions/midi.gs5 + chown system system /config/usb_gadget/g1/functions/midi.gs5/buflen + chown system system /config/usb_gadget/g1/functions/midi.gs5/id + chown system system /config/usb_gadget/g1/functions/midi.gs5/in_ports + chown system system /config/usb_gadget/g1/functions/midi.gs5/index + chown system system /config/usb_gadget/g1/functions/midi.gs5/out_ports + chown system system /config/usb_gadget/g1/functions/midi.gs5/qlen + chown system system /config/usb_gadget/g1/functions/mtp.gs0 + chown system system /config/usb_gadget/g1/functions/mtp.gs0/os_desc + chown system system /config/usb_gadget/g1/functions/mtp.gs0/os_desc/interface.MTP + chown system system /config/usb_gadget/g1/functions/mtp.gs0/os_desc/interface.MTP/compatible_id + chown system system /config/usb_gadget/g1/functions/mtp.gs0/os_desc/interface.MTP/sub_compatible_id + chown system system /config/usb_gadget/g1/functions/ncm.gs9 + chown system system /config/usb_gadget/g1/functions/ncm.gs9/dev_addr + chown system system /config/usb_gadget/g1/functions/ncm.gs9/host_addr + chown system system /config/usb_gadget/g1/functions/ncm.gs9/ifname + chown system system /config/usb_gadget/g1/functions/ncm.gs9/os_desc + chown system system /config/usb_gadget/g1/functions/ncm.gs9/os_desc/interface.ncm + chown system system /config/usb_gadget/g1/functions/ncm.gs9/os_desc/interface.ncm/compatible_id + chown system system /config/usb_gadget/g1/functions/ncm.gs9/os_desc/interface.ncm/sub_compatible_id + chown system system /config/usb_gadget/g1/functions/ncm.gs9/qmult + chown system system /config/usb_gadget/g1/functions/ptp.gs1 + chown system system /config/usb_gadget/g1/functions/ptp.gs1/os_desc + chown system system /config/usb_gadget/g1/functions/ptp.gs1/os_desc/interface.MTP + chown system system /config/usb_gadget/g1/functions/ptp.gs1/os_desc/interface.MTP/compatible_id + chown system system /config/usb_gadget/g1/functions/ptp.gs1/os_desc/interface.MTP/sub_compatible_id + chown system system /config/usb_gadget/g1/functions/rndis.gs4 + chown system system /config/usb_gadget/g1/functions/rndis.gs4/class + chown system system /config/usb_gadget/g1/functions/rndis.gs4/dev_addr + chown system system /config/usb_gadget/g1/functions/rndis.gs4/host_addr + chown system system /config/usb_gadget/g1/functions/rndis.gs4/ifname + chown system system /config/usb_gadget/g1/functions/rndis.gs4/os_desc + chown system system /config/usb_gadget/g1/functions/rndis.gs4/os_desc/interface.rndis + chown system system /config/usb_gadget/g1/functions/rndis.gs4/os_desc/interface.rndis/compatible_id + chown system system /config/usb_gadget/g1/functions/rndis.gs4/os_desc/interface.rndis/sub_compatible_id + chown system system /config/usb_gadget/g1/functions/rndis.gs4/protocol + chown system system /config/usb_gadget/g1/functions/rndis.gs4/qmult + chown system system /config/usb_gadget/g1/functions/rndis.gs4/subclass + chown system system /config/usb_gadget/g1/idProduct + chown system system /config/usb_gadget/g1/idVendor + chown system system /config/usb_gadget/g1/max_speed + chown system system /config/usb_gadget/g1/os_desc + chown system system /config/usb_gadget/g1/os_desc/b.1 + chown system system /config/usb_gadget/g1/os_desc/b_vendor_code + chown system system /config/usb_gadget/g1/os_desc/qw_sign + chown system system /config/usb_gadget/g1/os_desc/use + chown system system /config/usb_gadget/g1/strings + chown system system /config/usb_gadget/g1/strings/0x409 + chown system system /config/usb_gadget/g1/strings/0x409/manufacturer + chown system system /config/usb_gadget/g1/strings/0x409/product + chown system system /config/usb_gadget/g1/strings/0x409/serialnumber diff --git a/hidl/usb/android.hardware.usb@1.3-service.gs101.xml b/hidl/usb/android.hardware.usb@1.3-service.gs101.xml new file mode 100644 index 00000000..cd542687 --- /dev/null +++ b/hidl/usb/android.hardware.usb@1.3-service.gs101.xml @@ -0,0 +1,12 @@ + + + android.hardware.usb + hwbinder + 1.3 + + IUsb + default + + + + diff --git a/hidl/usb/service.cpp b/hidl/usb/service.cpp new file mode 100644 index 00000000..a4a30d2a --- /dev/null +++ b/hidl/usb/service.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2018 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.gs101" + +#include +#include "Usb.h" +#include "UsbGadget.h" + +using android::sp; + +// libhwbinder: +using android::hardware::configureRpcThreadpool; +using android::hardware::joinRpcThreadpool; + +// Generated HIDL files +using android::hardware::usb::gadget::V1_2::IUsbGadget; +using android::hardware::usb::gadget::V1_2::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(); + + 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."); + joinRpcThreadpool(); + // Under noraml cases, execution will not reach this line. + ALOGI("USB HAL failed to join thread pool."); + return 1; +}