From ae3427232a978d11cd352ec7dc888dd081beade1 Mon Sep 17 00:00:00 2001 From: Ruchit Marathe Date: Mon, 19 Sep 2022 19:53:19 +0530 Subject: [PATCH] sm7125: Update usb impl and drop hardware samsung dependency --- aidl/usb/Android.bp | 35 + aidl/usb/Usb.cpp | 801 ++++++++++++++++++ aidl/usb/Usb.h | 87 ++ ...android.hardware.usb-service.exynos9810.rc | 15 + ...ndroid.hardware.usb-service.exynos9810.xml | 10 + aidl/usb/service.cpp | 35 + common.mk | 2 +- sepolicy/vendor/file_contexts | 1 + 8 files changed, 985 insertions(+), 1 deletion(-) create mode 100755 aidl/usb/Android.bp create mode 100755 aidl/usb/Usb.cpp create mode 100755 aidl/usb/Usb.h create mode 100755 aidl/usb/android.hardware.usb-service.exynos9810.rc create mode 100755 aidl/usb/android.hardware.usb-service.exynos9810.xml create mode 100755 aidl/usb/service.cpp diff --git a/aidl/usb/Android.bp b/aidl/usb/Android.bp new file mode 100755 index 0000000..a8622b3 --- /dev/null +++ b/aidl/usb/Android.bp @@ -0,0 +1,35 @@ +// +// 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.exynos9810", + relative_install_path: "hw", + init_rc: ["android.hardware.usb-service.exynos9810.rc"], + vintf_fragments: ["android.hardware.usb-service.exynos9810.xml"], + vendor: true, + srcs: [ + "service.cpp", + "Usb.cpp", + ], + shared_libs: [ + "android.hardware.usb-V1-ndk", + "libbase", + "libbinder_ndk", + "libcutils", + "liblog", + "libutils", + ], +} diff --git a/aidl/usb/Usb.cpp b/aidl/usb/Usb.cpp new file mode 100755 index 0000000..d39d7ca --- /dev/null +++ b/aidl/usb/Usb.cpp @@ -0,0 +1,801 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2022 The LineageOS 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 + +#include "Usb.h" + +using android::base::GetProperty; +using android::base::Trim; + +namespace aidl { +namespace android { +namespace hardware { +namespace usb { + +constexpr char kTypecPath[] = "/sys/class/typec/"; +constexpr char kDataRoleNode[] = "/data_role"; +constexpr char kPowerRoleNode[] = "/power_role"; + +// Set by the signal handler to destroy the thread +volatile bool destroyThread; + +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; +} + +void queryVersionHelper(android::hardware::usb::Usb *usb, + std::vector *currentPortStatus); + +ScopedAStatus Usb::enableUsbData(const string& in_portName, bool in_enable, int64_t in_transactionId) { + std::vector currentPortStatus; + bool result = true; + + pthread_mutex_lock(&mLock); + if (!WriteStringToFile(in_enable ? "1" : "0", USB_DATA_PATH)) { + ALOGE("Not able to turn %s usb connection notification", in_enable ? "on" : "off"); + result = false; + } + + if (mCallback != NULL) { + ScopedAStatus ret = mCallback->notifyEnableUsbDataStatus( + in_portName, true, 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) { + + pthread_mutex_lock(&mLock); + if (mCallback != NULL) { + ScopedAStatus ret = mCallback->notifyEnableUsbDataWhileDockedStatus( + in_portName, Status::NOT_SUPPORTED, in_transactionId); + if (!ret.isOk()) + ALOGE("notifyEnableUsbDataWhileDockedStatus 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::resetUsbPort(const string& in_portName, int64_t in_transactionId) { + + pthread_mutex_lock(&mLock); + if (mCallback != NULL) { + ScopedAStatus ret = mCallback->notifyResetUsbPortStatus( + in_portName, Status::NOT_SUPPORTED, in_transactionId); + if (!ret.isOk()) + ALOGE("notifyResetUsbPortStatus error %s", ret.getDescription().c_str()); + } else { + ALOGE("Not notifying the userspace. Callback is not set"); + } + pthread_mutex_unlock(&mLock); + + return ScopedAStatus::ok(); +} + +Status queryMoistureDetectionStatus(android::hardware::usb::Usb *usb, + std::vector *currentPortStatus) { + string enabled, status, path, DetectedPath; + + for (int i = 0; i < currentPortStatus->size(); i++) { + (*currentPortStatus)[i].supportedContaminantProtectionModes + .push_back(ContaminantProtectionMode::NONE); + (*currentPortStatus)[i].contaminantProtectionStatus + = ContaminantProtectionStatus::NONE; + (*currentPortStatus)[i].contaminantDetectionStatus + = ContaminantDetectionStatus::DISABLED; + (*currentPortStatus)[i].supportsEnableContaminantPresenceDetection = true; + (*currentPortStatus)[i].supportsEnableContaminantPresenceProtection = false; + + if (usb->mMoistureDetectionEnabled) { + if (readFile(CONTAMINANT_DETECTION_PATH, &status)) { + ALOGE("Failed to open %s", CONTAMINANT_DETECTION_PATH); + return Status::ERROR; + } + if (status == "1") { + (*currentPortStatus)[i].contaminantDetectionStatus = + ContaminantDetectionStatus::DETECTED; + (*currentPortStatus)[i].contaminantProtectionStatus = + ContaminantProtectionStatus::FORCE_SINK; + } else + (*currentPortStatus)[i].contaminantDetectionStatus = + ContaminantDetectionStatus::NOT_DETECTED; + } + + ALOGI("ContaminantDetectionStatus[%d]:%hhd ContaminantProtectionStatus[%d]:%hhd", + i, (*currentPortStatus)[i].contaminantDetectionStatus, + i, (*currentPortStatus)[i].contaminantProtectionStatus); + } + + return Status::SUCCESS; +} + +string appendRoleNodeHelper(const string &portName, PortRole::Tag tag) { + string node(kTypecPath + portName); + + switch (tag) { + case PortRole::dataRole: + return node + kDataRoleNode; + case PortRole::powerRole: + return node + kPowerRoleNode; + 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), + mMoistureDetectionEnabled(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(); +} + +ScopedAStatus Usb::limitPowerTransfer(const string& in_portName, bool /*in_limit*/, + int64_t in_transactionId) { + std::vector currentPortStatus; + + pthread_mutex_lock(&mLock); + if (mCallback != NULL && in_transactionId >= 0) { + ScopedAStatus ret = mCallback->notifyLimitPowerTransferStatus( + in_portName, false, 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); + + return ScopedAStatus::ok(); +} + +Status getAccessoryConnected(const string &portName, string *accessory) { + string filename = kTypecPath + 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; + + // Mode + + if (currentRole->getTag() == PortRole::powerRole) { + filename = kTypecPath + portName + kPowerRoleNode; + currentRole->set(PortPowerRole::NONE); + } else if (currentRole->getTag() == PortRole::dataRole) { + filename = kTypecPath + portName + kDataRoleNode; + currentRole->set(PortDataRole::NONE); + } else if (currentRole->getTag() == PortRole::mode) { + filename = kTypecPath + portName + kDataRoleNode; + 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 = kTypecPath + portName + "-partner/supports_usb_power_delivery"; + string supportsPD; + + if (ReadFileToString(filename, &supportsPD)) { + supportsPD = Trim(supportsPD); + if (supportsPD == "yes") { + return true; + } + } + + return false; +} + +Status getPortStatusHelper(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); + (*currentPortStatus)[i].usbDataStatus.push_back(UsbDataStatus::ENABLED); + + 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, 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(currentPortStatus); + queryMoistureDetectionStatus(usb, 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) { + std::vector currentPortStatus; + std::string disable = GetProperty(DISABLE_CONTAMINANT_DETECTION, ""); + + if (disable != "true") + mMoistureDetectionEnabled = in_enable; + + pthread_mutex_lock(&mLock); + if (mCallback != NULL) { + ScopedAStatus ret = mCallback->notifyContaminantEnabledStatus( + in_portName, false, Status::SUCCESS, in_transactionId); + if (!ret.isOk()) + ALOGE("enableContaminantPresenceDetection 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, "CCIC=WATER", strlen("CCIC=WATER")) || + !strncmp(cp, "CCIC=DRY", strlen("CCIC=DRY"))) { + 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(kTypecPath + + 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; + + uevent_fd = uevent_open_socket(UEVENT_MAX_EVENTS * UEVENT_MSG_LEN, 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(UEVENT_MAX_EVENTS); + 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[UEVENT_MAX_EVENTS]; + + nevents = epoll_wait(epoll_fd, events, UEVENT_MAX_EVENTS, -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/aidl/usb/Usb.h b/aidl/usb/Usb.h new file mode 100755 index 0000000..a0794f3 --- /dev/null +++ b/aidl/usb/Usb.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2022 The LineageOS 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 +#define UEVENT_MAX_EVENTS 64 +// 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 +#define USB_DATA_PATH "/sys/devices/virtual/usb_notify/usb_control/usb_data_enabled" +#define CONTAMINANT_DETECTION_PATH "/sys/devices/virtual/sec/ccic/water" +#define DISABLE_CONTAMINANT_DETECTION "vendor.usb.contaminantdisable" + +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; + +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 std::string& in_portName, bool in_limit, + int64_t in_transactionId)override; + ScopedAStatus resetUsbPort(const std::string& in_portName, + int64_t in_transactionId)override; + + shared_ptr 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; + bool mMoistureDetectionEnabled; + private: + pthread_t mPoll; +}; + +} // namespace usb +} // namespace hardware +} // namespace android +} // aidl diff --git a/aidl/usb/android.hardware.usb-service.exynos9810.rc b/aidl/usb/android.hardware.usb-service.exynos9810.rc new file mode 100755 index 0000000..28e6580 --- /dev/null +++ b/aidl/usb/android.hardware.usb-service.exynos9810.rc @@ -0,0 +1,15 @@ +service vendor.usb_default /vendor/bin/hw/android.hardware.usb-service.exynos9810 + class hal + user system + group system + +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/virtual/sec/ccic/water + chown root system /sys/devices/virtual/usb_notify/usb_control/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/virtual/usb_notify/usb_control/usb_data_enabled diff --git a/aidl/usb/android.hardware.usb-service.exynos9810.xml b/aidl/usb/android.hardware.usb-service.exynos9810.xml new file mode 100755 index 0000000..6088194 --- /dev/null +++ b/aidl/usb/android.hardware.usb-service.exynos9810.xml @@ -0,0 +1,10 @@ + + + android.hardware.usb + 1 + + IUsb + default + + + diff --git a/aidl/usb/service.cpp b/aidl/usb/service.cpp new file mode 100755 index 0000000..398458a --- /dev/null +++ b/aidl/usb/service.cpp @@ -0,0 +1,35 @@ +/* + * 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 "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); + + ABinderProcess_joinThreadPool(); + return -1; // Should never be reached +} diff --git a/common.mk b/common.mk index 44a1cfd..d6823ae 100644 --- a/common.mk +++ b/common.mk @@ -389,7 +389,7 @@ PRODUCT_PACKAGES += \ # USB PRODUCT_PACKAGES += \ - android.hardware.usb@1.3-service-qti + android.hardware.usb@1.3-service.exynos9810 # Vibrator PRODUCT_PACKAGES += \ diff --git a/sepolicy/vendor/file_contexts b/sepolicy/vendor/file_contexts index 8da570b..f90b9a7 100644 --- a/sepolicy/vendor/file_contexts +++ b/sepolicy/vendor/file_contexts @@ -68,6 +68,7 @@ /(vendor|system/vendor)/bin/hw/android\.hardware\.light(@[0-9].[0-9])?-service\.exynos9810 u:object_r:hal_light_default_exec:s0 /(vendor|system/vendor)/bin/hw/android\.hardware\.vibrator(@[0-9].[0-9])?-service\.exynos9810 u:object_r:hal_vibrator_default_exec:s0 /(vendor|system/vendor)/bin/hw/android\.hardware\.power(@[0-9]\.[0-9])?-service\.exynos9810-libperfmgr u:object_r:hal_power_default_exec:s0 +/(vendor|system/vendor)/bin/hw/android\.hardware\.usb@[0-9]\.[0-9]-service\.exynos9810 u:object_r:hal_usb_default_exec:s0 /(vendor|system/vendor)/bin/hw/android\.hardware\.sensors-service(\.exynos9810-multihal)? u:object_r:hal_sensors_default_exec:s0 /(vendor|system/vendor)/bin/hw/android\.hardware\.memtrack-service.exynos9810 u:object_r:hal_memtrack_default_exec:s0