Home | History | Annotate | Download | only in input
      1 /*
      2  * Copyright (c) 2015 Cedric Hnyda <chnyda (at) suse.com>
      3  *
      4  * This program is free software; you can redistribute it and/or
      5  * modify it under the terms of the GNU General Public License as
      6  * published by the Free Software Foundation; either version 2 of
      7  * the License, or (at your option) any later version.
      8  *
      9  * This program is distributed in the hope that it would be useful,
     10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     12  * GNU General Public License for more details.
     13  *
     14  * You should have received a copy of the GNU General Public License
     15  * along with this program; if not, write the Free Software Foundation,
     16  * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     17  */
     18 
     19 #include <linux/input.h>
     20 #include <linux/uinput.h>
     21 #include <fnmatch.h>
     22 #include <errno.h>
     23 #include <poll.h>
     24 
     25 #include "test.h"
     26 #include "safe_macros.h"
     27 #include "input_helper.h"
     28 
     29 #define VIRTUAL_DEVICE "virtual-device-ltp"
     30 
     31 #define VIRTUAL_DEVICE_REGEX "*virtual-device-ltp*"
     32 
     33 static int uinput_loaded;
     34 static int check_device(void);
     35 
     36 static int try_open_device(void)
     37 {
     38 	char path[256];
     39 	char name[256];
     40 	int ret, fd = -1;
     41 	unsigned int i;
     42 
     43 	for (i = 0; i < 100; i++) {
     44 		snprintf(path, sizeof(path), "/dev/input/event%i", i);
     45 
     46 		fd = open(path, O_RDONLY);
     47 
     48 		if (fd < 0 && errno == ENOENT)
     49 			continue;
     50 
     51 		if (fd < 0) {
     52 			tst_resm(TINFO | TERRNO, "failed to open %s", path);
     53 			break;
     54 		}
     55 
     56 		ret = ioctl(fd, EVIOCGNAME(sizeof(name)), name);
     57 		if (ret < 0) {
     58 			tst_resm(TINFO | TERRNO,
     59 				"ioctl(%s, EVIOCGNAME(256), ...) failed",
     60 				path);
     61 			break;
     62 		}
     63 
     64 		if (strcmp(name, VIRTUAL_DEVICE) == 0)
     65 			return fd;
     66 		close(fd);
     67 	}
     68 
     69 	return -1;
     70 }
     71 
     72 int open_device(void)
     73 {
     74 	int fd;
     75 	int retries = 10;
     76 
     77 	while (retries--) {
     78 		fd = try_open_device();
     79 		if (fd > 0)
     80 			return fd;
     81 		tst_resm(TINFO, "Device not found, retrying...");
     82 		usleep(10000);
     83 	}
     84 
     85 	tst_brkm(TBROK, NULL, "Unable to find the input device");
     86 }
     87 
     88 static int try_load_uinput(void)
     89 {
     90 	const char *argv[] = {"modprobe", "uinput", NULL};
     91 	int ret;
     92 
     93 	tst_resm(TINFO, "Trying to load uinput kernel module");
     94 
     95 	ret = tst_run_cmd(NULL, argv, NULL, NULL, 1);
     96 	if (ret) {
     97 		tst_resm(TINFO, "Failed to load the uinput module");
     98 		return 0;
     99 	}
    100 
    101 	return 1;
    102 }
    103 
    104 static void unload_uinput(void)
    105 {
    106 	const char *argv[] = {"modprobe", "-r", "uinput", NULL};
    107 	int ret;
    108 
    109 	tst_resm(TINFO, "Unloading uinput kernel module");
    110 
    111 	ret = tst_run_cmd(NULL, argv, NULL, NULL, 1);
    112 	if (ret)
    113 		tst_resm(TWARN, "Failed to unload uinput module");
    114 }
    115 
    116 static const char *uinput_paths[] = {
    117 	"/dev/input/uinput",
    118 	"/dev/uinput",
    119 };
    120 
    121 static int try_open_uinput(void)
    122 {
    123 	unsigned int i;
    124 	int fd;
    125 
    126 	for (i = 0; i < ARRAY_SIZE(uinput_paths); i++) {
    127 		fd = open(uinput_paths[i], O_WRONLY | O_NONBLOCK);
    128 
    129 		if (fd > 0) {
    130 			tst_resm(TINFO, "Found uinput dev at %s",
    131 			         uinput_paths[i]);
    132 			return fd;
    133 		}
    134 
    135 		if (fd < 0 && errno != ENOENT) {
    136 			tst_brkm(TBROK | TERRNO, NULL,
    137 			         "open(%s)", uinput_paths[i]);
    138 		}
    139 	}
    140 
    141 	return -1;
    142 }
    143 
    144 int open_uinput(void)
    145 {
    146 	int fd;
    147 	int retries = 10;
    148 
    149 	fd = try_open_uinput();
    150 	if (fd > 0)
    151 		return fd;
    152 
    153 	if (try_load_uinput()) {
    154 		while (retries--) {
    155 			fd = try_open_uinput();
    156 			if (fd > 0) {
    157 				uinput_loaded = 1;
    158 				return fd;
    159 			}
    160 			tst_resm(TINFO, "Uinput dev not found, retrying...");
    161 			usleep(10000);
    162 		}
    163 
    164 		unload_uinput();
    165 	}
    166 
    167 	tst_brkm(TCONF, NULL, "Unable to find and open uinput");
    168 }
    169 
    170 void send_event(int fd, int event, int code, int value)
    171 {
    172 	struct input_event ev = {
    173 		.type = event,
    174 		.code = code,
    175 		.value = value,
    176 	};
    177 
    178 	SAFE_WRITE(NULL, 1, fd, &ev, sizeof(ev));
    179 }
    180 
    181 void send_rel_move(int fd, int x, int y)
    182 {
    183 	send_event(fd, EV_REL, REL_X, x);
    184 	send_event(fd, EV_REL, REL_Y, y);
    185 	send_event(fd, EV_SYN, 0, 0);
    186 }
    187 
    188 void create_device(int fd)
    189 {
    190 	int nb;
    191 	struct uinput_user_dev uidev = {
    192 		.name = VIRTUAL_DEVICE,
    193 		.id = {
    194 			.bustype = BUS_USB,
    195 			.vendor = 0x1,
    196 			.product = 0x1,
    197 			.version = 1,
    198 		}
    199 	};
    200 
    201 	SAFE_WRITE(NULL, 1, fd, &uidev, sizeof(uidev));
    202 	SAFE_IOCTL(NULL, fd, UI_DEV_CREATE, NULL);
    203 
    204 	for (nb = 100; nb > 0; nb--) {
    205 		if (check_device())
    206 			return;
    207 		usleep(10000);
    208 	}
    209 
    210 	destroy_device(fd);
    211 	tst_brkm(TBROK, NULL, "Failed to create device");
    212 }
    213 
    214 void setup_mouse_events(int fd)
    215 {
    216 	SAFE_IOCTL(NULL, fd, UI_SET_EVBIT, EV_KEY);
    217 	SAFE_IOCTL(NULL, fd, UI_SET_KEYBIT, BTN_LEFT);
    218 	SAFE_IOCTL(NULL, fd, UI_SET_EVBIT, EV_REL);
    219 	SAFE_IOCTL(NULL, fd, UI_SET_RELBIT, REL_X);
    220 	SAFE_IOCTL(NULL, fd, UI_SET_RELBIT, REL_Y);
    221 }
    222 
    223 void destroy_device(int fd)
    224 {
    225 	SAFE_IOCTL(NULL, fd, UI_DEV_DESTROY, NULL);
    226 	SAFE_CLOSE(NULL, fd);
    227 
    228 	if (uinput_loaded)
    229 		unload_uinput();
    230 }
    231 
    232 int check_event_code(struct input_event *iev, int event, int code)
    233 {
    234 	return iev->type == event && iev->code == code;
    235 }
    236 
    237 int check_sync_event(struct input_event *iev)
    238 {
    239 	return check_event_code(iev, EV_SYN, SYN_REPORT);
    240 }
    241 
    242 /*
    243  * the value of stray_sync_event:
    244  * 0: EV_SYN/SYN_REPORT events should not be received in /dev/input/eventX
    245  * 1: EV_SYN/SYN_REPORT events may be received in /dev/input/eventX
    246  * On an old kernel(before v3.7.0), EV_SYN/SYN_REPORT events are always
    247  * received even though we send empty moves.
    248  */
    249 int no_events_queued(int fd, int stray_sync_event)
    250 {
    251 	struct pollfd fds = {.fd = fd, .events = POLLIN};
    252 	int ret, res, sync_event_ignored;
    253 	struct input_event ev;
    254 
    255 	if (tst_kvercmp(3, 7, 0) < 0 && stray_sync_event)
    256 		sync_event_ignored = 1;
    257 
    258 	ret = poll(&fds, 1, 30);
    259 
    260 	if (ret > 0) {
    261 		res = read(fd, &ev, sizeof(ev));
    262 
    263 		if (res == sizeof(ev)) {
    264 			if (sync_event_ignored && check_sync_event(&ev)) {
    265 				ret = 0;
    266 				tst_resm(TINFO,
    267 					 "Ignoring stray sync event (known problem)");
    268 			} else {
    269 				tst_resm(TINFO,
    270 					 "Unexpected ev type=%i code=%i value=%i",
    271 					 ev.type, ev.code, ev.value);
    272 			}
    273 		}
    274 	}
    275 
    276 	return ret == 0;
    277 }
    278 
    279 static int check_device(void)
    280 {
    281 	FILE *file;
    282 	char line[256];
    283 
    284 	file = fopen("/proc/bus/input/devices", "r");
    285 	if (!file)
    286 		return 0;
    287 
    288 	while (fgets(line, 256, file)) {
    289 		if (fnmatch(VIRTUAL_DEVICE_REGEX, line, 0) == 0)
    290 			return 1;
    291 	}
    292 
    293 	fclose(file);
    294 
    295 	return 0;
    296 }
    297