diff options
author | Xihua Chen <xihua.chen@intel.com> | 2018-01-12 13:42:18 +0800 |
---|---|---|
committer | Michael Bestas <mkbestas@lineageos.org> | 2019-04-04 21:52:00 +0300 |
commit | 8f693495e8db9cb3b966ccf17c2e086684f81e37 (patch) | |
tree | c5a3af92ac803ecf9af74b9dbe79da1a2b8c5b37 | |
parent | 232ba3f87cb06235feba5c2abfb0578886f0ea54 (diff) | |
download | android_bootable_recovery-8f693495e8db9cb3b966ccf17c2e086684f81e37.tar.gz android_bootable_recovery-8f693495e8db9cb3b966ccf17c2e086684f81e37.tar.bz2 android_bootable_recovery-8f693495e8db9cb3b966ccf17c2e086684f81e37.zip |
minui: Support input device hotplug in recovery mode.
In the old code, the recovery only enumerated the input devices at the
startup, and read the input events from these devices.
So if a USB input device is probed after the recovery startup, then the
recovery can't read the events from this device.
This patch use inotify to monitor /dev/input for new added input
device, then support input device hotplug in recovery mode.
Bug: 111847510
Test: can use USB keyboard hotplugged in recovery mode
Change-Id: I7e7dcbd619d3c66a2f40a43418f5dac6a50c859e
Signed-off-by: Liu Shuo A <shuo.a.liu@intel.com>
Signed-off-by: Ming Tan <ming.tan@intel.com>
-rw-r--r-- | minui/events.cpp | 81 |
1 files changed, 81 insertions, 0 deletions
diff --git a/minui/events.cpp b/minui/events.cpp index 39cb4e1c..3db58aef 100644 --- a/minui/events.cpp +++ b/minui/events.cpp @@ -22,6 +22,7 @@ #include <stdlib.h> #include <string.h> #include <sys/epoll.h> +#include <sys/inotify.h> #include <sys/ioctl.h> #include <unistd.h> @@ -40,7 +41,9 @@ struct fd_info { ev_callback cb; }; +static ev_callback saved_input_cb; static int g_epoll_fd; +static int g_inotify_fd; static epoll_event polledevents[MAX_DEVICES + MAX_MISC_FDS]; static int npolledevents; @@ -54,12 +57,87 @@ static bool test_bit(size_t bit, unsigned long* array) { // NOLINT return (array[bit/BITS_PER_LONG] & (1UL << (bit % BITS_PER_LONG))) != 0; } +static int inotify_cb(int fd, __unused uint32_t epevents) { + struct inotify_event* pevent; + char* buf; + DIR* dir; + size_t event_len; + size_t offset; + + if (saved_input_cb == nullptr) return -1; + + // The inotify will put one or several complete events. + // Should not read part of one event. + int ret = ioctl(fd, FIONREAD, &event_len); + if (ret != 0) return -1; + + dir = opendir("/dev/input"); + if (dir == nullptr) return -1; + + buf = (char*)malloc(event_len); + if (buf == nullptr) { + closedir(dir); + return -1; + } + + ret = read(fd, buf, event_len); + if (ret != (int)event_len) { + free(buf); + closedir(dir); + return -1; + } + + offset = 0; + while (offset < event_len) { + pevent = (struct inotify_event*)(buf + offset); + if (offset + sizeof(inotify_event) + pevent->len > event_len) { + // The pevent->len is too large and buffer will over flow. + // In general, should not happen, just make more stable. + free(buf); + closedir(dir); + return -1; + } + offset += sizeof(inotify_event) + pevent->len; + + pevent->name[pevent->len] = '\0'; + if (strncmp(pevent->name, "event", 5)) continue; + + int dfd = openat(dirfd(dir), pevent->name, O_RDONLY); + if (dfd == -1) break; + + // Read the evbits of the input device. + unsigned long ev_bits[BITS_TO_LONGS(EV_MAX)]; + if (ioctl(dfd, EVIOCGBIT(0, sizeof(ev_bits)), ev_bits) == -1) { + close(dfd); + continue; + } + // We assume that only EV_KEY, EV_REL, and EV_SW event types are ever needed. + if (!test_bit(EV_KEY, ev_bits) && !test_bit(EV_REL, ev_bits) && !test_bit(EV_SW, ev_bits)) { + close(dfd); + continue; + } + + // Only add, we assume the user will not plug out and plug in USB device again and again :) + ev_add_fd(dfd, saved_input_cb); + } + + free(buf); + closedir(dir); + return 0; +} + int ev_init(ev_callback input_cb, bool allow_touch_inputs) { g_epoll_fd = epoll_create(MAX_DEVICES + MAX_MISC_FDS); if (g_epoll_fd == -1) { return -1; } + g_inotify_fd = inotify_init(); + if (g_inotify_fd >= 0) { + inotify_add_watch(g_inotify_fd, "/dev/input", IN_CREATE); + ev_add_fd(g_inotify_fd, inotify_cb); + } + bool epollctlfail = false; DIR* dir = opendir("/dev/input"); if (dir != nullptr) { @@ -117,6 +195,8 @@ int ev_init(ev_callback input_cb, bool allow_touch_inputs) { return -1; } + saved_input_cb = input_cb; + return 0; } @@ -149,6 +229,7 @@ void ev_exit(void) { } ev_misc_count = 0; ev_dev_count = 0; + saved_input_cb = nullptr; close(g_epoll_fd); } |