1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
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, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21
22/* Original hybrid wrapper for Linux by Valve Software. Their original notes:
23 *
24 * The libusb version doesn't support Bluetooth, but not all Linux
25 * distributions allow access to /dev/hidraw*
26 *
27 * This merges the two, at a small performance cost, until distributions
28 * have granted access to /dev/hidraw*
29 */
30
31#include "SDL_internal.h"
32
33#include "SDL_hidapi_c.h"
34#include "../joystick/usb_ids.h"
35#include "../SDL_hints_c.h"
36
37// Initial type declarations
38#define HID_API_NO_EXPORT_DEFINE // do not export hidapi procedures
39#include "hidapi/hidapi.h"
40
41#ifndef SDL_HIDAPI_DISABLED
42
43#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK)
44#include "../core/windows/SDL_windows.h"
45#endif
46
47#ifdef SDL_PLATFORM_MACOS
48#include <CoreFoundation/CoreFoundation.h>
49#include <mach/mach.h>
50#include <IOKit/IOKitLib.h>
51#include <IOKit/hid/IOHIDDevice.h>
52#include <IOKit/usb/USBSpec.h>
53#include <AvailabilityMacros.h>
54// Things named "Master" were renamed to "Main" in macOS 12.0's SDK.
55#if MAC_OS_X_VERSION_MIN_REQUIRED < 120000
56#define kIOMainPortDefault kIOMasterPortDefault
57#endif
58#endif
59
60#include "../core/linux/SDL_udev.h"
61#ifdef SDL_USE_LIBUDEV
62#include <poll.h>
63#endif
64
65#ifdef HAVE_INOTIFY
66#include <string.h> // strerror
67#include <errno.h> // errno
68#include <fcntl.h>
69#include <limits.h> // For the definition of NAME_MAX
70#include <sys/inotify.h>
71#endif
72
73#if defined(SDL_USE_LIBUDEV) || defined(HAVE_INOTIFY)
74#include <unistd.h>
75#endif
76
77#ifdef SDL_USE_LIBUDEV
78typedef enum
79{
80 ENUMERATION_UNSET,
81 ENUMERATION_LIBUDEV,
82 ENUMERATION_FALLBACK
83} LinuxEnumerationMethod;
84
85static LinuxEnumerationMethod linux_enumeration_method = ENUMERATION_UNSET;
86#endif
87
88#ifdef HAVE_INOTIFY
89static int inotify_fd = -1;
90#endif
91
92#ifdef SDL_USE_LIBUDEV
93static const SDL_UDEV_Symbols *usyms = NULL;
94#endif
95
96static struct
97{
98 bool m_bInitialized;
99 Uint32 m_unDeviceChangeCounter;
100 bool m_bCanGetNotifications;
101 Uint64 m_unLastDetect;
102
103#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK)
104 SDL_ThreadID m_nThreadID;
105 WNDCLASSEXA m_wndClass;
106 HWND m_hwndMsg;
107 HDEVNOTIFY m_hNotify;
108 double m_flLastWin32MessageCheck;
109#endif
110
111#ifdef SDL_PLATFORM_MACOS
112 IONotificationPortRef m_notificationPort;
113 mach_port_t m_notificationMach;
114#endif
115
116#ifdef SDL_USE_LIBUDEV
117 struct udev *m_pUdev;
118 struct udev_monitor *m_pUdevMonitor;
119 int m_nUdevFd;
120#endif
121} SDL_HIDAPI_discovery;
122
123#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK)
124struct _DEV_BROADCAST_HDR
125{
126 DWORD dbch_size;
127 DWORD dbch_devicetype;
128 DWORD dbch_reserved;
129};
130
131typedef struct _DEV_BROADCAST_DEVICEINTERFACE_A
132{
133 DWORD dbcc_size;
134 DWORD dbcc_devicetype;
135 DWORD dbcc_reserved;
136 GUID dbcc_classguid;
137 char dbcc_name[1];
138} DEV_BROADCAST_DEVICEINTERFACE_A, *PDEV_BROADCAST_DEVICEINTERFACE_A;
139
140typedef struct _DEV_BROADCAST_HDR DEV_BROADCAST_HDR;
141#define DBT_DEVICEARRIVAL 0x8000 // system detected a new device
142#define DBT_DEVICEREMOVECOMPLETE 0x8004 // device was removed from the system
143#define DBT_DEVTYP_DEVICEINTERFACE 0x00000005 // device interface class
144#define DBT_DEVNODES_CHANGED 0x0007
145#define DBT_CONFIGCHANGED 0x0018
146#define DBT_DEVICETYPESPECIFIC 0x8005 // type specific event
147#define DBT_DEVINSTSTARTED 0x8008 // device installed and started
148
149#include <initguid.h>
150DEFINE_GUID(GUID_DEVINTERFACE_USB_DEVICE, 0xA5DCBF10L, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED);
151
152static LRESULT CALLBACK ControllerWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
153{
154 switch (message) {
155 case WM_DEVICECHANGE:
156 switch (wParam) {
157 case DBT_DEVICEARRIVAL:
158 case DBT_DEVICEREMOVECOMPLETE:
159 if (((DEV_BROADCAST_HDR *)lParam)->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) {
160 ++SDL_HIDAPI_discovery.m_unDeviceChangeCounter;
161 }
162 break;
163 }
164 return TRUE;
165 }
166
167 return DefWindowProc(hwnd, message, wParam, lParam);
168}
169#endif // defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK)
170
171#ifdef SDL_PLATFORM_MACOS
172static void CallbackIOServiceFunc(void *context, io_iterator_t portIterator)
173{
174 // Must drain the iterator, or we won't receive new notifications
175 io_object_t entry;
176 while ((entry = IOIteratorNext(portIterator)) != 0) {
177 IOObjectRelease(entry);
178 ++SDL_HIDAPI_discovery.m_unDeviceChangeCounter;
179 }
180}
181#endif // SDL_PLATFORM_MACOS
182
183#ifdef HAVE_INOTIFY
184#ifdef HAVE_INOTIFY_INIT1
185static int SDL_inotify_init1(void)
186{
187 return inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
188}
189#else
190static int SDL_inotify_init1(void)
191{
192 int fd = inotify_init();
193 if (fd < 0) {
194 return -1;
195 }
196 fcntl(fd, F_SETFL, O_NONBLOCK);
197 fcntl(fd, F_SETFD, FD_CLOEXEC);
198 return fd;
199}
200#endif
201
202static int StrHasPrefix(const char *string, const char *prefix)
203{
204 return SDL_strncmp(string, prefix, SDL_strlen(prefix)) == 0;
205}
206
207static int StrIsInteger(const char *string)
208{
209 const char *p;
210
211 if (*string == '\0') {
212 return 0;
213 }
214
215 for (p = string; *p != '\0'; p++) {
216 if (*p < '0' || *p > '9') {
217 return 0;
218 }
219 }
220
221 return 1;
222}
223#endif // HAVE_INOTIFY
224
225static void HIDAPI_InitializeDiscovery(void)
226{
227 SDL_HIDAPI_discovery.m_bInitialized = true;
228 SDL_HIDAPI_discovery.m_unDeviceChangeCounter = 1;
229 SDL_HIDAPI_discovery.m_bCanGetNotifications = false;
230 SDL_HIDAPI_discovery.m_unLastDetect = 0;
231
232#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK)
233 SDL_HIDAPI_discovery.m_nThreadID = SDL_GetCurrentThreadID();
234
235 SDL_zero(SDL_HIDAPI_discovery.m_wndClass);
236 SDL_HIDAPI_discovery.m_wndClass.hInstance = GetModuleHandle(NULL);
237 SDL_HIDAPI_discovery.m_wndClass.lpszClassName = "SDL_HIDAPI_DEVICE_DETECTION";
238 SDL_HIDAPI_discovery.m_wndClass.lpfnWndProc = ControllerWndProc; // This function is called by windows
239 SDL_HIDAPI_discovery.m_wndClass.cbSize = sizeof(WNDCLASSEX);
240
241 RegisterClassExA(&SDL_HIDAPI_discovery.m_wndClass);
242 SDL_HIDAPI_discovery.m_hwndMsg = CreateWindowExA(0, "SDL_HIDAPI_DEVICE_DETECTION", NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
243
244 {
245 DEV_BROADCAST_DEVICEINTERFACE_A devBroadcast;
246
247 SDL_zero(devBroadcast);
248 devBroadcast.dbcc_size = sizeof(devBroadcast);
249 devBroadcast.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
250 devBroadcast.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE;
251
252 /* DEVICE_NOTIFY_ALL_INTERFACE_CLASSES is important, makes GUID_DEVINTERFACE_USB_DEVICE ignored,
253 * but that seems to be necessary to get a notice after each individual usb input device actually
254 * installs, rather than just as the composite device is seen.
255 */
256 SDL_HIDAPI_discovery.m_hNotify = RegisterDeviceNotification(SDL_HIDAPI_discovery.m_hwndMsg, &devBroadcast, DEVICE_NOTIFY_WINDOW_HANDLE | DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);
257 SDL_HIDAPI_discovery.m_bCanGetNotifications = (SDL_HIDAPI_discovery.m_hNotify != 0);
258 }
259#endif // defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK)
260
261#ifdef SDL_PLATFORM_MACOS
262 SDL_HIDAPI_discovery.m_notificationPort = IONotificationPortCreate(kIOMainPortDefault);
263 if (SDL_HIDAPI_discovery.m_notificationPort) {
264 {
265 io_iterator_t portIterator = 0;
266 io_object_t entry;
267 IOReturn result = IOServiceAddMatchingNotification(
268 SDL_HIDAPI_discovery.m_notificationPort,
269 kIOFirstMatchNotification,
270 IOServiceMatching(kIOHIDDeviceKey),
271 CallbackIOServiceFunc, NULL, &portIterator);
272
273 if (result == 0) {
274 // Must drain the existing iterator, or we won't receive new notifications
275 while ((entry = IOIteratorNext(portIterator)) != 0) {
276 IOObjectRelease(entry);
277 }
278 } else {
279 IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort);
280 SDL_HIDAPI_discovery.m_notificationPort = nil;
281 }
282 }
283 {
284 io_iterator_t portIterator = 0;
285 io_object_t entry;
286 IOReturn result = IOServiceAddMatchingNotification(
287 SDL_HIDAPI_discovery.m_notificationPort,
288 kIOTerminatedNotification,
289 IOServiceMatching(kIOHIDDeviceKey),
290 CallbackIOServiceFunc, NULL, &portIterator);
291
292 if (result == 0) {
293 // Must drain the existing iterator, or we won't receive new notifications
294 while ((entry = IOIteratorNext(portIterator)) != 0) {
295 IOObjectRelease(entry);
296 }
297 } else {
298 IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort);
299 SDL_HIDAPI_discovery.m_notificationPort = nil;
300 }
301 }
302 }
303
304 SDL_HIDAPI_discovery.m_notificationMach = MACH_PORT_NULL;
305 if (SDL_HIDAPI_discovery.m_notificationPort) {
306 SDL_HIDAPI_discovery.m_notificationMach = IONotificationPortGetMachPort(SDL_HIDAPI_discovery.m_notificationPort);
307 }
308
309 SDL_HIDAPI_discovery.m_bCanGetNotifications = (SDL_HIDAPI_discovery.m_notificationMach != MACH_PORT_NULL);
310
311#endif // SDL_PLATFORM_MACOS
312
313#ifdef SDL_USE_LIBUDEV
314 if (linux_enumeration_method == ENUMERATION_LIBUDEV) {
315 SDL_HIDAPI_discovery.m_pUdev = NULL;
316 SDL_HIDAPI_discovery.m_pUdevMonitor = NULL;
317 SDL_HIDAPI_discovery.m_nUdevFd = -1;
318
319 usyms = SDL_UDEV_GetUdevSyms();
320 if (usyms != NULL) {
321 SDL_HIDAPI_discovery.m_pUdev = usyms->udev_new();
322 if (SDL_HIDAPI_discovery.m_pUdev != NULL) {
323 SDL_HIDAPI_discovery.m_pUdevMonitor = usyms->udev_monitor_new_from_netlink(SDL_HIDAPI_discovery.m_pUdev, "udev");
324 if (SDL_HIDAPI_discovery.m_pUdevMonitor != NULL) {
325 usyms->udev_monitor_enable_receiving(SDL_HIDAPI_discovery.m_pUdevMonitor);
326 SDL_HIDAPI_discovery.m_nUdevFd = usyms->udev_monitor_get_fd(SDL_HIDAPI_discovery.m_pUdevMonitor);
327 SDL_HIDAPI_discovery.m_bCanGetNotifications = true;
328 }
329 }
330 }
331 } else
332#endif // SDL_USE_LIBUDEV
333 {
334#ifdef HAVE_INOTIFY
335 inotify_fd = SDL_inotify_init1();
336
337 if (inotify_fd < 0) {
338 SDL_LogWarn(SDL_LOG_CATEGORY_INPUT,
339 "Unable to initialize inotify, falling back to polling: %s",
340 strerror(errno));
341 return;
342 }
343
344 /* We need to watch for attribute changes in addition to
345 * creation, because when a device is first created, it has
346 * permissions that we can't read. When udev chmods it to
347 * something that we maybe *can* read, we'll get an
348 * IN_ATTRIB event to tell us. */
349 if (inotify_add_watch(inotify_fd, "/dev",
350 IN_CREATE | IN_DELETE | IN_MOVE | IN_ATTRIB) < 0) {
351 close(inotify_fd);
352 inotify_fd = -1;
353 SDL_LogWarn(SDL_LOG_CATEGORY_INPUT,
354 "Unable to add inotify watch, falling back to polling: %s",
355 strerror(errno));
356 return;
357 }
358
359 SDL_HIDAPI_discovery.m_bCanGetNotifications = true;
360#endif // HAVE_INOTIFY
361 }
362}
363
364static void HIDAPI_UpdateDiscovery(void)
365{
366 if (!SDL_HIDAPI_discovery.m_bInitialized) {
367 HIDAPI_InitializeDiscovery();
368 }
369
370 if (!SDL_HIDAPI_discovery.m_bCanGetNotifications) {
371 const Uint32 SDL_HIDAPI_DETECT_INTERVAL_MS = 3000; // Update every 3 seconds
372 Uint64 now = SDL_GetTicks();
373 if (!SDL_HIDAPI_discovery.m_unLastDetect || now >= (SDL_HIDAPI_discovery.m_unLastDetect + SDL_HIDAPI_DETECT_INTERVAL_MS)) {
374 ++SDL_HIDAPI_discovery.m_unDeviceChangeCounter;
375 SDL_HIDAPI_discovery.m_unLastDetect = now;
376 }
377 return;
378 }
379
380#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK)
381#if 0 // just let the usual SDL_PumpEvents loop dispatch these, fixing bug 4286. --ryan.
382 // We'll only get messages on the same thread that created the window
383 if (SDL_GetCurrentThreadID() == SDL_HIDAPI_discovery.m_nThreadID) {
384 MSG msg;
385 while (PeekMessage(&msg, SDL_HIDAPI_discovery.m_hwndMsg, 0, 0, PM_NOREMOVE)) {
386 if (GetMessageA(&msg, SDL_HIDAPI_discovery.m_hwndMsg, 0, 0) != 0) {
387 TranslateMessage(&msg);
388 DispatchMessage(&msg);
389 }
390 }
391 }
392#endif
393#endif // defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK)
394
395#ifdef SDL_PLATFORM_MACOS
396 if (SDL_HIDAPI_discovery.m_notificationPort) {
397 struct
398 {
399 mach_msg_header_t hdr;
400 char payload[4096];
401 } msg;
402 while (mach_msg(&msg.hdr, MACH_RCV_MSG | MACH_RCV_TIMEOUT, 0, sizeof(msg), SDL_HIDAPI_discovery.m_notificationMach, 0, MACH_PORT_NULL) == KERN_SUCCESS) {
403 IODispatchCalloutFromMessage(NULL, &msg.hdr, SDL_HIDAPI_discovery.m_notificationPort);
404 }
405 }
406#endif
407
408#ifdef SDL_USE_LIBUDEV
409 if (linux_enumeration_method == ENUMERATION_LIBUDEV) {
410 if (SDL_HIDAPI_discovery.m_nUdevFd >= 0) {
411 /* Drain all notification events.
412 * We don't expect a lot of device notifications so just
413 * do a new discovery on any kind or number of notifications.
414 * This could be made more restrictive if necessary.
415 */
416 for (;;) {
417 struct pollfd PollUdev;
418 struct udev_device *pUdevDevice;
419
420 PollUdev.fd = SDL_HIDAPI_discovery.m_nUdevFd;
421 PollUdev.events = POLLIN;
422 if (poll(&PollUdev, 1, 0) != 1) {
423 break;
424 }
425
426 pUdevDevice = usyms->udev_monitor_receive_device(SDL_HIDAPI_discovery.m_pUdevMonitor);
427 if (pUdevDevice) {
428 const char *action = NULL;
429 action = usyms->udev_device_get_action(pUdevDevice);
430 if (action == NULL || SDL_strcmp(action, "add") == 0 || SDL_strcmp(action, "remove") == 0) {
431 ++SDL_HIDAPI_discovery.m_unDeviceChangeCounter;
432 }
433 usyms->udev_device_unref(pUdevDevice);
434 }
435 }
436 }
437 } else
438#endif // SDL_USE_LIBUDEV
439 {
440#ifdef HAVE_INOTIFY
441 if (inotify_fd >= 0) {
442 union
443 {
444 struct inotify_event event;
445 char storage[4096];
446 char enough_for_inotify[sizeof(struct inotify_event) + NAME_MAX + 1];
447 } buf;
448 ssize_t bytes;
449 size_t remain = 0;
450 size_t len;
451
452 bytes = read(inotify_fd, &buf, sizeof(buf));
453
454 if (bytes > 0) {
455 remain = (size_t)bytes;
456 }
457
458 while (remain > 0) {
459 if (buf.event.len > 0) {
460 if (StrHasPrefix(buf.event.name, "hidraw") &&
461 StrIsInteger(buf.event.name + SDL_strlen("hidraw"))) {
462 ++SDL_HIDAPI_discovery.m_unDeviceChangeCounter;
463 /* We found an hidraw change. We still continue to
464 * drain the inotify fd to avoid leaving old
465 * notifications in the queue. */
466 }
467 }
468
469 len = sizeof(struct inotify_event) + buf.event.len;
470 remain -= len;
471
472 if (remain != 0) {
473 SDL_memmove(&buf.storage[0], &buf.storage[len], remain);
474 }
475 }
476 }
477#endif // HAVE_INOTIFY
478 }
479}
480
481static void HIDAPI_ShutdownDiscovery(void)
482{
483 if (!SDL_HIDAPI_discovery.m_bInitialized) {
484 return;
485 }
486
487#if defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK)
488 if (SDL_HIDAPI_discovery.m_hNotify) {
489 UnregisterDeviceNotification(SDL_HIDAPI_discovery.m_hNotify);
490 }
491
492 if (SDL_HIDAPI_discovery.m_hwndMsg) {
493 DestroyWindow(SDL_HIDAPI_discovery.m_hwndMsg);
494 }
495
496 UnregisterClassA(SDL_HIDAPI_discovery.m_wndClass.lpszClassName, SDL_HIDAPI_discovery.m_wndClass.hInstance);
497#endif
498
499#ifdef SDL_PLATFORM_MACOS
500 if (SDL_HIDAPI_discovery.m_notificationPort) {
501 IONotificationPortDestroy(SDL_HIDAPI_discovery.m_notificationPort);
502 }
503#endif
504
505#ifdef SDL_USE_LIBUDEV
506 if (linux_enumeration_method == ENUMERATION_LIBUDEV) {
507 if (usyms) {
508 if (SDL_HIDAPI_discovery.m_pUdevMonitor) {
509 usyms->udev_monitor_unref(SDL_HIDAPI_discovery.m_pUdevMonitor);
510 }
511 if (SDL_HIDAPI_discovery.m_pUdev) {
512 usyms->udev_unref(SDL_HIDAPI_discovery.m_pUdev);
513 }
514 SDL_UDEV_ReleaseUdevSyms();
515 usyms = NULL;
516 }
517 } else
518#endif // SDL_USE_LIBUDEV
519 {
520#ifdef HAVE_INOTIFY
521 if (inotify_fd >= 0) {
522 close(inotify_fd);
523 inotify_fd = -1;
524 }
525#endif
526 }
527
528 SDL_HIDAPI_discovery.m_bInitialized = false;
529}
530
531// Platform HIDAPI Implementation
532
533#define HIDAPI_USING_SDL_RUNTIME
534#define HIDAPI_IGNORE_DEVICE(BUS, VID, PID, USAGE_PAGE, USAGE) \
535 SDL_HIDAPI_ShouldIgnoreDevice(BUS, VID, PID, USAGE_PAGE, USAGE)
536
537struct PLATFORM_hid_device_;
538typedef struct PLATFORM_hid_device_ PLATFORM_hid_device;
539
540#define api_version PLATFORM_api_version
541#define create_device_info_for_device PLATFORM_create_device_info_for_device
542#define free_hid_device PLATFORM_free_hid_device
543#define hid_close PLATFORM_hid_close
544#define hid_device PLATFORM_hid_device
545#define hid_device_ PLATFORM_hid_device_
546#define hid_enumerate PLATFORM_hid_enumerate
547#define hid_error PLATFORM_hid_error
548#define hid_exit PLATFORM_hid_exit
549#define hid_free_enumeration PLATFORM_hid_free_enumeration
550#define hid_get_device_info PLATFORM_hid_get_device_info
551#define hid_get_feature_report PLATFORM_hid_get_feature_report
552#define hid_get_indexed_string PLATFORM_hid_get_indexed_string
553#define hid_get_input_report PLATFORM_hid_get_input_report
554#define hid_get_manufacturer_string PLATFORM_hid_get_manufacturer_string
555#define hid_get_product_string PLATFORM_hid_get_product_string
556#define hid_get_report_descriptor PLATFORM_hid_get_report_descriptor
557#define hid_get_serial_number_string PLATFORM_hid_get_serial_number_string
558#define hid_init PLATFORM_hid_init
559#define hid_open_path PLATFORM_hid_open_path
560#define hid_open PLATFORM_hid_open
561#define hid_read PLATFORM_hid_read
562#define hid_read_timeout PLATFORM_hid_read_timeout
563#define hid_send_feature_report PLATFORM_hid_send_feature_report
564#define hid_set_nonblocking PLATFORM_hid_set_nonblocking
565#define hid_version PLATFORM_hid_version
566#define hid_version_str PLATFORM_hid_version_str
567#define hid_write PLATFORM_hid_write
568#define input_report PLATFORM_input_report
569#define make_path PLATFORM_make_path
570#define new_hid_device PLATFORM_new_hid_device
571#define read_thread PLATFORM_read_thread
572#define return_data PLATFORM_return_data
573
574#ifdef SDL_PLATFORM_LINUX
575#include "SDL_hidapi_linux.h"
576#elif defined(SDL_PLATFORM_NETBSD)
577#include "SDL_hidapi_netbsd.h"
578#elif defined(SDL_PLATFORM_MACOS)
579#include "SDL_hidapi_mac.h"
580#elif defined(SDL_PLATFORM_WIN32) || defined(SDL_PLATFORM_WINGDK)
581#include "SDL_hidapi_windows.h"
582#elif defined(SDL_PLATFORM_ANDROID)
583#include "SDL_hidapi_android.h"
584#elif defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_TVOS)
585#include "SDL_hidapi_ios.h"
586#endif
587
588#undef api_version
589#undef create_device_info_for_device
590#undef free_hid_device
591#undef hid_close
592#undef hid_device
593#undef hid_device_
594#undef hid_enumerate
595#undef hid_error
596#undef hid_exit
597#undef hid_free_enumeration
598#undef hid_get_device_info
599#undef hid_get_feature_report
600#undef hid_get_indexed_string
601#undef hid_get_input_report
602#undef hid_get_manufacturer_string
603#undef hid_get_product_string
604#undef hid_get_report_descriptor
605#undef hid_get_serial_number_string
606#undef hid_init
607#undef hid_open
608#undef hid_open_path
609#undef hid_read
610#undef hid_read_timeout
611#undef hid_send_feature_report
612#undef hid_set_nonblocking
613#undef hid_version
614#undef hid_version_str
615#undef hid_write
616#undef input_report
617#undef make_path
618#undef new_hid_device
619#undef read_thread
620#undef return_data
621
622#ifdef SDL_JOYSTICK_HIDAPI_STEAMXBOX
623#define HAVE_DRIVER_BACKEND 1
624#endif
625
626#ifdef HAVE_DRIVER_BACKEND
627
628// DRIVER HIDAPI Implementation
629
630struct DRIVER_hid_device_;
631typedef struct DRIVER_hid_device_ DRIVER_hid_device;
632
633#define hid_close DRIVER_hid_close
634#define hid_device DRIVER_hid_device
635#define hid_device_ DRIVER_hid_device_
636#define hid_enumerate DRIVER_hid_enumerate
637#define hid_error DRIVER_hid_error
638#define hid_exit DRIVER_hid_exit
639#define hid_free_enumeration DRIVER_hid_free_enumeration
640#define hid_get_device_info DRIVER_hid_get_device_info
641#define hid_get_feature_report DRIVER_hid_get_feature_report
642#define hid_get_indexed_string DRIVER_hid_get_indexed_string
643#define hid_get_input_report DRIVER_hid_get_input_report
644#define hid_get_manufacturer_string DRIVER_hid_get_manufacturer_string
645#define hid_get_product_string DRIVER_hid_get_product_string
646#define hid_get_report_descriptor DRIVER_hid_get_report_descriptor
647#define hid_get_serial_number_string DRIVER_hid_get_serial_number_string
648#define hid_init DRIVER_hid_init
649#define hid_open DRIVER_hid_open
650#define hid_open_path DRIVER_hid_open_path
651#define hid_read DRIVER_hid_read
652#define hid_read_timeout DRIVER_hid_read_timeout
653#define hid_send_feature_report DRIVER_hid_send_feature_report
654#define hid_set_nonblocking DRIVER_hid_set_nonblocking
655#define hid_write DRIVER_hid_write
656
657#ifdef SDL_JOYSTICK_HIDAPI_STEAMXBOX
658#include "SDL_hidapi_steamxbox.h"
659#else
660#error Need a driver hid.c for this platform!
661#endif
662
663#undef hid_close
664#undef hid_device
665#undef hid_device_
666#undef hid_enumerate
667#undef hid_error
668#undef hid_exit
669#undef hid_free_enumeration
670#undef hid_get_device_info
671#undef hid_get_feature_report
672#undef hid_get_indexed_string
673#undef hid_get_input_report
674#undef hid_get_manufacturer_string
675#undef hid_get_product_string
676#undef hid_get_report_descriptor
677#undef hid_get_serial_number_string
678#undef hid_init
679#undef hid_open
680#undef hid_open_path
681#undef hid_read
682#undef hid_read_timeout
683#undef hid_send_feature_report
684#undef hid_set_nonblocking
685#undef hid_write
686
687#endif // HAVE_DRIVER_BACKEND
688
689#ifdef HAVE_LIBUSB
690// libusb HIDAPI Implementation
691
692// Include this now, for our dynamically-loaded libusb context
693#include <libusb.h>
694
695static struct
696{
697 SDL_SharedObject *libhandle;
698
699 /* *INDENT-OFF* */ // clang-format off
700 int (LIBUSB_CALL *init)(libusb_context **ctx);
701 void (LIBUSB_CALL *exit)(libusb_context *ctx);
702 ssize_t (LIBUSB_CALL *get_device_list)(libusb_context *ctx, libusb_device ***list);
703 void (LIBUSB_CALL *free_device_list)(libusb_device **list, int unref_devices);
704 int (LIBUSB_CALL *get_device_descriptor)(libusb_device *dev, struct libusb_device_descriptor *desc);
705 int (LIBUSB_CALL *get_active_config_descriptor)(libusb_device *dev, struct libusb_config_descriptor **config);
706 int (LIBUSB_CALL *get_config_descriptor)(
707 libusb_device *dev,
708 uint8_t config_index,
709 struct libusb_config_descriptor **config
710 );
711 void (LIBUSB_CALL *free_config_descriptor)(struct libusb_config_descriptor *config);
712 uint8_t (LIBUSB_CALL *get_bus_number)(libusb_device *dev);
713 int (LIBUSB_CALL *get_port_numbers)(libusb_device *dev, uint8_t *port_numbers, int port_numbers_len);
714 uint8_t (LIBUSB_CALL *get_device_address)(libusb_device *dev);
715 int (LIBUSB_CALL *open)(libusb_device *dev, libusb_device_handle **dev_handle);
716 void (LIBUSB_CALL *close)(libusb_device_handle *dev_handle);
717 libusb_device *(LIBUSB_CALL *get_device)(libusb_device_handle *dev_handle);
718 int (LIBUSB_CALL *claim_interface)(libusb_device_handle *dev_handle, int interface_number);
719 int (LIBUSB_CALL *release_interface)(libusb_device_handle *dev_handle, int interface_number);
720 int (LIBUSB_CALL *kernel_driver_active)(libusb_device_handle *dev_handle, int interface_number);
721 int (LIBUSB_CALL *detach_kernel_driver)(libusb_device_handle *dev_handle, int interface_number);
722 int (LIBUSB_CALL *attach_kernel_driver)(libusb_device_handle *dev_handle, int interface_number);
723 int (LIBUSB_CALL *set_interface_alt_setting)(libusb_device_handle *dev, int interface_number, int alternate_setting);
724 struct libusb_transfer * (LIBUSB_CALL *alloc_transfer)(int iso_packets);
725 int (LIBUSB_CALL *submit_transfer)(struct libusb_transfer *transfer);
726 int (LIBUSB_CALL *cancel_transfer)(struct libusb_transfer *transfer);
727 void (LIBUSB_CALL *free_transfer)(struct libusb_transfer *transfer);
728 int (LIBUSB_CALL *control_transfer)(
729 libusb_device_handle *dev_handle,
730 uint8_t request_type,
731 uint8_t bRequest,
732 uint16_t wValue,
733 uint16_t wIndex,
734 unsigned char *data,
735 uint16_t wLength,
736 unsigned int timeout
737 );
738 int (LIBUSB_CALL *interrupt_transfer)(
739 libusb_device_handle *dev_handle,
740 unsigned char endpoint,
741 unsigned char *data,
742 int length,
743 int *actual_length,
744 unsigned int timeout
745 );
746 int (LIBUSB_CALL *handle_events)(libusb_context *ctx);
747 int (LIBUSB_CALL *handle_events_completed)(libusb_context *ctx, int *completed);
748 const char * (LIBUSB_CALL *error_name)(int errcode);
749/* *INDENT-ON* */ // clang-format on
750
751} libusb_ctx;
752
753#define libusb_init libusb_ctx.init
754#define libusb_exit libusb_ctx.exit
755#define libusb_get_device_list libusb_ctx.get_device_list
756#define libusb_free_device_list libusb_ctx.free_device_list
757#define libusb_get_device_descriptor libusb_ctx.get_device_descriptor
758#define libusb_get_active_config_descriptor libusb_ctx.get_active_config_descriptor
759#define libusb_get_config_descriptor libusb_ctx.get_config_descriptor
760#define libusb_free_config_descriptor libusb_ctx.free_config_descriptor
761#define libusb_get_bus_number libusb_ctx.get_bus_number
762#define libusb_get_port_numbers libusb_ctx.get_port_numbers
763#define libusb_get_device_address libusb_ctx.get_device_address
764#define libusb_open libusb_ctx.open
765#define libusb_close libusb_ctx.close
766#define libusb_get_device libusb_ctx.get_device
767#define libusb_claim_interface libusb_ctx.claim_interface
768#define libusb_release_interface libusb_ctx.release_interface
769#define libusb_kernel_driver_active libusb_ctx.kernel_driver_active
770#define libusb_detach_kernel_driver libusb_ctx.detach_kernel_driver
771#define libusb_attach_kernel_driver libusb_ctx.attach_kernel_driver
772#define libusb_set_interface_alt_setting libusb_ctx.set_interface_alt_setting
773#define libusb_alloc_transfer libusb_ctx.alloc_transfer
774#define libusb_submit_transfer libusb_ctx.submit_transfer
775#define libusb_cancel_transfer libusb_ctx.cancel_transfer
776#define libusb_free_transfer libusb_ctx.free_transfer
777#define libusb_control_transfer libusb_ctx.control_transfer
778#define libusb_interrupt_transfer libusb_ctx.interrupt_transfer
779#define libusb_handle_events libusb_ctx.handle_events
780#define libusb_handle_events_completed libusb_ctx.handle_events_completed
781#define libusb_error_name libusb_ctx.error_name
782
783struct LIBUSB_hid_device_;
784typedef struct LIBUSB_hid_device_ LIBUSB_hid_device;
785
786#define free_hid_device LIBUSB_free_hid_device
787#define hid_close LIBUSB_hid_close
788#define hid_device LIBUSB_hid_device
789#define hid_device_ LIBUSB_hid_device_
790#define hid_enumerate LIBUSB_hid_enumerate
791#define hid_error LIBUSB_hid_error
792#define hid_exit LIBUSB_hid_exit
793#define hid_free_enumeration LIBUSB_hid_free_enumeration
794#define hid_get_device_info LIBUSB_hid_get_device_info
795#define hid_get_feature_report LIBUSB_hid_get_feature_report
796#define hid_get_indexed_string LIBUSB_hid_get_indexed_string
797#define hid_get_input_report LIBUSB_hid_get_input_report
798#define hid_get_manufacturer_string LIBUSB_hid_get_manufacturer_string
799#define hid_get_product_string LIBUSB_hid_get_product_string
800#define hid_get_report_descriptor LIBUSB_hid_get_report_descriptor
801#define hid_get_serial_number_string LIBUSB_hid_get_serial_number_string
802#define hid_init LIBUSB_hid_init
803#define hid_open LIBUSB_hid_open
804#define hid_open_path LIBUSB_hid_open_path
805#define hid_read LIBUSB_hid_read
806#define hid_read_timeout LIBUSB_hid_read_timeout
807#define hid_send_feature_report LIBUSB_hid_send_feature_report
808#define hid_set_nonblocking LIBUSB_hid_set_nonblocking
809#define hid_write LIBUSB_hid_write
810#define input_report LIBUSB_input_report
811#define make_path LIBUSB_make_path
812#define new_hid_device LIBUSB_new_hid_device
813#define read_thread LIBUSB_read_thread
814#define return_data LIBUSB_return_data
815
816#include "SDL_hidapi_libusb.h"
817
818#undef libusb_init
819#undef libusb_exit
820#undef libusb_get_device_list
821#undef libusb_free_device_list
822#undef libusb_get_device_descriptor
823#undef libusb_get_active_config_descriptor
824#undef libusb_get_config_descriptor
825#undef libusb_free_config_descriptor
826#undef libusb_get_bus_number
827#undef libusb_get_port_numbers
828#undef libusb_get_device_address
829#undef libusb_open
830#undef libusb_close
831#undef libusb_get_device
832#undef libusb_claim_interface
833#undef libusb_release_interface
834#undef libusb_kernel_driver_active
835#undef libusb_detach_kernel_driver
836#undef libusb_attach_kernel_driver
837#undef libusb_set_interface_alt_setting
838#undef libusb_alloc_transfer
839#undef libusb_submit_transfer
840#undef libusb_cancel_transfer
841#undef libusb_free_transfer
842#undef libusb_control_transfer
843#undef libusb_interrupt_transfer
844#undef libusb_handle_events
845#undef libusb_handle_events_completed
846#undef libusb_error_name
847
848#undef free_hid_device
849#undef hid_close
850#undef hid_device
851#undef hid_device_
852#undef hid_enumerate
853#undef hid_error
854#undef hid_exit
855#undef hid_free_enumeration
856#undef hid_get_device_info
857#undef hid_get_feature_report
858#undef hid_get_indexed_string
859#undef hid_get_input_report
860#undef hid_get_manufacturer_string
861#undef hid_get_product_string
862#undef hid_get_report_descriptor
863#undef hid_get_serial_number_string
864#undef hid_init
865#undef hid_open
866#undef hid_open_path
867#undef hid_read
868#undef hid_read_timeout
869#undef hid_send_feature_report
870#undef hid_set_nonblocking
871#undef hid_write
872#undef input_report
873#undef make_path
874#undef new_hid_device
875#undef read_thread
876#undef return_data
877
878/* If the platform has any backend other than libusb, try to avoid using
879 * libusb as the main backend for devices, since it detaches drivers and
880 * therefore makes devices inaccessible to the rest of the OS.
881 *
882 * We do this by whitelisting devices we know to be accessible _exclusively_
883 * via libusb; these are typically devices that look like HIDs but have a
884 * quirk that requires direct access to the hardware.
885 */
886static const struct {
887 Uint16 vendor;
888 Uint16 product;
889} SDL_libusb_whitelist[] = {
890 { 0x057e, 0x0337 } // Nintendo WUP-028, Wii U/Switch GameCube Adapter
891};
892
893static bool IsInWhitelist(Uint16 vendor, Uint16 product)
894{
895 int i;
896 for (i = 0; i < SDL_arraysize(SDL_libusb_whitelist); i += 1) {
897 if (vendor == SDL_libusb_whitelist[i].vendor &&
898 product == SDL_libusb_whitelist[i].product) {
899 return true;
900 }
901 }
902 return false;
903}
904
905#endif // HAVE_LIBUSB
906
907#endif // !SDL_HIDAPI_DISABLED
908
909#if defined(HAVE_PLATFORM_BACKEND) || defined(HAVE_DRIVER_BACKEND)
910// We have another way to get HID devices, so use the whitelist to get devices where libusb is preferred
911#define SDL_HINT_HIDAPI_LIBUSB_WHITELIST_DEFAULT true
912#else
913// libusb is the only way to get HID devices, so don't use the whitelist, get them all
914#define SDL_HINT_HIDAPI_LIBUSB_WHITELIST_DEFAULT false
915#endif // HAVE_PLATFORM_BACKEND || HAVE_DRIVER_BACKEND
916
917static bool use_libusb_whitelist = SDL_HINT_HIDAPI_LIBUSB_WHITELIST_DEFAULT;
918
919// Shared HIDAPI Implementation
920
921struct hidapi_backend
922{
923 int (*hid_write)(void *device, const unsigned char *data, size_t length);
924 int (*hid_read_timeout)(void *device, unsigned char *data, size_t length, int milliseconds);
925 int (*hid_read)(void *device, unsigned char *data, size_t length);
926 int (*hid_set_nonblocking)(void *device, int nonblock);
927 int (*hid_send_feature_report)(void *device, const unsigned char *data, size_t length);
928 int (*hid_get_feature_report)(void *device, unsigned char *data, size_t length);
929 int (*hid_get_input_report)(void *device, unsigned char *data, size_t length);
930 void (*hid_close)(void *device);
931 int (*hid_get_manufacturer_string)(void *device, wchar_t *string, size_t maxlen);
932 int (*hid_get_product_string)(void *device, wchar_t *string, size_t maxlen);
933 int (*hid_get_serial_number_string)(void *device, wchar_t *string, size_t maxlen);
934 int (*hid_get_indexed_string)(void *device, int string_index, wchar_t *string, size_t maxlen);
935 struct hid_device_info *(*hid_get_device_info)(void *device);
936 int (*hid_get_report_descriptor)(void *device, unsigned char *buf, size_t buf_size);
937 const wchar_t *(*hid_error)(void *device);
938};
939
940#ifdef HAVE_PLATFORM_BACKEND
941static const struct hidapi_backend PLATFORM_Backend = {
942 (void *)PLATFORM_hid_write,
943 (void *)PLATFORM_hid_read_timeout,
944 (void *)PLATFORM_hid_read,
945 (void *)PLATFORM_hid_set_nonblocking,
946 (void *)PLATFORM_hid_send_feature_report,
947 (void *)PLATFORM_hid_get_feature_report,
948 (void *)PLATFORM_hid_get_input_report,
949 (void *)PLATFORM_hid_close,
950 (void *)PLATFORM_hid_get_manufacturer_string,
951 (void *)PLATFORM_hid_get_product_string,
952 (void *)PLATFORM_hid_get_serial_number_string,
953 (void *)PLATFORM_hid_get_indexed_string,
954 (void *)PLATFORM_hid_get_device_info,
955 (void *)PLATFORM_hid_get_report_descriptor,
956 (void *)PLATFORM_hid_error
957};
958#endif // HAVE_PLATFORM_BACKEND
959
960#ifdef HAVE_DRIVER_BACKEND
961static const struct hidapi_backend DRIVER_Backend = {
962 (void *)DRIVER_hid_write,
963 (void *)DRIVER_hid_read_timeout,
964 (void *)DRIVER_hid_read,
965 (void *)DRIVER_hid_set_nonblocking,
966 (void *)DRIVER_hid_send_feature_report,
967 (void *)DRIVER_hid_get_feature_report,
968 (void *)DRIVER_hid_get_input_report,
969 (void *)DRIVER_hid_close,
970 (void *)DRIVER_hid_get_manufacturer_string,
971 (void *)DRIVER_hid_get_product_string,
972 (void *)DRIVER_hid_get_serial_number_string,
973 (void *)DRIVER_hid_get_indexed_string,
974 (void *)DRIVER_hid_get_device_info,
975 (void *)DRIVER_hid_get_report_descriptor,
976 (void *)DRIVER_hid_error
977};
978#endif // HAVE_DRIVER_BACKEND
979
980#ifdef HAVE_LIBUSB
981static const struct hidapi_backend LIBUSB_Backend = {
982 (void *)LIBUSB_hid_write,
983 (void *)LIBUSB_hid_read_timeout,
984 (void *)LIBUSB_hid_read,
985 (void *)LIBUSB_hid_set_nonblocking,
986 (void *)LIBUSB_hid_send_feature_report,
987 (void *)LIBUSB_hid_get_feature_report,
988 (void *)LIBUSB_hid_get_input_report,
989 (void *)LIBUSB_hid_close,
990 (void *)LIBUSB_hid_get_manufacturer_string,
991 (void *)LIBUSB_hid_get_product_string,
992 (void *)LIBUSB_hid_get_serial_number_string,
993 (void *)LIBUSB_hid_get_indexed_string,
994 (void *)LIBUSB_hid_get_device_info,
995 (void *)LIBUSB_hid_get_report_descriptor,
996 (void *)LIBUSB_hid_error
997};
998#endif // HAVE_LIBUSB
999
1000struct SDL_hid_device
1001{
1002 void *device;
1003 const struct hidapi_backend *backend;
1004 SDL_hid_device_info info;
1005};
1006
1007#if defined(HAVE_PLATFORM_BACKEND) || defined(HAVE_DRIVER_BACKEND) || defined(HAVE_LIBUSB)
1008
1009static SDL_hid_device *CreateHIDDeviceWrapper(void *device, const struct hidapi_backend *backend)
1010{
1011 SDL_hid_device *wrapper = (SDL_hid_device *)SDL_malloc(sizeof(*wrapper));
1012 SDL_SetObjectValid(wrapper, SDL_OBJECT_TYPE_HIDAPI_DEVICE, true);
1013 wrapper->device = device;
1014 wrapper->backend = backend;
1015 SDL_zero(wrapper->info);
1016 return wrapper;
1017}
1018
1019#endif // HAVE_PLATFORM_BACKEND || HAVE_DRIVER_BACKEND || HAVE_LIBUSB
1020
1021static void DeleteHIDDeviceWrapper(SDL_hid_device *wrapper)
1022{
1023 SDL_SetObjectValid(wrapper, SDL_OBJECT_TYPE_HIDAPI_DEVICE, false);
1024 SDL_free(wrapper->info.path);
1025 SDL_free(wrapper->info.serial_number);
1026 SDL_free(wrapper->info.manufacturer_string);
1027 SDL_free(wrapper->info.product_string);
1028 SDL_free(wrapper);
1029}
1030
1031#define CHECK_DEVICE_MAGIC(device, result) \
1032 if (!SDL_ObjectValid(device, SDL_OBJECT_TYPE_HIDAPI_DEVICE)) { \
1033 SDL_SetError("Invalid device"); \
1034 return result; \
1035 }
1036
1037#define COPY_IF_EXISTS(var) \
1038 if (pSrc->var != NULL) { \
1039 pDst->var = SDL_strdup(pSrc->var); \
1040 } else { \
1041 pDst->var = NULL; \
1042 }
1043#define WCOPY_IF_EXISTS(var) \
1044 if (pSrc->var != NULL) { \
1045 pDst->var = SDL_wcsdup(pSrc->var); \
1046 } else { \
1047 pDst->var = NULL; \
1048 }
1049
1050static void CopyHIDDeviceInfo(struct hid_device_info *pSrc, struct SDL_hid_device_info *pDst)
1051{
1052 COPY_IF_EXISTS(path)
1053 pDst->vendor_id = pSrc->vendor_id;
1054 pDst->product_id = pSrc->product_id;
1055 WCOPY_IF_EXISTS(serial_number)
1056 pDst->release_number = pSrc->release_number;
1057 WCOPY_IF_EXISTS(manufacturer_string)
1058 WCOPY_IF_EXISTS(product_string)
1059 pDst->usage_page = pSrc->usage_page;
1060 pDst->usage = pSrc->usage;
1061 pDst->interface_number = pSrc->interface_number;
1062 pDst->interface_class = pSrc->interface_class;
1063 pDst->interface_subclass = pSrc->interface_subclass;
1064 pDst->interface_protocol = pSrc->interface_protocol;
1065 pDst->bus_type = (SDL_hid_bus_type)pSrc->bus_type;
1066 pDst->next = NULL;
1067}
1068
1069#undef COPY_IF_EXISTS
1070#undef WCOPY_IF_EXISTS
1071
1072static int SDL_hidapi_refcount = 0;
1073static bool SDL_hidapi_only_controllers;
1074static char *SDL_hidapi_ignored_devices = NULL;
1075
1076static void SDLCALL OnlyControllersChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
1077{
1078 SDL_hidapi_only_controllers = SDL_GetStringBoolean(hint, true);
1079}
1080
1081static void SDLCALL IgnoredDevicesChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
1082{
1083 if (SDL_hidapi_ignored_devices) {
1084 SDL_free(SDL_hidapi_ignored_devices);
1085 }
1086 if (hint && *hint) {
1087 SDL_hidapi_ignored_devices = SDL_strdup(hint);
1088 } else {
1089 SDL_hidapi_ignored_devices = NULL;
1090 }
1091}
1092
1093bool SDL_HIDAPI_ShouldIgnoreDevice(int bus, Uint16 vendor_id, Uint16 product_id, Uint16 usage_page, Uint16 usage)
1094{
1095 // See if there are any devices we should skip in enumeration
1096 if (SDL_hidapi_only_controllers && usage_page) {
1097 if (vendor_id == USB_VENDOR_VALVE) {
1098 // Ignore the mouse/keyboard interface on Steam Controllers
1099 if (
1100#ifdef SDL_PLATFORM_WIN32
1101 // Check the usage page and usage on both USB and Bluetooth
1102#else
1103 // Only check the usage page and usage on USB
1104 bus == HID_API_BUS_USB &&
1105#endif
1106 usage_page == USB_USAGEPAGE_GENERIC_DESKTOP &&
1107 (usage == USB_USAGE_GENERIC_KEYBOARD || usage == USB_USAGE_GENERIC_MOUSE)) {
1108 return true;
1109 }
1110 } else if (usage_page == USB_USAGEPAGE_GENERIC_DESKTOP &&
1111 (usage == USB_USAGE_GENERIC_JOYSTICK || usage == USB_USAGE_GENERIC_GAMEPAD || usage == USB_USAGE_GENERIC_MULTIAXISCONTROLLER)) {
1112 // This is a controller
1113 } else {
1114 return true;
1115 }
1116 }
1117 if (SDL_hidapi_ignored_devices) {
1118 char vendor_match[16], product_match[16];
1119 SDL_snprintf(vendor_match, sizeof(vendor_match), "0x%.4x/0x0000", vendor_id);
1120 SDL_snprintf(product_match, sizeof(product_match), "0x%.4x/0x%.4x", vendor_id, product_id);
1121 if (SDL_strcasestr(SDL_hidapi_ignored_devices, vendor_match) ||
1122 SDL_strcasestr(SDL_hidapi_ignored_devices, product_match)) {
1123 return true;
1124 }
1125 }
1126 return false;
1127}
1128
1129int SDL_hid_init(void)
1130{
1131 int attempts = 0, success = 0;
1132
1133 if (SDL_hidapi_refcount > 0) {
1134 ++SDL_hidapi_refcount;
1135 return 0;
1136 }
1137
1138 SDL_AddHintCallback(SDL_HINT_HIDAPI_ENUMERATE_ONLY_CONTROLLERS, OnlyControllersChanged, NULL);
1139 SDL_AddHintCallback(SDL_HINT_HIDAPI_IGNORE_DEVICES, IgnoredDevicesChanged, NULL);
1140
1141#ifdef SDL_USE_LIBUDEV
1142 if (!SDL_GetHintBoolean(SDL_HINT_HIDAPI_UDEV, true)) {
1143 SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
1144 "udev disabled by SDL_HINT_HIDAPI_UDEV");
1145 linux_enumeration_method = ENUMERATION_FALLBACK;
1146 } else if (SDL_GetSandbox() != SDL_SANDBOX_NONE) {
1147 SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
1148 "Container detected, disabling HIDAPI udev integration");
1149 linux_enumeration_method = ENUMERATION_FALLBACK;
1150 } else {
1151 SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
1152 "Using udev for HIDAPI joystick device discovery");
1153 linux_enumeration_method = ENUMERATION_LIBUDEV;
1154 }
1155#endif
1156
1157 use_libusb_whitelist = SDL_GetHintBoolean(SDL_HINT_HIDAPI_LIBUSB_WHITELIST,
1158 SDL_HINT_HIDAPI_LIBUSB_WHITELIST_DEFAULT);
1159#ifdef HAVE_LIBUSB
1160 if (!SDL_GetHintBoolean(SDL_HINT_HIDAPI_LIBUSB, true)) {
1161 SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
1162 "libusb disabled with SDL_HINT_HIDAPI_LIBUSB");
1163 libusb_ctx.libhandle = NULL;
1164 } else {
1165 ++attempts;
1166#ifdef SDL_LIBUSB_DYNAMIC
1167 libusb_ctx.libhandle = SDL_LoadObject(SDL_LIBUSB_DYNAMIC);
1168#else
1169 libusb_ctx.libhandle = (void *)1;
1170#endif
1171 if (libusb_ctx.libhandle != NULL) {
1172 bool loaded = true;
1173#ifdef SDL_LIBUSB_DYNAMIC
1174#define LOAD_LIBUSB_SYMBOL(type, func) \
1175 if (!(libusb_ctx.func = (type)SDL_LoadFunction(libusb_ctx.libhandle, "libusb_" #func))) { \
1176 loaded = false; \
1177 }
1178#else
1179#define LOAD_LIBUSB_SYMBOL(type, func) \
1180 libusb_ctx.func = libusb_##func;
1181#endif
1182 LOAD_LIBUSB_SYMBOL(int (LIBUSB_CALL *)(libusb_context **), init)
1183 LOAD_LIBUSB_SYMBOL(void (LIBUSB_CALL *)(libusb_context *), exit)
1184 LOAD_LIBUSB_SYMBOL(ssize_t (LIBUSB_CALL *)(libusb_context *, libusb_device ***), get_device_list)
1185 LOAD_LIBUSB_SYMBOL(void (LIBUSB_CALL *)(libusb_device **, int), free_device_list)
1186 LOAD_LIBUSB_SYMBOL(int (LIBUSB_CALL *)(libusb_device *, struct libusb_device_descriptor *), get_device_descriptor)
1187 LOAD_LIBUSB_SYMBOL(int (LIBUSB_CALL *)(libusb_device *, struct libusb_config_descriptor **), get_active_config_descriptor)
1188 LOAD_LIBUSB_SYMBOL(int (LIBUSB_CALL *)(libusb_device *, uint8_t, struct libusb_config_descriptor **), get_config_descriptor)
1189 LOAD_LIBUSB_SYMBOL(void (LIBUSB_CALL *)(struct libusb_config_descriptor *), free_config_descriptor)
1190 LOAD_LIBUSB_SYMBOL(uint8_t (LIBUSB_CALL *)(libusb_device *), get_bus_number)
1191 LOAD_LIBUSB_SYMBOL(int (LIBUSB_CALL *)(libusb_device *dev, uint8_t *port_numbers, int port_numbers_len), get_port_numbers)
1192 LOAD_LIBUSB_SYMBOL(uint8_t (LIBUSB_CALL *)(libusb_device *), get_device_address)
1193 LOAD_LIBUSB_SYMBOL(int (LIBUSB_CALL *)(libusb_device *, libusb_device_handle **), open)
1194 LOAD_LIBUSB_SYMBOL(void (LIBUSB_CALL *)(libusb_device_handle *), close)
1195 LOAD_LIBUSB_SYMBOL(libusb_device * (LIBUSB_CALL *)(libusb_device_handle *dev_handle), get_device)
1196 LOAD_LIBUSB_SYMBOL(int (LIBUSB_CALL *)(libusb_device_handle *, int), claim_interface)
1197 LOAD_LIBUSB_SYMBOL(int (LIBUSB_CALL *)(libusb_device_handle *, int), release_interface)
1198 LOAD_LIBUSB_SYMBOL(int (LIBUSB_CALL *)(libusb_device_handle *, int), kernel_driver_active)
1199 LOAD_LIBUSB_SYMBOL(int (LIBUSB_CALL *)(libusb_device_handle *, int), detach_kernel_driver)
1200 LOAD_LIBUSB_SYMBOL(int (LIBUSB_CALL *)(libusb_device_handle *, int), attach_kernel_driver)
1201 LOAD_LIBUSB_SYMBOL(int (LIBUSB_CALL *)(libusb_device_handle *, int, int), set_interface_alt_setting)
1202 LOAD_LIBUSB_SYMBOL(struct libusb_transfer * (LIBUSB_CALL *)(int), alloc_transfer)
1203 LOAD_LIBUSB_SYMBOL(int (LIBUSB_CALL *)(struct libusb_transfer *), submit_transfer)
1204 LOAD_LIBUSB_SYMBOL(int (LIBUSB_CALL *)(struct libusb_transfer *), cancel_transfer)
1205 LOAD_LIBUSB_SYMBOL(void (LIBUSB_CALL *)(struct libusb_transfer *), free_transfer)
1206 LOAD_LIBUSB_SYMBOL(int (LIBUSB_CALL *)(libusb_device_handle *, uint8_t, uint8_t, uint16_t, uint16_t, unsigned char *, uint16_t, unsigned int), control_transfer)
1207 LOAD_LIBUSB_SYMBOL(int (LIBUSB_CALL *)(libusb_device_handle *, unsigned char, unsigned char *, int, int *, unsigned int), interrupt_transfer)
1208 LOAD_LIBUSB_SYMBOL(int (LIBUSB_CALL *)(libusb_context *), handle_events)
1209 LOAD_LIBUSB_SYMBOL(int (LIBUSB_CALL *)(libusb_context *, int *), handle_events_completed)
1210 LOAD_LIBUSB_SYMBOL(const char * (LIBUSB_CALL *)(int), error_name)
1211#undef LOAD_LIBUSB_SYMBOL
1212
1213 if (!loaded) {
1214#ifdef SDL_LIBUSB_DYNAMIC
1215 SDL_UnloadObject(libusb_ctx.libhandle);
1216#endif
1217 libusb_ctx.libhandle = NULL;
1218 // SDL_LogWarn(SDL_LOG_CATEGORY_INPUT, SDL_LIBUSB_DYNAMIC " found but could not load function");
1219 } else if (LIBUSB_hid_init() < 0) {
1220#ifdef SDL_LIBUSB_DYNAMIC
1221 SDL_UnloadObject(libusb_ctx.libhandle);
1222#endif
1223 libusb_ctx.libhandle = NULL;
1224 } else {
1225 ++success;
1226 }
1227 }
1228 }
1229#endif // HAVE_LIBUSB
1230
1231#ifdef HAVE_PLATFORM_BACKEND
1232 ++attempts;
1233#ifdef SDL_PLATFORM_LINUX
1234 udev_ctx = SDL_UDEV_GetUdevSyms();
1235#endif // __LINUX __
1236 if (udev_ctx && PLATFORM_hid_init() == 0) {
1237 ++success;
1238 }
1239#endif // HAVE_PLATFORM_BACKEND
1240
1241 if (attempts > 0 && success == 0) {
1242 return -1;
1243 }
1244
1245#if defined(SDL_PLATFORM_MACOS) && !defined(SDL_HIDAPI_DISABLED)
1246 hid_darwin_set_open_exclusive(0);
1247#endif
1248
1249 ++SDL_hidapi_refcount;
1250 return 0;
1251}
1252
1253int SDL_hid_exit(void)
1254{
1255 int result = 0;
1256
1257 if (SDL_hidapi_refcount == 0) {
1258 return 0;
1259 }
1260 --SDL_hidapi_refcount;
1261 if (SDL_hidapi_refcount > 0) {
1262 return 0;
1263 }
1264 SDL_hidapi_refcount = 0;
1265
1266#ifndef SDL_HIDAPI_DISABLED
1267 HIDAPI_ShutdownDiscovery();
1268#endif
1269
1270#ifdef HAVE_PLATFORM_BACKEND
1271 if (udev_ctx) {
1272 result |= PLATFORM_hid_exit();
1273 }
1274#ifdef SDL_PLATFORM_LINUX
1275 SDL_UDEV_ReleaseUdevSyms();
1276#endif // __LINUX __
1277#endif // HAVE_PLATFORM_BACKEND
1278
1279#ifdef HAVE_LIBUSB
1280 if (libusb_ctx.libhandle) {
1281 result |= LIBUSB_hid_exit();
1282#ifdef SDL_LIBUSB_DYNAMIC
1283 SDL_UnloadObject(libusb_ctx.libhandle);
1284#endif
1285 libusb_ctx.libhandle = NULL;
1286 }
1287#endif // HAVE_LIBUSB
1288
1289 SDL_RemoveHintCallback(SDL_HINT_HIDAPI_ENUMERATE_ONLY_CONTROLLERS, OnlyControllersChanged, NULL);
1290 SDL_RemoveHintCallback(SDL_HINT_HIDAPI_IGNORE_DEVICES, IgnoredDevicesChanged, NULL);
1291
1292 if (SDL_hidapi_ignored_devices) {
1293 SDL_free(SDL_hidapi_ignored_devices);
1294 SDL_hidapi_ignored_devices = NULL;
1295 }
1296
1297 return result;
1298}
1299
1300Uint32 SDL_hid_device_change_count(void)
1301{
1302 Uint32 counter = 0;
1303
1304#ifndef SDL_HIDAPI_DISABLED
1305 if (SDL_hidapi_refcount == 0 && SDL_hid_init() < 0) {
1306 return 0;
1307 }
1308
1309 HIDAPI_UpdateDiscovery();
1310
1311 if (SDL_HIDAPI_discovery.m_unDeviceChangeCounter == 0) {
1312 // Counter wrapped!
1313 ++SDL_HIDAPI_discovery.m_unDeviceChangeCounter;
1314 }
1315 counter = SDL_HIDAPI_discovery.m_unDeviceChangeCounter;
1316
1317#endif // !SDL_HIDAPI_DISABLED
1318
1319 return counter;
1320}
1321
1322static void AddDeviceToEnumeration(const char *driver_name, struct hid_device_info *dev, struct SDL_hid_device_info **devs, struct SDL_hid_device_info **last)
1323{
1324 struct SDL_hid_device_info *new_dev;
1325
1326#ifdef DEBUG_HIDAPI
1327 SDL_Log("Adding %s device to enumeration: %ls %ls 0x%.4hx/0x%.4hx/%d",
1328 driver_name, dev->manufacturer_string, dev->product_string, dev->vendor_id, dev->product_id, dev->interface_number);
1329#else
1330 (void)driver_name;
1331#endif
1332
1333 new_dev = (struct SDL_hid_device_info *)SDL_malloc(sizeof(struct SDL_hid_device_info));
1334 if (new_dev == NULL) {
1335 // Don't bother returning an error, get as many devices as possible
1336 return;
1337 }
1338 CopyHIDDeviceInfo(dev, new_dev);
1339
1340 if ((*last) != NULL) {
1341 (*last)->next = new_dev;
1342 } else {
1343 *devs = new_dev;
1344 }
1345 *last = new_dev;
1346}
1347
1348#if defined(HAVE_LIBUSB) || defined(HAVE_PLATFORM_BACKEND)
1349static void RemoveDeviceFromEnumeration(const char *driver_name, struct hid_device_info *dev, struct hid_device_info **devs, void (*free_device_info)(struct hid_device_info *))
1350{
1351 struct hid_device_info *last = NULL, *curr, *next;
1352
1353 for (curr = *devs; curr; curr = next) {
1354 next = curr->next;
1355
1356 if (dev->vendor_id == curr->vendor_id &&
1357 dev->product_id == curr->product_id &&
1358 (dev->interface_number < 0 || curr->interface_number < 0 || dev->interface_number == curr->interface_number)) {
1359#ifdef DEBUG_HIDAPI
1360 SDL_Log("Skipping %s device: %ls %ls 0x%.4hx/0x%.4hx/%d",
1361 driver_name, curr->manufacturer_string, curr->product_string, curr->vendor_id, curr->product_id, curr->interface_number);
1362#else
1363 (void)driver_name;
1364#endif
1365 if (last) {
1366 last->next = next;
1367 } else {
1368 *devs = next;
1369 }
1370
1371 curr->next = NULL;
1372 free_device_info(curr);
1373 continue;
1374 }
1375 last = curr;
1376 }
1377}
1378#endif // HAVE_LIBUSB || HAVE_PLATFORM_BACKEND
1379
1380#ifdef HAVE_LIBUSB
1381static void RemoveNonWhitelistedDevicesFromEnumeration(struct hid_device_info **devs, void (*free_device_info)(struct hid_device_info *))
1382{
1383 struct hid_device_info *last = NULL, *curr, *next;
1384
1385 for (curr = *devs; curr; curr = next) {
1386 next = curr->next;
1387
1388 if (!IsInWhitelist(curr->vendor_id, curr->product_id)) {
1389#ifdef DEBUG_HIDAPI
1390 SDL_Log("Device was not in libusb whitelist, skipping: %ls %ls 0x%.4hx/0x%.4hx/%d",
1391 curr->manufacturer_string, curr->product_string, curr->vendor_id, curr->product_id, curr->interface_number);
1392#endif
1393 if (last) {
1394 last->next = next;
1395 } else {
1396 *devs = next;
1397 }
1398
1399 curr->next = NULL;
1400 free_device_info(curr);
1401 continue;
1402 }
1403 last = curr;
1404 }
1405}
1406#endif // HAVE_LIBUSB
1407
1408struct SDL_hid_device_info *SDL_hid_enumerate(unsigned short vendor_id, unsigned short product_id)
1409{
1410 struct hid_device_info *driver_devs = NULL;
1411 struct hid_device_info *usb_devs = NULL;
1412 struct hid_device_info *raw_devs = NULL;
1413 struct hid_device_info *dev;
1414 struct SDL_hid_device_info *devs = NULL, *last = NULL;
1415
1416 if (SDL_hidapi_refcount == 0 && SDL_hid_init() < 0) {
1417 return NULL;
1418 }
1419
1420 // Collect the available devices
1421#ifdef HAVE_DRIVER_BACKEND
1422 driver_devs = DRIVER_hid_enumerate(vendor_id, product_id);
1423#endif
1424
1425#ifdef HAVE_LIBUSB
1426 if (libusb_ctx.libhandle) {
1427 usb_devs = LIBUSB_hid_enumerate(vendor_id, product_id);
1428
1429 if (use_libusb_whitelist) {
1430 RemoveNonWhitelistedDevicesFromEnumeration(&usb_devs, LIBUSB_hid_free_enumeration);
1431 }
1432 }
1433#endif // HAVE_LIBUSB
1434
1435#ifdef HAVE_PLATFORM_BACKEND
1436 if (udev_ctx) {
1437 raw_devs = PLATFORM_hid_enumerate(vendor_id, product_id);
1438 }
1439#endif
1440
1441 // Highest priority are custom driver devices
1442 for (dev = driver_devs; dev; dev = dev->next) {
1443 AddDeviceToEnumeration("driver", dev, &devs, &last);
1444#ifdef HAVE_LIBUSB
1445 RemoveDeviceFromEnumeration("libusb", dev, &usb_devs, LIBUSB_hid_free_enumeration);
1446#endif
1447#ifdef HAVE_PLATFORM_BACKEND
1448 RemoveDeviceFromEnumeration("raw", dev, &raw_devs, PLATFORM_hid_free_enumeration);
1449#endif
1450 }
1451
1452 // If whitelist is in effect, libusb has priority, otherwise raw devices do
1453 if (use_libusb_whitelist) {
1454 for (dev = usb_devs; dev; dev = dev->next) {
1455 AddDeviceToEnumeration("libusb", dev, &devs, &last);
1456#ifdef HAVE_PLATFORM_BACKEND
1457 RemoveDeviceFromEnumeration("raw", dev, &raw_devs, PLATFORM_hid_free_enumeration);
1458#endif
1459 }
1460 for (dev = raw_devs; dev; dev = dev->next) {
1461 AddDeviceToEnumeration("platform", dev, &devs, &last);
1462 }
1463 } else {
1464 for (dev = raw_devs; dev; dev = dev->next) {
1465 AddDeviceToEnumeration("raw", dev, &devs, &last);
1466#ifdef HAVE_LIBUSB
1467 RemoveDeviceFromEnumeration("libusb", dev, &usb_devs, LIBUSB_hid_free_enumeration);
1468#endif
1469 }
1470 for (dev = usb_devs; dev; dev = dev->next) {
1471 AddDeviceToEnumeration("libusb", dev, &devs, &last);
1472 }
1473 }
1474
1475#ifdef HAVE_DRIVER_BACKEND
1476 DRIVER_hid_free_enumeration(driver_devs);
1477#endif
1478#ifdef HAVE_LIBUSB
1479 LIBUSB_hid_free_enumeration(usb_devs);
1480#endif
1481#ifdef HAVE_PLATFORM_BACKEND
1482 PLATFORM_hid_free_enumeration(raw_devs);
1483#endif
1484
1485 return devs;
1486}
1487
1488void SDL_hid_free_enumeration(struct SDL_hid_device_info *devs)
1489{
1490 while (devs) {
1491 struct SDL_hid_device_info *next = devs->next;
1492 SDL_free(devs->path);
1493 SDL_free(devs->serial_number);
1494 SDL_free(devs->manufacturer_string);
1495 SDL_free(devs->product_string);
1496 SDL_free(devs);
1497 devs = next;
1498 }
1499}
1500
1501SDL_hid_device *SDL_hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
1502{
1503#if defined(HAVE_PLATFORM_BACKEND) || defined(HAVE_DRIVER_BACKEND) || defined(HAVE_LIBUSB)
1504 void *pDevice = NULL;
1505
1506 if (SDL_hidapi_refcount == 0 && SDL_hid_init() < 0) {
1507 return NULL;
1508 }
1509
1510#ifdef HAVE_PLATFORM_BACKEND
1511 if (udev_ctx) {
1512 pDevice = PLATFORM_hid_open(vendor_id, product_id, serial_number);
1513 if (pDevice != NULL) {
1514 return CreateHIDDeviceWrapper(pDevice, &PLATFORM_Backend);
1515 }
1516 }
1517#endif // HAVE_PLATFORM_BACKEND
1518
1519#ifdef HAVE_DRIVER_BACKEND
1520 pDevice = DRIVER_hid_open(vendor_id, product_id, serial_number);
1521 if (pDevice != NULL) {
1522 return CreateHIDDeviceWrapper(pDevice, &DRIVER_Backend);
1523 }
1524#endif // HAVE_DRIVER_BACKEND
1525
1526#ifdef HAVE_LIBUSB
1527 if (libusb_ctx.libhandle != NULL) {
1528 pDevice = LIBUSB_hid_open(vendor_id, product_id, serial_number);
1529 if (pDevice != NULL) {
1530 return CreateHIDDeviceWrapper(pDevice, &LIBUSB_Backend);
1531 }
1532 }
1533#endif // HAVE_LIBUSB
1534
1535#endif // HAVE_PLATFORM_BACKEND || HAVE_DRIVER_BACKEND || HAVE_LIBUSB
1536
1537 return NULL;
1538}
1539
1540SDL_hid_device *SDL_hid_open_path(const char *path)
1541{
1542#if defined(HAVE_PLATFORM_BACKEND) || defined(HAVE_DRIVER_BACKEND) || defined(HAVE_LIBUSB)
1543 void *pDevice = NULL;
1544
1545 if (SDL_hidapi_refcount == 0 && SDL_hid_init() < 0) {
1546 return NULL;
1547 }
1548
1549#ifdef HAVE_PLATFORM_BACKEND
1550 if (udev_ctx) {
1551 pDevice = PLATFORM_hid_open_path(path);
1552 if (pDevice != NULL) {
1553 return CreateHIDDeviceWrapper(pDevice, &PLATFORM_Backend);
1554 }
1555 }
1556#endif // HAVE_PLATFORM_BACKEND
1557
1558#ifdef HAVE_DRIVER_BACKEND
1559 pDevice = DRIVER_hid_open_path(path);
1560 if (pDevice != NULL) {
1561 return CreateHIDDeviceWrapper(pDevice, &DRIVER_Backend);
1562 }
1563#endif // HAVE_DRIVER_BACKEND
1564
1565#ifdef HAVE_LIBUSB
1566 if (libusb_ctx.libhandle != NULL) {
1567 pDevice = LIBUSB_hid_open_path(path);
1568 if (pDevice != NULL) {
1569 return CreateHIDDeviceWrapper(pDevice, &LIBUSB_Backend);
1570 }
1571 }
1572#endif // HAVE_LIBUSB
1573
1574#endif // HAVE_PLATFORM_BACKEND || HAVE_DRIVER_BACKEND || HAVE_LIBUSB
1575
1576 return NULL;
1577}
1578
1579int SDL_hid_write(SDL_hid_device *device, const unsigned char *data, size_t length)
1580{
1581 CHECK_DEVICE_MAGIC(device, -1);
1582
1583 return device->backend->hid_write(device->device, data, length);
1584}
1585
1586int SDL_hid_read_timeout(SDL_hid_device *device, unsigned char *data, size_t length, int milliseconds)
1587{
1588 CHECK_DEVICE_MAGIC(device, -1);
1589
1590 return device->backend->hid_read_timeout(device->device, data, length, milliseconds);
1591}
1592
1593int SDL_hid_read(SDL_hid_device *device, unsigned char *data, size_t length)
1594{
1595 CHECK_DEVICE_MAGIC(device, -1);
1596
1597 return device->backend->hid_read(device->device, data, length);
1598}
1599
1600int SDL_hid_set_nonblocking(SDL_hid_device *device, int nonblock)
1601{
1602 CHECK_DEVICE_MAGIC(device, -1);
1603
1604 return device->backend->hid_set_nonblocking(device->device, nonblock);
1605}
1606
1607int SDL_hid_send_feature_report(SDL_hid_device *device, const unsigned char *data, size_t length)
1608{
1609 CHECK_DEVICE_MAGIC(device, -1);
1610
1611 return device->backend->hid_send_feature_report(device->device, data, length);
1612}
1613
1614int SDL_hid_get_feature_report(SDL_hid_device *device, unsigned char *data, size_t length)
1615{
1616 CHECK_DEVICE_MAGIC(device, -1);
1617
1618 return device->backend->hid_get_feature_report(device->device, data, length);
1619}
1620
1621int SDL_hid_get_input_report(SDL_hid_device *device, unsigned char *data, size_t length)
1622{
1623 CHECK_DEVICE_MAGIC(device, -1);
1624
1625 return device->backend->hid_get_input_report(device->device, data, length);
1626}
1627
1628int SDL_hid_close(SDL_hid_device *device)
1629{
1630 CHECK_DEVICE_MAGIC(device, -1);
1631
1632 device->backend->hid_close(device->device);
1633 DeleteHIDDeviceWrapper(device);
1634 return 0;
1635}
1636
1637int SDL_hid_get_manufacturer_string(SDL_hid_device *device, wchar_t *string, size_t maxlen)
1638{
1639 CHECK_DEVICE_MAGIC(device, -1);
1640
1641 return device->backend->hid_get_manufacturer_string(device->device, string, maxlen);
1642}
1643
1644int SDL_hid_get_product_string(SDL_hid_device *device, wchar_t *string, size_t maxlen)
1645{
1646 CHECK_DEVICE_MAGIC(device, -1);
1647
1648 return device->backend->hid_get_product_string(device->device, string, maxlen);
1649}
1650
1651int SDL_hid_get_serial_number_string(SDL_hid_device *device, wchar_t *string, size_t maxlen)
1652{
1653 CHECK_DEVICE_MAGIC(device, -1);
1654
1655 return device->backend->hid_get_serial_number_string(device->device, string, maxlen);
1656}
1657
1658int SDL_hid_get_indexed_string(SDL_hid_device *device, int string_index, wchar_t *string, size_t maxlen)
1659{
1660 CHECK_DEVICE_MAGIC(device, -1);
1661
1662 return device->backend->hid_get_indexed_string(device->device, string_index, string, maxlen);
1663}
1664
1665SDL_hid_device_info *SDL_hid_get_device_info(SDL_hid_device *device)
1666{
1667 struct hid_device_info *info;
1668
1669 CHECK_DEVICE_MAGIC(device, NULL);
1670
1671 info = device->backend->hid_get_device_info(device->device);
1672 if (info) {
1673 CopyHIDDeviceInfo(info, &device->info);
1674 return &device->info;
1675 } else {
1676 return NULL;
1677 }
1678}
1679
1680int SDL_hid_get_report_descriptor(SDL_hid_device *device, unsigned char *buf, size_t buf_size)
1681{
1682 CHECK_DEVICE_MAGIC(device, -1);
1683
1684 return device->backend->hid_get_report_descriptor(device->device, buf, buf_size);
1685}
1686
1687void SDL_hid_ble_scan(bool active)
1688{
1689#if !defined(SDL_HIDAPI_DISABLED) && (defined(SDL_PLATFORM_IOS) || defined(SDL_PLATFORM_TVOS))
1690 extern void hid_ble_scan(int bStart);
1691 hid_ble_scan(active);
1692#endif
1693}
1694
1695#ifdef HAVE_ENABLE_GAMECUBE_ADAPTORS
1696// This is needed to enable input for Nyko and EVORETRO GameCube adaptors
1697void SDL_EnableGameCubeAdaptors(void)
1698{
1699#ifdef HAVE_LIBUSB
1700 libusb_context *context = NULL;
1701 libusb_device **devs = NULL;
1702 libusb_device_handle *handle = NULL;
1703 struct libusb_device_descriptor desc;
1704 ssize_t i, num_devs;
1705 int kernel_detached = 0;
1706
1707 if (libusb_ctx.libhandle == NULL) {
1708 return;
1709 }
1710
1711 if (libusb_ctx.init(&context) == 0) {
1712 num_devs = libusb_ctx.get_device_list(context, &devs);
1713 for (i = 0; i < num_devs; ++i) {
1714 if (libusb_ctx.get_device_descriptor(devs[i], &desc) != 0) {
1715 continue;
1716 }
1717
1718 if (desc.idVendor != 0x057e || desc.idProduct != 0x0337) {
1719 continue;
1720 }
1721
1722 if (libusb_ctx.open(devs[i], &handle) != 0) {
1723 continue;
1724 }
1725
1726 if (libusb_ctx.kernel_driver_active(handle, 0)) {
1727 if (libusb_ctx.detach_kernel_driver(handle, 0) == 0) {
1728 kernel_detached = 1;
1729 }
1730 }
1731
1732 if (libusb_ctx.claim_interface(handle, 0) == 0) {
1733 libusb_ctx.control_transfer(handle, 0x21, 11, 0x0001, 0, NULL, 0, 1000);
1734 libusb_ctx.release_interface(handle, 0);
1735 }
1736
1737 if (kernel_detached) {
1738 libusb_ctx.attach_kernel_driver(handle, 0);
1739 }
1740
1741 libusb_ctx.close(handle);
1742 }
1743
1744 libusb_ctx.free_device_list(devs, 1);
1745
1746 libusb_ctx.exit(context);
1747 }
1748#endif // HAVE_LIBUSB
1749}
1750#endif // HAVE_ENABLE_GAMECUBE_ADAPTORS
1751