• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <stdint.h>
5 #include <dirent.h>
6 #include <fcntl.h>
7 #include <sys/ioctl.h>
8 #include <sys/inotify.h>
9 #include <sys/limits.h>
10 #include <sys/poll.h>
11 #include <linux/input.h>
12 #include <errno.h>
13 
14 #include "getevent.h"
15 
16 static struct pollfd *ufds;
17 static char **device_names;
18 static int nfds;
19 
20 enum {
21     PRINT_DEVICE_ERRORS     = 1U << 0,
22     PRINT_DEVICE            = 1U << 1,
23     PRINT_DEVICE_NAME       = 1U << 2,
24     PRINT_DEVICE_INFO       = 1U << 3,
25     PRINT_VERSION           = 1U << 4,
26     PRINT_POSSIBLE_EVENTS   = 1U << 5,
27     PRINT_INPUT_PROPS       = 1U << 6,
28     PRINT_HID_DESCRIPTOR    = 1U << 7,
29 
30     PRINT_ALL_INFO          = (1U << 8) - 1,
31 
32     PRINT_LABELS            = 1U << 16,
33 };
34 
get_label(const struct label * labels,int value)35 static const char *get_label(const struct label *labels, int value)
36 {
37     while(labels->name && value != labels->value) {
38         labels++;
39     }
40     return labels->name;
41 }
42 
print_input_props(int fd)43 static int print_input_props(int fd)
44 {
45     uint8_t bits[INPUT_PROP_CNT / 8];
46     int i, j;
47     int res;
48     int count;
49     const char *bit_label;
50 
51     printf("  input props:\n");
52     res = ioctl(fd, EVIOCGPROP(sizeof(bits)), bits);
53     if(res < 0) {
54         printf("    <not available\n");
55         return 1;
56     }
57     count = 0;
58     for(i = 0; i < res; i++) {
59         for(j = 0; j < 8; j++) {
60             if (bits[i] & 1 << j) {
61                 bit_label = get_label(input_prop_labels, i * 8 + j);
62                 if(bit_label)
63                     printf("    %s\n", bit_label);
64                 else
65                     printf("    %04x\n", i * 8 + j);
66                 count++;
67             }
68         }
69     }
70     if (!count)
71         printf("    <none>\n");
72     return 0;
73 }
74 
print_possible_events(int fd,int print_flags)75 static int print_possible_events(int fd, int print_flags)
76 {
77     uint8_t *bits = NULL;
78     ssize_t bits_size = 0;
79     const char* label;
80     int i, j, k;
81     int res, res2;
82     struct label* bit_labels;
83     const char *bit_label;
84 
85     printf("  events:\n");
86     for(i = EV_KEY; i <= EV_MAX; i++) { // skip EV_SYN since we cannot query its available codes
87         int count = 0;
88         while(1) {
89             res = ioctl(fd, EVIOCGBIT(i, bits_size), bits);
90             if(res < bits_size)
91                 break;
92             bits_size = res + 16;
93             bits = realloc(bits, bits_size * 2);
94             if(bits == NULL) {
95                 fprintf(stderr, "failed to allocate buffer of size %d\n", (int)bits_size);
96                 return 1;
97             }
98         }
99         res2 = 0;
100         switch(i) {
101             case EV_KEY:
102                 res2 = ioctl(fd, EVIOCGKEY(res), bits + bits_size);
103                 label = "KEY";
104                 bit_labels = key_labels;
105                 break;
106             case EV_REL:
107                 label = "REL";
108                 bit_labels = rel_labels;
109                 break;
110             case EV_ABS:
111                 label = "ABS";
112                 bit_labels = abs_labels;
113                 break;
114             case EV_MSC:
115                 label = "MSC";
116                 bit_labels = msc_labels;
117                 break;
118             case EV_LED:
119                 res2 = ioctl(fd, EVIOCGLED(res), bits + bits_size);
120                 label = "LED";
121                 bit_labels = led_labels;
122                 break;
123             case EV_SND:
124                 res2 = ioctl(fd, EVIOCGSND(res), bits + bits_size);
125                 label = "SND";
126                 bit_labels = snd_labels;
127                 break;
128             case EV_SW:
129                 res2 = ioctl(fd, EVIOCGSW(bits_size), bits + bits_size);
130                 label = "SW ";
131                 bit_labels = sw_labels;
132                 break;
133             case EV_REP:
134                 label = "REP";
135                 bit_labels = rep_labels;
136                 break;
137             case EV_FF:
138                 label = "FF ";
139                 bit_labels = ff_labels;
140                 break;
141             case EV_PWR:
142                 label = "PWR";
143                 bit_labels = NULL;
144                 break;
145             case EV_FF_STATUS:
146                 label = "FFS";
147                 bit_labels = ff_status_labels;
148                 break;
149             default:
150                 res2 = 0;
151                 label = "???";
152                 bit_labels = NULL;
153         }
154         for(j = 0; j < res; j++) {
155             for(k = 0; k < 8; k++)
156                 if(bits[j] & 1 << k) {
157                     char down;
158                     if(j < res2 && (bits[j + bits_size] & 1 << k))
159                         down = '*';
160                     else
161                         down = ' ';
162                     if(count == 0)
163                         printf("    %s (%04x):", label, i);
164                     else if((count & (print_flags & PRINT_LABELS ? 0x3 : 0x7)) == 0 || i == EV_ABS)
165                         printf("\n               ");
166                     if(bit_labels && (print_flags & PRINT_LABELS)) {
167                         bit_label = get_label(bit_labels, j * 8 + k);
168                         if(bit_label)
169                             printf(" %.20s%c%*s", bit_label, down, 20 - strlen(bit_label), "");
170                         else
171                             printf(" %04x%c                ", j * 8 + k, down);
172                     } else {
173                         printf(" %04x%c", j * 8 + k, down);
174                     }
175                     if(i == EV_ABS) {
176                         struct input_absinfo abs;
177                         if(ioctl(fd, EVIOCGABS(j * 8 + k), &abs) == 0) {
178                             printf(" : value %d, min %d, max %d, fuzz %d, flat %d, resolution %d",
179                                 abs.value, abs.minimum, abs.maximum, abs.fuzz, abs.flat,
180                                 abs.resolution);
181                         }
182                     }
183                     count++;
184                 }
185         }
186         if(count)
187             printf("\n");
188     }
189     free(bits);
190     return 0;
191 }
192 
print_event(int type,int code,int value,int print_flags)193 static void print_event(int type, int code, int value, int print_flags)
194 {
195     const char *type_label, *code_label, *value_label;
196 
197     if (print_flags & PRINT_LABELS) {
198         type_label = get_label(ev_labels, type);
199         code_label = NULL;
200         value_label = NULL;
201 
202         switch(type) {
203             case EV_SYN:
204                 code_label = get_label(syn_labels, code);
205                 break;
206             case EV_KEY:
207                 code_label = get_label(key_labels, code);
208                 value_label = get_label(key_value_labels, value);
209                 break;
210             case EV_REL:
211                 code_label = get_label(rel_labels, code);
212                 break;
213             case EV_ABS:
214                 code_label = get_label(abs_labels, code);
215                 switch(code) {
216                     case ABS_MT_TOOL_TYPE:
217                         value_label = get_label(mt_tool_labels, value);
218                 }
219                 break;
220             case EV_MSC:
221                 code_label = get_label(msc_labels, code);
222                 break;
223             case EV_LED:
224                 code_label = get_label(led_labels, code);
225                 break;
226             case EV_SND:
227                 code_label = get_label(snd_labels, code);
228                 break;
229             case EV_SW:
230                 code_label = get_label(sw_labels, code);
231                 break;
232             case EV_REP:
233                 code_label = get_label(rep_labels, code);
234                 break;
235             case EV_FF:
236                 code_label = get_label(ff_labels, code);
237                 break;
238             case EV_FF_STATUS:
239                 code_label = get_label(ff_status_labels, code);
240                 break;
241         }
242 
243         if (type_label)
244             printf("%-12.12s", type_label);
245         else
246             printf("%04x        ", type);
247         if (code_label)
248             printf(" %-20.20s", code_label);
249         else
250             printf(" %04x                ", code);
251         if (value_label)
252             printf(" %-20.20s", value_label);
253         else
254             printf(" %08x            ", value);
255     } else {
256         printf("%04x %04x %08x", type, code, value);
257     }
258 }
259 
print_hid_descriptor(int bus,int vendor,int product)260 static void print_hid_descriptor(int bus, int vendor, int product)
261 {
262     const char *dirname = "/sys/kernel/debug/hid";
263     char prefix[16];
264     DIR *dir;
265     struct dirent *de;
266     char filename[PATH_MAX];
267     FILE *file;
268     char line[2048];
269 
270     snprintf(prefix, sizeof(prefix), "%04X:%04X:%04X.", bus, vendor, product);
271 
272     dir = opendir(dirname);
273     if(dir == NULL)
274         return;
275     while((de = readdir(dir))) {
276         if (strstr(de->d_name, prefix) == de->d_name) {
277             snprintf(filename, sizeof(filename), "%s/%s/rdesc", dirname, de->d_name);
278 
279             file = fopen(filename, "r");
280             if (file) {
281                 printf("  HID descriptor: %s\n\n", de->d_name);
282                 while (fgets(line, sizeof(line), file)) {
283                     fputs("    ", stdout);
284                     fputs(line, stdout);
285                 }
286                 fclose(file);
287                 puts("");
288             }
289         }
290     }
291     closedir(dir);
292 }
293 
open_device(const char * device,int print_flags)294 static int open_device(const char *device, int print_flags)
295 {
296     int version;
297     int fd;
298     struct pollfd *new_ufds;
299     char **new_device_names;
300     char name[80];
301     char location[80];
302     char idstr[80];
303     struct input_id id;
304 
305     fd = open(device, O_RDWR);
306     if(fd < 0) {
307         if(print_flags & PRINT_DEVICE_ERRORS)
308             fprintf(stderr, "could not open %s, %s\n", device, strerror(errno));
309         return -1;
310     }
311 
312     if(ioctl(fd, EVIOCGVERSION, &version)) {
313         if(print_flags & PRINT_DEVICE_ERRORS)
314             fprintf(stderr, "could not get driver version for %s, %s\n", device, strerror(errno));
315         return -1;
316     }
317     if(ioctl(fd, EVIOCGID, &id)) {
318         if(print_flags & PRINT_DEVICE_ERRORS)
319             fprintf(stderr, "could not get driver id for %s, %s\n", device, strerror(errno));
320         return -1;
321     }
322     name[sizeof(name) - 1] = '\0';
323     location[sizeof(location) - 1] = '\0';
324     idstr[sizeof(idstr) - 1] = '\0';
325     if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {
326         //fprintf(stderr, "could not get device name for %s, %s\n", device, strerror(errno));
327         name[0] = '\0';
328     }
329     if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) {
330         //fprintf(stderr, "could not get location for %s, %s\n", device, strerror(errno));
331         location[0] = '\0';
332     }
333     if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) {
334         //fprintf(stderr, "could not get idstring for %s, %s\n", device, strerror(errno));
335         idstr[0] = '\0';
336     }
337 
338     new_ufds = realloc(ufds, sizeof(ufds[0]) * (nfds + 1));
339     if(new_ufds == NULL) {
340         fprintf(stderr, "out of memory\n");
341         return -1;
342     }
343     ufds = new_ufds;
344     new_device_names = realloc(device_names, sizeof(device_names[0]) * (nfds + 1));
345     if(new_device_names == NULL) {
346         fprintf(stderr, "out of memory\n");
347         return -1;
348     }
349     device_names = new_device_names;
350 
351     if(print_flags & PRINT_DEVICE)
352         printf("add device %d: %s\n", nfds, device);
353     if(print_flags & PRINT_DEVICE_INFO)
354         printf("  bus:      %04x\n"
355                "  vendor    %04x\n"
356                "  product   %04x\n"
357                "  version   %04x\n",
358                id.bustype, id.vendor, id.product, id.version);
359     if(print_flags & PRINT_DEVICE_NAME)
360         printf("  name:     \"%s\"\n", name);
361     if(print_flags & PRINT_DEVICE_INFO)
362         printf("  location: \"%s\"\n"
363                "  id:       \"%s\"\n", location, idstr);
364     if(print_flags & PRINT_VERSION)
365         printf("  version:  %d.%d.%d\n",
366                version >> 16, (version >> 8) & 0xff, version & 0xff);
367 
368     if(print_flags & PRINT_POSSIBLE_EVENTS) {
369         print_possible_events(fd, print_flags);
370     }
371 
372     if(print_flags & PRINT_INPUT_PROPS) {
373         print_input_props(fd);
374     }
375     if(print_flags & PRINT_HID_DESCRIPTOR) {
376         print_hid_descriptor(id.bustype, id.vendor, id.product);
377     }
378 
379     ufds[nfds].fd = fd;
380     ufds[nfds].events = POLLIN;
381     device_names[nfds] = strdup(device);
382     nfds++;
383 
384     return 0;
385 }
386 
close_device(const char * device,int print_flags)387 int close_device(const char *device, int print_flags)
388 {
389     int i;
390     for(i = 1; i < nfds; i++) {
391         if(strcmp(device_names[i], device) == 0) {
392             int count = nfds - i - 1;
393             if(print_flags & PRINT_DEVICE)
394                 printf("remove device %d: %s\n", i, device);
395             free(device_names[i]);
396             memmove(device_names + i, device_names + i + 1, sizeof(device_names[0]) * count);
397             memmove(ufds + i, ufds + i + 1, sizeof(ufds[0]) * count);
398             nfds--;
399             return 0;
400         }
401     }
402     if(print_flags & PRINT_DEVICE_ERRORS)
403         fprintf(stderr, "remote device: %s not found\n", device);
404     return -1;
405 }
406 
read_notify(const char * dirname,int nfd,int print_flags)407 static int read_notify(const char *dirname, int nfd, int print_flags)
408 {
409     int res;
410     char devname[PATH_MAX];
411     char *filename;
412     char event_buf[512];
413     int event_size;
414     int event_pos = 0;
415     struct inotify_event *event;
416 
417     res = read(nfd, event_buf, sizeof(event_buf));
418     if(res < (int)sizeof(*event)) {
419         if(errno == EINTR)
420             return 0;
421         fprintf(stderr, "could not get event, %s\n", strerror(errno));
422         return 1;
423     }
424     //printf("got %d bytes of event information\n", res);
425 
426     strcpy(devname, dirname);
427     filename = devname + strlen(devname);
428     *filename++ = '/';
429 
430     while(res >= (int)sizeof(*event)) {
431         event = (struct inotify_event *)(event_buf + event_pos);
432         //printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : "");
433         if(event->len) {
434             strcpy(filename, event->name);
435             if(event->mask & IN_CREATE) {
436                 open_device(devname, print_flags);
437             }
438             else {
439                 close_device(devname, print_flags);
440             }
441         }
442         event_size = sizeof(*event) + event->len;
443         res -= event_size;
444         event_pos += event_size;
445     }
446     return 0;
447 }
448 
scan_dir(const char * dirname,int print_flags)449 static int scan_dir(const char *dirname, int print_flags)
450 {
451     char devname[PATH_MAX];
452     char *filename;
453     DIR *dir;
454     struct dirent *de;
455     dir = opendir(dirname);
456     if(dir == NULL)
457         return -1;
458     strcpy(devname, dirname);
459     filename = devname + strlen(devname);
460     *filename++ = '/';
461     while((de = readdir(dir))) {
462         if(de->d_name[0] == '.' &&
463            (de->d_name[1] == '\0' ||
464             (de->d_name[1] == '.' && de->d_name[2] == '\0')))
465             continue;
466         strcpy(filename, de->d_name);
467         open_device(devname, print_flags);
468     }
469     closedir(dir);
470     return 0;
471 }
472 
usage(int argc,char * argv[])473 static void usage(int argc, char *argv[])
474 {
475     fprintf(stderr, "Usage: %s [-t] [-n] [-s switchmask] [-S] [-v [mask]] [-d] [-p] [-i] [-l] [-q] [-c count] [-r] [device]\n", argv[0]);
476     fprintf(stderr, "    -t: show time stamps\n");
477     fprintf(stderr, "    -n: don't print newlines\n");
478     fprintf(stderr, "    -s: print switch states for given bits\n");
479     fprintf(stderr, "    -S: print all switch states\n");
480     fprintf(stderr, "    -v: verbosity mask (errs=1, dev=2, name=4, info=8, vers=16, pos. events=32, props=64)\n");
481     fprintf(stderr, "    -d: show HID descriptor, if available\n");
482     fprintf(stderr, "    -p: show possible events (errs, dev, name, pos. events)\n");
483     fprintf(stderr, "    -i: show all device info and possible events\n");
484     fprintf(stderr, "    -l: label event types and names in plain text\n");
485     fprintf(stderr, "    -q: quiet (clear verbosity mask)\n");
486     fprintf(stderr, "    -c: print given number of events then exit\n");
487     fprintf(stderr, "    -r: print rate events are received\n");
488 }
489 
getevent_main(int argc,char * argv[])490 int getevent_main(int argc, char *argv[])
491 {
492     int c;
493     int i;
494     int res;
495     int pollres;
496     int get_time = 0;
497     int print_device = 0;
498     char *newline = "\n";
499     uint16_t get_switch = 0;
500     struct input_event event;
501     int version;
502     int print_flags = 0;
503     int print_flags_set = 0;
504     int dont_block = -1;
505     int event_count = 0;
506     int sync_rate = 0;
507     int64_t last_sync_time = 0;
508     const char *device = NULL;
509     const char *device_path = "/dev/input";
510 
511     opterr = 0;
512     do {
513         c = getopt(argc, argv, "tns:Sv::dpilqc:rh");
514         if (c == EOF)
515             break;
516         switch (c) {
517         case 't':
518             get_time = 1;
519             break;
520         case 'n':
521             newline = "";
522             break;
523         case 's':
524             get_switch = strtoul(optarg, NULL, 0);
525             if(dont_block == -1)
526                 dont_block = 1;
527             break;
528         case 'S':
529             get_switch = ~0;
530             if(dont_block == -1)
531                 dont_block = 1;
532             break;
533         case 'v':
534             if(optarg)
535                 print_flags |= strtoul(optarg, NULL, 0);
536             else
537                 print_flags |= PRINT_DEVICE | PRINT_DEVICE_NAME | PRINT_DEVICE_INFO | PRINT_VERSION;
538             print_flags_set = 1;
539             break;
540         case 'd':
541             print_flags |= PRINT_HID_DESCRIPTOR;
542             break;
543         case 'p':
544             print_flags |= PRINT_DEVICE_ERRORS | PRINT_DEVICE
545                     | PRINT_DEVICE_NAME | PRINT_POSSIBLE_EVENTS | PRINT_INPUT_PROPS;
546             print_flags_set = 1;
547             if(dont_block == -1)
548                 dont_block = 1;
549             break;
550         case 'i':
551             print_flags |= PRINT_ALL_INFO;
552             print_flags_set = 1;
553             if(dont_block == -1)
554                 dont_block = 1;
555             break;
556         case 'l':
557             print_flags |= PRINT_LABELS;
558             break;
559         case 'q':
560             print_flags_set = 1;
561             break;
562         case 'c':
563             event_count = atoi(optarg);
564             dont_block = 0;
565             break;
566         case 'r':
567             sync_rate = 1;
568             break;
569         case '?':
570             fprintf(stderr, "%s: invalid option -%c\n",
571                 argv[0], optopt);
572         case 'h':
573             usage(argc, argv);
574             exit(1);
575         }
576     } while (1);
577     if(dont_block == -1)
578         dont_block = 0;
579 
580     if (optind + 1 == argc) {
581         device = argv[optind];
582         optind++;
583     }
584     if (optind != argc) {
585         usage(argc, argv);
586         exit(1);
587     }
588     nfds = 1;
589     ufds = calloc(1, sizeof(ufds[0]));
590     ufds[0].fd = inotify_init();
591     ufds[0].events = POLLIN;
592     if(device) {
593         if(!print_flags_set)
594             print_flags |= PRINT_DEVICE_ERRORS;
595         res = open_device(device, print_flags);
596         if(res < 0) {
597             return 1;
598         }
599     } else {
600         if(!print_flags_set)
601             print_flags |= PRINT_DEVICE_ERRORS | PRINT_DEVICE | PRINT_DEVICE_NAME;
602         print_device = 1;
603 		res = inotify_add_watch(ufds[0].fd, device_path, IN_DELETE | IN_CREATE);
604         if(res < 0) {
605             fprintf(stderr, "could not add watch for %s, %s\n", device_path, strerror(errno));
606             return 1;
607         }
608         res = scan_dir(device_path, print_flags);
609         if(res < 0) {
610             fprintf(stderr, "scan dir failed for %s\n", device_path);
611             return 1;
612         }
613     }
614 
615     if(get_switch) {
616         for(i = 1; i < nfds; i++) {
617             uint16_t sw;
618             res = ioctl(ufds[i].fd, EVIOCGSW(1), &sw);
619             if(res < 0) {
620                 fprintf(stderr, "could not get switch state, %s\n", strerror(errno));
621                 return 1;
622             }
623             sw &= get_switch;
624             printf("%04x%s", sw, newline);
625         }
626     }
627 
628     if(dont_block)
629         return 0;
630 
631     while(1) {
632         pollres = poll(ufds, nfds, -1);
633         //printf("poll %d, returned %d\n", nfds, pollres);
634         if(ufds[0].revents & POLLIN) {
635             read_notify(device_path, ufds[0].fd, print_flags);
636         }
637         for(i = 1; i < nfds; i++) {
638             if(ufds[i].revents) {
639                 if(ufds[i].revents & POLLIN) {
640                     res = read(ufds[i].fd, &event, sizeof(event));
641                     if(res < (int)sizeof(event)) {
642                         fprintf(stderr, "could not get event\n");
643                         return 1;
644                     }
645                     if(get_time) {
646                         printf("[%8ld.%06ld] ", event.time.tv_sec, event.time.tv_usec);
647                     }
648                     if(print_device)
649                         printf("%s: ", device_names[i]);
650                     print_event(event.type, event.code, event.value, print_flags);
651                     if(sync_rate && event.type == 0 && event.code == 0) {
652                         int64_t now = event.time.tv_sec * 1000000LL + event.time.tv_usec;
653                         if(last_sync_time)
654                             printf(" rate %lld", 1000000LL / (now - last_sync_time));
655                         last_sync_time = now;
656                     }
657                     printf("%s", newline);
658                     if(event_count && --event_count == 0)
659                         return 0;
660                 }
661             }
662         }
663     }
664 
665     return 0;
666 }
667