• 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> // this does not compile
12 #include <errno.h>
13 
14 static struct pollfd *ufds;
15 static char **device_names;
16 static int nfds;
17 
18 enum {
19     PRINT_DEVICE_ERRORS     = 1U << 0,
20     PRINT_DEVICE            = 1U << 1,
21     PRINT_DEVICE_NAME       = 1U << 2,
22     PRINT_DEVICE_INFO       = 1U << 3,
23     PRINT_VERSION           = 1U << 4,
24     PRINT_POSSIBLE_EVENTS   = 1U << 5,
25 };
26 
print_possible_events(int fd)27 static int print_possible_events(int fd)
28 {
29     uint8_t *bits = NULL;
30     ssize_t bits_size = 0;
31     const char* label;
32     int i, j, k;
33     int res, res2;
34 
35     printf("  events:\n");
36     for(i = 0; i <= EV_MAX; i++) {
37         int count = 0;
38         while(1) {
39             res = ioctl(fd, EVIOCGBIT(i, bits_size), bits);
40             if(res < bits_size)
41                 break;
42             bits_size = res + 16;
43             bits = realloc(bits, bits_size * 2);
44             if(bits == NULL) {
45                 fprintf(stderr, "failed to allocate buffer of size %d\n", bits_size);
46                 return 1;
47             }
48         }
49         res2 = 0;
50         switch(i) {
51             case EV_SYN:
52                 label = "SYN";
53                 break;
54             case EV_KEY:
55                 res2 = ioctl(fd, EVIOCGKEY(res), bits + bits_size);
56                 label = "KEY";
57                 break;
58             case EV_REL:
59                 label = "REL";
60                 break;
61             case EV_ABS:
62                 label = "ABS";
63                 break;
64             case EV_MSC:
65                 label = "MSC";
66                 break;
67             case EV_LED:
68                 res2 = ioctl(fd, EVIOCGLED(res), bits + bits_size);
69                 label = "LED";
70                 break;
71             case EV_SND:
72                 res2 = ioctl(fd, EVIOCGSND(res), bits + bits_size);
73                 label = "SND";
74                 break;
75             case EV_SW:
76                 res2 = ioctl(fd, EVIOCGSW(bits_size), bits + bits_size);
77                 label = "SW ";
78                 break;
79             case EV_REP:
80                 label = "REP";
81                 break;
82             case EV_FF:
83                 label = "FF ";
84                 break;
85             case EV_PWR:
86                 label = "PWR";
87                 break;
88             default:
89                 res2 = 0;
90                 label = "???";
91         }
92         for(j = 0; j < res; j++) {
93             for(k = 0; k < 8; k++)
94                 if(bits[j] & 1 << k) {
95                     char down;
96                     if(j < res2 && (bits[j + bits_size] & 1 << k))
97                         down = '*';
98                     else
99                         down = ' ';
100                     if(count == 0)
101                         printf("    %s (%04x):", label, i);
102                     else if((count & 0x7) == 0 || i == EV_ABS)
103                         printf("\n               ");
104                     printf(" %04x%c", j * 8 + k, down);
105                     if(i == EV_ABS) {
106                         struct input_absinfo abs;
107                         if(ioctl(fd, EVIOCGABS(j * 8 + k), &abs) == 0) {
108                             printf(" value %d, min %d, max %d, fuzz %d flat %d", abs.value, abs.minimum, abs.maximum, abs.fuzz, abs.flat);
109                         }
110                     }
111                     count++;
112                 }
113         }
114         if(count)
115             printf("\n");
116     }
117     free(bits);
118     return 0;
119 }
120 
open_device(const char * device,int print_flags)121 static int open_device(const char *device, int print_flags)
122 {
123     int version;
124     int fd;
125     struct pollfd *new_ufds;
126     char **new_device_names;
127     char name[80];
128     char location[80];
129     char idstr[80];
130     struct input_id id;
131 
132     fd = open(device, O_RDWR);
133     if(fd < 0) {
134         if(print_flags & PRINT_DEVICE_ERRORS)
135             fprintf(stderr, "could not open %s, %s\n", device, strerror(errno));
136         return -1;
137     }
138 
139     if(ioctl(fd, EVIOCGVERSION, &version)) {
140         if(print_flags & PRINT_DEVICE_ERRORS)
141             fprintf(stderr, "could not get driver version for %s, %s\n", device, strerror(errno));
142         return -1;
143     }
144     if(ioctl(fd, EVIOCGID, &id)) {
145         if(print_flags & PRINT_DEVICE_ERRORS)
146             fprintf(stderr, "could not get driver id for %s, %s\n", device, strerror(errno));
147         return -1;
148     }
149     name[sizeof(name) - 1] = '\0';
150     location[sizeof(location) - 1] = '\0';
151     idstr[sizeof(idstr) - 1] = '\0';
152     if(ioctl(fd, EVIOCGNAME(sizeof(name) - 1), &name) < 1) {
153         //fprintf(stderr, "could not get device name for %s, %s\n", device, strerror(errno));
154         name[0] = '\0';
155     }
156     if(ioctl(fd, EVIOCGPHYS(sizeof(location) - 1), &location) < 1) {
157         //fprintf(stderr, "could not get location for %s, %s\n", device, strerror(errno));
158         location[0] = '\0';
159     }
160     if(ioctl(fd, EVIOCGUNIQ(sizeof(idstr) - 1), &idstr) < 1) {
161         //fprintf(stderr, "could not get idstring for %s, %s\n", device, strerror(errno));
162         idstr[0] = '\0';
163     }
164 
165     new_ufds = realloc(ufds, sizeof(ufds[0]) * (nfds + 1));
166     if(new_ufds == NULL) {
167         fprintf(stderr, "out of memory\n");
168         return -1;
169     }
170     ufds = new_ufds;
171     new_device_names = realloc(device_names, sizeof(device_names[0]) * (nfds + 1));
172     if(new_device_names == NULL) {
173         fprintf(stderr, "out of memory\n");
174         return -1;
175     }
176     device_names = new_device_names;
177 
178     if(print_flags & PRINT_DEVICE)
179         printf("add device %d: %s\n", nfds, device);
180     if(print_flags & PRINT_DEVICE_INFO)
181         printf("  bus:      %04x\n"
182                "  vendor    %04x\n"
183                "  product   %04x\n"
184                "  version   %04x\n",
185                id.bustype, id.vendor, id.product, id.version);
186     if(print_flags & PRINT_DEVICE_NAME)
187         printf("  name:     \"%s\"\n", name);
188     if(print_flags & PRINT_DEVICE_INFO)
189         printf("  location: \"%s\"\n"
190                "  id:       \"%s\"\n", location, idstr);
191     if(print_flags & PRINT_VERSION)
192         printf("  version:  %d.%d.%d\n",
193                version >> 16, (version >> 8) & 0xff, version & 0xff);
194 
195     if(print_flags & PRINT_POSSIBLE_EVENTS) {
196         print_possible_events(fd);
197     }
198 
199     ufds[nfds].fd = fd;
200     ufds[nfds].events = POLLIN;
201     device_names[nfds] = strdup(device);
202     nfds++;
203 
204     return 0;
205 }
206 
close_device(const char * device,int print_flags)207 int close_device(const char *device, int print_flags)
208 {
209     int i;
210     for(i = 1; i < nfds; i++) {
211         if(strcmp(device_names[i], device) == 0) {
212             int count = nfds - i - 1;
213             if(print_flags & PRINT_DEVICE)
214                 printf("remove device %d: %s\n", i, device);
215             free(device_names[i]);
216             memmove(device_names + i, device_names + i + 1, sizeof(device_names[0]) * count);
217             memmove(ufds + i, ufds + i + 1, sizeof(ufds[0]) * count);
218             nfds--;
219             return 0;
220         }
221     }
222     if(print_flags & PRINT_DEVICE_ERRORS)
223         fprintf(stderr, "remote device: %s not found\n", device);
224     return -1;
225 }
226 
read_notify(const char * dirname,int nfd,int print_flags)227 static int read_notify(const char *dirname, int nfd, int print_flags)
228 {
229     int res;
230     char devname[PATH_MAX];
231     char *filename;
232     char event_buf[512];
233     int event_size;
234     int event_pos = 0;
235     struct inotify_event *event;
236 
237     res = read(nfd, event_buf, sizeof(event_buf));
238     if(res < (int)sizeof(*event)) {
239         if(errno == EINTR)
240             return 0;
241         fprintf(stderr, "could not get event, %s\n", strerror(errno));
242         return 1;
243     }
244     //printf("got %d bytes of event information\n", res);
245 
246     strcpy(devname, dirname);
247     filename = devname + strlen(devname);
248     *filename++ = '/';
249 
250     while(res >= (int)sizeof(*event)) {
251         event = (struct inotify_event *)(event_buf + event_pos);
252         //printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : "");
253         if(event->len) {
254             strcpy(filename, event->name);
255             if(event->mask & IN_CREATE) {
256                 open_device(devname, print_flags);
257             }
258             else {
259                 close_device(devname, print_flags);
260             }
261         }
262         event_size = sizeof(*event) + event->len;
263         res -= event_size;
264         event_pos += event_size;
265     }
266     return 0;
267 }
268 
scan_dir(const char * dirname,int print_flags)269 static int scan_dir(const char *dirname, int print_flags)
270 {
271     char devname[PATH_MAX];
272     char *filename;
273     DIR *dir;
274     struct dirent *de;
275     dir = opendir(dirname);
276     if(dir == NULL)
277         return -1;
278     strcpy(devname, dirname);
279     filename = devname + strlen(devname);
280     *filename++ = '/';
281     while((de = readdir(dir))) {
282         if(de->d_name[0] == '.' &&
283            (de->d_name[1] == '\0' ||
284             (de->d_name[1] == '.' && de->d_name[2] == '\0')))
285             continue;
286         strcpy(filename, de->d_name);
287         open_device(devname, print_flags);
288     }
289     closedir(dir);
290     return 0;
291 }
292 
usage(int argc,char * argv[])293 static void usage(int argc, char *argv[])
294 {
295     fprintf(stderr, "Usage: %s [-t] [-n] [-s switchmask] [-S] [-v [mask]] [-p] [-q] [-c count] [-r] [device]\n", argv[0]);
296     fprintf(stderr, "    -t: show time stamps\n");
297     fprintf(stderr, "    -n: don't print newlines\n");
298     fprintf(stderr, "    -s: print switch states for given bits\n");
299     fprintf(stderr, "    -S: print all switch states\n");
300     fprintf(stderr, "    -v: verbosity mask (errs=1, dev=2, name=4, info=8, vers=16, pos. events=32)\n");
301     fprintf(stderr, "    -p: show possible events (errs, dev, name, pos. events)\n");
302     fprintf(stderr, "    -q: quiet (clear verbosity mask)\n");
303     fprintf(stderr, "    -c: print given number of events then exit\n");
304     fprintf(stderr, "    -r: print rate events are received\n");
305 }
306 
getevent_main(int argc,char * argv[])307 int getevent_main(int argc, char *argv[])
308 {
309     int c;
310     int i;
311     int res;
312     int pollres;
313     int get_time = 0;
314     int print_device = 0;
315     char *newline = "\n";
316     uint16_t get_switch = 0;
317     struct input_event event;
318     int version;
319     int print_flags = PRINT_DEVICE_ERRORS | PRINT_DEVICE | PRINT_DEVICE_NAME;
320     int print_flags_set = 0;
321     int dont_block = -1;
322     int event_count = 0;
323     int sync_rate = 0;
324     int64_t last_sync_time = 0;
325     const char *device = NULL;
326     const char *device_path = "/dev/input";
327 
328     opterr = 0;
329     do {
330         c = getopt(argc, argv, "tns:Sv::pqc:rh");
331         if (c == EOF)
332             break;
333         switch (c) {
334         case 't':
335             get_time = 1;
336             break;
337         case 'n':
338             newline = "";
339             break;
340         case 's':
341             get_switch = strtoul(optarg, NULL, 0);
342             if(dont_block == -1)
343                 dont_block = 1;
344             break;
345         case 'S':
346             get_switch = ~0;
347             if(dont_block == -1)
348                 dont_block = 1;
349             break;
350         case 'v':
351             if(optarg)
352                 print_flags =  strtoul(optarg, NULL, 0);
353             else
354                 print_flags |= PRINT_DEVICE | PRINT_DEVICE_NAME | PRINT_DEVICE_INFO | PRINT_VERSION;
355             print_flags_set = 1;
356             break;
357         case 'p':
358             print_flags = PRINT_DEVICE_ERRORS | PRINT_DEVICE | PRINT_DEVICE_NAME | PRINT_POSSIBLE_EVENTS;
359             print_flags_set = 1;
360             if(dont_block == -1)
361                 dont_block = 1;
362             break;
363         case 'q':
364             print_flags = 0;
365             print_flags_set = 1;
366             break;
367         case 'c':
368             event_count = atoi(optarg);
369             dont_block = 0;
370             break;
371         case 'r':
372             sync_rate = 1;
373             break;
374         case '?':
375             fprintf(stderr, "%s: invalid option -%c\n",
376                 argv[0], optopt);
377         case 'h':
378             usage(argc, argv);
379             exit(1);
380         }
381     } while (1);
382     if(dont_block == -1)
383         dont_block = 0;
384 
385     if (optind + 1 == argc) {
386         device = argv[optind];
387         optind++;
388     }
389     if (optind != argc) {
390         usage(argc, argv);
391         exit(1);
392     }
393     nfds = 1;
394     ufds = calloc(1, sizeof(ufds[0]));
395     ufds[0].fd = inotify_init();
396     ufds[0].events = POLLIN;
397     if(device) {
398         if(!print_flags_set)
399             print_flags = PRINT_DEVICE_ERRORS;
400         res = open_device(device, print_flags);
401         if(res < 0) {
402             return 1;
403         }
404     }
405     else {
406         print_device = 1;
407 		res = inotify_add_watch(ufds[0].fd, device_path, IN_DELETE | IN_CREATE);
408         if(res < 0) {
409             fprintf(stderr, "could not add watch for %s, %s\n", device_path, strerror(errno));
410             return 1;
411         }
412         res = scan_dir(device_path, print_flags);
413         if(res < 0) {
414             fprintf(stderr, "scan dir failed for %s\n", device_path);
415             return 1;
416         }
417     }
418 
419     if(get_switch) {
420         for(i = 1; i < nfds; i++) {
421             uint16_t sw;
422             res = ioctl(ufds[i].fd, EVIOCGSW(1), &sw);
423             if(res < 0) {
424                 fprintf(stderr, "could not get switch state, %s\n", strerror(errno));
425                 return 1;
426             }
427             sw &= get_switch;
428             printf("%04x%s", sw, newline);
429         }
430     }
431 
432     if(dont_block)
433         return 0;
434 
435     while(1) {
436         pollres = poll(ufds, nfds, -1);
437         //printf("poll %d, returned %d\n", nfds, pollres);
438         if(ufds[0].revents & POLLIN) {
439             read_notify(device_path, ufds[0].fd, print_flags);
440         }
441         for(i = 1; i < nfds; i++) {
442             if(ufds[i].revents) {
443                 if(ufds[i].revents & POLLIN) {
444                     res = read(ufds[i].fd, &event, sizeof(event));
445                     if(res < (int)sizeof(event)) {
446                         fprintf(stderr, "could not get event\n");
447                         return 1;
448                     }
449                     if(get_time) {
450                         printf("%ld-%ld: ", event.time.tv_sec, event.time.tv_usec);
451                     }
452                     if(print_device)
453                         printf("%s: ", device_names[i]);
454                     printf("%04x %04x %08x", event.type, event.code, event.value);
455                     if(sync_rate && event.type == 0 && event.code == 0) {
456                         int64_t now = event.time.tv_sec * 1000000LL + event.time.tv_usec;
457                         if(last_sync_time)
458                             printf(" rate %lld", 1000000LL / (now - last_sync_time));
459                         last_sync_time = now;
460                     }
461                     printf("%s", newline);
462                     if(event_count && --event_count == 0)
463                         return 0;
464                 }
465             }
466         }
467     }
468 
469     return 0;
470 }
471