| /* |
| Copyright (C) 1997-2022 Sam Lantinga <slouken@libsdl.org> |
| Copyright (C) 2020-2022 Collabora Ltd. |
| |
| This software is provided 'as-is', without any express or implied |
| warranty. In no event will the authors be held liable for any damages |
| arising from the use of this software. |
| |
| Permission is granted to anyone to use this software for any purpose, |
| including commercial applications, and to alter it and redistribute it |
| freely. |
| */ |
| |
| #include "../src/SDL_internal.h" |
| |
| #include <stdio.h> |
| #include <string.h> |
| |
| static int run_test(void); |
| |
| #if HAVE_LIBUDEV_H || defined(SDL_JOYSTICK_LINUX) |
| |
| #include <stdint.h> |
| |
| #include "SDL_stdinc.h" |
| #include "SDL_endian.h" |
| #include "../src/core/linux/SDL_evdev_capabilities.h" |
| #include "../src/core/linux/SDL_evdev_capabilities.c" |
| |
| static const struct |
| { |
| int code; |
| const char *name; |
| } device_classes[] = |
| { |
| #define CLS(x) \ |
| { SDL_UDEV_DEVICE_ ## x, #x } |
| CLS(MOUSE), |
| CLS(KEYBOARD), |
| CLS(JOYSTICK), |
| CLS(SOUND), |
| CLS(TOUCHSCREEN), |
| CLS(ACCELEROMETER), |
| CLS(TOUCHPAD), |
| #undef CLS |
| { 0, NULL } |
| }; |
| |
| typedef struct |
| { |
| const char *name; |
| uint16_t bus_type; |
| uint16_t vendor_id; |
| uint16_t product_id; |
| uint16_t version; |
| uint8_t ev[(EV_MAX + 1) / 8]; |
| uint8_t keys[(KEY_MAX + 1) / 8]; |
| uint8_t abs[(ABS_MAX + 1) / 8]; |
| uint8_t rel[(REL_MAX + 1) / 8]; |
| uint8_t ff[(FF_MAX + 1) / 8]; |
| uint8_t props[INPUT_PROP_MAX / 8]; |
| int expected; |
| } GuessTest; |
| |
| /* |
| * Test-cases for guessing a device type from its capabilities. |
| * |
| * The bytes in ev, etc. are in little-endian byte order |
| * Trailing zeroes can be omitted. |
| * |
| * The evemu-describe tool is a convenient way to add a test-case for |
| * a physically available device. |
| */ |
| #define ZEROx4 0, 0, 0, 0 |
| #define ZEROx8 ZEROx4, ZEROx4 |
| #define FFx4 0xff, 0xff, 0xff, 0xff |
| #define FFx8 FFx4, FFx4 |
| |
| /* Test-cases derived from real devices or from Linux kernel source */ |
| static const GuessTest guess_tests[] = |
| { |
| { |
| .name = "Xbox 360 wired USB controller", |
| .bus_type = 0x0003, |
| .vendor_id = 0x045e, |
| .product_id = 0x028e, |
| .expected = SDL_UDEV_DEVICE_JOYSTICK, |
| /* SYN, KEY, ABS, FF */ |
| .ev = { 0x0b, 0x00, 0x20 }, |
| /* X, Y, Z, RX, RY, RZ, HAT0X, HAT0Y */ |
| .abs = { 0x3f, 0x00, 0x03 }, |
| .keys = { |
| /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, |
| /* A, B, X, Y, TL, TR, SELECT, START, MODE, THUMBL, THUMBR */ |
| /* 0x100 */ ZEROx4, 0x00, 0x00, 0xdb, 0x7c, |
| }, |
| }, |
| { |
| .name = "X-Box One Elite", |
| .bus_type = 0x0003, |
| .vendor_id = 0x045e, |
| .product_id = 0x02e3, |
| .expected = SDL_UDEV_DEVICE_JOYSTICK, |
| /* SYN, KEY, ABS */ |
| .ev = { 0x0b }, |
| /* X, Y, Z, RX, RY, RZ, HAT0X, HAT0Y */ |
| .abs = { 0x3f, 0x00, 0x03 }, |
| .keys = { |
| /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, |
| /* A, B, X, Y, TL, TR, SELECT, START, MODE, THUMBL, THUMBR */ |
| /* 0x100 */ ZEROx4, 0x00, 0x00, 0xdb, 0x7c, |
| }, |
| }, |
| { |
| .name = "X-Box One S via Bluetooth", |
| .bus_type = 0x0005, |
| .vendor_id = 0x045e, |
| .product_id = 0x02e0, |
| .version = 0x1130, |
| .expected = SDL_UDEV_DEVICE_JOYSTICK, |
| /* SYN, KEY, ABS */ |
| .ev = { 0x0b }, |
| /* X, Y, Z, RX, RY, RZ, HAT0X, HAT0Y */ |
| .abs = { 0x3f, 0x00, 0x03 }, |
| .keys = { |
| /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, |
| /* A, B, X, Y, TL, TR, SELECT, START, MODE, THUMBL, THUMBR */ |
| /* 0x100 */ ZEROx4, 0x00, 0x00, 0xdb, 0x7c, |
| }, |
| }, |
| { |
| .name = "X-Box One S wired", |
| .bus_type = 0x0003, |
| .vendor_id = 0x045e, |
| .product_id = 0x02ea, |
| .version = 0x0301, |
| .expected = SDL_UDEV_DEVICE_JOYSTICK, |
| /* SYN, KEY, ABS */ |
| .ev = { 0x0b }, |
| /* X, Y, Z, RX, RY, RZ, HAT0X, HAT0Y */ |
| .abs = { 0x3f, 0x00, 0x03 }, |
| .keys = { |
| /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, |
| /* A, B, X, Y, TL, TR, SELECT, START, MODE, THUMBL, THUMBR */ |
| /* 0x100 */ ZEROx4, 0x00, 0x00, 0xdb, 0x7c, |
| }, |
| }, |
| { |
| .name = "DualShock 4 - gamepad", |
| .bus_type = 0x0003, |
| .vendor_id = 0x054c, |
| .product_id = 0x09cc, |
| .expected = SDL_UDEV_DEVICE_JOYSTICK, |
| /* SYN, KEY, ABS, MSC, FF */ |
| /* Some versions only have 0x0b, SYN, KEY, ABS, like the |
| * Bluetooth example below */ |
| .ev = { 0x1b, 0x00, 0x20 }, |
| /* X, Y, Z, RX, RY, RZ, HAT0X, HAT0Y */ |
| .abs = { 0x3f, 0x00, 0x03 }, |
| .keys = { |
| /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, |
| /* A, B, X, Y, TL, TR, TL2, TR2, SELECT, START, MODE, |
| * THUMBL, THUMBR */ |
| /* 0x100 */ ZEROx4, 0x00, 0x00, 0xdb, 0x7f, |
| }, |
| }, |
| { |
| .name = "DualShock 4 - gamepad via Bluetooth", |
| .bus_type = 0x0005, |
| .vendor_id = 0x054c, |
| .product_id = 0x09cc, |
| .expected = SDL_UDEV_DEVICE_JOYSTICK, |
| /* SYN, KEY, ABS */ |
| .ev = { 0x0b }, |
| /* X, Y, Z, RX, RY, RZ, HAT0X, HAT0Y */ |
| .abs = { 0x3f, 0x00, 0x03 }, |
| .keys = { |
| /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, |
| /* A, B, X, Y, TL, TR, TL2, TR2, SELECT, START, MODE, |
| * THUMBL, THUMBR */ |
| /* 0x100 */ ZEROx4, 0x00, 0x00, 0xdb, 0x7f, |
| }, |
| }, |
| { |
| .name = "DualShock 4 - touchpad", |
| .bus_type = 0x0003, |
| .vendor_id = 0x054c, |
| .product_id = 0x09cc, |
| .expected = SDL_UDEV_DEVICE_TOUCHPAD, |
| /* SYN, KEY, ABS */ |
| .ev = { 0x0b }, |
| /* X, Y, multitouch */ |
| .abs = { 0x03, 0x00, 0x00, 0x00, 0x00, 0x80, 0x60, 0x02 }, |
| .keys = { |
| /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, |
| /* Left mouse button */ |
| /* 0x100 */ 0x00, 0x00, 0x01, 0x00, ZEROx4, |
| /* BTN_TOOL_FINGER and some multitouch stuff */ |
| /* 0x140 */ 0x20, 0x24, 0x00, 0x00 |
| }, |
| /* POINTER, BUTTONPAD */ |
| .props = { 0x05 }, |
| }, |
| { |
| .name = "DualShock 4 - accelerometer", |
| .bus_type = 0x0003, |
| .vendor_id = 0x054c, |
| .product_id = 0x09cc, |
| .expected = SDL_UDEV_DEVICE_ACCELEROMETER, |
| /* SYN, ABS, MSC */ |
| .ev = { 0x19 }, |
| /* X, Y, Z, RX, RY, RZ */ |
| .abs = { 0x3f }, |
| /* ACCELEROMETER */ |
| .props = { 0x40 }, |
| }, |
| { |
| .name = "DualShock 4 via USB dongle", |
| .bus_type = 0x0003, |
| .vendor_id = 0x054c, |
| .product_id = 0x0ba0, |
| .version = 0x8111, |
| .expected = SDL_UDEV_DEVICE_JOYSTICK, |
| /* SYN, ABS, KEY */ |
| .ev = { 0x0b }, |
| /* X, Y, Z, RX, RY, RZ, HAT0X, HAT0Y */ |
| .abs = { 0x3f, 0x00, 0x03 }, |
| .keys = { |
| /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, |
| /* A, B, X, Y, TL, TR, TL2, TR2, SELECT, START, MODE, |
| * THUMBL, THUMBR */ |
| /* 0x100 */ ZEROx4, 0x00, 0x00, 0xdb, 0x7f, |
| }, |
| }, |
| { |
| .name = "DualShock 3 - gamepad", |
| .bus_type = 0x0003, |
| .vendor_id = 0x054c, |
| .product_id = 0x0268, |
| .expected = SDL_UDEV_DEVICE_JOYSTICK, |
| /* SYN, KEY, ABS, MSC, FF */ |
| .ev = { 0x1b, 0x00, 0x20 }, |
| /* X, Y, Z, RX, RY, RZ */ |
| .abs = { 0x3f }, |
| .keys = { |
| /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, |
| /* A, B, X, Y, TL, TR, TL2, TR2, SELECT, START, MODE, |
| * THUMBL, THUMBR */ |
| /* 0x100 */ ZEROx4, 0x00, 0x00, 0xdb, 0x7f, |
| /* 0x140 */ ZEROx8, |
| /* 0x180 */ ZEROx8, |
| /* 0x1c0 */ ZEROx8, |
| /* Digital dpad */ |
| /* 0x200 */ ZEROx4, 0x0f, 0x00, 0x00, 0x00, |
| }, |
| }, |
| { |
| .name = "DualShock 3 - accelerometer", |
| .bus_type = 0x0003, |
| .vendor_id = 0x054c, |
| .product_id = 0x0268, |
| .expected = SDL_UDEV_DEVICE_ACCELEROMETER, |
| /* SYN, ABS */ |
| .ev = { 0x09 }, |
| /* X, Y, Z */ |
| .abs = { 0x07 }, |
| /* ACCELEROMETER */ |
| .props = { 0x40 }, |
| }, |
| { |
| .name = "Steam Controller - gamepad", |
| .bus_type = 0x0003, |
| .vendor_id = 0x28de, |
| .product_id = 0x1142, |
| .expected = SDL_UDEV_DEVICE_JOYSTICK, |
| /* SYN, KEY, ABS */ |
| .ev = { 0x0b }, |
| /* X, Y, RX, RY, HAT0X, HAT0Y, HAT2X, HAT2Y */ |
| .abs = { 0x1b, 0x00, 0x33 }, |
| .keys = { |
| /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, |
| /* A, B, X, Y, TL, TR, TL2, TR2, SELECT, START, MODE, |
| * THUMBL, THUMBR, joystick THUMB, joystick THUMB2 */ |
| /* 0x100 */ ZEROx4, 0x06, 0x00, 0xdb, 0x7f, |
| /* GEAR_DOWN, GEAR_UP */ |
| /* 0x140 */ 0x00, 0x00, 0x03, 0x00, ZEROx4, |
| /* 0x180 */ ZEROx8, |
| /* 0x1c0 */ ZEROx8, |
| /* Digital dpad */ |
| /* 0x200 */ ZEROx4, 0x0f, 0x00, 0x00, 0x00, |
| }, |
| }, |
| { |
| /* Present to support lizard mode, even if no Steam Controller |
| * is connected */ |
| .name = "Steam Controller - dongle", |
| .bus_type = 0x0003, |
| .vendor_id = 0x28de, |
| .product_id = 0x1142, |
| .expected = (SDL_UDEV_DEVICE_KEYBOARD |
| | SDL_UDEV_DEVICE_MOUSE), |
| /* SYN, KEY, REL, MSC, LED, REP */ |
| .ev = { 0x17, 0x00, 0x12 }, |
| /* X, Y, mouse wheel, high-res mouse wheel */ |
| .rel = { 0x03, 0x09 }, |
| .keys = { |
| /* 0x00 */ 0xfe, 0xff, 0xff, 0xff, FFx4, |
| /* 0x40 */ 0xff, 0xff, 0xcf, 0x01, 0xdf, 0xff, 0x80, 0xe0, |
| /* 0x80 */ ZEROx8, |
| /* 0xc0 */ ZEROx8, |
| /* 0x100 */ 0x00, 0x00, 0x1f, 0x00, ZEROx4, |
| }, |
| }, |
| { |
| .name = "Guitar Hero for PS3", |
| .bus_type = 0x0003, |
| .vendor_id = 0x12ba, |
| .product_id = 0x0100, |
| .version = 0x0110, |
| .expected = SDL_UDEV_DEVICE_JOYSTICK, |
| /* SYN, KEY, ABS */ |
| .ev = { 0x0b }, |
| /* X, Y, Z, RZ, HAT0X, HAT0Y */ |
| .abs = { 0x27, 0x00, 0x03 }, |
| .keys = { |
| /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, |
| /* ABC, XYZ, TL, TR, TL2, TR2, SELECT, START, MODE */ |
| /* 0x100 */ ZEROx4, 0x00, 0x00, 0xff, 0x1f, |
| }, |
| }, |
| { |
| .name = "G27 Racing Wheel, 0003:046d:c29b v0111", |
| .bus_type = 0x0003, |
| .vendor_id = 0x046d, |
| .product_id = 0xc29b, |
| .version = 0x0111, |
| .expected = SDL_UDEV_DEVICE_JOYSTICK, |
| /* SYN, KEY, ABS */ |
| .ev = { 0x0b }, |
| /* X, Y, Z, RZ, HAT0X, HAT0Y */ |
| .abs = { 0x27, 0x00, 0x03 }, |
| .keys = { |
| /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, |
| /* 16 buttons: TRIGGER, THUMB, THUMB2, TOP, TOP2, PINKIE, BASE, |
| * BASE2..BASE6, unregistered event codes 0x12c-0x12e, DEAD */ |
| /* 0x100 */ ZEROx4, 0xff, 0xff, 0x00, 0x00, |
| /* 0x140 */ ZEROx8, |
| /* 0x180 */ ZEROx8, |
| /* 0x1c0 */ ZEROx8, |
| /* 0x200 */ ZEROx8, |
| /* 0x240 */ ZEROx8, |
| /* 0x280 */ ZEROx8, |
| /* TRIGGER_HAPPY1..TRIGGER_HAPPY7 */ |
| /* 0x2c0 */ 0x7f, |
| }, |
| }, |
| { |
| .name = "Logitech Driving Force, 0003:046d:c294 v0100", |
| .bus_type = 0x0003, |
| .vendor_id = 0x046d, |
| .product_id = 0xc294, |
| .version = 0x0100, |
| .expected = SDL_UDEV_DEVICE_JOYSTICK, |
| /* SYN, KEY, ABS */ |
| .ev = { 0x0b }, |
| /* X, Y, RZ, HAT0X, HAT0Y */ |
| .abs = { 0x23, 0x00, 0x03 }, |
| .keys = { |
| /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, |
| /* 12 buttons: TRIGGER, THUMB, THUMB2, TOP, TOP2, PINKIE, BASE, |
| * BASE2..BASE6 */ |
| /* 0x100 */ ZEROx4, 0xff, 0x0f, 0x00, 0x00, |
| }, |
| }, |
| { |
| .name = "Logitech Dual Action", |
| .bus_type = 0x0003, |
| .vendor_id = 0x046d, |
| .product_id = 0xc216, |
| .version = 0x0110, |
| /* Logitech RumblePad 2 USB, 0003:046d:c218 v0110, is the same |
| * except for having force feedback, which we don't use in our |
| * heuristic */ |
| /* Jess Tech GGE909 PC Recoil Pad, 0003:0f30:010b v0110, is the same */ |
| /* 8BitDo SNES30, 0003:2dc8:ab20 v0110, is the same */ |
| .expected = SDL_UDEV_DEVICE_JOYSTICK, |
| /* SYN, KEY, ABS */ |
| .ev = { 0x0b }, |
| /* X, Y, Z, RZ, HAT0X, HAT0Y */ |
| .abs = { 0x27, 0x00, 0x03 }, |
| .keys = { |
| /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, |
| /* 12 buttons: TRIGGER, THUMB, THUMB2, TOP, TOP2, PINKIE, BASE, |
| * BASE2..BASE6 */ |
| /* 0x100 */ ZEROx4, 0xff, 0x0f, 0x00, 0x00, |
| }, |
| }, |
| { |
| .name = "Saitek ST290 Pro flight stick", |
| .bus_type = 0x0003, |
| .vendor_id = 0x06a3, |
| .product_id = 0x0160, /* 0x0460 seems to be the same */ |
| .version = 0x0100, |
| .expected = SDL_UDEV_DEVICE_JOYSTICK, |
| /* SYN, KEY, ABS, MSC */ |
| .ev = { 0x1b }, |
| /* X, Y, Z, RZ, HAT0X, HAT0Y */ |
| .abs = { 0x27, 0x00, 0x03 }, |
| .keys = { |
| /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, |
| /* TRIGGER, THUMB, THUMB2, TOP, TOP2, PINKIE */ |
| /* 0x100 */ ZEROx4, 0x3f, 0x00, 0x00, 0x00, |
| }, |
| }, |
| { |
| .name = "Saitek X52 Pro Flight Control System", |
| .bus_type = 0x0003, |
| .vendor_id = 0x06a3, |
| .product_id = 0x0762, |
| .version = 0x0111, |
| .expected = SDL_UDEV_DEVICE_JOYSTICK, |
| .ev = { 0x0b }, |
| /* XYZ, RXYZ, throttle, hat0, MISC, unregistered event code 0x29 */ |
| .abs = { 0x7f, 0x00, 0x03, 0x00, 0x00, 0x03 }, |
| .keys = { |
| /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, |
| /* 16 buttons: TRIGGER, THUMB, THUMB2, TOP, TOP2, PINKIE, BASE, |
| * BASE2..BASE6, unregistered event codes 0x12c-0x12e, DEAD */ |
| /* 0x100 */ ZEROx4, 0xff, 0xff, 0x00, 0x00, |
| /* 0x140 */ ZEROx8, |
| /* 0x180 */ ZEROx8, |
| /* 0x1c0 */ ZEROx8, |
| /* 0x200 */ ZEROx8, |
| /* 0x240 */ ZEROx8, |
| /* 0x280 */ ZEROx8, |
| /* TRIGGER_HAPPY1..TRIGGER_HAPPY23 */ |
| /* 0x2c0 */ 0xff, 0xff, 0x7f, |
| }, |
| }, |
| { |
| .name = "Logitech Extreme 3D", |
| .bus_type = 0x0003, |
| .vendor_id = 0x046d, |
| .product_id = 0xc215, |
| .version = 0x0110, |
| .expected = SDL_UDEV_DEVICE_JOYSTICK, |
| /* SYN, KEY, ABS, MSC */ |
| .ev = { 0x0b }, |
| /* X, Y, RZ, throttle, hat 0 */ |
| .abs = { 0x63, 0x00, 0x03 }, |
| .keys = { |
| /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, |
| /* 12 buttons: TRIGGER, THUMB, THUMB2, TOP, TOP2, PINKIE, BASE, |
| * BASE2..BASE6 */ |
| /* 0x100 */ ZEROx4, 0xff, 0x0f, 0x00, 0x00, |
| }, |
| }, |
| { |
| .name = "Hori Real Arcade Pro VX-SA", |
| .bus_type = 0x0003, |
| .vendor_id = 0x24c6, |
| .product_id = 0x5501, |
| .version = 0x0533, |
| .expected = SDL_UDEV_DEVICE_JOYSTICK, |
| /* SYN, KEY, ABS */ |
| .ev = { 0x0b }, |
| /* X, Y, Z, RX, RY, RZ, hat 0 */ |
| .abs = { 0x3f, 0x00, 0x03 }, |
| .keys = { |
| /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, |
| /* A, B, X, Y, TL, TR, SELECT, START, MODE, THUMBL, THUMBR */ |
| /* 0x100 */ ZEROx4, 0x00, 0x00, 0xdb, 0x7c, |
| }, |
| }, |
| { |
| .name = "Switch Pro Controller via Bluetooth", |
| .bus_type = 0x0005, |
| .vendor_id = 0x057e, |
| .product_id = 0x2009, |
| .version = 0x0001, |
| .expected = SDL_UDEV_DEVICE_JOYSTICK, |
| /* SYN, KEY, ABS */ |
| .ev = { 0x0b }, |
| /* X, Y, RX, RY, hat 0 */ |
| .abs = { 0x1b, 0x00, 0x03 }, |
| .keys = { |
| /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, |
| /* 16 buttons: TRIGGER, THUMB, THUMB2, TOP, TOP2, PINKIE, BASE, |
| * BASE2..BASE6, unregistered event codes 0x12c-0x12e, DEAD */ |
| /* 0x100 */ ZEROx4, 0xff, 0xff, 0x00, 0x00, |
| /* 0x140 */ ZEROx8, |
| /* 0x180 */ ZEROx8, |
| /* 0x1c0 */ ZEROx8, |
| /* 0x200 */ ZEROx8, |
| /* 0x240 */ ZEROx8, |
| /* 0x280 */ ZEROx8, |
| /* TRIGGER_HAPPY1..TRIGGER_HAPPY2 */ |
| /* 0x2c0 */ 0x03, |
| }, |
| }, |
| { |
| .name = "Switch Pro Controller via USB", |
| .bus_type = 0x0003, |
| .vendor_id = 0x057e, |
| .product_id = 0x2009, |
| .version = 0x0111, |
| .expected = SDL_UDEV_DEVICE_JOYSTICK, |
| /* SYN, KEY, ABS */ |
| .ev = { 0x0b }, |
| /* X, Y, Z, RZ, HAT0X, HAT0Y */ |
| .abs = { 0x27, 0x00, 0x03 }, |
| .keys = { |
| /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, |
| }, |
| }, |
| { |
| .name = "Thrustmaster Racing Wheel FFB", |
| /* Mad Catz FightStick TE S+ PS4, 0003:0738:8384:0111 v0111 |
| * (aka Street Fighter V Arcade FightStick TES+) |
| * is functionally the same */ |
| .bus_type = 0x0003, |
| .vendor_id = 0x044f, |
| .product_id = 0xb66d, |
| .version = 0x0110, |
| .expected = SDL_UDEV_DEVICE_JOYSTICK, |
| .ev = { 0x0b }, |
| /* XYZ, RXYZ, hat 0 */ |
| .abs = { 0x3f, 0x00, 0x03 }, |
| .keys = { |
| /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, |
| /* ABC, XYZ, TL, TR, TL2, TR2, SELECT, START, MODE, |
| * THUMBL */ |
| /* 0x100 */ ZEROx4, 0x00, 0x00, 0xff, 0x3f, |
| }, |
| }, |
| { |
| .name = "Thrustmaster T.Flight Hotas X", |
| .bus_type = 0x0003, |
| .vendor_id = 0x044f, |
| .product_id = 0xb108, |
| .version = 0x0100, |
| .expected = SDL_UDEV_DEVICE_JOYSTICK, |
| .ev = { 0x0b }, |
| /* XYZ, RZ, throttle, hat 0 */ |
| .abs = { 0x67, 0x00, 0x03 }, |
| .keys = { |
| /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, |
| /* trigger, thumb, thumb2, top, top2, pinkie, base, |
| * base2..base6 */ |
| /* 0x100 */ ZEROx4, 0xff, 0x0f |
| }, |
| }, |
| { |
| .name = "8BitDo N30 Pro 2", |
| .bus_type = 0x0003, |
| .vendor_id = 0x2dc8, |
| .product_id = 0x9015, |
| .version = 0x0111, |
| /* 8BitDo NES30 Pro, 0003:2dc8:9001 v0111, is the same */ |
| .expected = SDL_UDEV_DEVICE_JOYSTICK, |
| .ev = { 0x0b }, |
| /* XYZ, RZ, gas, brake, hat0 */ |
| .abs = { 0x27, 0x06, 0x03 }, |
| .keys = { |
| /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, |
| /* ABC, XYZ, TL, TR, TL2, TR2, select, start, mode, thumbl, |
| * thumbr */ |
| /* 0x100 */ ZEROx4, 0x00, 0x00, 0xff, 0x7f, |
| }, |
| }, |
| { |
| .name = "Retro Power SNES-style controller, 0003:0079:0011 v0110", |
| .bus_type = 0x0003, |
| .vendor_id = 0x0079, |
| .product_id = 0x0011, |
| .version = 0x0110, |
| .expected = SDL_UDEV_DEVICE_JOYSTICK, |
| .ev = { 0x0b }, |
| /* X, Y */ |
| .abs = { 0x03 }, |
| .keys = { |
| /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, |
| /* trigger, thumb, thumb2, top, top2, pinkie, base, |
| * base2..base4 */ |
| /* 0x100 */ ZEROx4, 0xff, 0x03, 0x00, 0x00, |
| }, |
| }, |
| { |
| .name = "Wiimote - buttons", |
| .bus_type = 0x0005, |
| .vendor_id = 0x057e, |
| .product_id = 0x0306, |
| .version = 0x8600, |
| /* This one is a bit weird because some of the buttons are mapped |
| * to the arrow, page up and page down keys, so it's a joystick |
| * with a subset of a keyboard attached. */ |
| /* TODO: Should this be JOYSTICK, or even JOYSTICK|KEYBOARD? */ |
| .expected = SDL_UDEV_DEVICE_KEYBOARD, |
| /* SYN, KEY */ |
| .ev = { 0x03 }, |
| .keys = { |
| /* 0x00 */ ZEROx8, |
| /* left, right, up down */ |
| /* 0x40 */ ZEROx4, 0x80, 0x16, 0x00, 0x00, |
| /* 0x80 */ ZEROx8, |
| /* 0xc0 */ ZEROx8, |
| /* BTN_1, BTN_2, BTN_A, BTN_B, BTN_MODE */ |
| /* 0x100 */ 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x10, |
| /* 0x140 */ ZEROx8, |
| /* next (keyboard page down), previous (keyboard page up) */ |
| /* 0x180 */ 0x00, 0x00, 0x80, 0x10, ZEROx4, |
| }, |
| }, |
| { |
| .name = "Wiimote - Motion Plus or accelerometer", |
| .bus_type = 0x0005, |
| .vendor_id = 0x057e, |
| .product_id = 0x0306, |
| .version = 0x8600, |
| .expected = SDL_UDEV_DEVICE_ACCELEROMETER, |
| /* SYN, ABS */ |
| .ev = { 0x09 }, |
| /* RX, RY, RZ */ |
| .abs = { 0x38 }, |
| }, |
| { |
| .name = "Wiimote - IR positioning", |
| .bus_type = 0x0005, |
| .vendor_id = 0x057e, |
| .product_id = 0x0306, |
| .version = 0x8600, |
| .expected = SDL_UDEV_DEVICE_UNKNOWN, |
| /* SYN, ABS */ |
| .ev = { 0x09 }, |
| /* HAT0 to HAT3 */ |
| .abs = { 0x00, 0x1f }, |
| }, |
| { |
| .name = "Wiimote - Nunchuck", |
| .bus_type = 0x0005, |
| .vendor_id = 0x057e, |
| .product_id = 0x0306, |
| .version = 0x8600, |
| /* TODO: Should this be JOYSTICK? It has one stick and two buttons */ |
| .expected = SDL_UDEV_DEVICE_UNKNOWN, |
| /* SYN, KEY, ABS */ |
| .ev = { 0x0b }, |
| /* RX, RY, RZ, hat 0 */ |
| .abs = { 0x38, 0x00, 0x03 }, |
| .keys = { |
| /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, |
| /* C and Z buttons */ |
| /* 0x100 */ ZEROx4, 0x00, 0x00, 0x24, 0x00, |
| }, |
| }, |
| { |
| /* Flags guessed from kernel source code */ |
| .name = "Wiimote - Classic Controller", |
| /* TODO: Should this be JOYSTICK, or maybe JOYSTICK|KEYBOARD? |
| * It's unusual in the same ways as the Wiimote */ |
| .expected = SDL_UDEV_DEVICE_KEYBOARD, |
| /* SYN, KEY, ABS */ |
| .ev = { 0x0b }, |
| /* Hat 1-3 */ |
| .abs = { 0x00, 0x1c }, |
| .keys = { |
| /* 0x00 */ ZEROx8, |
| /* left, right, up down */ |
| /* 0x40 */ ZEROx4, 0x80, 0x16, 0x00, 0x00, |
| /* 0x80 */ ZEROx8, |
| /* 0xc0 */ ZEROx8, |
| /* A, B, X, Y, MODE, TL, TL2, TR, TR2 */ |
| /* 0x100 */ ZEROx4, 0x00, 0x13, 0xdb, 0x10, |
| /* 0x140 */ ZEROx8, |
| /* next (keyboard page down), previous (keyboard page up) */ |
| /* 0x180 */ 0x00, 0x00, 0x80, 0x10, ZEROx4, |
| }, |
| }, |
| { |
| /* Flags guessed from kernel source code */ |
| .name = "Wiimote - Balance Board", |
| /* TODO: Should this be JOYSTICK? */ |
| .expected = SDL_UDEV_DEVICE_UNKNOWN, |
| /* SYN, KEY, ABS */ |
| .ev = { 0x0b }, |
| /* Hat 0-1 */ |
| .abs = { 0x00, 0x0f }, |
| .keys = { |
| /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, |
| /* A */ |
| /* 0x100 */ ZEROx4, 0x00, 0x00, 0x01, 0x00, |
| }, |
| }, |
| { |
| /* Flags guessed from kernel source code */ |
| .name = "Wiimote - Wii U Pro Controller", |
| .expected = SDL_UDEV_DEVICE_JOYSTICK, |
| /* SYN, KEY, ABS */ |
| .ev = { 0x0b }, |
| /* X, Y, RX, RY */ |
| .abs = { 0x1b }, |
| .keys = { |
| /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, |
| /* A, B, X, Y, TL, TR, TL2, TR2, SELECT, START, MODE, |
| * THUMBL, THUMBR */ |
| /* 0x100 */ ZEROx4, 0x00, 0x00, 0xdb, 0x7f, |
| /* 0x140 */ ZEROx8, |
| /* 0x180 */ ZEROx8, |
| /* 0x1c0 */ ZEROx8, |
| /* Digital dpad */ |
| /* 0x200 */ ZEROx4, 0x0f, 0x00, 0x00, 0x00, |
| }, |
| }, |
| { |
| .name = "Synaptics TM3381-002 (Thinkpad X280 trackpad)", |
| .bus_type = 0x001d, /* BUS_RMI */ |
| .vendor_id = 0x06cb, |
| .product_id = 0x0000, |
| .version = 0x0000, |
| .expected = SDL_UDEV_DEVICE_TOUCHPAD, |
| /* SYN, KEY, ABS */ |
| .ev = { 0x0b }, |
| /* X, Y, pressure, multitouch */ |
| .abs = { 0x03, 0x00, 0x00, 0x01, 0x00, 0x80, 0xf3, 0x06 }, |
| .keys = { |
| /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, |
| /* Left mouse button */ |
| /* 0x100 */ 0x00, 0x00, 0x01, 0x00, ZEROx4, |
| /* BTN_TOOL_FINGER and some multitouch gestures */ |
| /* 0x140 */ 0x20, 0xe5 |
| }, |
| /* POINTER, BUTTONPAD */ |
| .props = { 0x05 }, |
| }, |
| { |
| .name = "TPPS/2 Elan TrackPoint (Thinkpad X280)", |
| .bus_type = 0x0011, /* BUS_I8042 */ |
| .vendor_id = 0x0002, |
| .product_id = 0x000a, |
| .version = 0x0000, |
| .expected = SDL_UDEV_DEVICE_MOUSE, |
| /* SYN, KEY, REL */ |
| .ev = { 0x07 }, |
| /* X, Y */ |
| .rel = { 0x03 }, |
| .keys = { |
| /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, |
| /* Left, middle, right mouse buttons */ |
| /* 0x100 */ 0x00, 0x00, 0x07, |
| }, |
| /* POINTER, POINTING_STICK */ |
| .props = { 0x21 }, |
| }, |
| { |
| .name = "Thinkpad ACPI buttons", |
| /* SDL treats this as a keyboard because it has a power button */ |
| .expected = SDL_UDEV_DEVICE_KEYBOARD, |
| /* SYN, KEY, MSC, SW */ |
| .ev = { 0x33 }, |
| .keys = { |
| /* 0x00 */ ZEROx8, |
| /* 0x40 */ ZEROx4, 0x00, 0x00, 0x0e, 0x01, |
| /* 0x80 */ 0x00, 0x50, 0x11, 0x51, 0x00, 0x28, 0x00, 0xc0, |
| /* 0xc0 */ 0x04, 0x20, 0x10, 0x02, 0x1b, 0x70, 0x01, 0x00, |
| /* 0x100 */ ZEROx8, |
| /* 0x140 */ ZEROx4, 0x00, 0x00, 0x50, 0x00, |
| /* 0x180 */ ZEROx8, |
| /* 0x1c0 */ 0x00, 0x00, 0x04, 0x18, ZEROx4, |
| /* 0x200 */ ZEROx8, |
| /* 0x240 */ 0x40, 0x00, 0x01, 0x00, ZEROx4, |
| }, |
| }, |
| { |
| .name = "PC speaker", |
| .bus_type = 0x0010, /* BUS_ISA */ |
| .vendor_id = 0x001f, |
| .product_id = 0x0001, |
| .version = 0x0100, |
| .expected = SDL_UDEV_DEVICE_UNKNOWN, |
| /* SYN, SND */ |
| .ev = { 0x01, 0x00, 0x04 }, |
| }, |
| { |
| .name = "ALSA headphone detection, etc.", |
| .bus_type = 0x0000, |
| .vendor_id = 0x0000, |
| .product_id = 0x0000, |
| .version = 0x0000, |
| .expected = SDL_UDEV_DEVICE_UNKNOWN, |
| /* SYN, SW */ |
| .ev = { 0x21 }, |
| }, |
| { |
| /* Assumed to be a reasonably typical i8042 (PC AT) keyboard */ |
| .name = "Thinkpad T520 and X280 keyboards", |
| .bus_type = 0x0011, /* BUS_I8042 */ |
| .vendor_id = 0x0001, |
| .product_id = 0x0001, |
| .version = 0xab54, |
| .expected = SDL_UDEV_DEVICE_KEYBOARD, |
| /* SYN, KEY, MSC, LED, REP */ |
| .ev = { 0x13, 0x00, 0x12 }, |
| .keys = { |
| /* 0x00 */ 0xfe, 0xff, 0xff, 0xff, FFx4, |
| /* 0x40 */ 0xff, 0xff, 0xef, 0xff, 0xdf, 0xff, 0xff, 0xfe, |
| /* 0x80 */ 0x01, 0xd0, 0x00, 0xf8, 0x78, 0x30, 0x80, 0x03, |
| /* 0xc0 */ 0x00, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x00, |
| }, |
| }, |
| { |
| .name = "Thinkpad X280 sleep button", |
| .bus_type = 0x0019, /* BUS_HOST */ |
| .vendor_id = 0x0000, |
| .product_id = 0x0003, |
| .version = 0x0000, |
| /* SDL treats KEY_SLEEP as indicating a keyboard */ |
| .expected = SDL_UDEV_DEVICE_KEYBOARD, |
| /* SYN, KEY */ |
| .ev = { 0x03 }, |
| .keys = { |
| /* 0x00 */ ZEROx8, |
| /* 0x40 */ ZEROx8, |
| /* KEY_SLEEP */ |
| /* 0x80 */ 0x00, 0x40, |
| }, |
| }, |
| { |
| .name = "Thinkpad X280 lid switch", |
| .bus_type = 0x0019, /* BUS_HOST */ |
| .vendor_id = 0x0000, |
| .product_id = 0x0005, |
| .version = 0x0000, |
| .expected = SDL_UDEV_DEVICE_UNKNOWN, |
| /* SYN, SW */ |
| .ev = { 0x21 }, |
| }, |
| { |
| .name = "Thinkpad X280 power button", |
| .bus_type = 0x0019, /* BUS_HOST */ |
| .vendor_id = 0x0000, |
| .product_id = 0x0001, |
| .version = 0x0000, |
| /* SDL treats KEY_POWER as indicating a keyboard */ |
| .expected = SDL_UDEV_DEVICE_KEYBOARD, |
| /* SYN, KEY */ |
| .ev = { 0x03 }, |
| .keys = { |
| /* 0x00 */ ZEROx8, |
| /* KEY_POWER */ |
| /* 0x40 */ ZEROx4, 0x00, 0x00, 0x10, 0x00, |
| }, |
| }, |
| { |
| .name = "Thinkpad X280 video bus", |
| .bus_type = 0x0019, /* BUS_HOST */ |
| .vendor_id = 0x0000, |
| .product_id = 0x0006, |
| .version = 0x0000, |
| /* SDL treats brightness control, etc. as keyboard keys */ |
| .expected = SDL_UDEV_DEVICE_KEYBOARD, |
| /* SYN, KEY */ |
| .ev = { 0x03 }, |
| .keys = { |
| /* 0x00 */ ZEROx8, |
| /* 0x40 */ ZEROx8, |
| /* 0x80 */ ZEROx8, |
| /* brightness control, video mode, display off */ |
| /* 0xc0 */ ZEROx4, 0x0b, 0x00, 0x3e, 0x00, |
| }, |
| }, |
| { |
| .name = "Thinkpad X280 extra buttons", |
| .bus_type = 0x0019, /* BUS_HOST */ |
| .vendor_id = 0x17aa, |
| .product_id = 0x5054, |
| .version = 0x4101, |
| .expected = SDL_UDEV_DEVICE_KEYBOARD, |
| /* SYN, KEY */ |
| .ev = { 0x03 }, |
| .keys = { |
| /* 0x00 */ ZEROx8, |
| /* 0x40 */ ZEROx4, 0x00, 0x00, 0x0e, 0x01, |
| /* 0x80 */ 0x00, 0x50, 0x11, 0x51, 0x00, 0x28, 0x00, 0xc0, |
| /* 0xc0 */ 0x04, 0x20, 0x10, 0x02, 0x1b, 0x70, 0x01, 0x00, |
| /* 0x100 */ ZEROx8, |
| /* 0x140 */ ZEROx4, 0x00, 0x00, 0x50, 0x00, |
| /* 0x180 */ ZEROx8, |
| /* 0x1c0 */ 0x00, 0x00, 0x04, 0x18, ZEROx4, |
| /* 0x200 */ ZEROx8, |
| /* 0x240 */ 0x40, 0x00, 0x01, 0x00, ZEROx4, |
| }, |
| }, |
| { |
| .name = "Thinkpad USB keyboard with Trackpoint - keyboard", |
| .bus_type = 0x0003, |
| .vendor_id = 0x17ef, |
| .product_id = 0x6009, |
| .expected = SDL_UDEV_DEVICE_KEYBOARD, |
| /* SYN, KEY, MSC, LED, REP */ |
| .ev = { 0x13, 0x00, 0x12 }, |
| .keys = { |
| /* 0x00 */ 0xfe, 0xff, 0xff, 0xff, FFx4, |
| /* 0x40 */ 0xff, 0xff, 0xef, 0xff, 0xdf, 0xff, 0xbe, 0xfe, |
| /* 0x80 */ 0xff, 0x57, 0x40, 0xc1, 0x7a, 0x20, 0x9f, 0xff, |
| /* 0xc0 */ 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, |
| }, |
| }, |
| { |
| .name = "Thinkpad USB keyboard with Trackpoint - Trackpoint", |
| .bus_type = 0x0003, |
| .vendor_id = 0x17ef, |
| .product_id = 0x6009, |
| /* For some reason the special keys like mute and wlan toggle |
| * show up here instead of, or in addition to, as part of |
| * the keyboard - so both udev and SDL report this as having keys too. */ |
| .expected = SDL_UDEV_DEVICE_MOUSE | SDL_UDEV_DEVICE_KEYBOARD, |
| /* SYN, KEY, REL, MSC, LED */ |
| .ev = { 0x17, 0x00, 0x02 }, |
| /* X, Y */ |
| .rel = { 0x03 }, |
| .keys = { |
| /* 0x00 */ ZEROx8, |
| /* 0x40 */ ZEROx4, 0x00, 0x00, 0x1e, 0x00, |
| /* 0x80 */ 0x00, 0xcc, 0x11, 0x01, 0x78, 0x40, 0x00, 0xc0, |
| /* 0xc0 */ 0x00, 0x20, 0x10, 0x00, 0x0b, 0x50, 0x00, 0x00, |
| /* Mouse buttons: left, right, middle, "task" */ |
| /* 0x100 */ 0x00, 0x00, 0x87, 0x68, ZEROx4, |
| /* 0x140 */ ZEROx4, 0x00, 0x00, 0x10, 0x00, |
| /* 0x180 */ ZEROx4, 0x00, 0x00, 0x40, 0x00, |
| }, |
| }, |
| { |
| .name = "No information", |
| .expected = SDL_UDEV_DEVICE_UNKNOWN, |
| } |
| }; |
| |
| /* The Linux kernel provides capability info in EVIOCGBIT and in /sys |
| * as an array of unsigned long in native byte order, rather than an array |
| * of bytes, an array of native-endian 32-bit words or an array of |
| * native-endian 64-bit words like you might have reasonably expected. |
| * The order of words in the array is always lowest-valued first: for |
| * instance, the first unsigned long in abs[] contains the bit representing |
| * absolute axis 0 (ABS_X). |
| * |
| * The constant arrays above provide test data in little-endian, because |
| * that's the easiest representation for hard-coding into a test like this. |
| * On a big-endian platform we need to byteswap it, one unsigned long at a |
| * time, to match what the kernel would produce. This requires us to choose |
| * an appropriate byteswapping function for the architecture's word size. */ |
| SDL_COMPILE_TIME_ASSERT(sizeof_long, sizeof(unsigned long) == 4 || sizeof(unsigned long) == 8); |
| #define SwapLongLE(X) \ |
| ((sizeof(unsigned long) == 4) ? SDL_SwapLE32(X) : SDL_SwapLE64(X)) |
| |
| static int |
| run_test(void) |
| { |
| int success = 1; |
| size_t i; |
| |
| for (i = 0; i < SDL_arraysize(guess_tests); i++) { |
| const GuessTest *t = &guess_tests[i]; |
| size_t j; |
| int actual; |
| struct { |
| unsigned long ev[NBITS(EV_MAX)]; |
| unsigned long abs[NBITS(ABS_MAX)]; |
| unsigned long keys[NBITS(KEY_MAX)]; |
| unsigned long rel[NBITS(REL_MAX)]; |
| } caps = {}; |
| |
| printf("%s...\n", t->name); |
| |
| memset(&caps, '\0', sizeof(caps)); |
| memcpy(caps.ev, t->ev, sizeof(t->ev)); |
| memcpy(caps.keys, t->keys, sizeof(t->keys)); |
| memcpy(caps.abs, t->abs, sizeof(t->abs)); |
| memcpy(caps.rel, t->rel, sizeof(t->rel)); |
| |
| for (j = 0; j < SDL_arraysize(caps.ev); j++) { |
| caps.ev[j] = SwapLongLE(caps.ev[j]); |
| } |
| |
| for (j = 0; j < SDL_arraysize(caps.keys); j++) { |
| caps.keys[j] = SwapLongLE(caps.keys[j]); |
| } |
| |
| for (j = 0; j < SDL_arraysize(caps.abs); j++) { |
| caps.abs[j] = SwapLongLE(caps.abs[j]); |
| } |
| |
| for (j = 0; j < SDL_arraysize(caps.rel); j++) { |
| caps.rel[j] = SwapLongLE(caps.rel[j]); |
| } |
| |
| actual = SDL_EVDEV_GuessDeviceClass(caps.ev, caps.abs, caps.keys, |
| caps.rel); |
| |
| if (actual == t->expected) { |
| printf("\tOK\n"); |
| } |
| else { |
| printf("\tExpected 0x%08x\n", t->expected); |
| |
| for (j = 0; device_classes[j].code != 0; j++) { |
| if (t->expected & device_classes[j].code) { |
| printf("\t\t%s\n", device_classes[j].name); |
| } |
| } |
| |
| printf("\tGot 0x%08x\n", actual); |
| |
| for (j = 0; device_classes[j].code != 0; j++) { |
| if (actual & device_classes[j].code) { |
| printf("\t\t%s\n", device_classes[j].name); |
| } |
| } |
| |
| success = 0; |
| } |
| } |
| |
| return success; |
| } |
| |
| #else /* !(HAVE_LIBUDEV_H || defined(SDL_JOYSTICK_LINUX)) */ |
| |
| static int |
| run_test(void) |
| { |
| printf("SDL compiled without evdev capability check.\n"); |
| return 1; |
| } |
| |
| #endif |
| |
| int |
| main(int argc, char *argv[]) |
| { |
| return run_test() ? 0 : 1; |
| } |