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