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
ca6b06a48f
commit
381adb1243
2 changed files with 185 additions and 28 deletions
190
usb/usb/Usb.cpp
190
usb/usb/Usb.cpp
|
@ -81,10 +81,15 @@ constexpr char kThermalZoneForTempReadSecondary2[] = "qi_therm";
|
|||
constexpr char kPogoUsbActive[] = "/sys/devices/platform/google,pogo/pogo_usb_active";
|
||||
constexpr char KPogoMoveDataToUsb[] = "/sys/devices/platform/google,pogo/move_data_to_usb";
|
||||
constexpr char kPowerSupplyUsbType[] = "/sys/class/power_supply/usb/usb_type";
|
||||
constexpr char kUdcState[] = "/sys/devices/platform/11110000.usb/11110000.dwc3/udc/11110000.dwc3/state";
|
||||
// xhci-hcd-exynos and usb device numbering could vary on different platforms
|
||||
constexpr char kHostUeventRegex[] = "^(bind|unbind)@(/devices/platform/11110000\\.usb/11110000\\.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<PortStatus> *currentPortStatus);
|
||||
|
||||
#define USB_STATE_MAX_LEN 20
|
||||
|
||||
ScopedAStatus Usb::enableUsbData(const string& in_portName, bool in_enable,
|
||||
int64_t in_transactionId) {
|
||||
bool result = true;
|
||||
|
@ -907,17 +912,113 @@ void report_overheat_event(android::hardware::usb::Usb *usb) {
|
|||
}
|
||||
}
|
||||
|
||||
struct data {
|
||||
int uevent_fd;
|
||||
::aidl::android::hardware::usb::Usb *usb;
|
||||
};
|
||||
static void unregisterEpollEntry(Usb *usb, std::string name) {
|
||||
std::map<std::string, struct Usb::epollEntry> *map;
|
||||
int fd;
|
||||
|
||||
static void uevent_event(uint32_t /*epevents*/, struct data *payload) {
|
||||
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 *cp;
|
||||
int n;
|
||||
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 */
|
||||
|
@ -963,6 +1064,29 @@ static void uevent_event(uint32_t /*epevents*/, struct data *payload) {
|
|||
} else if (!strncmp(cp, kOverheatStatsDev, strlen(kOverheatStatsDev))) {
|
||||
ALOGV("Overheat Cooling device suez update");
|
||||
report_overheat_event(payload->usb);
|
||||
} 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++) {
|
||||
|
@ -970,37 +1094,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;
|
||||
}
|
||||
|
||||
|
@ -1017,14 +1150,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<void(uint32_t)>*)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);
|
||||
|
|
|
@ -89,6 +89,29 @@ struct Usb : public BnUsb {
|
|||
float mPluggedTemperatureCelsius;
|
||||
// Usb Data status
|
||||
bool mUsbDataEnabled;
|
||||
|
||||
// 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:
|
||||
pthread_t mPoll;
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue