Elliott Hughes | c187376 | 2018-12-19 15:13:36 -0800 | [diff] [blame^] | 1 | #include "tests.h" |
| 2 | |
| 3 | #ifdef HAVE_LINUX_INPUT_H |
| 4 | |
| 5 | # include <inttypes.h> |
| 6 | # include <stdio.h> |
| 7 | # include <stdlib.h> |
| 8 | # include <sys/ioctl.h> |
| 9 | # include <linux/input.h> |
| 10 | # include "print_fields.h" |
| 11 | |
| 12 | static const char *errstr; |
| 13 | |
| 14 | struct evdev_check { |
| 15 | unsigned long cmd; |
| 16 | const char *cmd_str; |
| 17 | void *arg_ptr; |
| 18 | void (*print_arg)(long rc, void *ptr, void *arg); |
| 19 | }; |
| 20 | |
| 21 | static long |
| 22 | invoke_test_syscall(unsigned long cmd, void *p) |
| 23 | { |
| 24 | long rc = ioctl(-1, cmd, p); |
| 25 | errstr = sprintrc(rc); |
| 26 | static char inj_errstr[4096]; |
| 27 | |
| 28 | snprintf(inj_errstr, sizeof(inj_errstr), "%s (INJECTED)", errstr); |
| 29 | errstr = inj_errstr; |
| 30 | return rc; |
| 31 | } |
| 32 | |
| 33 | static void |
| 34 | test_evdev(struct evdev_check *check, void *arg) |
| 35 | { |
| 36 | long rc = invoke_test_syscall(check->cmd, check->arg_ptr); |
| 37 | printf("ioctl(-1, %s, ", check->cmd_str); |
| 38 | if (check->print_arg) |
| 39 | check->print_arg(rc, check->arg_ptr, arg); |
| 40 | else |
| 41 | printf("%p", check->arg_ptr); |
| 42 | printf(") = %s\n", errstr); |
| 43 | } |
| 44 | |
| 45 | static void |
| 46 | print_input_absinfo(long rc, void *ptr, void *arg) |
| 47 | { |
| 48 | struct input_absinfo *absinfo = ptr; |
| 49 | |
| 50 | if (rc < 0) { |
| 51 | printf("%p", absinfo); |
| 52 | return; |
| 53 | } |
| 54 | PRINT_FIELD_U("{", *absinfo, value); |
| 55 | PRINT_FIELD_U(", ", *absinfo, minimum); |
| 56 | # if VERBOSE |
| 57 | PRINT_FIELD_U(", ", *absinfo, maximum); |
| 58 | PRINT_FIELD_U(", ", *absinfo, fuzz); |
| 59 | PRINT_FIELD_U(", ", *absinfo, flat); |
| 60 | # ifdef HAVE_STRUCT_INPUT_ABSINFO_RESOLUTION |
| 61 | PRINT_FIELD_U(", ", *absinfo, resolution); |
| 62 | # endif |
| 63 | # else |
| 64 | printf(", ..."); |
| 65 | # endif |
| 66 | printf("}"); |
| 67 | } |
| 68 | |
| 69 | static void |
| 70 | print_input_id(long rc, void *ptr, void *arg) |
| 71 | { |
| 72 | struct input_id *id = ptr; |
| 73 | |
| 74 | if (rc < 0) { |
| 75 | printf("%p", id); |
| 76 | return; |
| 77 | } |
| 78 | printf("{ID_BUS=%" PRIu16 |
| 79 | ", ID_VENDOR=%" PRIu16 |
| 80 | ", ID_PRODUCT=%" PRIu16 |
| 81 | ", ID_VERSION=%" PRIu16 "}", |
| 82 | id->bustype, id->vendor, id->product, id->version); |
| 83 | } |
| 84 | |
| 85 | # ifdef EVIOCGMTSLOTS |
| 86 | static void |
| 87 | print_mtslots(long rc, void *ptr, void *arg) |
| 88 | { |
| 89 | int *buffer = ptr; |
| 90 | const char **str = arg; |
| 91 | int num = atoi(*(str + 1)); |
| 92 | |
| 93 | if (rc < 0) { |
| 94 | printf("%p", buffer); |
| 95 | return; |
| 96 | } |
| 97 | |
| 98 | printf("{code=%s", *str); |
| 99 | printf(", values=["); |
| 100 | for (unsigned int i = 1; i <= (unsigned) num; i++) |
| 101 | printf("%s%s", i > 1 ? ", " : "", *(str + i + 1)); |
| 102 | printf("]}"); |
| 103 | } |
| 104 | # endif |
| 105 | |
| 106 | static void |
| 107 | print_getbit(long rc, void *ptr, void *arg) |
| 108 | { |
| 109 | const char **str = arg; |
| 110 | int num = atoi(*str); |
| 111 | |
| 112 | if (rc < 0) { |
| 113 | printf("%p", ptr); |
| 114 | return; |
| 115 | } |
| 116 | |
| 117 | printf("["); |
| 118 | printf("%s", *(str + 1)); |
| 119 | for (unsigned int i = 2; i <= (unsigned) num; i++) { |
| 120 | # if ! VERBOSE |
| 121 | if (i > 4) { |
| 122 | printf(", ..."); |
| 123 | break; |
| 124 | } |
| 125 | # endif |
| 126 | printf(", "); |
| 127 | printf("%s", *(str + i)); |
| 128 | } |
| 129 | printf("]"); |
| 130 | } |
| 131 | |
| 132 | int |
| 133 | main(int argc, char **argv) |
| 134 | { |
| 135 | unsigned long num_skip; |
| 136 | long inject_retval; |
| 137 | bool locked = false; |
| 138 | |
| 139 | if (argc == 1) |
| 140 | return 0; |
| 141 | |
| 142 | if (argc < 3) |
| 143 | error_msg_and_fail("Usage: %s NUM_SKIP INJECT_RETVAL", argv[0]); |
| 144 | |
| 145 | num_skip = strtoul(argv[1], NULL, 0); |
| 146 | inject_retval = strtol(argv[2], NULL, 0); |
| 147 | |
| 148 | if (inject_retval < 0) |
| 149 | error_msg_and_fail("Expected non-negative INJECT_RETVAL, " |
| 150 | "but got %ld", inject_retval); |
| 151 | |
| 152 | for (unsigned int i = 0; i < num_skip; i++) { |
| 153 | long rc = ioctl(-1, EVIOCGID, NULL); |
| 154 | printf("ioctl(-1, EVIOCGID, NULL) = %s%s\n", |
| 155 | sprintrc(rc), |
| 156 | rc == inject_retval ? " (INJECTED)" : ""); |
| 157 | |
| 158 | if (rc != inject_retval) |
| 159 | continue; |
| 160 | |
| 161 | locked = true; |
| 162 | break; |
| 163 | } |
| 164 | |
| 165 | if (!locked) |
| 166 | error_msg_and_fail("Hasn't locked on ioctl(-1" |
| 167 | ", EVIOCGID, NULL) returning %lu", |
| 168 | inject_retval); |
| 169 | |
| 170 | TAIL_ALLOC_OBJECT_CONST_PTR(struct input_id, id); |
| 171 | TAIL_ALLOC_OBJECT_CONST_PTR(struct input_absinfo, absinfo); |
| 172 | TAIL_ALLOC_OBJECT_CONST_PTR(int, bad_addr_slot); |
| 173 | # ifdef EVIOCGMTSLOTS |
| 174 | int mtslots[] = { ABS_MT_SLOT, 1, 3 }; |
| 175 | /* we use the second element to indicate the number of values */ |
| 176 | /* mtslots_str[1] is "2" so the number of values is 2 */ |
| 177 | const char *mtslots_str[] = { "ABS_MT_SLOT", "2", "1", "3" }; |
| 178 | |
| 179 | /* invalid flag */ |
| 180 | int invalid_mtslot[] = { -1, 1 }; |
| 181 | char invalid_str[4096]; |
| 182 | snprintf(invalid_str, sizeof(invalid_str), "%#x /* ABS_MT_??? */", invalid_mtslot[0]); |
| 183 | const char *invalid_mtslot_str[] = { invalid_str, "1", "1" }; |
| 184 | # endif |
| 185 | |
| 186 | /* set more than 4 bits */ |
| 187 | unsigned long ev_more[] = { 1 << EV_ABS | 1 << EV_MSC | 1 << EV_LED | 1 << EV_SND | 1 << EV_PWR }; |
| 188 | /* we use the first element to indicate the number of set bits */ |
| 189 | /* ev_more_str[0] is "5" so the number of set bits is 5 */ |
| 190 | const char *ev_more_str[] = { "5", "EV_ABS", "EV_MSC", "EV_LED", "EV_SND", "EV_PWR" }; |
| 191 | |
| 192 | /* set less than 4 bits */ |
| 193 | unsigned long ev_less[] = { 1 << EV_ABS | 1 << EV_MSC | 1 << EV_LED }; |
| 194 | const char *ev_less_str[] = { "3", "EV_ABS", "EV_MSC", "EV_LED" }; |
| 195 | |
| 196 | /* set zero bit */ |
| 197 | unsigned long ev_zero[] = { 0x0 }; |
| 198 | const char *ev_zero_str[] = { "0", " 0 " }; |
| 199 | |
| 200 | /* KEY_MAX is 0x2ff which is greater than retval * 8 */ |
| 201 | unsigned long key[] = { 1 << KEY_1 | 1 << KEY_2, 0 }; |
| 202 | const char *key_str[] = { "2", "KEY_1", "KEY_2" }; |
| 203 | |
| 204 | struct { |
| 205 | struct evdev_check check; |
| 206 | void *ptr; |
| 207 | } a[] = { |
| 208 | { { ARG_STR(EVIOCGID), id, print_input_id }, NULL }, |
| 209 | { { ARG_STR(EVIOCGABS(ABS_X)), absinfo, print_input_absinfo }, NULL }, |
| 210 | { { ARG_STR(EVIOCGABS(ABS_Y)), absinfo, print_input_absinfo }, NULL }, |
| 211 | { { ARG_STR(EVIOCGABS(ABS_Y)), absinfo, print_input_absinfo }, NULL }, |
| 212 | { { ARG_STR(EVIOCGBIT(0, 0)), ev_more, print_getbit }, &ev_more_str }, |
| 213 | { { ARG_STR(EVIOCGBIT(0, 0)), ev_less, print_getbit }, &ev_less_str }, |
| 214 | { { ARG_STR(EVIOCGBIT(0, 0)), ev_zero, print_getbit }, &ev_zero_str }, |
| 215 | { { ARG_STR(EVIOCGBIT(EV_KEY, 0)), key, print_getbit }, &key_str}, |
| 216 | # ifdef EVIOCGMTSLOTS |
| 217 | { { ARG_STR(EVIOCGMTSLOTS(12)), mtslots, print_mtslots }, &mtslots_str }, |
| 218 | { { ARG_STR(EVIOCGMTSLOTS(8)), invalid_mtslot, print_mtslots }, &invalid_mtslot_str } |
| 219 | # endif |
| 220 | }; |
| 221 | for (unsigned int i = 0; i < ARRAY_SIZE(a); i++) { |
| 222 | test_evdev(&a[i].check, a[i].ptr); |
| 223 | } |
| 224 | |
| 225 | puts("+++ exited with 0 +++"); |
| 226 | return 0; |
| 227 | } |
| 228 | #else |
| 229 | |
| 230 | SKIP_MAIN_UNDEFINED("HAVE_LINUX_INPUT_H") |
| 231 | |
| 232 | #endif |