From a6462edbe394dedb2fcb04ebc9c0e65b7e2b8de0 Mon Sep 17 00:00:00 2001 From: Roy Luo Date: Mon, 7 Aug 2023 19:48:05 +0000 Subject: [PATCH] Usb: Listen to USB sysfs attribute for device state Poll the sysfs attributes that represents usb device state in either of the following states: not attached, powered, default, addressed, configured. The information is useful in detecting non compliant USB cable, which will be supported in later patch sets. This patch lays the ground work to monitor the sysfs attributes in both device and host modes. The thread to poll uevent is re-used to poll sysfs because this serialize type-C port events and usb device state changes, hence prevent potential races. Added a thin abstration layer and a map to keep epoll data so that it's easier to dynamically add/delete files to epoll, which is needed for usb devices in host mode. Bug: 285199434 Test: trigger usb device state changes in device and host mode Change-Id: Ie5389d051deb28dbb486c2f27319b3cc9e89312f --- usb/usb/Usb.cpp | 187 +++++++++++++++++++++++++++++++++++++++++------- usb/usb/Usb.h | 23 ++++++ 2 files changed, 186 insertions(+), 24 deletions(-) diff --git a/usb/usb/Usb.cpp b/usb/usb/Usb.cpp index 74070770..138709ce 100644 --- a/usb/usb/Usb.cpp +++ b/usb/usb/Usb.cpp @@ -88,10 +88,15 @@ constexpr char kPogoUsbActive[] = "/sys/devices/platform/google,pogo/pogo_usb_ac constexpr char kPogoEnableUsb[] = "/sys/devices/platform/google,pogo/enable_usb"; constexpr char kPowerSupplyUsbType[] = "/sys/class/power_supply/usb/usb_type"; constexpr char kIrqHpdCounPath[] = "-0025/irq_hpd_count"; +constexpr char kUdcState[] = "/sys/devices/platform/11210000.usb/11210000.dwc3/udc/11210000.dwc3/state"; +// xhci-hcd-exynos and usb device numbering could vary on different platforms +constexpr char kHostUeventRegex[] = "^(bind|unbind)@(/devices/platform/11210000\\.usb/11210000\\.dwc3/xhci-hcd-exynos\\.[0-9]\\.auto/)usb([0-9])/[0-9]-0:1\\.0"; constexpr int kSamplingIntervalSec = 5; void queryVersionHelper(android::hardware::usb::Usb *usb, std::vector *currentPortStatus); +#define USB_STATE_MAX_LEN 20 + ScopedAStatus Usb::enableUsbData(const string& in_portName, bool in_enable, int64_t in_transactionId) { bool result = true; @@ -942,13 +947,114 @@ enum UeventType matchUeventType(char* str) { return UeventType::UNKNOWN; } -static void uevent_event(uint32_t /*epevents*/, struct data *payload) { +static void unregisterEpollEntry(Usb *usb, std::string name) { + std::map *map; + int fd; + + map = &usb->mEpollEntries; + auto it = map->find(name); + if (it != map->end()) { + ALOGI("epoll unregister %s", name.c_str()); + fd = it->second.payload.fd; + epoll_ctl(usb->mEpollFd, EPOLL_CTL_DEL, fd, NULL); + close(fd); + map->erase(it); + } +} + +static void unregisterEpollEntries(Usb *usb) { + std::map *map; + std::string name; + + map = &usb->mEpollEntries; + for (auto it = map->begin(); it != map->end();) { + name = it->first; + it++; + unregisterEpollEntry(usb, name); + } +} + +static int registerEpollEntry(Usb *usb, std::string name, int fd, int flags, + void (*func)(uint32_t, struct Usb::payload*)) { + std::map *map; + struct Usb::epollEntry *entry; + struct epoll_event ev; + + map = &usb->mEpollEntries; + if (map->find(name) != map->end()) { + ALOGE("%s already registered", name.c_str()); + unregisterEpollEntry(usb, name); + } + + entry = &(*map)[name]; + entry->payload.fd = fd; + entry->payload.name = name; + entry->payload.usb = usb; + entry->cb = std::bind(func, std::placeholders::_1, &entry->payload); + + ev.events = flags; + ev.data.ptr = (void *)&entry->cb; + + if (epoll_ctl(usb->mEpollFd, EPOLL_CTL_ADD, fd, &ev) != 0) { + ALOGE("epoll_ctl failed; errno=%d", errno); + unregisterEpollEntry(usb, name); + return -1; + } + + ALOGI("epoll register %s", name.c_str()); + + return 0; +} + +static int registerEpollEntryByFile(Usb *usb, std::string name, int flags, + void (*func)(uint32_t, struct Usb::payload*)) { + int fd; + + fd = open(name.c_str(), O_RDONLY); + if (fd < 0) { + ALOGE("Cannot open %s", name.c_str()); + return -1; + } + + return registerEpollEntry(usb, name, fd, flags, func); +} + +static void clearUsbDeviceState(struct Usb::usbDeviceState *device) { + device->latestState.clear(); + device->portResetCount = 0; +} + +static void updateUsbDeviceState(struct Usb::usbDeviceState *device, char *state) { + ALOGI("Update USB device state: %s", state); + + device->latestState = state; + + if (!std::strcmp(state, "configured\n")) { + device->portResetCount = 0; + } else if (!std::strcmp(state, "default\n")) { + device->portResetCount++; + } +} + +static void host_event(uint32_t /*epevents*/, struct Usb::payload *payload) { + int n; + char state[USB_STATE_MAX_LEN] = {0}; + struct Usb::usbDeviceState *device; + + lseek(payload->fd, 0, SEEK_SET); + n = read(payload->fd, &state, USB_STATE_MAX_LEN); + + updateUsbDeviceState(&payload->usb->mHostStateMap[payload->name], state); +} + +static void uevent_event(uint32_t /*epevents*/, struct Usb::payload *payload) { char msg[UEVENT_MSG_LEN + 2]; char *cp; int n; enum UeventType uevent_type = UeventType::UNKNOWN; + std::cmatch match; - n = uevent_kernel_multicast_recv(payload->uevent_fd, msg, UEVENT_MSG_LEN); + n = uevent_kernel_multicast_recv(payload->fd, msg, UEVENT_MSG_LEN); if (n <= 0) return; if (n >= UEVENT_MSG_LEN) /* overflow -- discard */ @@ -1016,6 +1122,29 @@ static void uevent_event(uint32_t /*epevents*/, struct data *payload) { pthread_mutex_unlock(&payload->usb->mDisplayPortLock); } break; + } else if (std::regex_match(cp, match, std::regex(kHostUeventRegex))) { + /* + * Matched strings: + * 1st: entire string + * 2nd: uevent action, either "bind" or "unbind" + * 3rd: xhci device path, e.g. devices/platform/11210000.usb/11210000.dwc3/xhci-hcd-exynos.4.auto + * 4th: usb device number, e.g. 1 for usb1 + * + * The strings are used to composed usb device state path, e.g. + * /sys/devices/platform/11210000.usb/11210000.dwc3/xhci-hcd-exynos.4.auto/usb2/2-0:1.0/usb2-port1/state + */ + if (match.size() == 4) { + std::string action = match[1].str(); + std::string id = match[3].str(); + std::string path = "/sys" + match[2].str() + "usb" + id + "/" + + id + "-0:1.0/usb" + id + "-port1/state"; + if (action == "bind") { + registerEpollEntryByFile(payload->usb, path, EPOLLPRI, host_event); + } else if (action == "unbind") { + unregisterEpollEntry(payload->usb, path); + clearUsbDeviceState(&payload->usb->mHostStateMap[path]); + } + } } /* advance to after the next \0 */ while (*cp++) { @@ -1023,37 +1152,46 @@ static void uevent_event(uint32_t /*epevents*/, struct data *payload) { } } +static void udc_event(uint32_t /*epevents*/, struct Usb::payload *payload) { + int n; + char state[USB_STATE_MAX_LEN] = {0}; + + lseek(payload->fd, 0, SEEK_SET); + n = read(payload->fd, &state, USB_STATE_MAX_LEN); + + updateUsbDeviceState(&payload->usb->mDeviceState, state); +} + void *work(void *param) { int epoll_fd, uevent_fd; - struct epoll_event ev; int nevents = 0; - struct data payload; + Usb *usb = (Usb *)param; ALOGE("creating thread"); - uevent_fd = uevent_open_socket(64 * 1024, true); - - if (uevent_fd < 0) { - ALOGE("uevent_init: uevent_open_socket failed\n"); - return NULL; - } - - payload.uevent_fd = uevent_fd; - payload.usb = (::aidl::android::hardware::usb::Usb *)param; - - fcntl(uevent_fd, F_SETFL, O_NONBLOCK); - - ev.events = EPOLLIN; - ev.data.ptr = (void *)uevent_event; - epoll_fd = epoll_create(64); if (epoll_fd == -1) { ALOGE("epoll_create failed; errno=%d", errno); + return NULL; + } + usb->mEpollFd = epoll_fd; + + // Monitor uevent + uevent_fd = uevent_open_socket(64 * 1024, true); + if (uevent_fd < 0) { + ALOGE("uevent_init: uevent_open_socket failed"); + goto error; + } + fcntl(uevent_fd, F_SETFL, O_NONBLOCK); + + if (registerEpollEntry(usb, "uevent", uevent_fd, EPOLLIN, uevent_event)) { + ALOGE("failed to monitor uevent"); goto error; } - if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, uevent_fd, &ev) == -1) { - ALOGE("epoll_ctl failed; errno=%d", errno); + // Monitor udc state + if (registerEpollEntryByFile(usb, kUdcState, EPOLLPRI, udc_event)) { + ALOGE("failed to monitor udc state"); goto error; } @@ -1070,14 +1208,15 @@ void *work(void *param) { 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); + (*(std::function*)events[n].data.ptr)(events[n].events); } } ALOGI("exiting worker thread"); error: - close(uevent_fd); + unregisterEpollEntries(usb); + + usb->mEpollFd = -1; if (epoll_fd >= 0) close(epoll_fd); diff --git a/usb/usb/Usb.h b/usb/usb/Usb.h index 892d769e..f6bbd695 100644 --- a/usb/usb/Usb.h +++ b/usb/usb/Usb.h @@ -121,6 +121,29 @@ struct Usb : public BnUsb { pthread_mutex_t mDisplayPortLock; // eventfd to signal DisplayPort thread int mDisplayPortEventPipe; + + // USB device state monitoring + struct usbDeviceState { + std::string latestState; + int portResetCount; + }; + struct usbDeviceState mDeviceState; + // Map host device path name to usbDeviceState + std::map mHostStateMap; + + // File monitoring through epoll + int mEpollFd; + struct payload { + int fd; + std::string name; + Usb *usb; + }; + struct epollEntry { + struct payload payload; + std::function cb; + }; + std::map mEpollEntries; + private: pthread_t mPoll; pthread_t mDisplayPortPoll;