From 6aaedb813fa11ba0679c3051bc2eb28646b9506c Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Sat, 30 Aug 2025 16:53:58 -0700 Subject: Update to SDL3 --- src/contrib/SDL-3.2.20/test/testevdev.c | 2249 +++++++++++++++++++++++++++++++ 1 file changed, 2249 insertions(+) create mode 100644 src/contrib/SDL-3.2.20/test/testevdev.c (limited to 'src/contrib/SDL-3.2.20/test/testevdev.c') diff --git a/src/contrib/SDL-3.2.20/test/testevdev.c b/src/contrib/SDL-3.2.20/test/testevdev.c new file mode 100644 index 0000000..cdb16a9 --- /dev/null +++ b/src/contrib/SDL-3.2.20/test/testevdev.c @@ -0,0 +1,2249 @@ +/* + Copyright (C) 1997-2025 Sam Lantinga + 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. +*/ + +/* Hack #1: avoid inclusion of SDL_main.h by SDL_internal.h */ +#define SDL_main_h_ + +/* Hack #2: avoid dynapi renaming (must be done before #include ) */ +#include "../src/dynapi/SDL_dynapi.h" +#ifdef SDL_DYNAMIC_API +#undef SDL_DYNAMIC_API +#endif +#define SDL_DYNAMIC_API 0 + +#ifdef HAVE_BUILD_CONFIG +#include "../src/SDL_internal.h" +#endif + +/* Hack #3: undo Hack #1 */ +#ifdef SDL_main_h_ +#undef SDL_main_h_ +#endif +#ifdef SDL_MAIN_NOIMPL +#undef SDL_MAIN_NOIMPL +#endif + +#include +#include +#include + +#include +#include + +static int run_test(void); + +/* FIXME: Need CMake tests for this */ +#if (defined(HAVE_LIBUDEV_H) || defined(SDL_JOYSTICK_LINUX)) && defined(HAVE_LINUX_INPUT_H) + +#include + +#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(HAS_KEYS), + CLS(JOYSTICK), + CLS(SOUND), + CLS(TOUCHSCREEN), + CLS(ACCELEROMETER), + CLS(TOUCHPAD), +#undef CLS + { 0, NULL } +}; + +typedef struct +{ + const char *name; + const char *eviocgname; + const char *usb_vendor_name; + const char *usb_product_name; + uint16_t bus_type; + uint16_t vendor_id; + uint16_t product_id; + uint16_t version; + uint16_t usb_device_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 + 1) / 8]; + int expected; + const char *todo; + size_t hid_report_descriptor_length; + const unsigned char *hid_report_descriptor; +} 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. To contribute new test-cases, see: + * https://github.com/libsdl-org/SDL/issues/7801#issuecomment-1589114910 + */ +#define ZEROx4 0, 0, 0, 0 +#define ZEROx8 ZEROx4, ZEROx4 +#define FFx4 0xff, 0xff, 0xff, 0xff +#define FFx8 FFx4, FFx4 + +static unsigned char xbox_one_elite_2_hid_report_descriptor[] = +{ + /* Generic Desktop / Game Pad, Generic Desktop / Keyboard */ + 0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x01, + 0x09, 0x01, 0xa1, 0x00, 0x09, 0x30, 0x09, 0x31, + 0x15, 0x00, 0x27, 0xff, 0xff, 0x00, 0x00, 0x95, + 0x02, 0x75, 0x10, 0x81, 0x02, 0xc0, 0x09, 0x01, + 0xa1, 0x00, 0x09, 0x32, 0x09, 0x35, 0x15, 0x00, + 0x27, 0xff, 0xff, 0x00, 0x00, 0x95, 0x02, 0x75, + 0x10, 0x81, 0x02, 0xc0, 0x05, 0x02, 0x09, 0xc5, + 0x15, 0x00, 0x26, 0xff, 0x03, 0x95, 0x01, 0x75, + 0x0a, 0x81, 0x02, 0x15, 0x00, 0x25, 0x00, 0x75, + 0x06, 0x95, 0x01, 0x81, 0x03, 0x05, 0x02, 0x09, + 0xc4, 0x15, 0x00, 0x26, 0xff, 0x03, 0x95, 0x01, + 0x75, 0x0a, 0x81, 0x02, 0x15, 0x00, 0x25, 0x00, + 0x75, 0x06, 0x95, 0x01, 0x81, 0x03, 0x05, 0x01, + 0x09, 0x39, 0x15, 0x01, 0x25, 0x08, 0x35, 0x00, + 0x46, 0x3b, 0x01, 0x66, 0x14, 0x00, 0x75, 0x04, + 0x95, 0x01, 0x81, 0x42, 0x75, 0x04, 0x95, 0x01, + 0x15, 0x00, 0x25, 0x00, 0x35, 0x00, 0x45, 0x00, + 0x65, 0x00, 0x81, 0x03, 0x05, 0x09, 0x19, 0x01, + 0x29, 0x0f, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, + 0x95, 0x0f, 0x81, 0x02, 0x15, 0x00, 0x25, 0x00, + 0x75, 0x01, 0x95, 0x01, 0x81, 0x03, 0x05, 0x0c, + 0x0a, 0xb2, 0x00, 0x15, 0x00, 0x25, 0x01, 0x95, + 0x01, 0x75, 0x01, 0x81, 0x02, 0x15, 0x00, 0x25, + 0x00, 0x75, 0x07, 0x95, 0x01, 0x81, 0x03, 0x05, + 0x0c, 0x09, 0x01, 0xa1, 0x01, 0x0a, 0x85, 0x00, + 0x15, 0x00, 0x26, 0xff, 0x00, 0x95, 0x01, 0x75, + 0x08, 0x81, 0x02, 0x0a, 0x99, 0x00, 0x15, 0x00, + 0x26, 0xff, 0x00, 0x95, 0x01, 0x75, 0x04, 0x81, + 0x02, 0x15, 0x00, 0x25, 0x00, 0x95, 0x01, 0x75, + 0x04, 0x81, 0x03, 0x0a, 0x81, 0x00, 0x15, 0x00, + 0x26, 0xff, 0x00, 0x95, 0x01, 0x75, 0x04, 0x81, + 0x02, 0x15, 0x00, 0x25, 0x00, 0x95, 0x01, 0x75, + 0x04, 0x81, 0x03, 0xc0, 0x05, 0x0f, 0x09, 0x21, + 0x85, 0x03, 0xa1, 0x02, 0x09, 0x97, 0x15, 0x00, + 0x25, 0x01, 0x75, 0x04, 0x95, 0x01, 0x91, 0x02, + 0x15, 0x00, 0x25, 0x00, 0x75, 0x04, 0x95, 0x01, + 0x91, 0x03, 0x09, 0x70, 0x15, 0x00, 0x25, 0x64, + 0x75, 0x08, 0x95, 0x04, 0x91, 0x02, 0x09, 0x50, + 0x66, 0x01, 0x10, 0x55, 0x0e, 0x15, 0x00, 0x26, + 0xff, 0x00, 0x75, 0x08, 0x95, 0x01, 0x91, 0x02, + 0x09, 0xa7, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, + 0x08, 0x95, 0x01, 0x91, 0x02, 0x65, 0x00, 0x55, + 0x00, 0x09, 0x7c, 0x15, 0x00, 0x26, 0xff, 0x00, + 0x75, 0x08, 0x95, 0x01, 0x91, 0x02, 0xc0, 0x05, + 0x0c, 0x09, 0x01, 0x85, 0x0c, 0xa1, 0x01, 0x0a, + 0x9e, 0x00, 0x15, 0x00, 0x26, 0xff, 0x00, 0x95, + 0x01, 0x75, 0x08, 0x81, 0x02, 0x0a, 0xa1, 0x00, + 0x15, 0x00, 0x26, 0xff, 0x00, 0x95, 0x01, 0x75, + 0x08, 0x81, 0x02, 0x0a, 0xa2, 0x00, 0x15, 0x00, + 0x26, 0xff, 0x00, 0x95, 0x01, 0x75, 0x08, 0x81, + 0x02, 0x0a, 0xa3, 0x00, 0x15, 0x00, 0x26, 0xff, + 0x00, 0x95, 0x01, 0x75, 0x08, 0x81, 0x02, 0xc0, + 0xc0, 0x05, 0x01, 0x09, 0x06, 0xa1, 0x01, 0x85, + 0x05, 0x05, 0x07, 0x19, 0xe0, 0x29, 0xe7, 0x15, + 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, + 0x02, 0x95, 0x01, 0x75, 0x08, 0x81, 0x03, 0x95, + 0x06, 0x75, 0x08, 0x15, 0x00, 0x25, 0x65, 0x05, + 0x07, 0x19, 0x00, 0x29, 0x65, 0x81, 0x00, 0xc0, +}; +SDL_COMPILE_TIME_ASSERT (xbox_one_elite_2, sizeof (xbox_one_elite_2_hid_report_descriptor) == 0720); + +static unsigned char ps3_hid_report_descriptor[] = +{ + /* Generic Desktop / Joystick */ + 0x05, 0x01, 0x09, 0x04, 0xa1, 0x01, 0xa1, 0x02, + 0x85, 0x01, 0x75, 0x08, 0x95, 0x01, 0x15, 0x00, + 0x26, 0xff, 0x00, 0x81, 0x03, 0x75, 0x01, 0x95, + 0x13, 0x15, 0x00, 0x25, 0x01, 0x35, 0x00, 0x45, + 0x01, 0x05, 0x09, 0x19, 0x01, 0x29, 0x13, 0x81, + 0x02, 0x75, 0x01, 0x95, 0x0d, 0x06, 0x00, 0xff, + 0x81, 0x03, 0x15, 0x00, 0x26, 0xff, 0x00, 0x05, + 0x01, 0x09, 0x01, 0xa1, 0x00, 0x75, 0x08, 0x95, + 0x04, 0x35, 0x00, 0x46, 0xff, 0x00, 0x09, 0x30, + 0x09, 0x31, 0x09, 0x32, 0x09, 0x35, 0x81, 0x02, + 0xc0, 0x05, 0x01, 0x75, 0x08, 0x95, 0x27, 0x09, + 0x01, 0x81, 0x02, 0x75, 0x08, 0x95, 0x30, 0x09, + 0x01, 0x91, 0x02, 0x75, 0x08, 0x95, 0x30, 0x09, + 0x01, 0xb1, 0x02, 0xc0, 0xa1, 0x02, 0x85, 0x02, + 0x75, 0x08, 0x95, 0x30, 0x09, 0x01, 0xb1, 0x02, + 0xc0, 0xa1, 0x02, 0x85, 0xee, 0x75, 0x08, 0x95, + 0x30, 0x09, 0x01, 0xb1, 0x02, 0xc0, 0xa1, 0x02, + 0x85, 0xef, 0x75, 0x08, 0x95, 0x30, 0x09, 0x01, + 0xb1, 0x02, 0xc0, 0xc0, 0x00, +}; +SDL_COMPILE_TIME_ASSERT (ps3, sizeof (ps3_hid_report_descriptor) == 149); + +/* Same for Steam Deck LCD (jupiter) and OLED (galileo) */ +static unsigned char steam_deck_mouse_hid_report_descriptor[] = +{ + 0x05, 0x01, 0x09, 0x02, 0xa1, 0x01, 0x09, 0x01, + 0xa1, 0x00, 0x05, 0x09, 0x19, 0x01, 0x29, 0x02, + 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x02, + 0x81, 0x02, 0x75, 0x06, 0x95, 0x01, 0x81, 0x01, + 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, 0x15, 0x81, + 0x25, 0x7f, 0x75, 0x08, 0x95, 0x02, 0x81, 0x06, + 0x95, 0x01, 0x09, 0x38, 0x81, 0x06, 0x05, 0x0c, + 0x0a, 0x38, 0x02, 0x95, 0x01, 0x81, 0x06, 0xc0, + 0xc0, +}; +SDL_COMPILE_TIME_ASSERT (steam_deck_mouse, + sizeof (steam_deck_mouse_hid_report_descriptor) == 65); + +/* Same for Steam Deck LCD (jupiter) and OLED (galileo) */ +static unsigned char steam_deck_kb_hid_report_descriptor[] = +{ + 0x05, 0x01, 0x09, 0x06, 0xa1, 0x01, 0x05, 0x07, + 0x19, 0xe0, 0x29, 0xe7, 0x15, 0x00, 0x25, 0x01, + 0x75, 0x01, 0x95, 0x08, 0x81, 0x02, 0x81, 0x01, + 0x19, 0x00, 0x29, 0x65, 0x15, 0x00, 0x25, 0x65, + 0x75, 0x08, 0x95, 0x06, 0x81, 0x00, 0xc0, +}; +SDL_COMPILE_TIME_ASSERT (steam_deck_oled_kb, + sizeof (steam_deck_kb_hid_report_descriptor) == 39); + +static unsigned char steam_deck_lcd_js_hid_report_descriptor[] = +{ + 0x06, 0xff, 0xff, 0x09, 0x01, 0xa1, 0x01, 0x15, + 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x40, + 0x09, 0x01, 0x81, 0x02, 0x09, 0x01, 0xb1, 0x02, + 0xc0, +}; +SDL_COMPILE_TIME_ASSERT (steam_deck_lcd_js, + sizeof (steam_deck_lcd_js_hid_report_descriptor) == 25); + +static unsigned char steam_deck_oled_js_hid_report_descriptor[] = +{ + 0x06, 0xff, 0xff, 0x09, 0x01, 0xa1, 0x01, 0x09, + 0x02, 0x09, 0x03, 0x15, 0x00, 0x26, 0xff, 0x00, + 0x75, 0x08, 0x95, 0x40, 0x81, 0x02, 0x09, 0x06, + 0x09, 0x07, 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, + 0x08, 0x95, 0x40, 0xb1, 0x02, 0xc0, +}; +SDL_COMPILE_TIME_ASSERT (steam_deck_oled_js, + sizeof (steam_deck_oled_js_hid_report_descriptor) == 38); + +static unsigned char vrs_pedals_hid_report_descriptor[] = +{ + /* Generic Desktop / Joystick */ + 0x05, 0x01, 0x09, 0x04, 0xa1, 0x01, 0x05, 0x01, + 0xa1, 0x02, 0x85, 0x01, 0x09, 0x30, 0x09, 0x31, + 0x09, 0x32, 0x15, 0x00, 0x27, 0xff, 0xff, 0x00, + 0x00, 0x35, 0x00, 0x47, 0xff, 0xff, 0x00, 0x00, + 0x75, 0x10, 0x95, 0x03, 0x81, 0x02, 0x06, 0x00, + 0xff, 0x09, 0x01, 0x95, 0x39, 0x75, 0x08, 0x26, + 0xff, 0x00, 0x15, 0x00, 0x81, 0x02, 0xc0, 0x06, + 0x00, 0xff, 0x09, 0x01, 0xa1, 0x01, 0x85, 0x64, + 0x95, 0x3f, 0x75, 0x08, 0x26, 0xff, 0x00, 0x15, + 0x00, 0x09, 0x01, 0x91, 0x02, 0x85, 0x65, 0x95, + 0x3f, 0x75, 0x08, 0x26, 0xff, 0x00, 0x15, 0x00, + 0x09, 0x01, 0x81, 0x02, 0xc0, 0xc0, +}; +SDL_COMPILE_TIME_ASSERT (vrs_pedals, sizeof (vrs_pedals_hid_report_descriptor) == 0136); + +static unsigned char thinkpad_usb_keyboard_hid_report_descriptor[] = +{ + /* Generic Desktop / Keyboard */ + 0x05, 0x01, 0x09, 0x06, 0xa1, 0x01, 0x05, 0x07, + 0x19, 0xe0, 0x29, 0xe7, 0x15, 0x00, 0x25, 0x01, + 0x95, 0x08, 0x75, 0x01, 0x81, 0x02, 0x95, 0x08, + 0x75, 0x01, 0x81, 0x01, 0x05, 0x08, 0x19, 0x01, + 0x29, 0x03, 0x95, 0x03, 0x75, 0x01, 0x91, 0x02, + 0x95, 0x01, 0x75, 0x05, 0x91, 0x01, 0x05, 0x07, + 0x19, 0x00, 0x2a, 0xff, 0x00, 0x15, 0x00, 0x26, + 0xff, 0x00, 0x95, 0x06, 0x75, 0x08, 0x81, 0x00, + 0xc0, +}; +SDL_COMPILE_TIME_ASSERT (thinkpad_usb_keyboard, sizeof (thinkpad_usb_keyboard_hid_report_descriptor) == 65); + +static unsigned char thinkpad_usb_trackpoint_hid_report_descriptor[] = +{ + /* Generic Desktop / Mouse, Generic Desktop / System Control, + * Consumer Devices / Consumer Control */ + 0x05, 0x01, 0x09, 0x02, 0xa1, 0x01, 0x85, 0x01, + 0x09, 0x01, 0xa1, 0x00, 0x05, 0x09, 0x19, 0x01, + 0x29, 0x03, 0x15, 0x00, 0x25, 0x01, 0x95, 0x03, + 0x75, 0x01, 0x81, 0x02, 0x95, 0x01, 0x75, 0x05, + 0x81, 0x01, 0x05, 0x01, 0x09, 0x30, 0x09, 0x31, + 0x15, 0x81, 0x25, 0x7f, 0x95, 0x02, 0x75, 0x08, + 0x81, 0x06, 0xc0, 0xc0, 0x05, 0x01, 0x09, 0x80, + 0xa1, 0x01, 0x85, 0x02, 0x05, 0x01, 0x15, 0x00, + 0x25, 0x01, 0x95, 0x08, 0x75, 0x01, 0x19, 0x81, + 0x29, 0x88, 0x81, 0x02, 0xc0, 0x05, 0x0c, 0x09, + 0x01, 0xa1, 0x01, 0x85, 0x03, 0x95, 0x08, 0x75, + 0x01, 0x15, 0x00, 0x25, 0x01, 0x09, 0xe9, 0x09, + 0xea, 0x09, 0xe2, 0x09, 0xb7, 0x09, 0xcd, 0x09, + 0xb5, 0x09, 0xb6, 0x0a, 0x94, 0x01, 0x81, 0x02, + 0x09, 0x03, 0xa1, 0x02, 0x05, 0x09, 0x19, 0x10, + 0x29, 0x17, 0x81, 0x02, 0x05, 0x09, 0x19, 0x18, + 0x29, 0x1f, 0x81, 0x02, 0xc0, 0x05, 0x08, 0x95, + 0x02, 0x09, 0x09, 0x09, 0x21, 0x91, 0x02, 0x95, + 0x01, 0x75, 0x06, 0x91, 0x03, 0xc0, 0x06, 0x01, + 0xff, 0x09, 0x01, 0xa1, 0x01, 0x85, 0x04, 0x95, + 0x01, 0x75, 0x08, 0x15, 0x00, 0x26, 0xff, 0x00, + 0x09, 0x20, 0xb1, 0x03, 0x09, 0x21, 0xb1, 0x03, + 0x09, 0x22, 0xb1, 0x03, 0x09, 0x23, 0xb1, 0x03, + 0xc0, +}; +SDL_COMPILE_TIME_ASSERT (thinkpad_usb_trackpoint, sizeof (thinkpad_usb_trackpoint_hid_report_descriptor) == 185); + +static unsigned char heusinkveld_pedals_hid_report_descriptor[] = +{ + /* Generic Desktop / Joystick */ + 0x05, 0x01, 0x09, 0x04, 0xa1, 0x01, 0x09, 0x33, + 0x09, 0x34, 0x09, 0x35, 0x15, 0x00, 0x26, 0xff, + 0x0f, 0x85, 0x01, 0x75, 0x10, 0x95, 0x03, 0x81, + 0x02, 0x09, 0x00, 0x75, 0x10, 0x95, 0x06, 0x82, + 0x01, 0x01, 0x85, 0x02, 0x75, 0x10, 0x95, 0x03, + 0x09, 0x00, 0x09, 0x00, 0xb1, 0x02, 0x85, 0x03, + 0x75, 0x08, 0x95, 0x03, 0x09, 0x00, 0x82, 0x01, + 0x01, 0xc0, +}; +SDL_COMPILE_TIME_ASSERT (heusinkveld_pedals, sizeof (heusinkveld_pedals_hid_report_descriptor) == 072); + +static unsigned char fanatec_handbrake_hid_report_descriptor[] = +{ + /* Generic Desktop / Joystick */ + 0x05, 0x01, 0x09, 0x04, 0xa1, 0x01, 0x15, 0x00, + 0x26, 0xff, 0x00, 0x95, 0x01, 0x75, 0x08, 0x09, + 0x30, 0x81, 0x02, 0x06, 0x00, 0xff, 0x09, 0x01, + 0x95, 0x03, 0x81, 0x02, 0x06, 0x00, 0xff, 0x09, + 0x01, 0x95, 0x02, 0x91, 0x02, 0xc0, +}; +SDL_COMPILE_TIME_ASSERT (fanatec_handbrake, sizeof (fanatec_handbrake_hid_report_descriptor) == 046); + +static unsigned char xpadneo09_xb1s_hid_report_descriptor[] = +{ + 0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x01, + 0x09, 0x01, 0xa1, 0x00, 0x09, 0x30, 0x09, 0x31, + 0x15, 0x00, 0x27, 0xff, 0xff, 0x00, 0x00, 0x95, + 0x02, 0x75, 0x10, 0x81, 0x02, 0xc0, 0x09, 0x01, + 0xa1, 0x00, 0x09, 0x33, 0x09, 0x34, 0x15, 0x00, + 0x27, 0xff, 0xff, 0x00, 0x00, 0x95, 0x02, 0x75, + 0x10, 0x81, 0x02, 0xc0, 0x05, 0x01, 0x09, 0x32, + 0x15, 0x00, 0x26, 0xff, 0x03, 0x95, 0x01, 0x75, + 0x0a, 0x81, 0x02, 0x15, 0x00, 0x25, 0x00, 0x75, + 0x06, 0x95, 0x01, 0x81, 0x03, 0x05, 0x01, 0x09, + 0x35, 0x15, 0x00, 0x26, 0xff, 0x03, 0x95, 0x01, + 0x75, 0x0a, 0x81, 0x02, 0x15, 0x00, 0x25, 0x00, + 0x75, 0x06, 0x95, 0x01, 0x81, 0x03, 0x05, 0x01, + 0x09, 0x39, 0x15, 0x01, 0x25, 0x08, 0x35, 0x00, + 0x46, 0x3b, 0x01, 0x66, 0x14, 0x00, 0x75, 0x04, + 0x95, 0x01, 0x81, 0x42, 0x75, 0x04, 0x95, 0x01, + 0x15, 0x00, 0x25, 0x00, 0x35, 0x00, 0x45, 0x00, + 0x65, 0x00, 0x81, 0x03, 0x05, 0x09, 0x19, 0x01, + 0x29, 0x0c, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, + 0x95, 0x0c, 0x81, 0x02, 0x15, 0x00, 0x25, 0x00, + 0x75, 0x01, 0x95, 0x04, 0x81, 0x03, 0x05, 0x0c, + 0x0a, 0xb2, 0x00, 0x15, 0x00, 0x25, 0x01, 0x95, + 0x01, 0x75, 0x01, 0x81, 0x02, 0x15, 0x00, 0x25, + 0x00, 0x75, 0x07, 0x95, 0x01, 0x81, 0x03, 0x05, + 0x0f, 0x09, 0x21, 0x85, 0x03, 0xa1, 0x02, 0x09, + 0x97, 0x15, 0x00, 0x25, 0x01, 0x75, 0x04, 0x95, + 0x01, 0x91, 0x02, 0x15, 0x00, 0x25, 0x00, 0x75, + 0x04, 0x95, 0x01, 0x91, 0x03, 0x09, 0x70, 0x15, + 0x00, 0x25, 0x64, 0x75, 0x08, 0x95, 0x04, 0x91, + 0x02, 0x09, 0x50, 0x66, 0x01, 0x10, 0x55, 0x0e, + 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, + 0x01, 0x91, 0x02, 0x09, 0xa7, 0x15, 0x00, 0x26, + 0xff, 0x00, 0x75, 0x08, 0x95, 0x01, 0x91, 0x02, + 0x65, 0x00, 0x55, 0x00, 0x09, 0x7c, 0x15, 0x00, + 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, 0x01, 0x91, + 0x02, 0xc0, 0xc0, +}; +SDL_COMPILE_TIME_ASSERT (xpadneo09_xb1s, sizeof (xpadneo09_xb1s_hid_report_descriptor) == 283); + +/* Test-cases derived from real devices or from Linux kernel source */ +/* *INDENT-OFF* */ /* clang-format off */ +static const GuessTest guess_tests[] = +{ + { + .name = "Xbox 360 wired USB controller", + .eviocgname = "Microsoft X-Box 360 pad", + .usb_vendor_name = "©Microsoft Corporation", + .usb_product_name = "Controller", + /* 8BitDo N30 Pro 2 v0114 via USB-C (with the xpad driver) is + * reported as 0003:045e:028e v0114, and is functionally equivalent */ + .bus_type = 0x0003, + .vendor_id = 0x045e, + .product_id = 0x028e, + .version = 0x0114, + .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, + }, + }, + { /* Reference: https://github.com/libsdl-org/SDL/issues/7814 */ + .name = "X-Box One Elite 2 via USB", + /* The same physical device via Bluetooth, 0005:045e:0b22 v0517, + * is reported differently (below). */ + /* Version 0407 is functionally equivalent. */ + .bus_type = 0x0003, + .vendor_id = 0x045e, + .product_id = 0x0b00, + .version = 0x0511, + .expected = SDL_UDEV_DEVICE_JOYSTICK, + /* SYN, KEY, ABS, FF */ + .ev = { 0x0b, 0x00, 0x20 }, + /* XY (left stick), RX/RY (right stick), Z/RZ (triggers), HAT0 (dpad) */ + .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, + /* 0x140 */ ZEROx8, + /* 0x180 */ ZEROx8, + /* 0x1c0 */ ZEROx8, + /* 0x200 */ ZEROx8, + /* 0x240 */ ZEROx8, + /* 0x280 */ ZEROx8, + /* BTN_TRIGGER_HAPPY5 up to BTN_TRIGGER_HAPPY8 inclusive are the + * back buttons (paddles) */ + /* 0x2c0 */ 0xf0, + }, + }, + { /* Reference: https://github.com/libsdl-org/SDL/issues/7814 */ + .name = "X-Box One Elite 2 via Bluetooth", + /* The same physical device via USB, 0003:045e:0b00 v0511, + * is reported differently (above). */ + .bus_type = 0x0003, + .vendor_id = 0x045e, + .product_id = 0x0b22, + .version = 0x0517, + .expected = SDL_UDEV_DEVICE_JOYSTICK | SDL_UDEV_DEVICE_HAS_KEYS, + /* SYN, KEY, ABS, FF */ + .ev = { 0x0b, 0x00, 0x20 }, + /* Android-style mapping: + * XY (left stick), Z/RZ (right stick), GAS/BRAKE (triggers), HAT0 (dpad) */ + .abs = { 0x27, 0x06, 0x03 }, + .keys = { + /* 0x00 */ ZEROx8, + /* 0x40 */ ZEROx8, + /* KEY_RECORD is advertised but isn't generated in practice */ + /* 0x80 */ ZEROx4, 0x80, 0x00, 0x00, 0x00, + /* KEY_UNKNOWN (240) is reported for the profile selector and all + * four back buttons (paddles) */ + /* 0xc0 */ ZEROx4, 0x00, 0x00, 0x01, 0x00, + /* ABXY, TL, TR, TL2, TR2, SELECT, START, MODE, THUMBL, + * THUMBR have their obvious meanings; C and Z are also + * advertised, but are not generated in practice. */ + /* 0x100 */ ZEROx4, 0x00, 0x00, 0xff, 0x7f, + }, + .hid_report_descriptor_length = sizeof (xbox_one_elite_2_hid_report_descriptor), + .hid_report_descriptor = &xbox_one_elite_2_hid_report_descriptor[0], + }, + { + .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 = "X-Box One S via xpadneo 0.9.x", + /* Reference: https://github.com/libsdl-org/SDL/issues/7823 */ + .eviocgname = "Xbox Wireless Controller", + .bus_type = 0x0005, + .vendor_id = 0x045e, + .product_id = 0x028e, + .version = 0x1130, + .expected = SDL_UDEV_DEVICE_JOYSTICK, + /* SYN, KEY, ABS, MSC, FF */ + .ev = { 0x1b }, + /* X, Y, Z, RX, RY, RZ, HAT0X, HAT0Y + * plus MISC as a deprecated axis reporting (rz - z) */ + .abs = { 0x3f, 0x00, 0x03, 0x00, 0x00, 0x01 }, + .keys = { + /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, + /* A, B, X, Y, TL, TR, SELECT, START, MODE, THUMBL, THUMBR */ + /* 0x100 */ ZEROx4, 0x00, 0x00, 0xdb, 0x7c, + /* 0x140 */ ZEROx8, + /* 0x180 */ ZEROx8, + /* 0x1c0 */ ZEROx8, + /* 0x200 */ ZEROx8, + /* 0x240 */ ZEROx8, + /* 0x280 */ ZEROx8, + /* BTN_TRIGGER_HAPPY33 up to BTN_TRIGGER_HAPPY36 inclusive: + * used to represent the current profile */ + /* 0x2c0 */ ZEROx4, 0xf0, + }, + .hid_report_descriptor_length = sizeof (xpadneo09_xb1s_hid_report_descriptor), + .hid_report_descriptor = &xpadneo09_xb1s_hid_report_descriptor[0], + }, + { + .name = "DualSense (PS5) - gamepad", + .bus_type = 0x0003, + .vendor_id = 0x054c, + .product_id = 0x0ce6, + .version = 0x111, + .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, + /* ABC, XYZ, TL, TR, TL2, TR2, select, start, mode, thumbl, + * thumbr; note that C and Z don't physically exist */ + /* 0x100 */ ZEROx4, 0x00, 0x00, 0xff, 0x7f, + }, + }, + { + .name = "DualSense (PS5) v8111 - gamepad", + .eviocgname = "Sony Interactive Entertainment Wireless Controller", + .usb_vendor_name = "Sony Interactive Entertainment", + .usb_product_name = "Wireless Controller", + /* Same physical device via Bluetooth is 0005:054c:0ce6 v8100 + * and EVIOCGNAME is just "Wireless Controller", otherwise equivalent */ + .bus_type = 0x0003, + .vendor_id = 0x054c, + .product_id = 0x0ce6, + .version = 0x8111, + .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 - gamepad", + /* EVIOCGNAME is just "Wireless Controller" when seen via Bluetooth */ + .eviocgname = "Sony Interactive Entertainment Wireless Controller", + .usb_vendor_name = "Sony Interactive Entertainment", + .usb_product_name = "Wireless Controller", + /* Same physical device via Bluetooth is 0005:054c:09cc v8100, + * but otherwise equivalent */ + .bus_type = 0x0003, + .vendor_id = 0x054c, + .product_id = 0x09cc, + .version = 0x8111, + .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 (unknown version)", + .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", + /* EVIOCGNAME is just "Wireless Controller Touchpad" when seen via Bluetooth */ + .eviocgname = "Sony Interactive Entertainment Wireless Controller Touchpad", + .usb_vendor_name = "Sony Interactive Entertainment", + .usb_product_name = "Wireless Controller", + /* Same physical device via Bluetooth is 0005:054c:09cc v8100 and is + * functionally equivalent. */ + /* DualSense (PS5), 0003:054c:0ce6 v8111, is functionally equivalent. + * Same physical device via Bluetooth is 0005:054c:0ce6 v8100 and also + * functionally equivalent. */ + .bus_type = 0x0003, + .vendor_id = 0x054c, + .product_id = 0x09cc, + .version = 0x8111, + .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", + /* EVIOCGNAME is just "Wireless Controller Motion Sensors" when seen via Bluetooth */ + .eviocgname = "Sony Interactive Entertainment Wireless Controller Motion Sensors", + .usb_vendor_name = "Sony Interactive Entertainment", + .usb_product_name = "Wireless Controller", + /* Same physical device via Bluetooth is 0005:054c:09cc v8100 and is + * functionally equivalent. */ + /* DualSense (PS5), 0003:054c:0ce6 v8111, is functionally equivalent. + * Same physical device via Bluetooth is 0005:054c:0ce6 v8100 and also + * functionally equivalent. */ + .bus_type = 0x0003, + .vendor_id = 0x054c, + .product_id = 0x09cc, + .version = 0x8111, + .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", + .eviocgname = "Sony PLAYSTATION(R)3 Controller", + .usb_vendor_name = "Sony", + .usb_product_name = "PLAYSTATION(R)3 Controller", + .bus_type = 0x0003, + .vendor_id = 0x054c, + .product_id = 0x0268, + .version = 0x8111, + .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, + }, + .hid_report_descriptor_length = sizeof (ps3_hid_report_descriptor), + .hid_report_descriptor = &ps3_hid_report_descriptor[0], + }, + { + .name = "DualShock 3 - accelerometer", + .eviocgname = "Sony PLAYSTATION(R)3 Controller Motion Sensors", + .usb_vendor_name = "Sony", + .usb_product_name = "PLAYSTATION(R)3 Controller", + .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 }, + .hid_report_descriptor_length = sizeof (ps3_hid_report_descriptor), + .hid_report_descriptor = &ps3_hid_report_descriptor[0], + }, + { + .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_HAS_KEYS + | 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 = "Steam Deck - mouse", + /* This is the LCD model (jupiter). + * Steam Deck OLED (galileo, possibly pre-production) has + * .eviocgname = "Valve Software Steam Controller" + * .version = 0x0110 + * .usb_device_version = 0x0300 + * but is otherwise equivalent. + */ + .eviocgname = "Valve Software Steam Deck Controller", + .usb_vendor_name = "Valve Software", + .usb_product_name = "Steam Deck Controller", + .bus_type = 0x0003, + .vendor_id = 0x28de, + .product_id = 0x1205, + .version = 0x011, + .usb_device_version = 0x0200, + /* SYN, KEY, REL, MSC */ + .ev = { 0x17 }, + /* X, Y, mouse wheel v/h, high-res mouse wheel v/h */ + .rel = { 0x43, 0x19 }, + .keys = { + /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, + /* left/right mouse button */ + /* 0x100 */ 0x00, 0x00, 0x03, 0x00, ZEROx4, + }, + .expected = SDL_UDEV_DEVICE_MOUSE, + .hid_report_descriptor_length = sizeof (steam_deck_mouse_hid_report_descriptor), + .hid_report_descriptor = &steam_deck_mouse_hid_report_descriptor[0], + }, + { + .name = "Steam Deck - keyboard", + /* This is the LCD model (jupiter). + * Steam Deck OLED (galileo, possibly pre-production) has + * .eviocgname = "Valve Software Steam Controller" + * .version = 0x0110 + * .usb_device_version = 0x0300 + * but is otherwise equivalent. + */ + .eviocgname = "Valve Software Steam Deck Controller", + .usb_vendor_name = "Valve Software", + .usb_product_name = "Steam Deck Controller", + .bus_type = 0x0003, + .vendor_id = 0x28de, + .product_id = 0x1205, + .version = 0x0110, + .usb_device_version = 0x0300, + /* SYN, KEY, MSC, REP */ + .ev = { 0x13, 0x00, 0x10 }, + .keys = { + /* 0x00 */ 0xfe, 0xff, 0xff, 0xff, FFx4, + /* 0x40 */ 0xff, 0xff, 0xcf, 0x01, 0xdf, 0xff, 0x80, 0xe0, + }, + .expected = SDL_UDEV_DEVICE_KEYBOARD | SDL_UDEV_DEVICE_HAS_KEYS, + .hid_report_descriptor_length = sizeof (steam_deck_kb_hid_report_descriptor), + .hid_report_descriptor = &steam_deck_kb_hid_report_descriptor[0], + }, + { + .name = "Steam Deck LCD - gamepad", + .eviocgname = "Valve Software Steam Deck Controller", + .usb_vendor_name = "Valve Software", + .usb_product_name = "Steam Deck Controller", + .bus_type = 0x0003, + .vendor_id = 0x28de, + .product_id = 0x1205, + .version = 0x0111, + .usb_device_version = 0x0200, + .expected = SDL_UDEV_DEVICE_JOYSTICK, + /* TODO: The data I have for Steam Deck LCD didn't seem to have + * an evdev device available, so this is extrapolated from + * kernel source code as being the same as the OLED model + * (the kernel driver doesn't distinguish). */ + /* SYN, KEY, ABS */ + .ev = { 0x0b }, + /* X, Y, RX, RY, hat 0-2 x/y */ + .abs = { 0x1b, 0x00, 0x3f }, + .keys = { + /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, + /* 0x120 0x46: joystick THUMB, THUMB2, BASE */ + /* 0x130 0xdb: gamepad ABXY, TL/TR */ + /* 0x138 0x7f: gamepad TL2/TR2, SELECT/START, MODE, THUMBL/R */ + /* 0x100 */ ZEROx4, 0x46, 0x00, 0xdb, 0x7f, + /* 0x140 */ ZEROx8, + /* 0x180 */ ZEROx8, + /* 0x1c0 */ ZEROx8, + /* 0x220 0x0f: dpad up/down/left/right */ + /* 0x200 */ ZEROx4, 0x0f, 0x00, 0x00, 0x00, + /* 0x240 */ ZEROx8, + /* 0x280 */ ZEROx8, + /* 0x2c0 0x0f: joystick TRIGGER_HAPPY1..TRIGGER_HAPPY4 */ + /* 0x2c0 */ 0x0f, + }, + .hid_report_descriptor_length = sizeof (steam_deck_lcd_js_hid_report_descriptor), + .hid_report_descriptor = &steam_deck_lcd_js_hid_report_descriptor[0], + }, + { + .name = "Steam Deck OLED - gamepad", + .eviocgname = "Valve Software Steam Controller", + .usb_vendor_name = "Valve Software", + .usb_product_name = "Steam Controller", + .bus_type = 0x0003, + .vendor_id = 0x28de, + .product_id = 0x1205, + .version = 0x0110, + .usb_device_version = 0x0300, + /* SYN, KEY, ABS */ + .ev = { 0x0b }, + /* X, Y, RX, RY, hat 0-2 x/y */ + .abs = { 0x1b, 0x00, 0x3f }, + .keys = { + /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, + /* 0x120 0x46: joystick THUMB, THUMB2, BASE */ + /* 0x130 0xdb: gamepad ABXY, TL/TR */ + /* 0x138 0x7f: gamepad TL2/TR2, SELECT/START, MODE, THUMBL/R */ + /* 0x100 */ ZEROx4, 0x46, 0x00, 0xdb, 0x7f, + /* 0x140 */ ZEROx8, + /* 0x180 */ ZEROx8, + /* 0x1c0 */ ZEROx8, + /* 0x220 0x0f: dpad up/down/left/right */ + /* 0x200 */ ZEROx4, 0x0f, 0x00, 0x00, 0x00, + /* 0x240 */ ZEROx8, + /* 0x280 */ ZEROx8, + /* 0x2c0 0x0f: joystick TRIGGER_HAPPY1..TRIGGER_HAPPY4 */ + /* 0x2c0 */ 0x0f, + }, + .expected = SDL_UDEV_DEVICE_JOYSTICK, + .hid_report_descriptor_length = sizeof (steam_deck_oled_js_hid_report_descriptor), + .hid_report_descriptor = &steam_deck_oled_js_hid_report_descriptor[0], + }, + { + .name = "Steam Input virtual controller", + .eviocgname = "Microsoft X-Box 360 pad 0", + .bus_type = 0x0003, + .vendor_id = 0x28de, + .product_id = 0x11ff, + .version = 0x0001, + .expected = SDL_UDEV_DEVICE_JOYSTICK, + /* SYN, KEY, ABS, FF */ + .ev = { 0x0b, 0x00, 0x20 }, + /* XYZ, RXYZ, hat 0 */ + .abs = { 0x3f, 0x00, 0x03 }, + .keys = { + /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, + /* 0x130 0xdb: gamepad ABXY, TL/TR */ + /* 0x138 0x7f: gamepad SELECT/START, MODE, THUMBL/R */ + /* 0x100 */ ZEROx4, 0x00, 0x00, 0xdb, 0x7c, + }, + }, + { + .name = "Guitar Hero for PS3", + /* SWITCH CO.,LTD. Controller (Dinput) off-brand N64-style USB controller + * 0003:2563:0575 v0111 is functionally equivalent. + * https://linux-hardware.org/?id=usb:2563-0575 reports the same IDs as + * ShenZhen ShanWan Technology ZD-V+ Wired Gaming Controller */ + .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 via USB, 0003:2dc8:ab20 v0110, is the same; + * see below for the same physical device via Bluetooth, + * 0005:2dc8:2840 v0100 */ + .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 = "8BitDo SNES30 v0100 via Bluetooth", + .eviocgname = "8Bitdo SNES30 GamePad", + /* The same physical device via USB, 0003:2dc8:ab20 v0110, + * is reported differently (above). */ + /* 8BitDo NES30 Pro (aka N30 Pro) via Bluetooth, 0005:2dc8:3820 v0100, + * is functionally equivalent; but the same physical device via USB, + * 0003:2dc8:9001 v0111, matches N30 Pro 2 v0111. */ + .bus_type = 0x0005, + .vendor_id = 0x2dc8, + .product_id = 0x2840, + .version = 0x0100, + .expected = SDL_UDEV_DEVICE_JOYSTICK, + /* SYN, KEY, ABS, MSC */ + .ev = { 0x1b }, + /* XYZ, RZ, GAS, BRAKE, HAT0X, HAT0Y */ + .abs = { 0x27, 0x06, 0x03 }, + .keys = { + /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, + /* ABC, XYZ, TL, TR, TL2, TR2, SELECT, START, MODE, THUMBL, THUMBR, + * and an unassigned button code */ + /* 0x100 */ ZEROx4, 0x00, 0x00, 0xff, 0xff, + }, + }, + { + .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, + }, + }, + { + /* https://github.com/ValveSoftware/steam-devices/pull/42 + * PS4 mode is functionally equivalent, but with product ID 0x011c + * and version 0x1101. */ + .name = "Hori Fighting Stick Alpha - PS5 mode", + .bus_type = 0x0003, /* USB */ + .vendor_id = 0x0f0d, /* Hori Co., Ltd. */ + .product_id = 0x0184, /* HORI FIGHTING STICK α (PS5 mode) */ + .version = 0x0111, + .expected = SDL_UDEV_DEVICE_JOYSTICK, + /* SYN, KEY, ABS, MSC */ + .ev = { 0x1b }, + /* X, Y, Z, RX, RY, RZ, HAT0X, HAT0Y */ + .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, + }, + }, + { /* https://github.com/ValveSoftware/steam-devices/pull/42 */ + .name = "Hori Fighting Stick Alpha - PC mode", + .bus_type = 0x0003, /* USB */ + .vendor_id = 0x0f0d, /* Hori Co., Ltd. */ + .product_id = 0x011e, /* HORI FIGHTING STICK α (PC mode) */ + .version = 0x0116, + .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, + }, + }, + { /* https://github.com/ValveSoftware/steam-devices/issues/29 */ + .name = "HORIPAD S for Nintendo", + .bus_type = 0x0003, /* USB */ + .vendor_id = 0x0f0d, /* Hori Co., Ltd. */ + .product_id = 0x00dc, /* HORIPAD S */ + .version = 0x0112, + .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 = "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 Bluetooth (Linux 6.2.11)", + .eviocgname = "Pro Controller", + .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, + /* ABC, XYZ, TL, TR, TL2, TR2, SELECT, START, MODE, THUMBL, THUMBR, + * and an unassigned button code */ + /* 0x100 */ ZEROx4, 0x00, 0x00, 0xff, 0xff, + }, + }, + { + .name = "Switch Pro Controller via USB", + .eviocgname = "Nintendo Co., Ltd. Pro Controller", + .usb_vendor_name = "Nintendo Co., Ltd.", + .usb_product_name = "Pro Controller", + .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, + /* 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, + }, + }, + { /* https://github.com/ValveSoftware/steam-devices/pull/40 */ + .name = "PDP wired Pro Controller for Switch", + /* 0003:0e6f:0184 "Performance Designed Products" / + * "Faceoff Deluxe+ Audio Wired Controller for Nintendo Switch" appears + * to be functionally equivalent */ + .eviocgname = "PDP CO.,LTD. Faceoff Wired Pro Controller for Nintendo Switch", + .usb_vendor_name = "PDP CO.,LTD.", + .usb_product_name = "Faceoff Wired Pro Controller for Nintendo Switch", + .bus_type = 0x0003, + .vendor_id = 0x0e6f, + .product_id = 0x0180, + .version = 0x0111, + .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, + /* ABC, XYZ, TL, TR, TL2, TR2, SELECT, START, MODE, + * THUMBL */ + /* 0x100 */ ZEROx4, 0x00, 0x00, 0xff, 0x3f, + }, + }, + { + .name = "NES Controller (R) NES-style Joycon from Nintendo Online", + .eviocgname = "NES Controller (R)", + /* Joy-Con (L), 0005:057e:2006 v0001, is functionally equivalent. + * Ordinary Joy-Con (R) and NES-style Joy-Con (L) are assumed to be + * functionally equivalent as well. */ + .bus_type = 0x0005, /* Bluetooth-only */ + .vendor_id = 0x057e, + .product_id = 0x2007, + .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, + /* ABC, XYZ, TL, TR, TL2, TR2, SELECT, START, MODE, THUMBL, THUMBR, + * and an unassigned button code */ + /* 0x100 */ ZEROx4, 0x00, 0x00, 0xff, 0xff, + }, + }, + { + .name = "Thrustmaster Racing Wheel FFB", + /* Several devices intended for PS4 are functionally equivalent to this: + * https://github.com/ValveSoftware/steam-devices/pull/34 + * Mad Catz FightStick TE S+ PS4, 0003:0738:8384:0111 v0111 + * (aka Street Fighter V Arcade FightStick TES+) + * Mad Catz FightStick TE2+ PS4, 0003:0738:8481 v0100 + * (aka Street Fighter V Arcade FightStick TE2+) + * Bigben Interactive DAIJA Arcade Stick, 0003:146b:0d09 v0111 + * (aka Nacon Daija PS4 Arcade Stick) + * Razer Razer Raiju Ultimate Wired, 0003:1532:1009 v0000 + * Razer Razer Raiju Ultimate (Bluetooth), 0005:1532:1009 v0001 + */ + .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 via Bluetooth", + /* This device has also been seen reported with an additional + * unassigned button code, the same as the SNES30 v0100 via Bluetooth */ + .bus_type = 0x0005, + .vendor_id = 0x2dc8, + .product_id = 0x3820, + .version = 0x0100, + .expected = SDL_UDEV_DEVICE_JOYSTICK, + /* SYN, KEY, ABS, MSC */ + .ev = { 0x1b }, + /* 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 = "8BitDo N30 Pro 2 v0111", + .bus_type = 0x0003, + .vendor_id = 0x2dc8, + .product_id = 0x9015, + .version = 0x0111, + /* 8BitDo NES30 Pro via USB, 0003:2dc8:9001 v0111, is the same; + * but the same physical device via Bluetooth, 0005:2dc8:3820 v0100, + * matches 8BitDo SNES30 v0100 via Bluetooth instead (see above). */ + .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 = "8BitDo N30 Pro 2 via Bluetooth", + /* Physically the same device as the one that mimics an Xbox 360 + * USB controller when wired */ + .bus_type = 0x0005, + .vendor_id = 0x045e, + .product_id = 0x02e0, + .version = 0x0903, + .expected = SDL_UDEV_DEVICE_JOYSTICK | SDL_UDEV_DEVICE_HAS_KEYS, + /* SYN, KEY, ABS, MSC, FF */ + .ev = { 0x1b, 0x00, 0x20 }, + /* X, Y, Z, RX, RY, RZ, HAT0X, HAT0Y */ + .abs = { 0x3f, 0x00, 0x03 }, + .keys = { + /* 0x00 */ ZEROx8, + /* 0x40 */ ZEROx8, + /* KEY_MENU */ + /* 0x80 */ 0x00, 0x08, 0x00, 0x00, ZEROx4, + /* 0xc0 */ ZEROx8, + /* ABC, XYZ, TL, TR, TL2, TR2 */ + /* 0x100 */ ZEROx4, 0x00, 0x00, 0xff, 0x03, + }, + }, + { + .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 = "Google Stadia Controller rev.A", + .eviocgname = "Google LLC Stadia Controller rev. A", + .usb_vendor_name = "Google LLC", + .usb_product_name = "Stadia Controller rev. A", + /* This data is with USB-C, but the same physical device via Bluetooth, + * 0005:18d1:9400 v0000, is functionally equivalent other than having + * EVIOCGNAME = StadiaXXXX-YYYY where XXXX is the last 4 digits of + * the serial number and YYYY is some other mystery number */ + .bus_type = 0x0003, + .vendor_id = 0x18d1, + .product_id = 0x9400, + .version = 0x0100, + .expected = SDL_UDEV_DEVICE_JOYSTICK | SDL_UDEV_DEVICE_HAS_KEYS, + .ev = { 0x0b }, + /* XYZ, RZ, gas, brake, hat0 */ + .abs = { 0x27, 0x06, 0x03 }, + .keys = { + /* 0x00 */ ZEROx8, + /* Volume up/down */ + /* 0x40 */ ZEROx4, 0x00, 0x00, 0x0c, 0x00, + /* Media play/pause */ + /* 0x80 */ ZEROx4, 0x10, 0x00, 0x00, 0x00, + /* 0xc0 */ ZEROx8, + /* ABXY, TL, TR, SELECT, START, MODE, THUMBL, THUMBR */ + /* 0x100 */ ZEROx4, 0x00, 0x00, 0xdb, 0x7c, + /* 0x140 */ ZEROx8, + /* 0x180 */ ZEROx8, + /* 0x1c0 */ ZEROx8, + /* 0x200 */ ZEROx8, + /* 0x240 */ ZEROx8, + /* 0x280 */ ZEROx8, + /* TRIGGER_HAPPY 1-4 */ + /* 0x2c0 */ 0x0f, 0x00, 0x00, 0x00, ZEROx4, + }, + }, + { + .name = "Microsoft Xbox Series S|X Controller (model 1914) via USB", + .eviocgname = "Microsoft Xbox Series S|X Controller", + .usb_vendor_name = "Microsoft", + .usb_product_name = "Controller", + /* Physically the same device as 0003:045e:0b13 v0515 below, + * but some functionality is mapped differently */ + .bus_type = 0x0003, + .vendor_id = 0x045e, + .product_id = 0x0b12, + .version = 0x050f, + .expected = SDL_UDEV_DEVICE_JOYSTICK | SDL_UDEV_DEVICE_HAS_KEYS, + .ev = { 0x0b }, + /* X, Y, Z, RX, RY, RZ, hat 0 */ + .abs = { 0x3f, 0x00, 0x03 }, + .keys = { + /* 0x00 */ ZEROx8, + /* 0x40 */ ZEROx8, + /* Record */ + /* 0x80 */ ZEROx4, 0x80, 0x00, 0x00, 0x00, + /* 0xc0 */ ZEROx8, + /* ABXY, TL, TR, SELECT, START, MODE, THUMBL, THUMBR */ + /* 0x100 */ ZEROx4, 0x00, 0x00, 0xdb, 0x7c, + }, + }, + { + .name = "Microsoft Xbox Series S|X Controller (model 1914) via Bluetooth", + .eviocgname = "Xbox Wireless Controller", + /* Physically the same device as 0003:045e:0b12 v050f above, + * but some functionality is mapped differently */ + .bus_type = 0x0005, + .vendor_id = 0x045e, + .product_id = 0x0b13, + .version = 0x0515, + .expected = SDL_UDEV_DEVICE_JOYSTICK | SDL_UDEV_DEVICE_HAS_KEYS, + .ev = { 0x0b }, + /* XYZ, RZ, gas, brake, hat0 */ + .abs = { 0x27, 0x06, 0x03 }, + .keys = { + /* 0x00 */ ZEROx8, + /* 0x40 */ ZEROx8, + /* Record */ + /* 0x80 */ ZEROx4, 0x80, 0x00, 0x00, 0x00, + /* 0xc0 */ ZEROx8, + /* ABC, XYZ, TL, TR, TL2, TR2, select, start, mode, thumbl, + * thumbr */ + /* 0x100 */ ZEROx4, 0x00, 0x00, 0xff, 0x7f, + }, + }, + { + .name = "Wiimote - buttons", + .eviocgname = "Nintendo Wii Remote", + .bus_type = 0x0005, + .vendor_id = 0x057e, + .product_id = 0x0306, + .version = 0x0600, + /* 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|HAS_KEYS? */ + .expected = SDL_UDEV_DEVICE_HAS_KEYS, + /* SYN, KEY, FF */ + .ev = { 0x03, 0x00, 0x20 }, + .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 - accelerometer", + .eviocgname = "Nintendo Wii Remote Accelerometer", + .bus_type = 0x0005, + .vendor_id = 0x057e, + .product_id = 0x0306, + .version = 0x0600, + .expected = SDL_UDEV_DEVICE_ACCELEROMETER, + /* SYN, ABS */ + .ev = { 0x09 }, + /* RX, RY, RZ - even though it would more conventionally be X, Y, Z */ + .abs = { 0x38 }, + }, + { + .name = "Wiimote - Motion Plus gyroscope", + .eviocgname = "Nintendo Wii Remote Motion Plus", + /* Note that if we only look at the bus type, vendor, product, version + * and axes, this is indistinguishable from the accelerometer */ + .bus_type = 0x0005, + .vendor_id = 0x057e, + .product_id = 0x0306, + .version = 0x0600, + .expected = SDL_UDEV_DEVICE_ACCELEROMETER, + /* SYN, ABS */ + .ev = { 0x09 }, + /* RX, RY, RZ */ + .abs = { 0x38 }, + }, + { + .name = "Wiimote - IR positioning", + .eviocgname = "Nintendo Wii Remote IR", + .bus_type = 0x0005, + .vendor_id = 0x057e, + .product_id = 0x0306, + .version = 0x0600, + .expected = SDL_UDEV_DEVICE_UNKNOWN, + /* SYN, ABS */ + .ev = { 0x09 }, + /* HAT0X, Y to HAT3X, Y */ + .abs = { 0x00, 0x00, 0xff }, + }, + { + .name = "Wiimote - Nunchuck", + .eviocgname = "Nintendo Wii Remote Nunchuk", + .bus_type = 0x0005, + .vendor_id = 0x057e, + .product_id = 0x0306, + .version = 0x0600, + /* 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 - even though this is an accelerometer, which + * would more conventionally be X, Y, Z, and a left joystick, which + * would more conventionally be X, Y */ + .abs = { 0x38, 0x00, 0x03 }, + .keys = { + /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, + /* C and Z buttons */ + /* 0x100 */ ZEROx4, 0x00, 0x00, 0x24, 0x00, + }, + }, + { + .name = "Wiimote - Classic Controller", + .eviocgname = "Nintendo Wii Remote Classic Controller", + .bus_type = 0x0005, + .vendor_id = 0x057e, + .product_id = 0x0306, + .version = 0x0600, + /* TODO: Should this be JOYSTICK, or maybe JOYSTICK|HAS_KEYS? + * It's unusual in the same ways as the Wiimote */ + .expected = SDL_UDEV_DEVICE_HAS_KEYS, + /* SYN, KEY, ABS */ + .ev = { 0x0b }, + /* Hat 1-3 X and Y */ + .abs = { 0x00, 0x00, 0xfc }, + .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, 0x00, 0xdb, 0x13, + /* 0x140 */ ZEROx8, + /* next (keyboard page down), previous (keyboard page up) */ + /* 0x180 */ 0x00, 0x00, 0x80, 0x10, ZEROx4, + }, + }, + { + /* Flags guessed from kernel source code, not confirmed with real + * hardware */ + .name = "Wiimote - Balance Board", + /* TODO: Should this be JOYSTICK? */ + .expected = SDL_UDEV_DEVICE_UNKNOWN, + /* SYN, KEY, ABS */ + .ev = { 0x0b }, + /* Hat 0-1 */ + .abs = { 0x00, 0x00, 0x0f }, + .keys = { + /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, + /* BTN_A */ + /* 0x100 */ ZEROx4, 0x00, 0x00, 0x01, 0x00, + }, + }, + { + /* Flags guessed from kernel source code, not confirmed with real + * hardware */ + .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)", + .eviocgname = "Synaptics TM3381-002", + .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 = "DELL08AF:00 (Dell XPS laptop touchpad)", + .bus_type = 0x18, + .vendor_id = 0x6cb, + .product_id = 0x76af, + .version = 0x100, + .ev = { 0x0b }, + .expected = SDL_UDEV_DEVICE_TOUCHPAD, + /* X, Y, multitouch */ + .abs = { 0x03, 0x00, 0x00, 0x00, 0x00, 0x80, 0xe0, 0x02 }, + .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)", + .eviocgname = "TPPS/2 Elan TrackPoint", + .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", + .eviocgname = "ThinkPad Extra Buttons", + .bus_type = 0x0019, + .vendor_id = 0x17aa, + .product_id = 0x5054, + .version = 0x4101, + .expected = SDL_UDEV_DEVICE_HAS_KEYS, + /* 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 = "Thinkpad ACPI buttons (Linux 6.1)", + .eviocgname = "ThinkPad Extra Buttons", + .bus_type = 0x0019, + .vendor_id = 0x17aa, + .product_id = 0x5054, + .version = 0x4101, + .expected = SDL_UDEV_DEVICE_HAS_KEYS, + /* 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 */ ZEROx4, 0x00, 0x00, 0x00, 0x70, + /* 0x1c0 */ 0x00, 0x00, 0x04, 0x18, 0x20, 0x00, 0x00, 0x00, + /* 0x200 */ ZEROx8, + /* 0x240 */ ZEROx8, + }, + }, + { + .name = "PC speaker", + .eviocgname = "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 = "HDA Digital PCBeep", + .eviocgname = "HDA Digital PCBeep", + .bus_type = 0x0001, + .vendor_id = 0x10ec, + .product_id = 0x0257, + .version = 0x0001, + .expected = SDL_UDEV_DEVICE_UNKNOWN, + /* SYN, SND */ + .ev = { 0x01, 0x00, 0x04 }, + }, + { + .name = "ALSA headphone detection, etc.", + .eviocgname = "HDA Intel PCH Mic", + /* HDA Intel PCH Headphone is functionally equivalent */ + /* HDA Intel PCH HDMI/DP,pcm=3 is functionally equivalent */ + /* HDA Intel PCH HDMI/DP,pcm=7 is functionally equivalent */ + /* HDA Intel PCH HDMI/DP,pcm=8 is functionally equivalent */ + .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", + /* Steam Deck LCD/OLED keyboard interface is version 0xab83 but + * otherwise equivalent */ + .eviocgname = "AT Translated Set 2 keyboard", + .bus_type = 0x0011, /* BUS_I8042 */ + .vendor_id = 0x0001, + .product_id = 0x0001, + .version = 0xab54, + .expected = SDL_UDEV_DEVICE_HAS_KEYS | 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", + .eviocgname = "Sleep Button", + .bus_type = 0x0019, /* BUS_HOST */ + .vendor_id = 0x0000, + .product_id = 0x0003, + .version = 0x0000, + .expected = SDL_UDEV_DEVICE_HAS_KEYS, + /* SYN, KEY */ + .ev = { 0x03 }, + .keys = { + /* 0x00 */ ZEROx8, + /* 0x40 */ ZEROx8, + /* KEY_SLEEP */ + /* 0x80 */ 0x00, 0x40, + }, + }, + { + /* As seen on Thinkpad X280, Steam Deck LCD, Steam Deck OLED */ + .name = "ACPI lid switch", + .eviocgname = "Lid Switch", + .bus_type = 0x0019, /* BUS_HOST */ + .vendor_id = 0x0000, + .product_id = 0x0005, + .version = 0x0000, + .expected = SDL_UDEV_DEVICE_UNKNOWN, + /* SYN, SW */ + .ev = { 0x21 }, + }, + { + /* As seen on Thinkpad X280, Steam Deck LCD, Steam Deck OLED */ + .name = "ACPI power button", + .eviocgname = "Power Button", + .bus_type = 0x0019, /* BUS_HOST */ + .vendor_id = 0x0000, + .product_id = 0x0001, + .version = 0x0000, + .expected = SDL_UDEV_DEVICE_HAS_KEYS, + /* SYN, KEY */ + .ev = { 0x03 }, + .keys = { + /* 0x00 */ ZEROx8, + /* KEY_POWER */ + /* 0x40 */ ZEROx4, 0x00, 0x00, 0x10, 0x00, + }, + }, + { + /* As seen on Thinkpad X280, Steam Deck LCD, Steam Deck OLED */ + .name = "ACPI video bus", + .eviocgname = "Video Bus", + .bus_type = 0x0019, /* BUS_HOST */ + .vendor_id = 0x0000, + .product_id = 0x0006, + .version = 0x0000, + .expected = SDL_UDEV_DEVICE_HAS_KEYS, + /* 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 webcam", + .eviocgname = "Integrated Camera: Integrated C", + .usb_vendor_name = "Chicony Electronics Co.,Ltd.", + .usb_product_name = "Integrated Camera", + .bus_type = 0x0003, + .vendor_id = 0x04f2, + .product_id = 0xb604, + .version = 0x0027, + .expected = SDL_UDEV_DEVICE_HAS_KEYS, + /* SYN, KEY */ + .ev = { 0x03 }, + .keys = { + /* 0x00 */ ZEROx8, + /* 0x40 */ ZEROx8, + /* 0x80 */ ZEROx8, + /* KEY_CAMERA */ + /* 0xc0 */ 0x00, 0x00, 0x10, 0x00, ZEROx4, + }, + }, + { + .name = "Thinkpad X280 extra buttons", + .bus_type = 0x0019, /* BUS_HOST */ + .vendor_id = 0x17aa, + .product_id = 0x5054, + .version = 0x4101, + .expected = SDL_UDEV_DEVICE_HAS_KEYS, + /* 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", + .eviocgname = "Lite-On Technology Corp. ThinkPad USB Keyboard with TrackPoint", + .usb_vendor_name = "Lite-On Technology Corp.", + .usb_product_name = "ThinkPad USB Keyboard with TrackPoint", + .bus_type = 0x0003, + .vendor_id = 0x17ef, + .product_id = 0x6009, + .expected = SDL_UDEV_DEVICE_HAS_KEYS | 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, + }, + .hid_report_descriptor_length = sizeof (thinkpad_usb_keyboard_hid_report_descriptor), + .hid_report_descriptor = &thinkpad_usb_keyboard_hid_report_descriptor[0], + }, + { + .name = "Thinkpad USB keyboard with Trackpoint - Trackpoint", + .eviocgname = "Lite-On Technology Corp. ThinkPad USB Keyboard with TrackPoint", + .usb_vendor_name = "Lite-On Technology Corp.", + .usb_product_name = "ThinkPad USB Keyboard with TrackPoint", + .bus_type = 0x0003, + .vendor_id = 0x17ef, + .product_id = 0x6009, + .version = 0x0110, + /* 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_HAS_KEYS, + /* 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, + }, + .hid_report_descriptor_length = sizeof (thinkpad_usb_trackpoint_hid_report_descriptor), + .hid_report_descriptor = &thinkpad_usb_trackpoint_hid_report_descriptor[0], + }, + { /* https://github.com/ValveSoftware/Proton/issues/5126 */ + .name = "Smarty Co. VRS DirectForce Pro Pedals", + .bus_type = 0x0003, + .vendor_id = 0x0483, /* STMicroelectronics */ + .product_id = 0xa3be, /* VRS DirectForce Pro Pedals */ + .version = 0x0111, + /* TODO: Ideally we would identify this as a joystick, but there + * isn't currently enough information to do that without a table + * of known devices. */ + .expected = SDL_UDEV_DEVICE_JOYSTICK, + .todo = "https://github.com/ValveSoftware/Proton/issues/5126", + /* SYN, ABS */ + .ev = { 0x09 }, + /* X, Y, Z */ + .abs = { 0x07 }, + .hid_report_descriptor_length = sizeof (vrs_pedals_hid_report_descriptor), + .hid_report_descriptor = &vrs_pedals_hid_report_descriptor[0], + }, + { /* https://github.com/ValveSoftware/Proton/issues/5126 */ + .name = "Heusinkveld Heusinkveld Sim Pedals Ultimate", + .bus_type = 0x0003, + .vendor_id = 0x30b7, /* Heusinkveld Engineering */ + .product_id = 0x1003, /* Heusinkveld Sim Pedals Ultimate */ + .version = 0x0000, + /* TODO: Ideally we would identify this as a joystick, but there + * isn't currently enough information to do that without a table + * of known devices. */ + .expected = SDL_UDEV_DEVICE_JOYSTICK, + .todo = "https://github.com/ValveSoftware/Proton/issues/5126", + /* SYN, ABS */ + .ev = { 0x09 }, + /* RX, RY, RZ */ + .abs = { 0x38 }, + .hid_report_descriptor_length = sizeof (heusinkveld_pedals_hid_report_descriptor), + .hid_report_descriptor = &heusinkveld_pedals_hid_report_descriptor[0], + }, + { /* https://github.com/ValveSoftware/Proton/issues/5126 */ + .name = "Vitaly [mega_mozg] Naidentsev ODDOR-handbrake", + .bus_type = 0x0003, + .vendor_id = 0x0000, + .product_id = 0x0000, + .version = 0x0001, + /* TODO: Ideally we would identify this as a joystick by it having + * the joystick-specific THROTTLE axis and TRIGGER/THUMB buttons */ + .expected = SDL_UDEV_DEVICE_JOYSTICK, + .todo = "https://github.com/ValveSoftware/Proton/issues/5126", + /* SYN, KEY, ABS, MSC */ + .ev = { 0x1b }, + /* THROTTLE only */ + .abs = { 0x40 }, + .keys = { + /* 0x00-0xff */ ZEROx8, ZEROx8, ZEROx8, ZEROx8, + /* TRIGGER = 0x120, THUMB = 0x121 */ + /* 0x100 */ ZEROx4, 0x03, 0x00, 0x00, 0x00, + }, + }, + { /* https://github.com/ValveSoftware/Proton/issues/5126 */ + .name = "Leo Bodnar Logitech® G25 Pedals", + .bus_type = 0x0003, + .vendor_id = 0x1dd2, /* Leo Bodnar Electronics Ltd */ + .product_id = 0x100c, + .version = 0x0110, + /* TODO: Ideally we would identify this as a joystick, but there + * isn't currently enough information to do that without a table + * of known devices. */ + .expected = SDL_UDEV_DEVICE_JOYSTICK, + .todo = "https://github.com/ValveSoftware/Proton/issues/5126", + /* SYN, ABS */ + .ev = { 0x09 }, + /* RX, RY, RZ */ + .abs = { 0x38 }, + }, + { /* https://github.com/ValveSoftware/Proton/issues/5126 */ + .name = "FANATEC ClubSport USB Handbrake", + .bus_type = 0x0003, + .vendor_id = 0x0eb7, + .product_id = 0x1a93, + .version = 0x0111, + /* TODO: Ideally we would identify this as a joystick, but there + * isn't currently enough information to do that without a table + * of known devices. */ + .expected = SDL_UDEV_DEVICE_JOYSTICK, + .todo = "https://github.com/ValveSoftware/Proton/issues/5126", + /* SYN, ABS */ + .ev = { 0x09 }, + /* X only */ + .abs = { 0x01 }, + .hid_report_descriptor_length = sizeof (fanatec_handbrake_hid_report_descriptor), + .hid_report_descriptor = &fanatec_handbrake_hid_report_descriptor[0], + }, + { /* Artificial test data, not a real device */ + .name = "Fake accelerometer with fewer than usual axes reported", + .expected = SDL_UDEV_DEVICE_ACCELEROMETER, + /* SYN, ABS */ + .ev = { 0x09 }, + /* X only */ + .abs = { 0x01 }, + /* ACCELEROMETER */ + .props = { 0x40 }, + }, + { /* Artificial test data, not a real device */ + .name = "Fake pointing stick with no buttons", + .expected = SDL_UDEV_DEVICE_MOUSE, + /* SYN, REL */ + .ev = { 0x05 }, + /* X,Y */ + .rel = { 0x03 }, + /* POINTER, POINTING_STICK */ + .props = { 0x21 }, + }, + { /* Artificial test data, not a real device */ + .name = "Fake buttonpad", + .expected = SDL_UDEV_DEVICE_TOUCHPAD, + /* SYN, ABS */ + .ev = { 0x09 }, + /* X,Y */ + .abs = { 0x03 }, + /* POINTER, BUTTONPAD */ + .props = { 0x05 }, + }, + { + .name = "No information", + .expected = SDL_UDEV_DEVICE_UNKNOWN, + } +}; +/* *INDENT-ON* */ /* clang-format on */ + +/* 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_Swap32LE(X) : SDL_Swap64LE(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 props[NBITS(INPUT_PROP_MAX)]; + 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); + + SDL_memset(&caps, '\0', sizeof(caps)); + SDL_memcpy(caps.props, t->props, sizeof(t->props)); + SDL_memcpy(caps.ev, t->ev, sizeof(t->ev)); + SDL_memcpy(caps.keys, t->keys, sizeof(t->keys)); + SDL_memcpy(caps.abs, t->abs, sizeof(t->abs)); + SDL_memcpy(caps.rel, t->rel, sizeof(t->rel)); + + for (j = 0; j < SDL_arraysize(caps.props); j++) { + caps.props[j] = SwapLongLE(caps.props[j]); + } + + 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.props, 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); + } + } + + if (t->todo) { + printf("\tKnown issue, ignoring: %s\n", t->todo); + } else { + printf("\tFailed\n"); + 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[]) +{ + int result; + SDLTest_CommonState *state; + + /* Initialize test framework */ + state = SDLTest_CommonCreateState(argv, 0); + if (!state) { + return 1; + } + + /* Parse commandline */ + if (!SDLTest_CommonDefaultArgs(state, argc, argv)) { + return 1; + } + + result = run_test() ? 0 : 1; + + SDL_Quit(); + SDLTest_CommonDestroyState(state); + return result; +} -- cgit v1.2.3