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