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 25static void 26usage(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 39enum mode { 40 MODE_NONE = 0, 41 MODE_ABS, 42 MODE_LED, 43 MODE_RESOLUTION, 44 MODE_HELP, 45}; 46 47enum 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 61static bool 62parse_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 81static inline bool 82safe_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 100static int 101parse_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 115static int 116parse_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; 168error: 169 return rc; 170} 171 172static int 173parse_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; 216error: 217 return rc; 218} 219 220static int 221parse_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; 252error: 253 return rc; 254} 255 256static enum mode 257parse_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 303static void 304set_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 339static void 340set_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 362static void 363set_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 380int 381main(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 459out: 460 libevdev_free(dev); 461 if (fd != -1) 462 close(fd); 463 464 return rc; 465} 466