• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: MIT
2 /*
3  * Copyright © 2014 Red Hat, Inc.
4  */
5 
6 #include "config.h"
7 
8 #include <assert.h>
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <getopt.h>
12 #include <libgen.h>
13 #include <limits.h>
14 #include <linux/input.h>
15 #include <stdbool.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <sys/stat.h>
20 #include <sys/types.h>
21 #include <unistd.h>
22 
23 #include "libevdev/libevdev.h"
24 
25 static void
usage(const char * progname)26 usage(const char *progname)
27 {
28 	printf("%s --abs <axis> [--min min] [--max max] [--res res] [--fuzz fuzz] [--flat flat] /dev/input/eventXYZ\n"
29 	       "\tChange the absinfo struct for the named axis\n"
30 	       "%s --resolution res[,yres] /dev/input/eventXYZ\n"
31 	       "\tChange the x/y resolution on the given device\n"
32 	       "%s --led <led> --on|--off /dev/input/eventXYZ\n"
33 	       "\tEnable or disable the named LED\n",
34 	       progname,
35 	       progname,
36 	       progname);
37 }
38 
39 enum mode {
40 	MODE_NONE = 0,
41 	MODE_ABS,
42 	MODE_LED,
43 	MODE_RESOLUTION,
44 	MODE_HELP,
45 };
46 
47 enum opts {
48 	OPT_ABS = 1 << 0,
49 	OPT_MIN = 1 << 1,
50 	OPT_MAX = 1 << 2,
51 	OPT_FUZZ = 1 << 3,
52 	OPT_FLAT = 1 << 4,
53 	OPT_RES = 1 << 5,
54 	OPT_LED = 1 << 6,
55 	OPT_ON = 1 << 7,
56 	OPT_OFF = 1 << 8,
57 	OPT_RESOLUTION = 1 << 9,
58 	OPT_HELP = 1 << 10,
59 };
60 
61 static bool
parse_resolution_argument(const char * arg,int * xres,int * yres)62 parse_resolution_argument(const char *arg, int *xres, int *yres)
63 {
64 	int matched;
65 
66 	matched = sscanf(arg, "%d,%d", xres, yres);
67 
68 	switch(matched) {
69 		case 2:
70 			break;
71 		case 1:
72 			*yres = *xres;
73 			break;
74 		default:
75 			return false;
76 	}
77 
78 	return true;
79 }
80 
81 static inline bool
safe_atoi(const char * str,int * val)82 safe_atoi(const char *str, int *val)
83 {
84         char *endptr;
85         long v;
86 
87         v = strtol(str, &endptr, 10);
88         if (str == endptr)
89                 return false;
90         if (*str != '\0' && *endptr != '\0')
91                 return false;
92 
93         if (v > INT_MAX || v < INT_MIN)
94                 return false;
95 
96         *val = v;
97         return true;
98 }
99 
100 static int
parse_event_code(int type,const char * str)101 parse_event_code(int type, const char *str)
102 {
103 	int code;
104 
105 	code = libevdev_event_code_from_name(type, str);
106 	if (code != -1)
107 		return code;
108 
109 	if (safe_atoi(str, &code))
110 		return code;
111 
112 	return -1;
113 }
114 
115 static int
parse_options_abs(int argc,char ** argv,unsigned int * changes,int * axis,struct input_absinfo * absinfo)116 parse_options_abs(int argc, char **argv, unsigned int *changes,
117 		  int *axis, struct input_absinfo *absinfo)
118 {
119 	int rc = 1;
120 	int c;
121 	int option_index = 0;
122 	static struct option opts[] = {
123 		{ "abs", 1, 0, OPT_ABS },
124 		{ "min", 1, 0, OPT_MIN },
125 		{ "max", 1, 0, OPT_MAX },
126 		{ "fuzz", 1, 0, OPT_FUZZ },
127 		{ "flat", 1, 0, OPT_FLAT },
128 		{ "res", 1, 0, OPT_RES },
129 		{ NULL, 0, 0, 0 },
130 	};
131 
132 	if (argc < 2)
133 		goto error;
134 
135 	optind = 1;
136 	while (1) {
137 		c = getopt_long(argc, argv, "h", opts, &option_index);
138 		if (c == -1)
139 			break;
140 
141 		switch (c) {
142 			case OPT_ABS:
143 				*axis = parse_event_code(EV_ABS, optarg);
144 				if (*axis == -1)
145 					goto error;
146 				break;
147 			case OPT_MIN:
148 				absinfo->minimum = atoi(optarg);
149 				break;
150 			case OPT_MAX:
151 				absinfo->maximum = atoi(optarg);
152 				break;
153 			case OPT_FUZZ:
154 				absinfo->fuzz = atoi(optarg);
155 				break;
156 			case OPT_FLAT:
157 				absinfo->flat = atoi(optarg);
158 				break;
159 			case OPT_RES:
160 				absinfo->resolution = atoi(optarg);
161 				break;
162 			default:
163 				goto error;
164 		}
165 		*changes |= c;
166 	}
167 	rc = 0;
168 error:
169 	return rc;
170 }
171 
172 static int
parse_options_led(int argc,char ** argv,int * led,int * led_state)173 parse_options_led(int argc, char **argv, int *led, int *led_state)
174 {
175 	int rc = 1;
176 	int c;
177 	int option_index = 0;
178 	static struct option opts[] = {
179 		{ "led", 1, 0, OPT_LED },
180 		{ "on", 0, 0, OPT_ON },
181 		{ "off", 0, 0, OPT_OFF },
182 		{ NULL, 0, 0, 0 },
183 	};
184 
185 	if (argc < 2)
186 		goto error;
187 
188 	optind = 1;
189 	while (1) {
190 		c = getopt_long(argc, argv, "h", opts, &option_index);
191 		if (c == -1)
192 			break;
193 
194 		switch (c) {
195 			case OPT_LED:
196 				*led = parse_event_code(EV_LED, optarg);
197 				if (*led == -1)
198 					goto error;
199 				break;
200 			case OPT_ON:
201 				if (*led_state != -1)
202 					goto error;
203 				*led_state = 1;
204 				break;
205 			case OPT_OFF:
206 				if (*led_state != -1)
207 					goto error;
208 				*led_state = 0;
209 				break;
210 			default:
211 				goto error;
212 		}
213 	}
214 
215 	rc = 0;
216 error:
217 	return rc;
218 }
219 
220 static int
parse_options_resolution(int argc,char ** argv,int * xres,int * yres)221 parse_options_resolution(int argc, char **argv, int *xres, int *yres)
222 {
223 	int rc = 1;
224 	int c;
225 	int option_index = 0;
226 	static struct option opts[] = {
227 		{ "resolution", 1, 0, OPT_RESOLUTION },
228 		{ NULL, 0, 0, 0 },
229 	};
230 
231 	if (argc < 2)
232 		goto error;
233 
234 	optind = 1;
235 	while (1) {
236 		c = getopt_long(argc, argv, "h", opts, &option_index);
237 		if (c == -1)
238 			break;
239 
240 		switch (c) {
241 			case OPT_RESOLUTION:
242 				if (!parse_resolution_argument(optarg,
243 							       xres, yres))
244 					goto error;
245 				break;
246 			default:
247 				goto error;
248 		}
249 	}
250 
251 	rc = 0;
252 error:
253 	return rc;
254 }
255 
256 static enum mode
parse_options_mode(int argc,char ** argv)257 parse_options_mode(int argc, char **argv)
258 {
259 	int c;
260 	int option_index = 0;
261 	static const struct option opts[] = {
262 		{ "abs", 1, 0, OPT_ABS },
263 		{ "led", 1, 0, OPT_LED },
264 		{ "resolution", 1, 0, OPT_RESOLUTION },
265 		{ "help", 0, 0, OPT_HELP },
266 		{ NULL, 0, 0, 0 },
267 	};
268 	enum mode mode = MODE_NONE;
269 
270 	if (argc < 2)
271 		return mode;
272 
273 	while (mode == MODE_NONE) {
274 		c = getopt_long(argc, argv, "h", opts, &option_index);
275 		if (c == -1)
276 			break;
277 
278 		switch (c) {
279 			case 'h':
280 			case OPT_HELP:
281 				mode = MODE_HELP;
282 				break;
283 			case OPT_ABS:
284 				mode = MODE_ABS;
285 				break;
286 			case OPT_LED:
287 				mode = MODE_LED;
288 				break;
289 			case OPT_RESOLUTION:
290 				mode = MODE_RESOLUTION;
291 				break;
292 			default:
293 				break;
294 		}
295 	}
296 
297 	if (optind >= argc && mode != MODE_HELP)
298 		return MODE_NONE;
299 
300 	return mode;
301 }
302 
303 static void
set_abs(struct libevdev * dev,unsigned int changes,unsigned int axis,struct input_absinfo * absinfo)304 set_abs(struct libevdev *dev, unsigned int changes,
305 	unsigned int axis, struct input_absinfo *absinfo)
306 {
307 	int rc;
308 	struct input_absinfo abs;
309 	const struct input_absinfo *a;
310 
311 	if ((a = libevdev_get_abs_info(dev, axis)) == NULL) {
312 		fprintf(stderr,
313 			"Device '%s' doesn't have axis %s\n",
314 			libevdev_get_name(dev),
315 			libevdev_event_code_get_name(EV_ABS, axis));
316 		return;
317 	}
318 
319 	abs = *a;
320 	if (changes & OPT_MIN)
321 		abs.minimum = absinfo->minimum;
322 	if (changes & OPT_MAX)
323 		abs.maximum = absinfo->maximum;
324 	if (changes & OPT_FUZZ)
325 		abs.fuzz = absinfo->fuzz;
326 	if (changes & OPT_FLAT)
327 		abs.flat = absinfo->flat;
328 	if (changes & OPT_RES)
329 		abs.resolution = absinfo->resolution;
330 
331 	rc = libevdev_kernel_set_abs_info(dev, axis, &abs);
332 	if (rc != 0)
333 		fprintf(stderr,
334 			"Failed to set absinfo %s: %s",
335 			libevdev_event_code_get_name(EV_ABS, axis),
336 			strerror(-rc));
337 }
338 
339 static void
set_led(struct libevdev * dev,unsigned int led,int led_state)340 set_led(struct libevdev *dev, unsigned int led, int led_state)
341 {
342 	int rc;
343 	enum libevdev_led_value state =
344 		led_state ? LIBEVDEV_LED_ON : LIBEVDEV_LED_OFF;
345 
346 	if (!libevdev_has_event_code(dev, EV_LED, led)) {
347 		fprintf(stderr,
348 			"Device '%s' doesn't have %s\n",
349 			libevdev_get_name(dev),
350 			libevdev_event_code_get_name(EV_LED, led));
351 		return;
352 	}
353 
354 	rc = libevdev_kernel_set_led_value(dev, led, state);
355 	if (rc != 0)
356 		fprintf(stderr,
357 			"Failed to set LED %s: %s",
358 			libevdev_event_code_get_name(EV_LED, led),
359 			strerror(-rc));
360 }
361 
362 static void
set_resolution(struct libevdev * dev,int xres,int yres)363 set_resolution(struct libevdev *dev, int xres, int yres)
364 {
365 	struct input_absinfo abs;
366 
367 	abs.resolution = xres;
368 	if (libevdev_has_event_code(dev, EV_ABS, ABS_X))
369 		set_abs(dev, OPT_RES, ABS_X, &abs);
370 	if (libevdev_has_event_code(dev, EV_ABS, ABS_MT_POSITION_X))
371 		set_abs(dev, OPT_RES, ABS_MT_POSITION_X, &abs);
372 
373 	abs.resolution = yres;
374 	if (libevdev_has_event_code(dev, EV_ABS, ABS_Y))
375 		set_abs(dev, OPT_RES, ABS_Y, &abs);
376 	if (libevdev_has_event_code(dev, EV_ABS, ABS_MT_POSITION_Y))
377 		set_abs(dev, OPT_RES, ABS_MT_POSITION_Y, &abs);
378 }
379 
380 int
main(int argc,char ** argv)381 main(int argc, char **argv)
382 {
383 	struct libevdev *dev = NULL;
384 	int fd = -1;
385 	int rc = EXIT_FAILURE;
386 	enum mode mode;
387 	const char *path;
388 	struct input_absinfo absinfo;
389 	int axis = -1;
390 	int led = -1;
391 	int led_state = -1;
392 	unsigned int changes = 0; /* bitmask of changes */
393 	int xres = 0,
394 	    yres = 0;
395 
396 	mode = parse_options_mode(argc, argv);
397 	switch (mode) {
398 		case MODE_HELP:
399 			rc = EXIT_SUCCESS;
400 			/* fallthrough */
401 		case MODE_NONE:
402 			usage(basename(argv[0]));
403 			goto out;
404 		case MODE_ABS:
405 			rc = parse_options_abs(argc, argv, &changes, &axis,
406 					       &absinfo);
407 			break;
408 		case MODE_LED:
409 			rc = parse_options_led(argc, argv, &led, &led_state);
410 			break;
411 		case MODE_RESOLUTION:
412 			rc = parse_options_resolution(argc, argv, &xres,
413 						      &yres);
414 			break;
415 		default:
416 			fprintf(stderr,
417 				"++?????++ Out of Cheese Error. Redo From Start.\n");
418 			goto out;
419 	}
420 
421 	if (rc != EXIT_SUCCESS)
422 		goto out;
423 
424 	if (optind >= argc) {
425 		rc = EXIT_FAILURE;
426 		usage(basename(argv[0]));
427 		goto out;
428 	}
429 
430 	path = argv[optind];
431 
432 	fd = open(path, O_RDWR);
433 	if (fd < 0) {
434 		rc = EXIT_FAILURE;
435 		perror("Failed to open device");
436 		goto out;
437 	}
438 
439 	rc = libevdev_new_from_fd(fd, &dev);
440 	if (rc < 0) {
441 		fprintf(stderr, "Failed to init libevdev (%s)\n", strerror(-rc));
442 		goto out;
443 	}
444 
445 	switch (mode) {
446 		case MODE_ABS:
447 			set_abs(dev, changes, axis, &absinfo);
448 			break;
449 		case MODE_LED:
450 			set_led(dev, led, led_state);
451 			break;
452 		case MODE_RESOLUTION:
453 			set_resolution(dev, xres, yres);
454 			break;
455 		default:
456 			break;
457 	}
458 
459 out:
460 	libevdev_free(dev);
461 	if (fd != -1)
462 		close(fd);
463 
464 	return rc;
465 }
466