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