From 81ea748afa5cfd9685b429601262015deaa63151 Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Fri, 20 Jan 2017 06:05:58 -0800 Subject: [PATCH] USB: HIDL: default implentation for the type-c interface The default implementation should take care of legacy devices that lauched before O. BUG: 31015010 Test: Manually tested on Angler and sailfish for type-c role switches. Change-Id: I8762598f4f82da5b2fe8a87aacc30728eb7921f5 --- usb/1.0/default/Android.mk | 21 + usb/1.0/default/Usb.cpp | 428 ++++++++++++++++++ usb/1.0/default/Usb.h | 50 ++ .../android.hardware.usb@1.0-service.rc | 4 + usb/1.0/default/service.cpp | 40 ++ 5 files changed, 543 insertions(+) create mode 100644 usb/1.0/default/Android.mk create mode 100644 usb/1.0/default/Usb.cpp create mode 100644 usb/1.0/default/Usb.h create mode 100644 usb/1.0/default/android.hardware.usb@1.0-service.rc create mode 100644 usb/1.0/default/service.cpp diff --git a/usb/1.0/default/Android.mk b/usb/1.0/default/Android.mk new file mode 100644 index 0000000000..09d7ce7390 --- /dev/null +++ b/usb/1.0/default/Android.mk @@ -0,0 +1,21 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE_RELATIVE_PATH := hw +LOCAL_MODULE := android.hardware.usb@1.0-service +LOCAL_INIT_RC := android.hardware.usb@1.0-service.rc +LOCAL_SRC_FILES := \ + service.cpp \ + Usb.cpp + +LOCAL_SHARED_LIBRARIES := \ + libcutils \ + libhidlbase \ + libhidltransport \ + liblog \ + libhwbinder \ + libutils \ + libhardware \ + android.hardware.usb@1.0 \ + +include $(BUILD_EXECUTABLE) diff --git a/usb/1.0/default/Usb.cpp b/usb/1.0/default/Usb.cpp new file mode 100644 index 0000000000..f46ff66534 --- /dev/null +++ b/usb/1.0/default/Usb.cpp @@ -0,0 +1,428 @@ +/* + * Copyright (C) 2016 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 +#include +#include + +#include +#include +#include +#include + +#include "Usb.h" + +namespace android { +namespace hardware { +namespace usb { +namespace V1_0 { +namespace implementation { + +int32_t readFile(std::string filename, std::string& contents) { + std::ifstream file(filename); + + if (file.is_open()) { + getline(file, contents); + file.close(); + return 0; + } + return -1; +} + +std::string appendRoleNodeHelper(const std::string portName, PortRoleType type) { + std::string node("/sys/class/dual_role_usb/" + portName); + + switch(type) { + case PortRoleType::DATA_ROLE: + return node + "/data_role"; + case PortRoleType::POWER_ROLE: + return node + "/power_role"; + default: + return node + "/mode"; + } +} + +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::UFP)) + return "ufp"; + if (role.role == static_cast (PortMode::DFP)) + return "dfp"; + } + return "none"; +} + +Return Usb::switchRole(const hidl_string& portName, + const PortRole& newRole) { + std::string filename = appendRoleNodeHelper(std::string(portName.c_str()), + newRole.type); + std::ofstream file(filename); + std::string written; + + ALOGI("filename write: %s role:%d", filename.c_str(), newRole.role); + + if (file.is_open()) { + file << convertRoletoString(newRole).c_str(); + file.close(); + if (!readFile(filename, written)) { + ALOGI("written: %s", written.c_str()); + if (written == convertRoletoString(newRole)) { + ALOGI("Role switch successfull"); + Return ret = + mCallback->notifyRoleSwitchStatus(portName, newRole, + Status::SUCCESS); + if (!ret.isOk()) + ALOGE("RoleSwitchStatus error %s", + ret.description().c_str()); + } + } + } + + Return ret = mCallback->notifyRoleSwitchStatus(portName, newRole, Status::ERROR); + if (!ret.isOk()) + ALOGE("RoleSwitchStatus error %s", ret.description().c_str()); + + return Void(); +} + +Status getCurrentRoleHelper(std::string portName, + PortRoleType type, uint32_t ¤tRole) { + std::string filename; + std::string roleName; + + if (type == PortRoleType::POWER_ROLE) { + filename = "/sys/class/dual_role_usb/" + + portName + "/power_role"; + currentRole = static_cast(PortPowerRole::NONE); + } else if (type == PortRoleType::DATA_ROLE) { + filename = "/sys/class/dual_role_usb/" + + portName + "/data_role"; + currentRole = static_cast (PortDataRole::NONE); + } else if (type == PortRoleType::MODE) { + filename = "/sys/class/dual_role_usb/" + + portName + "/mode"; + currentRole = static_cast (PortMode::NONE); + } + + if (readFile(filename, roleName)) { + ALOGE("getCurrentRole: Failed to open filesystem node"); + return Status::ERROR; + } + + if (roleName == "dfp") + currentRole = static_cast (PortMode::DFP); + else if (roleName == "ufp") + currentRole = static_cast (PortMode::UFP); + else if (roleName == "source") + currentRole = static_cast (PortPowerRole::SOURCE); + else if (roleName == "sink") + currentRole = static_cast (PortPowerRole::SINK); + else if (roleName == "host") + currentRole = static_cast (PortDataRole::HOST); + else if (roleName == "device") + currentRole = static_cast (PortDataRole::DEVICE); + 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::vector& names) { + DIR *dp; + + dp = opendir("/sys/class/dual_role_usb"); + if (dp != NULL) + { +rescan: + int32_t ports = 0; + int32_t current = 0; + struct dirent *ep; + + while ((ep = readdir (dp))) { + if (ep->d_type == DT_LNK) { + ports++; + } + } + names.resize(ports); + rewinddir(dp); + + while ((ep = readdir (dp))) { + /* Check to see if new ports were added since the first pass. */ + if (current >= ports) { + rewinddir(dp); + goto rescan; + } + + if (ep->d_type == DT_LNK) { + names[current++] = ep->d_name; + } + } + + closedir (dp); + return Status::SUCCESS; + } + + ALOGE("Failed to open /sys/class/dual_role_usb"); + return Status::ERROR; +} + +bool canSwitchRoleHelper(const std::string portName, PortRoleType type) { + std::string filename = appendRoleNodeHelper(portName, type); + std::ofstream file(filename); + + if (file.is_open()) { + file.close(); + return true; + } + return false; +} + +Status getPortModeHelper(const std::string portName, PortMode& portMode) { + std::string filename = "/sys/class/dual_role_usb/" + + std::string(portName.c_str()) + "/supported_modes"; + std::string modes; + + if (readFile(filename, modes)) { + ALOGE("getSupportedRoles: Failed to open filesystem node"); + return Status::ERROR; + } + + if (modes == "ufp dfp") + portMode = PortMode::DRP; + else if (modes == "ufp") + portMode = PortMode::UFP; + else if (modes == "dfp") + portMode = PortMode::DFP; + else + return Status::UNRECOGNIZED_ROLE; + + return Status::SUCCESS; +} + +Status getPortStatusHelper (hidl_vec& currentPortStatus) { + std::vector names; + Status result = getTypeCPortNamesHelper(names); + + if (result == Status::SUCCESS) { + currentPortStatus.resize(names.size()); + for(std::vector::size_type i = 0; i != names.size(); i++) { + ALOGI("%s", names[i].c_str()); + currentPortStatus[i].portName = names[i]; + + uint32_t currentRole; + if (getCurrentRoleHelper(names[i], PortRoleType::POWER_ROLE, + currentRole) == Status::SUCCESS) { + currentPortStatus[i].currentPowerRole = + static_cast (currentRole); + } else { + ALOGE("Error while retreiving portNames"); + goto done; + } + + if (getCurrentRoleHelper(names[i], + PortRoleType::DATA_ROLE, currentRole) == Status::SUCCESS) { + currentPortStatus[i].currentDataRole = + static_cast (currentRole); + } else { + ALOGE("Error while retreiving current port role"); + goto done; + } + + if (getCurrentRoleHelper(names[i], PortRoleType::MODE, + currentRole) == Status::SUCCESS) { + currentPortStatus[i].currentMode = + static_cast (currentRole); + } else { + ALOGE("Error while retreiving current data role"); + goto done; + } + + currentPortStatus[i].canChangeMode = + canSwitchRoleHelper(names[i], PortRoleType::MODE); + currentPortStatus[i].canChangeDataRole = + canSwitchRoleHelper(names[i], PortRoleType::DATA_ROLE); + currentPortStatus[i].canChangePowerRole = + canSwitchRoleHelper(names[i], PortRoleType::POWER_ROLE); + + ALOGI("canChangeMode: %d canChagedata: %d canChangePower:%d", + currentPortStatus[i].canChangeMode, + currentPortStatus[i].canChangeDataRole, + currentPortStatus[i].canChangePowerRole); + + if (getPortModeHelper(names[i], currentPortStatus[i].supportedModes) + != Status::SUCCESS) { + ALOGE("Error while retrieving port modes"); + goto done; + } + } + return Status::SUCCESS; + } +done: + return Status::ERROR; +} + +Return Usb::queryPortStatus() { + hidl_vec currentPortStatus; + Status status; + + status = getPortStatusHelper(currentPortStatus); + Return ret = mCallback->notifyPortStatusChange(currentPortStatus, + status); + if (!ret.isOk()) + ALOGE("queryPortStatus error %s", ret.description().c_str()); + + return Void(); +} +struct data { + int uevent_fd; + android::hardware::usb::V1_0::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 (!strcmp(cp, "SUBSYSTEM=dual_role_usb")) { + ALOGE("uevent received %s", cp); + if (payload->usb->mCallback != NULL) { + hidl_vec currentPortStatus; + Status status = getPortStatusHelper(currentPortStatus); + Return ret = + payload->usb->mCallback->notifyPortStatusChange(currentPortStatus, status); + if (!ret.isOk()) + ALOGE("error %s", ret.description().c_str()); + } + 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_0::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 (1) { + 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); + } + } + +error: + close(uevent_fd); + + if (epoll_fd >= 0) + close(epoll_fd); + + return NULL; +} + + +Return Usb::setCallback(const sp& callback) { + + if (mCallback != NULL) { + ALOGE("Callback already registered"); + return Void(); + } + + mCallback = callback; + ALOGI("registering callback"); + + if (pthread_create(&mPoll, NULL, work, this)) { + ALOGE("pthread creation failed %d", errno); + mCallback = NULL; + return Void(); + } + + return Void(); +} + +} // namespace implementation +} // namespace V1_0 +} // namespace usb +} // namespace hardware +} // namespace android diff --git a/usb/1.0/default/Usb.h b/usb/1.0/default/Usb.h new file mode 100644 index 0000000000..cf418f4840 --- /dev/null +++ b/usb/1.0/default/Usb.h @@ -0,0 +1,50 @@ +#ifndef ANDROID_HARDWARE_USB_V1_0_USB_H +#define ANDROID_HARDWARE_USB_V1_0_USB_H + +#include +#include +#include +#include + +#ifdef LOG_TAG +#undef LOG_TAG +#endif + +#define LOG_TAG "android.hardware.usb@1.0-service" +#define UEVENT_MSG_LEN 2048 + +namespace android { +namespace hardware { +namespace usb { +namespace V1_0 { +namespace implementation { + +using ::android::hardware::usb::V1_0::IUsb; +using ::android::hardware::usb::V1_0::IUsbCallback; +using ::android::hardware::usb::V1_0::PortRole; +using ::android::hidl::base::V1_0::IBase; +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::sp; + +struct Usb : public IUsb { + Return switchRole(const hidl_string& portName, const PortRole& role) override; + Return setCallback(const sp& callback) override; + Return queryPortStatus() override; + + sp mCallback; + private: + pthread_t mPoll; +}; + +} // namespace implementation +} // namespace V1_0 +} // namespace usb +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_USB_V1_0_USB_H diff --git a/usb/1.0/default/android.hardware.usb@1.0-service.rc b/usb/1.0/default/android.hardware.usb@1.0-service.rc new file mode 100644 index 0000000000..77dfc937ee --- /dev/null +++ b/usb/1.0/default/android.hardware.usb@1.0-service.rc @@ -0,0 +1,4 @@ +service usb-hal-1-0 /system/bin/hw/android.hardware.usb@1.0-service + class hal + user system + group system diff --git a/usb/1.0/default/service.cpp b/usb/1.0/default/service.cpp new file mode 100644 index 0000000000..b4db241447 --- /dev/null +++ b/usb/1.0/default/service.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2016 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 "Usb.h" + +using android::sp; + +// libhwbinder: +using android::hardware::configureRpcThreadpool; +using android::hardware::joinRpcThreadpool; + +// Generated HIDL files +using android::hardware::usb::V1_0::IUsb; +using android::hardware::usb::V1_0::implementation::Usb; + +int main() { + const char instance[] = "usb_hal"; + + android::sp service = new Usb(); + + configureRpcThreadpool(1, true /*callerWillJoin*/); + service->registerAsService(instance); + + ALOGI("USB HAL Ready."); + joinRpcThreadpool(); +}