testevdev.c (32056B)
1 /* 2 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org> 3 Copyright (C) 2020 Collabora Ltd. 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely. 12 */ 13 14 #include "../src/SDL_internal.h" 15 16 #include <stdio.h> 17 #include <string.h> 18 19 static int run_test(void); 20 21 #if HAVE_LIBUDEV_H || defined(SDL_JOYSTICK_LINUX) 22 23 #include <stdint.h> 24 25 #include "SDL_stdinc.h" 26 #include "SDL_endian.h" 27 #include "../src/core/linux/SDL_evdev_capabilities.h" 28 #include "../src/core/linux/SDL_evdev_capabilities.c" 29 30 static const struct 31 { 32 int code; 33 const char *name; 34 } device_classes[] = 35 { 36 #define CLS(x) \ 37 { SDL_UDEV_DEVICE_ ## x, #x } 38 CLS(MOUSE), 39 CLS(KEYBOARD), 40 CLS(JOYSTICK), 41 CLS(SOUND), 42 CLS(TOUCHSCREEN), 43 CLS(ACCELEROMETER), 44 #undef CLS 45 { 0, NULL } 46 }; 47 48 typedef struct 49 { 50 const char *name; 51 uint16_t bus_type; 52 uint16_t vendor_id; 53 uint16_t product_id; 54 uint16_t version; 55 uint8_t ev[(EV_MAX + 1) / 8]; 56 uint8_t keys[(KEY_MAX + 1) / 8]; 57 uint8_t abs[(ABS_MAX + 1) / 8]; 58 uint8_t rel[(REL_MAX + 1) / 8]; 59 uint8_t ff[(FF_MAX + 1) / 8]; 60 uint8_t props[INPUT_PROP_MAX / 8]; 61 int expected; 62 } GuessTest; 63 64 /* 65 * Test-cases for guessing a device type from its capabilities. 66 * 67 * The bytes in ev, etc. are in little-endian byte order 68 * Trailing zeroes can be omitted. 69 * 70 * The evemu-describe tool is a convenient way to add a test-case for 71 * a physically available device. 72 */ 73 #define ZEROx4 0, 0, 0, 0 74 #define ZEROx8 ZEROx4, ZEROx4 75 #define FFx4 0xff, 0xff, 0xff, 0xff 76 #define FFx8 FFx4, FFx4 77 78 /* Test-cases derived from real devices or from Linux kernel source */ 79 static const GuessTest guess_tests[] = 80 { 81 { 82 .name = "Xbox 360 wired USB controller", 83 .bus_type = 0x0003, 84 .vendor_id = 0x045e, 85 .product_id = 0x028e, 86 .expected = SDL_UDEV_DEVICE_JOYSTICK, 87 /* SYN, KEY, ABS, FF */ 88 .ev = { 0x0b, 0x00, 0x20 }, 89 /* X, Y, Z, RX, RY, RZ, HAT0X, HAT0Y */ 90 .abs = { 0x3f, 0x00, 0x03 }, 91 .keys = { 92 /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, 93 /* A, B, X, Y, TL, TR, SELECT, START, MODE, THUMBL, THUMBR */ 94 /* 0x100 */ ZEROx4, 0x00, 0x00, 0xdb, 0x7c, 95 }, 96 }, 97 { 98 .name = "X-Box One Elite", 99 .bus_type = 0x0003, 100 .vendor_id = 0x045e, 101 .product_id = 0x02e3, 102 .expected = SDL_UDEV_DEVICE_JOYSTICK, 103 /* SYN, KEY, ABS */ 104 .ev = { 0x0b }, 105 /* X, Y, Z, RX, RY, RZ, HAT0X, HAT0Y */ 106 .abs = { 0x3f, 0x00, 0x03 }, 107 .keys = { 108 /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, 109 /* A, B, X, Y, TL, TR, SELECT, START, MODE, THUMBL, THUMBR */ 110 /* 0x100 */ ZEROx4, 0x00, 0x00, 0xdb, 0x7c, 111 }, 112 }, 113 { 114 .name = "X-Box One S via Bluetooth", 115 .bus_type = 0x0005, 116 .vendor_id = 0x045e, 117 .product_id = 0x02e0, 118 .version = 0x1130, 119 .expected = SDL_UDEV_DEVICE_JOYSTICK, 120 /* SYN, KEY, ABS */ 121 .ev = { 0x0b }, 122 /* X, Y, Z, RX, RY, RZ, HAT0X, HAT0Y */ 123 .abs = { 0x3f, 0x00, 0x03 }, 124 .keys = { 125 /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, 126 /* A, B, X, Y, TL, TR, SELECT, START, MODE, THUMBL, THUMBR */ 127 /* 0x100 */ ZEROx4, 0x00, 0x00, 0xdb, 0x7c, 128 }, 129 }, 130 { 131 .name = "X-Box One S wired", 132 .bus_type = 0x0003, 133 .vendor_id = 0x045e, 134 .product_id = 0x02ea, 135 .version = 0x0301, 136 .expected = SDL_UDEV_DEVICE_JOYSTICK, 137 /* SYN, KEY, ABS */ 138 .ev = { 0x0b }, 139 /* X, Y, Z, RX, RY, RZ, HAT0X, HAT0Y */ 140 .abs = { 0x3f, 0x00, 0x03 }, 141 .keys = { 142 /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, 143 /* A, B, X, Y, TL, TR, SELECT, START, MODE, THUMBL, THUMBR */ 144 /* 0x100 */ ZEROx4, 0x00, 0x00, 0xdb, 0x7c, 145 }, 146 }, 147 { 148 .name = "DualShock 4 - gamepad", 149 .bus_type = 0x0003, 150 .vendor_id = 0x054c, 151 .product_id = 0x09cc, 152 .expected = SDL_UDEV_DEVICE_JOYSTICK, 153 /* SYN, KEY, ABS, MSC, FF */ 154 /* Some versions only have 0x0b, SYN, KEY, ABS, like the 155 * Bluetooth example below */ 156 .ev = { 0x1b, 0x00, 0x20 }, 157 /* X, Y, Z, RX, RY, RZ, HAT0X, HAT0Y */ 158 .abs = { 0x3f, 0x00, 0x03 }, 159 .keys = { 160 /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, 161 /* A, B, X, Y, TL, TR, TL2, TR2, SELECT, START, MODE, 162 * THUMBL, THUMBR */ 163 /* 0x100 */ ZEROx4, 0x00, 0x00, 0xdb, 0x7f, 164 }, 165 }, 166 { 167 .name = "DualShock 4 - gamepad via Bluetooth", 168 .bus_type = 0x0005, 169 .vendor_id = 0x054c, 170 .product_id = 0x09cc, 171 .expected = SDL_UDEV_DEVICE_JOYSTICK, 172 /* SYN, KEY, ABS */ 173 .ev = { 0x0b }, 174 /* X, Y, Z, RX, RY, RZ, HAT0X, HAT0Y */ 175 .abs = { 0x3f, 0x00, 0x03 }, 176 .keys = { 177 /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, 178 /* A, B, X, Y, TL, TR, TL2, TR2, SELECT, START, MODE, 179 * THUMBL, THUMBR */ 180 /* 0x100 */ ZEROx4, 0x00, 0x00, 0xdb, 0x7f, 181 }, 182 }, 183 { 184 .name = "DualShock 4 - touchpad", 185 .bus_type = 0x0003, 186 .vendor_id = 0x054c, 187 .product_id = 0x09cc, 188 /* TODO: Should this be MOUSE? That's what it most closely 189 * resembles */ 190 .expected = SDL_UDEV_DEVICE_UNKNOWN, 191 /* SYN, KEY, ABS */ 192 .ev = { 0x0b }, 193 /* X, Y, multitouch */ 194 .abs = { 0x03, 0x00, 0x00, 0x00, 0x00, 0x80, 0x60, 0x02 }, 195 .keys = { 196 /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, 197 /* Left mouse button */ 198 /* 0x100 */ 0x00, 0x00, 0x01, 0x00, ZEROx4, 199 /* BTN_TOOL_FINGER and some multitouch stuff */ 200 /* 0x140 */ 0x20, 0x24, 0x00, 0x00 201 }, 202 /* POINTER, BUTTONPAD */ 203 .props = { 0x05 }, 204 }, 205 { 206 .name = "DualShock 4 - accelerometer", 207 .bus_type = 0x0003, 208 .vendor_id = 0x054c, 209 .product_id = 0x09cc, 210 .expected = SDL_UDEV_DEVICE_ACCELEROMETER, 211 /* SYN, ABS, MSC */ 212 .ev = { 0x19 }, 213 /* X, Y, Z, RX, RY, RZ */ 214 .abs = { 0x3f }, 215 /* ACCELEROMETER */ 216 .props = { 0x40 }, 217 }, 218 { 219 .name = "DualShock 4 via USB dongle", 220 .bus_type = 0x0003, 221 .vendor_id = 0x054c, 222 .product_id = 0x0ba0, 223 .version = 0x8111, 224 .expected = SDL_UDEV_DEVICE_JOYSTICK, 225 /* SYN, ABS, KEY */ 226 .ev = { 0x0b }, 227 /* X, Y, Z, RX, RY, RZ, HAT0X, HAT0Y */ 228 .abs = { 0x3f, 0x00, 0x03 }, 229 .keys = { 230 /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, 231 /* A, B, X, Y, TL, TR, TL2, TR2, SELECT, START, MODE, 232 * THUMBL, THUMBR */ 233 /* 0x100 */ ZEROx4, 0x00, 0x00, 0xdb, 0x7f, 234 }, 235 }, 236 { 237 .name = "DualShock 3 - gamepad", 238 .bus_type = 0x0003, 239 .vendor_id = 0x054c, 240 .product_id = 0x0268, 241 .expected = SDL_UDEV_DEVICE_JOYSTICK, 242 /* SYN, KEY, ABS, MSC, FF */ 243 .ev = { 0x1b, 0x00, 0x20 }, 244 /* X, Y, Z, RX, RY, RZ */ 245 .abs = { 0x3f }, 246 .keys = { 247 /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, 248 /* A, B, X, Y, TL, TR, TL2, TR2, SELECT, START, MODE, 249 * THUMBL, THUMBR */ 250 /* 0x100 */ ZEROx4, 0x00, 0x00, 0xdb, 0x7f, 251 /* 0x140 */ ZEROx8, 252 /* 0x180 */ ZEROx8, 253 /* 0x1c0 */ ZEROx8, 254 /* Digital dpad */ 255 /* 0x200 */ ZEROx4, 0x0f, 0x00, 0x00, 0x00, 256 }, 257 }, 258 { 259 .name = "DualShock 3 - accelerometer", 260 .bus_type = 0x0003, 261 .vendor_id = 0x054c, 262 .product_id = 0x0268, 263 .expected = SDL_UDEV_DEVICE_ACCELEROMETER, 264 /* SYN, ABS */ 265 .ev = { 0x09 }, 266 /* X, Y, Z */ 267 .abs = { 0x07 }, 268 /* ACCELEROMETER */ 269 .props = { 0x40 }, 270 }, 271 { 272 .name = "Steam Controller - gamepad", 273 .bus_type = 0x0003, 274 .vendor_id = 0x28de, 275 .product_id = 0x1142, 276 .expected = SDL_UDEV_DEVICE_JOYSTICK, 277 /* SYN, KEY, ABS */ 278 .ev = { 0x0b }, 279 /* X, Y, RX, RY, HAT0X, HAT0Y, HAT2X, HAT2Y */ 280 .abs = { 0x1b, 0x00, 0x33 }, 281 .keys = { 282 /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, 283 /* A, B, X, Y, TL, TR, TL2, TR2, SELECT, START, MODE, 284 * THUMBL, THUMBR, joystick THUMB, joystick THUMB2 */ 285 /* 0x100 */ ZEROx4, 0x06, 0x00, 0xdb, 0x7f, 286 /* GEAR_DOWN, GEAR_UP */ 287 /* 0x140 */ 0x00, 0x00, 0x03, 0x00, ZEROx4, 288 /* 0x180 */ ZEROx8, 289 /* 0x1c0 */ ZEROx8, 290 /* Digital dpad */ 291 /* 0x200 */ ZEROx4, 0x0f, 0x00, 0x00, 0x00, 292 }, 293 }, 294 { 295 /* Present to support lizard mode, even if no Steam Controller 296 * is connected */ 297 .name = "Steam Controller - dongle", 298 .bus_type = 0x0003, 299 .vendor_id = 0x28de, 300 .product_id = 0x1142, 301 .expected = (SDL_UDEV_DEVICE_KEYBOARD 302 | SDL_UDEV_DEVICE_MOUSE), 303 /* SYN, KEY, REL, MSC, LED, REP */ 304 .ev = { 0x17, 0x00, 0x12 }, 305 /* X, Y, mouse wheel, high-res mouse wheel */ 306 .rel = { 0x03, 0x09 }, 307 .keys = { 308 /* 0x00 */ 0xfe, 0xff, 0xff, 0xff, FFx4, 309 /* 0x40 */ 0xff, 0xff, 0xcf, 0x01, 0xdf, 0xff, 0x80, 0xe0, 310 /* 0x80 */ ZEROx8, 311 /* 0xc0 */ ZEROx8, 312 /* 0x100 */ 0x00, 0x00, 0x1f, 0x00, ZEROx4, 313 }, 314 }, 315 { 316 .name = "Guitar Hero for PS3", 317 .bus_type = 0x0003, 318 .vendor_id = 0x12ba, 319 .product_id = 0x0100, 320 .version = 0x0110, 321 .expected = SDL_UDEV_DEVICE_JOYSTICK, 322 /* SYN, KEY, ABS */ 323 .ev = { 0x0b }, 324 /* X, Y, Z, RZ, HAT0X, HAT0Y */ 325 .abs = { 0x27, 0x00, 0x03 }, 326 .keys = { 327 /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, 328 /* ABC, XYZ, TL, TR, TL2, TR2, SELECT, START, MODE */ 329 /* 0x100 */ ZEROx4, 0x00, 0x00, 0xff, 0x1f, 330 }, 331 }, 332 { 333 .name = "G27 Racing Wheel, 0003:046d:c29b v0111", 334 .bus_type = 0x0003, 335 .vendor_id = 0x046d, 336 .product_id = 0xc29b, 337 .version = 0x0111, 338 .expected = SDL_UDEV_DEVICE_JOYSTICK, 339 /* SYN, KEY, ABS */ 340 .ev = { 0x0b }, 341 /* X, Y, Z, RZ, HAT0X, HAT0Y */ 342 .abs = { 0x27, 0x00, 0x03 }, 343 .keys = { 344 /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, 345 /* 16 buttons: TRIGGER, THUMB, THUMB2, TOP, TOP2, PINKIE, BASE, 346 * BASE2..BASE6, unregistered event codes 0x12c-0x12e, DEAD */ 347 /* 0x100 */ ZEROx4, 0xff, 0xff, 0x00, 0x00, 348 /* 0x140 */ ZEROx8, 349 /* 0x180 */ ZEROx8, 350 /* 0x1c0 */ ZEROx8, 351 /* 0x200 */ ZEROx8, 352 /* 0x240 */ ZEROx8, 353 /* 0x280 */ ZEROx8, 354 /* TRIGGER_HAPPY1..TRIGGER_HAPPY7 */ 355 /* 0x2c0 */ 0x7f, 356 }, 357 }, 358 { 359 .name = "Logitech Driving Force, 0003:046d:c294 v0100", 360 .bus_type = 0x0003, 361 .vendor_id = 0x046d, 362 .product_id = 0xc294, 363 .version = 0x0100, 364 .expected = SDL_UDEV_DEVICE_JOYSTICK, 365 /* SYN, KEY, ABS */ 366 .ev = { 0x0b }, 367 /* X, Y, RZ, HAT0X, HAT0Y */ 368 .abs = { 0x23, 0x00, 0x03 }, 369 .keys = { 370 /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, 371 /* 12 buttons: TRIGGER, THUMB, THUMB2, TOP, TOP2, PINKIE, BASE, 372 * BASE2..BASE6 */ 373 /* 0x100 */ ZEROx4, 0xff, 0x0f, 0x00, 0x00, 374 }, 375 }, 376 { 377 .name = "Logitech Dual Action", 378 .bus_type = 0x0003, 379 .vendor_id = 0x046d, 380 .product_id = 0xc216, 381 .version = 0x0110, 382 /* Logitech RumblePad 2 USB, 0003:046d:c218 v0110, is the same 383 * except for having force feedback, which we don't use in our 384 * heuristic */ 385 /* Jess Tech GGE909 PC Recoil Pad, 0003:0f30:010b v0110, is the same */ 386 /* 8BitDo SNES30, 0003:2dc8:ab20 v0110, is the same */ 387 .expected = SDL_UDEV_DEVICE_JOYSTICK, 388 /* SYN, KEY, ABS */ 389 .ev = { 0x0b }, 390 /* X, Y, Z, RZ, HAT0X, HAT0Y */ 391 .abs = { 0x27, 0x00, 0x03 }, 392 .keys = { 393 /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, 394 /* 12 buttons: TRIGGER, THUMB, THUMB2, TOP, TOP2, PINKIE, BASE, 395 * BASE2..BASE6 */ 396 /* 0x100 */ ZEROx4, 0xff, 0x0f, 0x00, 0x00, 397 }, 398 }, 399 { 400 .name = "Saitek ST290 Pro flight stick", 401 .bus_type = 0x0003, 402 .vendor_id = 0x06a3, 403 .product_id = 0x0160, /* 0x0460 seems to be the same */ 404 .version = 0x0100, 405 .expected = SDL_UDEV_DEVICE_JOYSTICK, 406 /* SYN, KEY, ABS, MSC */ 407 .ev = { 0x1b }, 408 /* X, Y, Z, RZ, HAT0X, HAT0Y */ 409 .abs = { 0x27, 0x00, 0x03 }, 410 .keys = { 411 /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, 412 /* TRIGGER, THUMB, THUMB2, TOP, TOP2, PINKIE */ 413 /* 0x100 */ ZEROx4, 0x3f, 0x00, 0x00, 0x00, 414 }, 415 }, 416 { 417 .name = "Saitek X52 Pro Flight Control System", 418 .bus_type = 0x0003, 419 .vendor_id = 0x06a3, 420 .product_id = 0x0762, 421 .version = 0x0111, 422 .expected = SDL_UDEV_DEVICE_JOYSTICK, 423 .ev = { 0x0b }, 424 /* XYZ, RXYZ, throttle, hat0, MISC, unregistered event code 0x29 */ 425 .abs = { 0x7f, 0x00, 0x03, 0x00, 0x00, 0x03 }, 426 .keys = { 427 /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, 428 /* 16 buttons: TRIGGER, THUMB, THUMB2, TOP, TOP2, PINKIE, BASE, 429 * BASE2..BASE6, unregistered event codes 0x12c-0x12e, DEAD */ 430 /* 0x100 */ ZEROx4, 0xff, 0xff, 0x00, 0x00, 431 /* 0x140 */ ZEROx8, 432 /* 0x180 */ ZEROx8, 433 /* 0x1c0 */ ZEROx8, 434 /* 0x200 */ ZEROx8, 435 /* 0x240 */ ZEROx8, 436 /* 0x280 */ ZEROx8, 437 /* TRIGGER_HAPPY1..TRIGGER_HAPPY23 */ 438 /* 0x2c0 */ 0xff, 0xff, 0x7f, 439 }, 440 }, 441 { 442 .name = "Logitech Extreme 3D", 443 .bus_type = 0x0003, 444 .vendor_id = 0x046d, 445 .product_id = 0xc215, 446 .version = 0x0110, 447 .expected = SDL_UDEV_DEVICE_JOYSTICK, 448 /* SYN, KEY, ABS, MSC */ 449 .ev = { 0x0b }, 450 /* X, Y, RZ, throttle, hat 0 */ 451 .abs = { 0x63, 0x00, 0x03 }, 452 .keys = { 453 /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, 454 /* 12 buttons: TRIGGER, THUMB, THUMB2, TOP, TOP2, PINKIE, BASE, 455 * BASE2..BASE6 */ 456 /* 0x100 */ ZEROx4, 0xff, 0x0f, 0x00, 0x00, 457 }, 458 }, 459 { 460 .name = "Hori Real Arcade Pro VX-SA", 461 .bus_type = 0x0003, 462 .vendor_id = 0x24c6, 463 .product_id = 0x5501, 464 .version = 0x0533, 465 .expected = SDL_UDEV_DEVICE_JOYSTICK, 466 /* SYN, KEY, ABS */ 467 .ev = { 0x0b }, 468 /* X, Y, Z, RX, RY, RZ, hat 0 */ 469 .abs = { 0x3f, 0x00, 0x03 }, 470 .keys = { 471 /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, 472 /* A, B, X, Y, TL, TR, SELECT, START, MODE, THUMBL, THUMBR */ 473 /* 0x100 */ ZEROx4, 0x00, 0x00, 0xdb, 0x7c, 474 }, 475 }, 476 { 477 .name = "Switch Pro Controller via Bluetooth", 478 .bus_type = 0x0005, 479 .vendor_id = 0x057e, 480 .product_id = 0x2009, 481 .version = 0x0001, 482 .expected = SDL_UDEV_DEVICE_JOYSTICK, 483 /* SYN, KEY, ABS */ 484 .ev = { 0x0b }, 485 /* X, Y, RX, RY, hat 0 */ 486 .abs = { 0x1b, 0x00, 0x03 }, 487 .keys = { 488 /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, 489 /* 16 buttons: TRIGGER, THUMB, THUMB2, TOP, TOP2, PINKIE, BASE, 490 * BASE2..BASE6, unregistered event codes 0x12c-0x12e, DEAD */ 491 /* 0x100 */ ZEROx4, 0xff, 0xff, 0x00, 0x00, 492 /* 0x140 */ ZEROx8, 493 /* 0x180 */ ZEROx8, 494 /* 0x1c0 */ ZEROx8, 495 /* 0x200 */ ZEROx8, 496 /* 0x240 */ ZEROx8, 497 /* 0x280 */ ZEROx8, 498 /* TRIGGER_HAPPY1..TRIGGER_HAPPY2 */ 499 /* 0x2c0 */ 0x03, 500 }, 501 }, 502 { 503 .name = "Switch Pro Controller via USB", 504 .bus_type = 0x0003, 505 .vendor_id = 0x057e, 506 .product_id = 0x2009, 507 .version = 0x0111, 508 .expected = SDL_UDEV_DEVICE_JOYSTICK, 509 /* SYN, KEY, ABS */ 510 .ev = { 0x0b }, 511 /* X, Y, Z, RZ, HAT0X, HAT0Y */ 512 .abs = { 0x27, 0x00, 0x03 }, 513 .keys = { 514 /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, 515 }, 516 }, 517 { 518 .name = "Thrustmaster Racing Wheel FFB", 519 /* Mad Catz FightStick TE S+ PS4, 0003:0738:8384:0111 v0111 520 * (aka Street Fighter V Arcade FightStick TES+) 521 * is functionally the same */ 522 .bus_type = 0x0003, 523 .vendor_id = 0x044f, 524 .product_id = 0xb66d, 525 .version = 0x0110, 526 .expected = SDL_UDEV_DEVICE_JOYSTICK, 527 .ev = { 0x0b }, 528 /* XYZ, RXYZ, hat 0 */ 529 .abs = { 0x3f, 0x00, 0x03 }, 530 .keys = { 531 /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, 532 /* ABC, XYZ, TL, TR, TL2, TR2, SELECT, START, MODE, 533 * THUMBL */ 534 /* 0x100 */ ZEROx4, 0x00, 0x00, 0xff, 0x3f, 535 }, 536 }, 537 { 538 .name = "Thrustmaster T.Flight Hotas X", 539 .bus_type = 0x0003, 540 .vendor_id = 0x044f, 541 .product_id = 0xb108, 542 .version = 0x0100, 543 .expected = SDL_UDEV_DEVICE_JOYSTICK, 544 .ev = { 0x0b }, 545 /* XYZ, RZ, throttle, hat 0 */ 546 .abs = { 0x67, 0x00, 0x03 }, 547 .keys = { 548 /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, 549 /* trigger, thumb, thumb2, top, top2, pinkie, base, 550 * base2..base6 */ 551 /* 0x100 */ ZEROx4, 0xff, 0x0f 552 }, 553 }, 554 { 555 .name = "8BitDo N30 Pro 2", 556 .bus_type = 0x0003, 557 .vendor_id = 0x2dc8, 558 .product_id = 0x9015, 559 .version = 0x0111, 560 /* 8BitDo NES30 Pro, 0003:2dc8:9001 v0111, is the same */ 561 .expected = SDL_UDEV_DEVICE_JOYSTICK, 562 .ev = { 0x0b }, 563 /* XYZ, RZ, gas, brake, hat0 */ 564 .abs = { 0x27, 0x06, 0x03 }, 565 .keys = { 566 /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, 567 /* ABC, XYZ, TL, TR, TL2, TR2, select, start, mode, thumbl, 568 * thumbr */ 569 /* 0x100 */ ZEROx4, 0x00, 0x00, 0xff, 0x7f, 570 }, 571 }, 572 { 573 .name = "Retro Power SNES-style controller, 0003:0079:0011 v0110", 574 .bus_type = 0x0003, 575 .vendor_id = 0x0079, 576 .product_id = 0x0011, 577 .version = 0x0110, 578 .expected = SDL_UDEV_DEVICE_JOYSTICK, 579 .ev = { 0x0b }, 580 /* X, Y */ 581 .abs = { 0x03 }, 582 .keys = { 583 /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, 584 /* trigger, thumb, thumb2, top, top2, pinkie, base, 585 * base2..base4 */ 586 /* 0x100 */ ZEROx4, 0xff, 0x03, 0x00, 0x00, 587 }, 588 }, 589 { 590 .name = "Wiimote - buttons", 591 .bus_type = 0x0005, 592 .vendor_id = 0x057e, 593 .product_id = 0x0306, 594 .version = 0x8600, 595 /* This one is a bit weird because some of the buttons are mapped 596 * to the arrow, page up and page down keys, so it's a joystick 597 * with a subset of a keyboard attached. */ 598 /* TODO: Should this be JOYSTICK, or even JOYSTICK|KEYBOARD? */ 599 .expected = SDL_UDEV_DEVICE_UNKNOWN, 600 /* SYN, KEY */ 601 .ev = { 0x03 }, 602 .keys = { 603 /* 0x00 */ ZEROx8, 604 /* left, right, up down */ 605 /* 0x40 */ ZEROx4, 0x80, 0x16, 0x00, 0x00, 606 /* 0x80 */ ZEROx8, 607 /* 0xc0 */ ZEROx8, 608 /* BTN_1, BTN_2, BTN_A, BTN_B, BTN_MODE */ 609 /* 0x100 */ 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x10, 610 /* 0x140 */ ZEROx8, 611 /* next, previous */ 612 /* 0x180 */ 0x00, 0x00, 0x80, 0x10, ZEROx4, 613 }, 614 }, 615 { 616 .name = "Wiimote - Motion Plus or accelerometer", 617 .bus_type = 0x0005, 618 .vendor_id = 0x057e, 619 .product_id = 0x0306, 620 .version = 0x8600, 621 .expected = SDL_UDEV_DEVICE_ACCELEROMETER, 622 /* SYN, ABS */ 623 .ev = { 0x09 }, 624 /* RX, RY, RZ */ 625 .abs = { 0x38 }, 626 }, 627 { 628 .name = "Wiimote - IR positioning", 629 .bus_type = 0x0005, 630 .vendor_id = 0x057e, 631 .product_id = 0x0306, 632 .version = 0x8600, 633 .expected = SDL_UDEV_DEVICE_UNKNOWN, 634 /* SYN, ABS */ 635 .ev = { 0x09 }, 636 /* HAT0 to HAT3 */ 637 .abs = { 0x00, 0x1f }, 638 }, 639 { 640 .name = "Wiimote - Nunchuck", 641 .bus_type = 0x0005, 642 .vendor_id = 0x057e, 643 .product_id = 0x0306, 644 .version = 0x8600, 645 /* TODO: Should this be JOYSTICK? It has one stick and two buttons */ 646 .expected = SDL_UDEV_DEVICE_UNKNOWN, 647 /* SYN, KEY, ABS */ 648 .ev = { 0x0b }, 649 /* RX, RY, RZ, hat 0 */ 650 .abs = { 0x38, 0x00, 0x03 }, 651 .keys = { 652 /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, 653 /* C and Z buttons */ 654 /* 0x100 */ ZEROx4, 0x00, 0x00, 0x24, 0x00, 655 }, 656 }, 657 { 658 /* Flags guessed from kernel source code */ 659 .name = "Wiimote - Classic Controller", 660 /* TODO: Should this be JOYSTICK, or maybe JOYSTICK|KEYBOARD? 661 * It's unusual in the same ways as the Wiimote */ 662 .expected = SDL_UDEV_DEVICE_UNKNOWN, 663 /* SYN, KEY, ABS */ 664 .ev = { 0x0b }, 665 /* Hat 1-3 */ 666 .abs = { 0x00, 0x1c }, 667 .keys = { 668 /* 0x00 */ ZEROx8, 669 /* left, right, up down */ 670 /* 0x40 */ ZEROx4, 0x80, 0x16, 0x00, 0x00, 671 /* 0x80 */ ZEROx8, 672 /* 0xc0 */ ZEROx8, 673 /* A, B, X, Y, MODE, TL, TL2, TR, TR2 */ 674 /* 0x100 */ ZEROx4, 0x00, 0x13, 0xdb, 0x10, 675 /* 0x140 */ ZEROx8, 676 /* next, previous */ 677 /* 0x180 */ 0x00, 0x00, 0x80, 0x10, ZEROx4, 678 }, 679 }, 680 { 681 /* Flags guessed from kernel source code */ 682 .name = "Wiimote - Balance Board", 683 /* TODO: Should this be JOYSTICK? */ 684 .expected = SDL_UDEV_DEVICE_UNKNOWN, 685 /* SYN, KEY, ABS */ 686 .ev = { 0x0b }, 687 /* Hat 0-1 */ 688 .abs = { 0x00, 0x0f }, 689 .keys = { 690 /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, 691 /* A */ 692 /* 0x100 */ ZEROx4, 0x00, 0x00, 0x01, 0x00, 693 }, 694 }, 695 { 696 /* Flags guessed from kernel source code */ 697 .name = "Wiimote - Wii U Pro Controller", 698 .expected = SDL_UDEV_DEVICE_JOYSTICK, 699 /* SYN, KEY, ABS */ 700 .ev = { 0x0b }, 701 /* X, Y, RX, RY */ 702 .abs = { 0x1b }, 703 .keys = { 704 /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, 705 /* A, B, X, Y, TL, TR, TL2, TR2, SELECT, START, MODE, 706 * THUMBL, THUMBR */ 707 /* 0x100 */ ZEROx4, 0x00, 0x00, 0xdb, 0x7f, 708 /* 0x140 */ ZEROx8, 709 /* 0x180 */ ZEROx8, 710 /* 0x1c0 */ ZEROx8, 711 /* Digital dpad */ 712 /* 0x200 */ ZEROx4, 0x0f, 0x00, 0x00, 0x00, 713 }, 714 }, 715 { 716 .name = "Synaptics TM3381-002 (Thinkpad X280 trackpad)", 717 .bus_type = 0x001d, /* BUS_RMI */ 718 .vendor_id = 0x06cb, 719 .product_id = 0x0000, 720 .version = 0x0000, 721 /* TODO: Should this be MOUSE? That's what it most closely 722 * resembles */ 723 .expected = SDL_UDEV_DEVICE_UNKNOWN, 724 /* SYN, KEY, ABS */ 725 .ev = { 0x0b }, 726 /* X, Y, pressure, multitouch */ 727 .abs = { 0x03, 0x00, 0x00, 0x01, 0x00, 0x80, 0xf3, 0x06 }, 728 .keys = { 729 /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, 730 /* Left mouse button */ 731 /* 0x100 */ 0x00, 0x00, 0x01, 0x00, ZEROx4, 732 /* BTN_TOOL_FINGER and some multitouch gestures */ 733 /* 0x140 */ 0x20, 0xe5 734 }, 735 /* POINTER, BUTTONPAD */ 736 .props = { 0x05 }, 737 }, 738 { 739 .name = "TPPS/2 Elan TrackPoint (Thinkpad X280)", 740 .bus_type = 0x0011, /* BUS_I8042 */ 741 .vendor_id = 0x0002, 742 .product_id = 0x000a, 743 .version = 0x0000, 744 .expected = SDL_UDEV_DEVICE_MOUSE, 745 /* SYN, KEY, REL */ 746 .ev = { 0x07 }, 747 /* X, Y */ 748 .rel = { 0x03 }, 749 .keys = { 750 /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, 751 /* Left, middle, right mouse buttons */ 752 /* 0x100 */ 0x00, 0x00, 0x07, 753 }, 754 /* POINTER, POINTING_STICK */ 755 .props = { 0x21 }, 756 }, 757 { 758 .name = "Thinkpad ACPI buttons", 759 .expected = SDL_UDEV_DEVICE_UNKNOWN, 760 /* SYN, KEY, MSC, SW */ 761 .ev = { 0x33 }, 762 .keys = { 763 /* 0x00 */ ZEROx8, 764 /* 0x40 */ ZEROx4, 0x00, 0x00, 0x0e, 0x01, 765 /* 0x80 */ 0x00, 0x50, 0x11, 0x51, 0x00, 0x28, 0x00, 0xc0, 766 /* 0xc0 */ 0x04, 0x20, 0x10, 0x02, 0x1b, 0x70, 0x01, 0x00, 767 /* 0x100 */ ZEROx8, 768 /* 0x140 */ ZEROx4, 0x00, 0x00, 0x50, 0x00, 769 /* 0x180 */ ZEROx8, 770 /* 0x1c0 */ 0x00, 0x00, 0x04, 0x18, ZEROx4, 771 /* 0x200 */ ZEROx8, 772 /* 0x240 */ 0x40, 0x00, 0x01, 0x00, ZEROx4, 773 }, 774 }, 775 { 776 .name = "PC speaker", 777 .bus_type = 0x0010, /* BUS_ISA */ 778 .vendor_id = 0x001f, 779 .product_id = 0x0001, 780 .version = 0x0100, 781 .expected = SDL_UDEV_DEVICE_UNKNOWN, 782 /* SYN, SND */ 783 .ev = { 0x01, 0x00, 0x04 }, 784 }, 785 { 786 .name = "ALSA headphone detection, etc.", 787 .bus_type = 0x0000, 788 .vendor_id = 0x0000, 789 .product_id = 0x0000, 790 .version = 0x0000, 791 .expected = SDL_UDEV_DEVICE_UNKNOWN, 792 /* SYN, SW */ 793 .ev = { 0x21 }, 794 }, 795 { 796 /* Assumed to be a reasonably typical i8042 (PC AT) keyboard */ 797 .name = "Thinkpad T520 and X280 keyboards", 798 .bus_type = 0x0011, /* BUS_I8042 */ 799 .vendor_id = 0x0001, 800 .product_id = 0x0001, 801 .version = 0xab54, 802 .expected = SDL_UDEV_DEVICE_KEYBOARD, 803 /* SYN, KEY, MSC, LED, REP */ 804 .ev = { 0x13, 0x00, 0x12 }, 805 .keys = { 806 /* 0x00 */ 0xfe, 0xff, 0xff, 0xff, FFx4, 807 /* 0x40 */ 0xff, 0xff, 0xef, 0xff, 0xdf, 0xff, 0xff, 0xfe, 808 /* 0x80 */ 0x01, 0xd0, 0x00, 0xf8, 0x78, 0x30, 0x80, 0x03, 809 /* 0xc0 */ 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x00, 810 }, 811 }, 812 { 813 .name = "Thinkpad X280 sleep button", 814 .bus_type = 0x0019, /* BUS_HOST */ 815 .vendor_id = 0x0000, 816 .product_id = 0x0003, 817 .version = 0x0000, 818 .expected = SDL_UDEV_DEVICE_UNKNOWN, 819 /* SYN, KEY */ 820 .ev = { 0x03 }, 821 .keys = { 822 /* 0x00 */ ZEROx8, 823 /* 0x40 */ ZEROx8, 824 /* KEY_SLEEP */ 825 /* 0x80 */ 0x00, 0x40, 826 }, 827 }, 828 { 829 .name = "Thinkpad X280 lid switch", 830 .bus_type = 0x0019, /* BUS_HOST */ 831 .vendor_id = 0x0000, 832 .product_id = 0x0005, 833 .version = 0x0000, 834 .expected = SDL_UDEV_DEVICE_UNKNOWN, 835 /* SYN, SW */ 836 .ev = { 0x21 }, 837 }, 838 { 839 .name = "Thinkpad X280 power button", 840 .bus_type = 0x0019, /* BUS_HOST */ 841 .vendor_id = 0x0000, 842 .product_id = 0x0001, 843 .version = 0x0000, 844 .expected = SDL_UDEV_DEVICE_UNKNOWN, 845 /* SYN, KEY */ 846 .ev = { 0x03 }, 847 .keys = { 848 /* 0x00 */ ZEROx8, 849 /* KEY_POWER */ 850 /* 0x40 */ ZEROx4, 0x00, 0x00, 0x10, 0x00, 851 }, 852 }, 853 { 854 .name = "Thinkpad X280 video bus", 855 .bus_type = 0x0019, /* BUS_HOST */ 856 .vendor_id = 0x0000, 857 .product_id = 0x0006, 858 .version = 0x0000, 859 .expected = SDL_UDEV_DEVICE_UNKNOWN, 860 /* SYN, KEY */ 861 .ev = { 0x03 }, 862 .keys = { 863 /* 0x00 */ ZEROx8, 864 /* 0x40 */ ZEROx8, 865 /* 0x80 */ ZEROx8, 866 /* brightness control, video mode, display off */ 867 /* 0xc0 */ ZEROx4, 0x0b, 0x00, 0x3e, 0x00, 868 }, 869 }, 870 { 871 .name = "Thinkpad X280 extra buttons", 872 .bus_type = 0x0019, /* BUS_HOST */ 873 .vendor_id = 0x17aa, 874 .product_id = 0x5054, 875 .version = 0x4101, 876 .expected = SDL_UDEV_DEVICE_UNKNOWN, 877 /* SYN, KEY */ 878 .ev = { 0x03 }, 879 .keys = { 880 /* 0x00 */ ZEROx8, 881 /* 0x40 */ ZEROx4, 0x00, 0x00, 0x0e, 0x01, 882 /* 0x80 */ 0x00, 0x50, 0x11, 0x51, 0x00, 0x28, 0x00, 0xc0, 883 /* 0xc0 */ 0x04, 0x20, 0x10, 0x02, 0x1b, 0x70, 0x01, 0x00, 884 /* 0x100 */ ZEROx8, 885 /* 0x140 */ ZEROx4, 0x00, 0x00, 0x50, 0x00, 886 /* 0x180 */ ZEROx8, 887 /* 0x1c0 */ 0x00, 0x00, 0x04, 0x18, ZEROx4, 888 /* 0x200 */ ZEROx8, 889 /* 0x240 */ 0x40, 0x00, 0x01, 0x00, ZEROx4, 890 }, 891 }, 892 { 893 .name = "Thinkpad USB keyboard with Trackpoint - keyboard", 894 .bus_type = 0x0003, 895 .vendor_id = 0x17ef, 896 .product_id = 0x6009, 897 .expected = SDL_UDEV_DEVICE_KEYBOARD, 898 /* SYN, KEY, MSC, LED, REP */ 899 .ev = { 0x13, 0x00, 0x12 }, 900 .keys = { 901 /* 0x00 */ 0xfe, 0xff, 0xff, 0xff, FFx4, 902 /* 0x40 */ 0xff, 0xff, 0xef, 0xff, 0xdf, 0xff, 0xbe, 0xfe, 903 /* 0x80 */ 0xff, 0x57, 0x40, 0xc1, 0x7a, 0x20, 0x9f, 0xff, 904 /* 0xc0 */ 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 905 }, 906 }, 907 { 908 .name = "Thinkpad USB keyboard with Trackpoint - Trackpoint", 909 .bus_type = 0x0003, 910 .vendor_id = 0x17ef, 911 .product_id = 0x6009, 912 /* For some reason the special keys like mute and wlan toggle 913 * show up here instead of, or in addition to, as part of 914 * the keyboard - so udev reports this as having keys too. 915 * SDL currently doesn't. */ 916 .expected = SDL_UDEV_DEVICE_MOUSE, 917 /* SYN, KEY, REL, MSC, LED */ 918 .ev = { 0x17, 0x00, 0x02 }, 919 /* X, Y */ 920 .rel = { 0x03 }, 921 .keys = { 922 /* 0x00 */ ZEROx8, 923 /* 0x40 */ ZEROx4, 0x00, 0x00, 0x1e, 0x00, 924 /* 0x80 */ 0x00, 0xcc, 0x11, 0x01, 0x78, 0x40, 0x00, 0xc0, 925 /* 0xc0 */ 0x00, 0x20, 0x10, 0x00, 0x0b, 0x50, 0x00, 0x00, 926 /* Mouse buttons: left, right, middle, "task" */ 927 /* 0x100 */ 0x00, 0x00, 0x87, 0x68, ZEROx4, 928 /* 0x140 */ ZEROx4, 0x00, 0x00, 0x10, 0x00, 929 /* 0x180 */ ZEROx4, 0x00, 0x00, 0x40, 0x00, 930 }, 931 }, 932 { 933 .name = "No information", 934 .expected = SDL_UDEV_DEVICE_UNKNOWN, 935 } 936 }; 937 938 #if ULONG_MAX == 0xFFFFFFFFUL 939 # define SwapLongLE(X) SDL_SwapLE32(X) 940 #else 941 /* assume 64-bit */ 942 # define SwapLongLE(X) SDL_SwapLE64(X) 943 #endif 944 945 static int 946 run_test(void) 947 { 948 int success = 1; 949 size_t i; 950 951 for (i = 0; i < SDL_arraysize(guess_tests); i++) { 952 const GuessTest *t = &guess_tests[i]; 953 size_t j; 954 int actual; 955 struct { 956 unsigned long ev[NBITS(EV_MAX)]; 957 unsigned long abs[NBITS(ABS_MAX)]; 958 unsigned long keys[NBITS(KEY_MAX)]; 959 unsigned long rel[NBITS(REL_MAX)]; 960 } caps = {}; 961 962 printf("%s...\n", t->name); 963 964 memset(&caps, '\0', sizeof(caps)); 965 memcpy(caps.ev, t->ev, sizeof(t->ev)); 966 memcpy(caps.keys, t->keys, sizeof(t->keys)); 967 memcpy(caps.abs, t->abs, sizeof(t->abs)); 968 memcpy(caps.rel, t->rel, sizeof(t->rel)); 969 970 for (j = 0; j < SDL_arraysize(caps.ev); j++) { 971 caps.ev[j] = SwapLongLE(caps.ev[j]); 972 } 973 974 for (j = 0; j < SDL_arraysize(caps.keys); j++) { 975 caps.keys[j] = SwapLongLE(caps.keys[j]); 976 } 977 978 for (j = 0; j < SDL_arraysize(caps.abs); j++) { 979 caps.abs[j] = SwapLongLE(caps.abs[j]); 980 } 981 982 for (j = 0; j < SDL_arraysize(caps.rel); j++) { 983 caps.rel[j] = SwapLongLE(caps.rel[j]); 984 } 985 986 actual = SDL_EVDEV_GuessDeviceClass(caps.ev, caps.abs, caps.keys, 987 caps.rel); 988 989 if (actual == t->expected) { 990 printf("\tOK\n"); 991 } 992 else { 993 printf("\tExpected 0x%08x\n", t->expected); 994 995 for (j = 0; device_classes[j].code != 0; j++) { 996 if (t->expected & device_classes[j].code) { 997 printf("\t\t%s\n", device_classes[j].name); 998 } 999 } 1000 1001 printf("\tGot 0x%08x\n", actual); 1002 1003 for (j = 0; device_classes[j].code != 0; j++) { 1004 if (actual & device_classes[j].code) { 1005 printf("\t\t%s\n", device_classes[j].name); 1006 } 1007 } 1008 1009 success = 0; 1010 } 1011 } 1012 1013 return success; 1014 } 1015 1016 #else /* !(HAVE_LIBUDEV_H || defined(SDL_JOYSTICK_LINUX)) */ 1017 1018 static int 1019 run_test(void) 1020 { 1021 printf("SDL compiled without evdev capability check.\n"); 1022 return 0; 1023 } 1024 1025 #endif 1026 1027 int 1028 main(int argc, char *argv[]) 1029 { 1030 return run_test() ? 0 : 1; 1031 }