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