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
This commit is contained in:
parent
3bf31e7efb
commit
a6462edbe3
2 changed files with 186 additions and 24 deletions
187
usb/usb/Usb.cpp
187
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 kPogoEnableUsb[] = "/sys/devices/platform/google,pogo/enable_usb";
|
||||||
constexpr char kPowerSupplyUsbType[] = "/sys/class/power_supply/usb/usb_type";
|
constexpr char kPowerSupplyUsbType[] = "/sys/class/power_supply/usb/usb_type";
|
||||||
constexpr char kIrqHpdCounPath[] = "-0025/irq_hpd_count";
|
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;
|
constexpr int kSamplingIntervalSec = 5;
|
||||||
void queryVersionHelper(android::hardware::usb::Usb *usb,
|
void queryVersionHelper(android::hardware::usb::Usb *usb,
|
||||||
std::vector<PortStatus> *currentPortStatus);
|
std::vector<PortStatus> *currentPortStatus);
|
||||||
|
|
||||||
|
#define USB_STATE_MAX_LEN 20
|
||||||
|
|
||||||
ScopedAStatus Usb::enableUsbData(const string& in_portName, bool in_enable,
|
ScopedAStatus Usb::enableUsbData(const string& in_portName, bool in_enable,
|
||||||
int64_t in_transactionId) {
|
int64_t in_transactionId) {
|
||||||
bool result = true;
|
bool result = true;
|
||||||
|
@ -942,13 +947,114 @@ enum UeventType matchUeventType(char* str) {
|
||||||
return UeventType::UNKNOWN;
|
return UeventType::UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uevent_event(uint32_t /*epevents*/, struct data *payload) {
|
static void unregisterEpollEntry(Usb *usb, std::string name) {
|
||||||
|
std::map<std::string, struct Usb::epollEntry> *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<std::string, struct Usb::epollEntry> *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<std::string, struct Usb::epollEntry> *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 msg[UEVENT_MSG_LEN + 2];
|
||||||
char *cp;
|
char *cp;
|
||||||
int n;
|
int n;
|
||||||
enum UeventType uevent_type = UeventType::UNKNOWN;
|
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)
|
if (n <= 0)
|
||||||
return;
|
return;
|
||||||
if (n >= UEVENT_MSG_LEN) /* overflow -- discard */
|
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);
|
pthread_mutex_unlock(&payload->usb->mDisplayPortLock);
|
||||||
}
|
}
|
||||||
break;
|
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 */
|
/* advance to after the next \0 */
|
||||||
while (*cp++) {
|
while (*cp++) {
|
||||||
|
@ -1023,37 +1152,46 @@ static void uevent_event(uint32_t /*epevents*/, struct data *payload) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void *work(void *param) {
|
static void udc_event(uint32_t /*epevents*/, struct Usb::payload *payload) {
|
||||||
int epoll_fd, uevent_fd;
|
int n;
|
||||||
struct epoll_event ev;
|
char state[USB_STATE_MAX_LEN] = {0};
|
||||||
int nevents = 0;
|
|
||||||
struct data payload;
|
|
||||||
|
|
||||||
ALOGE("creating thread");
|
lseek(payload->fd, 0, SEEK_SET);
|
||||||
|
n = read(payload->fd, &state, USB_STATE_MAX_LEN);
|
||||||
|
|
||||||
uevent_fd = uevent_open_socket(64 * 1024, true);
|
updateUsbDeviceState(&payload->usb->mDeviceState, state);
|
||||||
|
|
||||||
if (uevent_fd < 0) {
|
|
||||||
ALOGE("uevent_init: uevent_open_socket failed\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
payload.uevent_fd = uevent_fd;
|
void *work(void *param) {
|
||||||
payload.usb = (::aidl::android::hardware::usb::Usb *)param;
|
int epoll_fd, uevent_fd;
|
||||||
|
int nevents = 0;
|
||||||
|
Usb *usb = (Usb *)param;
|
||||||
|
|
||||||
fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
|
ALOGE("creating thread");
|
||||||
|
|
||||||
ev.events = EPOLLIN;
|
|
||||||
ev.data.ptr = (void *)uevent_event;
|
|
||||||
|
|
||||||
epoll_fd = epoll_create(64);
|
epoll_fd = epoll_create(64);
|
||||||
if (epoll_fd == -1) {
|
if (epoll_fd == -1) {
|
||||||
ALOGE("epoll_create failed; errno=%d", errno);
|
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;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, uevent_fd, &ev) == -1) {
|
// Monitor udc state
|
||||||
ALOGE("epoll_ctl failed; errno=%d", errno);
|
if (registerEpollEntryByFile(usb, kUdcState, EPOLLPRI, udc_event)) {
|
||||||
|
ALOGE("failed to monitor udc state");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1070,14 +1208,15 @@ void *work(void *param) {
|
||||||
|
|
||||||
for (int n = 0; n < nevents; ++n) {
|
for (int n = 0; n < nevents; ++n) {
|
||||||
if (events[n].data.ptr)
|
if (events[n].data.ptr)
|
||||||
(*(void (*)(int, struct data *payload))events[n].data.ptr)(events[n].events,
|
(*(std::function<void(uint32_t)>*)events[n].data.ptr)(events[n].events);
|
||||||
&payload);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ALOGI("exiting worker thread");
|
ALOGI("exiting worker thread");
|
||||||
error:
|
error:
|
||||||
close(uevent_fd);
|
unregisterEpollEntries(usb);
|
||||||
|
|
||||||
|
usb->mEpollFd = -1;
|
||||||
|
|
||||||
if (epoll_fd >= 0)
|
if (epoll_fd >= 0)
|
||||||
close(epoll_fd);
|
close(epoll_fd);
|
||||||
|
|
|
@ -121,6 +121,29 @@ struct Usb : public BnUsb {
|
||||||
pthread_mutex_t mDisplayPortLock;
|
pthread_mutex_t mDisplayPortLock;
|
||||||
// eventfd to signal DisplayPort thread
|
// eventfd to signal DisplayPort thread
|
||||||
int mDisplayPortEventPipe;
|
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<std::string, struct usbDeviceState> mHostStateMap;
|
||||||
|
|
||||||
|
// File monitoring through epoll
|
||||||
|
int mEpollFd;
|
||||||
|
struct payload {
|
||||||
|
int fd;
|
||||||
|
std::string name;
|
||||||
|
Usb *usb;
|
||||||
|
};
|
||||||
|
struct epollEntry {
|
||||||
|
struct payload payload;
|
||||||
|
std::function<void(uint32_t)> cb;
|
||||||
|
};
|
||||||
|
std::map<std::string, struct epollEntry> mEpollEntries;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
pthread_t mPoll;
|
pthread_t mPoll;
|
||||||
pthread_t mDisplayPortPoll;
|
pthread_t mDisplayPortPoll;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue