/* * Copyright (c) 2015 Cedric Hnyda * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it would be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include "test.h" #include "safe_macros.h" #include "input_helper.h" #define VIRTUAL_DEVICE "virtual-device-ltp" #define VIRTUAL_DEVICE_REGEX "*virtual-device-ltp*" static int uinput_loaded; static int check_device(void); static int try_open_device(void) { char path[256]; char name[256]; int ret, fd = -1; unsigned int i; for (i = 0; i < 100; i++) { snprintf(path, sizeof(path), "/dev/input/event%i", i); fd = open(path, O_RDONLY); if (fd < 0 && errno == ENOENT) continue; if (fd < 0) { tst_resm(TINFO | TERRNO, "failed to open %s", path); break; } ret = ioctl(fd, EVIOCGNAME(sizeof(name)), name); if (ret < 0) { tst_resm(TINFO | TERRNO, "ioctl(%s, EVIOCGNAME(256), ...) failed", path); break; } if (strcmp(name, VIRTUAL_DEVICE) == 0) return fd; close(fd); } return -1; } int open_device(void) { int fd; int retries = 10; while (retries--) { fd = try_open_device(); if (fd > 0) return fd; tst_resm(TINFO, "Device not found, retrying..."); usleep(10000); } tst_brkm(TBROK, NULL, "Unable to find the input device"); } static int try_load_uinput(void) { const char *argv[] = {"modprobe", "uinput", NULL}; int ret; tst_resm(TINFO, "Trying to load uinput kernel module"); ret = tst_run_cmd(NULL, argv, NULL, NULL, 1); if (ret) { tst_resm(TINFO, "Failed to load the uinput module"); return 0; } return 1; } static void unload_uinput(void) { const char *argv[] = {"modprobe", "-r", "uinput", NULL}; int ret; tst_resm(TINFO, "Unloading uinput kernel module"); ret = tst_run_cmd(NULL, argv, NULL, NULL, 1); if (ret) tst_resm(TWARN, "Failed to unload uinput module"); } static const char *uinput_paths[] = { "/dev/input/uinput", "/dev/uinput", }; static int try_open_uinput(void) { unsigned int i; int fd; for (i = 0; i < ARRAY_SIZE(uinput_paths); i++) { fd = open(uinput_paths[i], O_WRONLY | O_NONBLOCK); if (fd > 0) { tst_resm(TINFO, "Found uinput dev at %s", uinput_paths[i]); return fd; } if (fd < 0 && errno != ENOENT) { tst_brkm(TBROK | TERRNO, NULL, "open(%s)", uinput_paths[i]); } } return -1; } int open_uinput(void) { int fd; int retries = 10; fd = try_open_uinput(); if (fd > 0) return fd; if (try_load_uinput()) { while (retries--) { fd = try_open_uinput(); if (fd > 0) { uinput_loaded = 1; return fd; } tst_resm(TINFO, "Uinput dev not found, retrying..."); usleep(10000); } unload_uinput(); } tst_brkm(TCONF, NULL, "Unable to find and open uinput"); } void send_event(int fd, int event, int code, int value) { struct input_event ev = { .type = event, .code = code, .value = value, }; SAFE_WRITE(NULL, 1, fd, &ev, sizeof(ev)); } void send_rel_move(int fd, int x, int y) { send_event(fd, EV_REL, REL_X, x); send_event(fd, EV_REL, REL_Y, y); send_event(fd, EV_SYN, 0, 0); } void create_device(int fd) { int nb; struct uinput_user_dev uidev = { .name = VIRTUAL_DEVICE, .id = { .bustype = BUS_USB, .vendor = 0x1, .product = 0x1, .version = 1, } }; SAFE_WRITE(NULL, 1, fd, &uidev, sizeof(uidev)); SAFE_IOCTL(NULL, fd, UI_DEV_CREATE, NULL); for (nb = 100; nb > 0; nb--) { if (check_device()) return; usleep(10000); } destroy_device(fd); tst_brkm(TBROK, NULL, "Failed to create device"); } void setup_mouse_events(int fd) { SAFE_IOCTL(NULL, fd, UI_SET_EVBIT, EV_KEY); SAFE_IOCTL(NULL, fd, UI_SET_KEYBIT, BTN_LEFT); SAFE_IOCTL(NULL, fd, UI_SET_EVBIT, EV_REL); SAFE_IOCTL(NULL, fd, UI_SET_RELBIT, REL_X); SAFE_IOCTL(NULL, fd, UI_SET_RELBIT, REL_Y); } void destroy_device(int fd) { SAFE_IOCTL(NULL, fd, UI_DEV_DESTROY, NULL); SAFE_CLOSE(NULL, fd); if (uinput_loaded) unload_uinput(); } int check_event_code(struct input_event *iev, int event, int code) { return iev->type == event && iev->code == code; } int check_sync_event(struct input_event *iev) { return check_event_code(iev, EV_SYN, SYN_REPORT); } /* * the value of stray_sync_event: * 0: EV_SYN/SYN_REPORT events should not be received in /dev/input/eventX * 1: EV_SYN/SYN_REPORT events may be received in /dev/input/eventX * On an old kernel(before v3.7.0), EV_SYN/SYN_REPORT events are always * received even though we send empty moves. */ int no_events_queued(int fd, int stray_sync_event) { struct pollfd fds = {.fd = fd, .events = POLLIN}; int ret, res, sync_event_ignored; struct input_event ev; if (tst_kvercmp(3, 7, 0) < 0 && stray_sync_event) sync_event_ignored = 1; ret = poll(&fds, 1, 30); if (ret > 0) { res = read(fd, &ev, sizeof(ev)); if (res == sizeof(ev)) { if (sync_event_ignored && check_sync_event(&ev)) { ret = 0; tst_resm(TINFO, "Ignoring stray sync event (known problem)"); } else { tst_resm(TINFO, "Unexpected ev type=%i code=%i value=%i", ev.type, ev.code, ev.value); } } } return ret == 0; } static int check_device(void) { FILE *file; char line[256]; file = fopen("/proc/bus/input/devices", "r"); if (!file) return 0; while (fgets(line, 256, file)) { if (fnmatch(VIRTUAL_DEVICE_REGEX, line, 0) == 0) return 1; } fclose(file); return 0; }