diff options
| author | Chris Fries <cfries@google.com> | 2017-06-27 12:02:00 -0500 |
|---|---|---|
| committer | Chris Fries <cfries@google.com> | 2017-06-27 12:02:00 -0500 |
| commit | dc2b50dfc6ee630b9021b5a576c7fa90c5316928 (patch) | |
| tree | 032ff63fb2c1c7a31b06033d701a49bfcf98c5fb /usb | |
| parent | 3578d54ef27d3fc05ffa827ae6318f4d50484224 (diff) | |
crosshatch: Reset to wahoo on master.
commit dc78ac4aaa64e427c0f5c0672dc22939a9a7b85f
Merge: 820b880 163a21c
Author: Glenn Kasten <gkasten@google.com>
Date: Tue Jun 27 15:12:54 2017 +0000
Merge "Report feature android.software.midi to enable testing" into oc-dr1-dev am: 614e203677
am: 163a21c15f
Change-Id: I18876c0ce8e0435fdcb947d475c66fe7fae57da1
Change-Id: Id08e99f0603407199020818485eb29d92d421c43
Diffstat (limited to 'usb')
| -rw-r--r-- | usb/Android.bp | 32 | ||||
| -rw-r--r-- | usb/Usb.cpp | 696 | ||||
| -rw-r--r-- | usb/Usb.h | 70 | ||||
| -rw-r--r-- | usb/android.hardware.usb@1.1-service.wahoo.rc | 12 | ||||
| -rw-r--r-- | usb/service.cpp | 52 |
5 files changed, 862 insertions, 0 deletions
diff --git a/usb/Android.bp b/usb/Android.bp new file mode 100644 index 00000000..4370d6c7 --- /dev/null +++ b/usb/Android.bp @@ -0,0 +1,32 @@ +// +// 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. +cc_binary { + name: "android.hardware.usb@1.1-service.wahoo", + relative_install_path: "hw", + init_rc: ["android.hardware.usb@1.1-service.wahoo.rc"], + srcs: ["service.cpp", "Usb.cpp"], + shared_libs: [ + "libbase", + "libhidlbase", + "libhidltransport", + "liblog", + "libutils", + "libhardware", + "android.hardware.usb@1.0", + "android.hardware.usb@1.1", + "libcutils", + ], + proprietary: true, +} diff --git a/usb/Usb.cpp b/usb/Usb.cpp new file mode 100644 index 00000000..601c70b5 --- /dev/null +++ b/usb/Usb.cpp @@ -0,0 +1,696 @@ +/* + * 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. + */ + +#define LOG_TAG "android.hardware.usb@1.1-service.wahoo" + +#include <android-base/logging.h> +#include <assert.h> +#include <chrono> +#include <dirent.h> +#include <pthread.h> +#include <regex> +#include <stdio.h> +#include <sys/types.h> +#include <thread> +#include <unistd.h> +#include <unordered_map> + +#include <cutils/uevent.h> +#include <sys/epoll.h> +#include <utils/Errors.h> +#include <utils/StrongPointer.h> + +#include "Usb.h" + +namespace android { +namespace hardware { +namespace usb { +namespace V1_1 { +namespace implementation { + +// 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; +} + +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<uint32_t>(PortPowerRole::SOURCE)) + return "source"; + else if (role.role == static_cast<uint32_t>(PortPowerRole::SINK)) + return "sink"; + } else if (role.type == PortRoleType::DATA_ROLE) { + if (role.role == static_cast<uint32_t>(PortDataRole::HOST)) return "host"; + if (role.role == static_cast<uint32_t>(PortDataRole::DEVICE)) + return "device"; + } else if (role.type == PortRoleType::MODE) { + if (role.role == static_cast<uint32_t>(PortMode_1_1::UFP)) return "sink"; + if (role.role == static_cast<uint32_t>(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 timeval tp; + +wait_again: + gettimeofday(&tp, NULL); + to.tv_sec = tp.tv_sec + PORT_TYPE_TIMEOUT; + to.tv_nsec = tp.tv_usec * 1000;; + + int err = pthread_cond_timedwait(&usb->mPartnerCV, &usb->mPartnerLock, &to); + // There are no uevent signals which implies role swap timed out. + if (err == ETIMEDOUT) { + ALOGI("uevents wait timedout"); + // Sanity check. + } else if (!usb->mPartnerUp) { + goto wait_again; + // Role switch succeeded since usb->mPartnerUp is true. + } else { + roleSwitch = true; + } + } else { + ALOGI("Role switch failed while wrting to file"); + } + pthread_mutex_unlock(&usb->mPartnerLock); + } + + if (!roleSwitch) + switchToDrp(std::string(portName.c_str())); + + return roleSwitch; +} + + + +Return<void> Usb::switchRole(const hidl_string &portName, + const PortRole &newRole) { + std::string filename = + appendRoleNodeHelper(std::string(portName.c_str()), newRole.type); + std::string written; + FILE *fp; + bool roleSwitch = false; + + if (filename == "") { + ALOGE("Fatal: invalid node type"); + return Void(); + } + + pthread_mutex_lock(&mRoleSwitchLock); + + ALOGI("filename write: %s role:%s", filename.c_str(), + convertRoletoString(newRole).c_str()); + + 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<void> 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<uint32_t>(PortPowerRole::NONE); + } else if (type == PortRoleType::DATA_ROLE) { + filename = "/sys/class/typec/" + portName + "/data_role"; + *currentRole = static_cast<uint32_t>(PortDataRole::NONE); + } else if (type == PortRoleType::MODE) { + filename = "/sys/class/typec/" + portName + "/data_role"; + *currentRole = static_cast<uint32_t>(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<uint32_t>(PortMode_1_1::AUDIO_ACCESSORY); + return Status::SUCCESS; + } else if (accessory == "debug") { + *currentRole = static_cast<uint32_t>(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<uint32_t>(PortPowerRole::SOURCE); + } else if (roleName == "sink") { + *currentRole = static_cast<uint32_t>(PortPowerRole::SINK); + } else if (roleName == "host") { + if (type == PortRoleType::DATA_ROLE) + *currentRole = static_cast<uint32_t>(PortDataRole::HOST); + else + *currentRole = static_cast<uint32_t>(PortMode_1_1::DFP); + } else if (roleName == "device") { + if (type == PortRoleType::DATA_ROLE) + *currentRole = static_cast<uint32_t>(PortDataRole::DEVICE); + else + *currentRole = static_cast<uint32_t>(PortMode_1_1::UFP); + } else if (roleName != "none") { + /* case for none has already been addressed. + * so we check if the role isnt none. + */ + return Status::UNRECOGNIZED_ROLE; + } + + return Status::SUCCESS; +} + +Status getTypeCPortNamesHelper(std::unordered_map<std::string, bool> *names) { + DIR *dp; + + dp = opendir("/sys/class/typec"); + if (dp != NULL) { + int32_t ports = 0; + int32_t current = 0; + 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<std::string, bool>::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<PortStatus_1_1> *currentPortStatus_1_1, + bool V1_0) { + std::unordered_map<std::string, bool> names; + Status result = getTypeCPortNamesHelper(&names); + int i = -1; + + if (result == Status::SUCCESS) { + currentPortStatus_1_1->resize(names.size()); + for (std::pair<std::string, bool> port : names) { + i++; + ALOGI("%s", port.first.c_str()); + (*currentPortStatus_1_1)[i].status.portName = port.first; + + uint32_t currentRole; + if (getCurrentRoleHelper(port.first, port.second, + PortRoleType::POWER_ROLE, + ¤tRole) == Status::SUCCESS) { + (*currentPortStatus_1_1)[i].status.currentPowerRole = + static_cast<PortPowerRole>(currentRole); + } else { + ALOGE("Error while retreiving portNames"); + goto done; + } + + if (getCurrentRoleHelper(port.first, port.second, PortRoleType::DATA_ROLE, + ¤tRole) == Status::SUCCESS) { + (*currentPortStatus_1_1)[i].status.currentDataRole = + static_cast<PortDataRole>(currentRole); + } else { + ALOGE("Error while retreiving current port role"); + goto done; + } + + if (getCurrentRoleHelper(port.first, port.second, PortRoleType::MODE, + ¤tRole) == Status::SUCCESS) { + (*currentPortStatus_1_1)[i].currentMode = + static_cast<PortMode_1_1>(currentRole); + (*currentPortStatus_1_1)[i].status.currentMode = + static_cast<V1_0::PortMode>(currentRole); + } else { + ALOGE("Error while retreiving current data role"); + goto done; + } + + (*currentPortStatus_1_1)[i].status.canChangeMode = true; + (*currentPortStatus_1_1)[i].status.canChangeDataRole = + port.second ? canSwitchRoleHelper(port.first, PortRoleType::DATA_ROLE) + : false; + (*currentPortStatus_1_1)[i].status.canChangePowerRole = + port.second + ? canSwitchRoleHelper(port.first, PortRoleType::POWER_ROLE) + : false; + + ALOGI("connected:%d canChangeMode:%d canChagedata:%d canChangePower:%d", + port.second, (*currentPortStatus_1_1)[i].status.canChangeMode, + (*currentPortStatus_1_1)[i].status.canChangeDataRole, + (*currentPortStatus_1_1)[i].status.canChangePowerRole); + + if (V1_0) { + (*currentPortStatus_1_1)[i].status.supportedModes = V1_0::PortMode::DFP; + } else { + (*currentPortStatus_1_1)[i].supportedModes = PortMode_1_1::UFP | PortMode_1_1::DFP; + (*currentPortStatus_1_1)[i].status.supportedModes = V1_0::PortMode::NONE; + (*currentPortStatus_1_1)[i].status.currentMode = V1_0::PortMode::NONE; + } + } + return Status::SUCCESS; + } +done: + return Status::ERROR; +} + +Return<void> Usb::queryPortStatus() { + hidl_vec<PortStatus_1_1> currentPortStatus_1_1; + hidl_vec<V1_0::PortStatus> currentPortStatus; + Status status; + sp<IUsbCallback> callback_V1_1 = IUsbCallback::castFrom(mCallback_1_0); + + pthread_mutex_lock(&mLock); + if (mCallback_1_0 != NULL) { + if (callback_V1_1 != NULL) { + status = getPortStatusHelper(¤tPortStatus_1_1, false); + } else { + status = getPortStatusHelper(¤tPortStatus_1_1, true); + currentPortStatus.resize(currentPortStatus_1_1.size()); + for (unsigned long i = 0; i < currentPortStatus_1_1.size(); i++) + currentPortStatus[i] = currentPortStatus_1_1[i].status; + } + + Return<void> ret; + + if (callback_V1_1 != NULL) + ret = callback_V1_1->notifyPortStatusChange_1_1(currentPortStatus_1_1, status); + else + ret = mCallback_1_0->notifyPortStatusChange(currentPortStatus, status); + + if (!ret.isOk()) + ALOGE("queryPortStatus_1_1 error %s", ret.description().c_str()); + } else { + ALOGI("Notifying userspace skipped. Callback is NULL"); + } + pthread_mutex_unlock(&mLock); + + return Void(); +} + +struct data { + int uevent_fd; + android::hardware::usb::V1_1::implementation::Usb *usb; +}; + +static void uevent_event(uint32_t /*epevents*/, struct data *payload) { + char msg[UEVENT_MSG_LEN + 2]; + char *cp; + int n; + + n = uevent_kernel_multicast_recv(payload->uevent_fd, msg, UEVENT_MSG_LEN); + if (n <= 0) return; + if (n >= UEVENT_MSG_LEN) /* overflow -- discard */ + return; + + msg[n] = '\0'; + msg[n + 1] = '\0'; + cp = msg; + + while (*cp) { + if (std::regex_match(cp, std::regex("(add)(.*)(-partner)"))) { + ALOGI("partner added"); + pthread_mutex_lock(&payload->usb->mPartnerLock); + payload->usb->mPartnerUp = true; + pthread_cond_signal(&payload->usb->mPartnerCV); + pthread_mutex_unlock(&payload->usb->mPartnerLock); + } else if (!strncmp(cp, "DEVTYPE=typec_", strlen("DEVTYPE=typec_"))) { + hidl_vec<PortStatus_1_1> currentPortStatus_1_1; + ALOGI("uevent received %s", cp); + pthread_mutex_lock(&payload->usb->mLock); + if (payload->usb->mCallback_1_0 != NULL) { + sp<IUsbCallback> callback_V1_1 = IUsbCallback::castFrom(payload->usb->mCallback_1_0); + Return<void> ret; + + // V1_1 callback + if (callback_V1_1 != NULL) { + Status status = getPortStatusHelper(¤tPortStatus_1_1, false); + ret = callback_V1_1->notifyPortStatusChange_1_1( + currentPortStatus_1_1, status); + } else { // V1_0 callback + Status status = getPortStatusHelper(¤tPortStatus_1_1, true); + + /* + * Copying the result from getPortStatusHelper + * into V1_0::PortStatus to pass back through + * the V1_0 callback object. + */ + hidl_vec<V1_0::PortStatus> currentPortStatus; + currentPortStatus.resize(currentPortStatus_1_1.size()); + for (unsigned long i = 0; i < currentPortStatus_1_1.size(); i++) + currentPortStatus[i] = currentPortStatus_1_1[i].status; + + ret = payload->usb->mCallback_1_0->notifyPortStatusChange( + currentPortStatus, status); + } + if (!ret.isOk()) ALOGE("error %s", ret.description().c_str()); + } else { + ALOGI("Notifying userspace skipped. Callback is NULL"); + } + pthread_mutex_unlock(&payload->usb->mLock); + + //Role switch is not in progress and port is in disconnected state + if (!pthread_mutex_trylock(&payload->usb->mRoleSwitchLock)) { + for (unsigned long i = 0; i < currentPortStatus_1_1.size(); i++) { + DIR *dp = opendir(std::string("/sys/class/typec/" + + std::string(currentPortStatus_1_1[i].status.portName.c_str()) + + "-partner").c_str()); + if (dp == NULL) { + //PortRole role = {.role = static_cast<uint32_t>(PortMode::UFP)}; + switchToDrp(currentPortStatus_1_1[i].status.portName); + } else { + closedir(dp); + } + } + pthread_mutex_unlock(&payload->usb->mRoleSwitchLock); + } + break; + } + /* advance to after the next \0 */ + 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_1::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<void> Usb::setCallback(const sp<V1_0::IUsbCallback> &callback) { + + sp<IUsbCallback> callback_V1_1 = IUsbCallback::castFrom(callback); + + if (callback != NULL) + if (callback_V1_1 == NULL) + ALOGI("Registering 1.0 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_0 +} // namespace usb +} // namespace hardware +} // namespace android diff --git a/usb/Usb.h b/usb/Usb.h new file mode 100644 index 00000000..bfa63aec --- /dev/null +++ b/usb/Usb.h @@ -0,0 +1,70 @@ +#ifndef ANDROID_HARDWARE_USB_V1_1_USB_H +#define ANDROID_HARDWARE_USB_V1_1_USB_H + +#include <android/hardware/usb/1.1/IUsb.h> +#include <android/hardware/usb/1.1/types.h> +#include <android/hardware/usb/1.1/IUsbCallback.h> +#include <hidl/Status.h> +#include <utils/Log.h> + +#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_1 { +namespace implementation { + +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::Status; +using ::android::hardware::usb::V1_0::IUsb; +using ::android::hardware::usb::V1_1::IUsbCallback; +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::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<void> switchRole(const hidl_string& portName, const PortRole& role) override; + Return<void> setCallback(const sp<V1_0::IUsbCallback>& callback) override; + Return<void> queryPortStatus() override; + + + sp<V1_0::IUsbCallback> mCallback_1_0; + // Protects mCallback variable + pthread_mutex_t mLock = PTHREAD_MUTEX_INITIALIZER; + // Protects roleSwitch operation + pthread_mutex_t mRoleSwitchLock = PTHREAD_MUTEX_INITIALIZER; + // Threads waiting for the partner to come back wait here + pthread_cond_t mPartnerCV = PTHREAD_COND_INITIALIZER; + // lock protecting mPartnerCV + pthread_mutex_t mPartnerLock = PTHREAD_MUTEX_INITIALIZER; + // Variable to signal partner coming back online after type switch + bool mPartnerUp; + + private: + pthread_t mPoll; +}; + +} // namespace implementation +} // namespace V1_0 +} // namespace usb +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_USB_V1_1_USB_H diff --git a/usb/android.hardware.usb@1.1-service.wahoo.rc b/usb/android.hardware.usb@1.1-service.wahoo.rc new file mode 100644 index 00000000..bc1101d1 --- /dev/null +++ b/usb/android.hardware.usb@1.1-service.wahoo.rc @@ -0,0 +1,12 @@ +service usb-hal-1-1 /vendor/bin/hw/android.hardware.usb@1.1-service.wahoo + class hal + user system + group system + +on boot + chown root system /sys/class/typec/port0/power_role + chown root system /sys/class/typec/port0/data_role + chown root system /sys/class/typec/port0/port_type + chmod 664 /sys/class/typec/port0/power_role + chmod 664 /sys/class/typec/port0/data_role + chmod 664 /sys/class/typec/port0/port_type diff --git a/usb/service.cpp b/usb/service.cpp new file mode 100644 index 00000000..495d76f5 --- /dev/null +++ b/usb/service.cpp @@ -0,0 +1,52 @@ +/* + * 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. + */ + +#define LOG_TAG "android.hardware.usb@1.1-service.wahoo" + +#include <hidl/HidlTransportSupport.h> +#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_1::implementation::Usb; + +using android::status_t; +using android::OK; + +int main() { + android::sp<IUsb> service = new Usb(); + + configureRpcThreadpool(1, true /*callerWillJoin*/); + status_t status = service->registerAsService(); + + if (status != OK) { + ALOGE("Cannot register USB 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; + +} |
