diff options
author | Daniel Drake <dsd@laptop.org> | 2011-07-20 18:25:03 +0100 |
---|---|---|
committer | Peter Hutterer <peter.hutterer@who-t.net> | 2011-07-22 09:55:37 +1000 |
commit | 8a10dfe3b7bc53b319107de89434922a2c79ef94 (patch) | |
tree | 4bc3f50a848f0b4161a38c1775e38cfb84d08701 | |
parent | 6015b4c4cf0efeb845673c37d80f3049e86c04a1 (diff) |
Add one-shot query functionality
Add functionality to query evdev state of a specific key, switch, button,
LED or sound event. This is useful in programs such as powerd
(http://wiki.laptop.org/go/Powerd) which need to query things like the
state of the laptop lid switch from shell code.
Original capture-mode functionality is left unchanged and is still
activated by default. New usage modes are explained in the man page.
Signed-off-by: Daniel Drake <dsd@laptop.org>
Reviewed-by: Peter Hutterer <peter.hutterer@who-t.net>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
-rw-r--r-- | evtest.c | 248 | ||||
-rw-r--r-- | evtest.txt | 33 |
2 files changed, 266 insertions, 15 deletions
@@ -49,6 +49,8 @@ | |||
49 | #include <stdlib.h> | 49 | #include <stdlib.h> |
50 | #include <dirent.h> | 50 | #include <dirent.h> |
51 | #include <errno.h> | 51 | #include <errno.h> |
52 | #include <getopt.h> | ||
53 | #include <ctype.h> | ||
52 | 54 | ||
53 | #define BITS_PER_LONG (sizeof(long) * 8) | 55 | #define BITS_PER_LONG (sizeof(long) * 8) |
54 | #define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) | 56 | #define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) |
@@ -69,6 +71,83 @@ | |||
69 | 71 | ||
70 | #define NAME_ELEMENT(element) [element] = #element | 72 | #define NAME_ELEMENT(element) [element] = #element |
71 | 73 | ||
74 | enum evtest_mode { | ||
75 | MODE_CAPTURE, | ||
76 | MODE_QUERY, | ||
77 | }; | ||
78 | |||
79 | static const struct query_mode { | ||
80 | const char *name; | ||
81 | int event_type; | ||
82 | int max; | ||
83 | int rq; | ||
84 | } query_modes[] = { | ||
85 | { "EV_KEY", EV_KEY, KEY_MAX, EVIOCGKEY(KEY_MAX) }, | ||
86 | { "EV_LED", EV_LED, LED_MAX, EVIOCGLED(LED_MAX) }, | ||
87 | { "EV_SND", EV_SND, SND_MAX, EVIOCGSND(SND_MAX) }, | ||
88 | { "EV_SW", EV_SW, SW_MAX, EVIOCGSW(SW_MAX) }, | ||
89 | }; | ||
90 | |||
91 | /** | ||
92 | * Look up an entry in the query_modes table by its textual name. The search | ||
93 | * is case-insensitive. | ||
94 | * | ||
95 | * @param mode The name of the entry to be found. | ||
96 | * | ||
97 | * @return The requested query_mode, or NULL if it could not be found. | ||
98 | */ | ||
99 | static const struct query_mode *find_query_mode_by_name(const char *name) | ||
100 | { | ||
101 | int i; | ||
102 | for (i = 0; i < sizeof(query_modes) / sizeof(*query_modes); i++) { | ||
103 | const struct query_mode *mode = &query_modes[i]; | ||
104 | if (strcmp(mode->name, name) == 0) | ||
105 | return mode; | ||
106 | } | ||
107 | return NULL; | ||
108 | } | ||
109 | |||
110 | /** | ||
111 | * Look up an entry in the query_modes table by value. | ||
112 | * | ||
113 | * @param event_type The value of the entry to be found. | ||
114 | * | ||
115 | * @return The requested query_mode, or NULL if it could not be found. | ||
116 | */ | ||
117 | static const struct query_mode *find_query_mode_by_value(int event_type) | ||
118 | { | ||
119 | int i; | ||
120 | for (i = 0; i < sizeof(query_modes) / sizeof(*query_modes); i++) { | ||
121 | const struct query_mode *mode = &query_modes[i]; | ||
122 | if (mode->event_type == event_type) | ||
123 | return mode; | ||
124 | } | ||
125 | return NULL; | ||
126 | } | ||
127 | |||
128 | /** | ||
129 | * Find a query_mode based on a string identifier. The string can either | ||
130 | * be a numerical value (e.g. "5") or the name of the event type in question | ||
131 | * (e.g. "EV_SW"). | ||
132 | * | ||
133 | * @param query_mode The mode to search for | ||
134 | * | ||
135 | * @return The requested code's numerical value, or negative on error. | ||
136 | */ | ||
137 | static const struct query_mode *find_query_mode(const char *query_mode) | ||
138 | { | ||
139 | if (isdigit(query_mode[0])) { | ||
140 | unsigned long val; | ||
141 | errno = 0; | ||
142 | val = strtoul(query_mode, NULL, 0); | ||
143 | if (errno) | ||
144 | return NULL; | ||
145 | return find_query_mode_by_value(val); | ||
146 | } else { | ||
147 | return find_query_mode_by_name(query_mode); | ||
148 | } | ||
149 | } | ||
150 | |||
72 | static const char * const events[EV_MAX + 1] = { | 151 | static const char * const events[EV_MAX + 1] = { |
73 | [0 ... EV_MAX] = NULL, | 152 | [0 ... EV_MAX] = NULL, |
74 | NAME_ELEMENT(EV_SYN), NAME_ELEMENT(EV_KEY), | 153 | NAME_ELEMENT(EV_SYN), NAME_ELEMENT(EV_KEY), |
@@ -480,6 +559,41 @@ static const char * const * const names[EV_MAX + 1] = { | |||
480 | }; | 559 | }; |
481 | 560 | ||
482 | /** | 561 | /** |
562 | * Convert a string to a specific key/snd/led/sw code. The string can either | ||
563 | * be the name of the key in question (e.g. "SW_DOCK") or the numerical | ||
564 | * value, either as decimal (e.g. "5") or as hex (e.g. "0x5"). | ||
565 | * | ||
566 | * @param mode The mode being queried (key, snd, led, sw) | ||
567 | * @param kstr The string to parse and convert | ||
568 | * | ||
569 | * @return The requested code's numerical value, or negative on error. | ||
570 | */ | ||
571 | static int get_keycode(const struct query_mode *query_mode, const char *kstr) | ||
572 | { | ||
573 | if (isdigit(kstr[0])) { | ||
574 | unsigned long val; | ||
575 | errno = 0; | ||
576 | val = strtoul(kstr, NULL, 0); | ||
577 | if (errno) { | ||
578 | fprintf(stderr, "Could not interpret value %s\n", kstr); | ||
579 | return -1; | ||
580 | } | ||
581 | return (int) val; | ||
582 | } else { | ||
583 | const char * const *keynames = names[query_mode->event_type]; | ||
584 | int i; | ||
585 | |||
586 | for (i = 0; i < query_mode->max; i++) { | ||
587 | const char *name = keynames[i]; | ||
588 | if (name && strcmp(name, kstr) == 0) | ||
589 | return i; | ||
590 | } | ||
591 | |||
592 | return -1; | ||
593 | } | ||
594 | } | ||
595 | |||
596 | /** | ||
483 | * Filter for the AutoDevProbe scandir on /dev/input. | 597 | * Filter for the AutoDevProbe scandir on /dev/input. |
484 | * | 598 | * |
485 | * @param dir The current directory entry provided by scandir. | 599 | * @param dir The current directory entry provided by scandir. |
@@ -544,10 +658,22 @@ static char* scan_devices(void) | |||
544 | /** | 658 | /** |
545 | * Print usage information. | 659 | * Print usage information. |
546 | */ | 660 | */ |
547 | static void usage(void) | 661 | static int usage(void) |
548 | { | 662 | { |
549 | printf("Usage: evtest /dev/input/eventX\n"); | 663 | printf("USAGE:\n"); |
550 | printf("Where X = input device number\n"); | 664 | printf(" Grab mode:\n"); |
665 | printf(" %s /dev/input/eventX\n", program_invocation_short_name); | ||
666 | printf("\n"); | ||
667 | printf(" Query mode: (check exit code)\n"); | ||
668 | printf(" %s --query /dev/input/eventX <type> <value>\n", | ||
669 | program_invocation_short_name); | ||
670 | |||
671 | printf("\n"); | ||
672 | printf("<type> is one of: EV_KEY, EV_SW, EV_LED, EV_SND\n"); | ||
673 | printf("<value> can either be a numerical value, or the textual name of the\n"); | ||
674 | printf("key/switch/LED/sound being queried (e.g. SW_DOCK).\n"); | ||
675 | |||
676 | return EXIT_FAILURE; | ||
551 | } | 677 | } |
552 | 678 | ||
553 | /** | 679 | /** |
@@ -700,10 +826,8 @@ static int do_capture(const char *device) | |||
700 | fprintf(stderr, "Not running as root, no devices may be available.\n"); | 826 | fprintf(stderr, "Not running as root, no devices may be available.\n"); |
701 | 827 | ||
702 | filename = scan_devices(); | 828 | filename = scan_devices(); |
703 | if (!filename) { | 829 | if (!filename) |
704 | usage(); | 830 | return usage(); |
705 | return EXIT_FAILURE; | ||
706 | } | ||
707 | } else | 831 | } else |
708 | filename = strdup(device); | 832 | filename = strdup(device); |
709 | 833 | ||
@@ -743,14 +867,118 @@ static int do_capture(const char *device) | |||
743 | return print_events(fd); | 867 | return print_events(fd); |
744 | } | 868 | } |
745 | 869 | ||
870 | /** | ||
871 | * Perform a one-shot state query on a specific device. The query can be of | ||
872 | * any known mode, on any valid keycode. | ||
873 | * | ||
874 | * @param device Path to the evdev device node that should be queried. | ||
875 | * @param query_mode The event type that is being queried (e.g. key, switch) | ||
876 | * @param keycode The code of the key/switch/sound/LED to be queried | ||
877 | * @return 0 if the state bit is unset, 10 if the state bit is set, 1 on error. | ||
878 | */ | ||
879 | static int query_device(const char *device, const struct query_mode *query_mode, int keycode) | ||
880 | { | ||
881 | int fd; | ||
882 | int r; | ||
883 | unsigned long state[NBITS(query_mode->max)]; | ||
884 | |||
885 | fd = open(device, O_RDONLY); | ||
886 | if (fd < 0) { | ||
887 | perror("open"); | ||
888 | return EXIT_FAILURE; | ||
889 | } | ||
890 | memset(state, 0, sizeof(state)); | ||
891 | r = ioctl(fd, query_mode->rq, state); | ||
892 | close(fd); | ||
893 | |||
894 | if (r == -1) { | ||
895 | perror("ioctl"); | ||
896 | return EXIT_FAILURE; | ||
897 | } | ||
898 | |||
899 | if (test_bit(keycode, state)) | ||
900 | return 10; /* different from EXIT_FAILURE */ | ||
901 | else | ||
902 | return 0; | ||
903 | } | ||
904 | |||
905 | /** | ||
906 | * Enter query mode. The requested event device will be queried for the state | ||
907 | * of a particular switch/key/sound/LED. | ||
908 | * | ||
909 | * @param device The device to query. | ||
910 | * @param mode The mode (event type) that is to be queried (snd, sw, key, led) | ||
911 | * @param keycode The key code to query the state of. | ||
912 | * @return 0 if the state bit is unset, 10 if the state bit is set. | ||
913 | */ | ||
914 | static int do_query(const char *device, const char *event_type, const char *keyname) | ||
915 | { | ||
916 | const struct query_mode *query_mode; | ||
917 | int keycode; | ||
918 | |||
919 | if (!device) { | ||
920 | fprintf(stderr, "Device argument is required for query.\n"); | ||
921 | return usage(); | ||
922 | } | ||
923 | |||
924 | query_mode = find_query_mode(event_type); | ||
925 | if (!query_mode) { | ||
926 | fprintf(stderr, "Unrecognised event type: %s\n", event_type); | ||
927 | return usage(); | ||
928 | } | ||
929 | |||
930 | keycode = get_keycode(query_mode, keyname); | ||
931 | if (keycode < 0) { | ||
932 | fprintf(stderr, "Unrecognised key name: %s\n", keyname); | ||
933 | return usage(); | ||
934 | } else if (keycode > query_mode->max) { | ||
935 | fprintf(stderr, "Key %d is out of bounds.\n", keycode); | ||
936 | return EXIT_FAILURE; | ||
937 | } | ||
938 | |||
939 | return query_device(device, query_mode, keycode); | ||
940 | } | ||
941 | |||
942 | static const struct option long_options[] = { | ||
943 | { "query", no_argument, NULL, MODE_QUERY }, | ||
944 | { 0, }, | ||
945 | }; | ||
946 | |||
746 | int main (int argc, char **argv) | 947 | int main (int argc, char **argv) |
747 | { | 948 | { |
748 | const char *device = NULL; | 949 | const char *device = NULL; |
950 | const char *keyname; | ||
951 | const char *event_type; | ||
952 | enum evtest_mode mode = MODE_CAPTURE; | ||
749 | 953 | ||
750 | if (argc >= 2) | 954 | while (1) { |
751 | device = argv[1]; | 955 | int option_index = 0; |
956 | int c = getopt_long(argc, argv, "", long_options, &option_index); | ||
957 | if (c == -1) | ||
958 | break; | ||
959 | switch (c) { | ||
960 | case MODE_QUERY: | ||
961 | mode = c; | ||
962 | break; | ||
963 | default: | ||
964 | return usage(); | ||
965 | } | ||
966 | } | ||
967 | |||
968 | if (optind < argc) | ||
969 | device = argv[optind++]; | ||
970 | |||
971 | if (mode == MODE_CAPTURE) | ||
972 | return do_capture(device); | ||
973 | |||
974 | if ((argc - optind) < 2) { | ||
975 | fprintf(stderr, "Query mode requires device, type and key parameters\n"); | ||
976 | return usage(); | ||
977 | } | ||
752 | 978 | ||
753 | return do_capture(device); | 979 | event_type = argv[optind++]; |
980 | keyname = argv[optind++]; | ||
981 | return do_query(device, event_type, keyname); | ||
754 | } | 982 | } |
755 | 983 | ||
756 | /* vim: set noexpandtab tabstop=8 shiftwidth=8: */ | 984 | /* vim: set noexpandtab tabstop=8 shiftwidth=8: */ |
@@ -4,17 +4,33 @@ EVTEST(1) | |||
4 | NAME | 4 | NAME |
5 | ---- | 5 | ---- |
6 | 6 | ||
7 | evtest - Input device event monitor | 7 | evtest - Input device event monitor and query tool |
8 | 8 | ||
9 | SYNOPSIS | 9 | SYNOPSIS |
10 | -------- | 10 | -------- |
11 | evtest "/dev/input/eventX" | 11 | evtest /dev/input/eventX |
12 | |||
13 | evtest --query /dev/input/eventX <type> <value> | ||
12 | 14 | ||
13 | DESCRIPTION | 15 | DESCRIPTION |
14 | ----------- | 16 | ----------- |
15 | evtest displays information on the input device specified on the command | 17 | The first invocation type displayed above ("capture mode") causes evtest to |
16 | line, including all the events supported by the device. It then monitors the | 18 | display information about the specified input device, including all the events |
17 | device and displays all the events layer events generated. | 19 | supported by the device. It then monitors the device and displays all the |
20 | events layer events generated. | ||
21 | |||
22 | In the second invocation type ("query mode"), evtest performs a one-shot query | ||
23 | of the state of a specific key *value* of an event *type*. | ||
24 | |||
25 | *type* is one of: *EV_KEY*, *EV_SW*, *EV_SND*, *EV_LED* (or the numerical value) | ||
26 | |||
27 | *value* can be either a decimal representation (e.g. 44), hex | ||
28 | (e.g. 0x2c), or the constant name (e.g. KEY_Z) of the key/switch/sound/LED | ||
29 | being queried. | ||
30 | |||
31 | If the state bit is set (key pressed, switch on, ...), evtest exits with | ||
32 | code 0. If the state bit is unset (key depressed, switch off, ...), evtest | ||
33 | exits with code 10. No other output is generated. | ||
18 | 34 | ||
19 | evtest needs to be able to read from the device; in most cases this means it | 35 | evtest needs to be able to read from the device; in most cases this means it |
20 | must be run as root. | 36 | must be run as root. |
@@ -32,6 +48,13 @@ when debugging a synaptics device from within X. VT switching to a TTY or | |||
32 | shutting down the X server terminates this grab and synaptics devices can be | 48 | shutting down the X server terminates this grab and synaptics devices can be |
33 | debugged. | 49 | debugged. |
34 | 50 | ||
51 | EXIT CODE | ||
52 | --------- | ||
53 | evtest returns 1 on error. | ||
54 | |||
55 | When used to query state, evtest returns 0 if the state bit is unset and | ||
56 | 10 if the state bit is set. | ||
57 | |||
35 | SEE ALSO | 58 | SEE ALSO |
36 | -------- | 59 | -------- |
37 | inputattach(1) | 60 | inputattach(1) |