فهرست منبع

Prefer hidraw over libusb when libusb whitelisting is not enabled

Since opening devices via libusb will unbind them from other drivers, we will either whitelist specific devices that we want to support via libusb or we will prefer other drivers over libusb.
Sam Lantinga 1 سال پیش
والد
کامیت
5774c9638c
1فایلهای تغییر یافته به همراه131 افزوده شده و 118 حذف شده
  1. 131 118
      src/hidapi/SDL_hidapi.c

+ 131 - 118
src/hidapi/SDL_hidapi.c

@@ -888,8 +888,7 @@ static const struct {
     { 0x057e, 0x0337 } /* Nintendo WUP-028, Wii U/Switch GameCube Adapter */
 };
 
-static SDL_bool
-IsInWhitelist(Uint16 vendor, Uint16 product)
+static SDL_bool IsInWhitelist(Uint16 vendor, Uint16 product)
 {
     int i;
     for (i = 0; i < SDL_arraysize(SDL_libusb_whitelist); i += 1) {
@@ -901,6 +900,10 @@ IsInWhitelist(Uint16 vendor, Uint16 product)
     return SDL_FALSE;
 }
 
+#endif /* HAVE_LIBUSB */
+
+#endif /* !SDL_HIDAPI_DISABLED */
+
 #if HAVE_PLATFORM_BACKEND || HAVE_DRIVER_BACKEND
 /* We have another way to get HID devices, so use the whitelist to get devices where libusb is preferred */
 #define SDL_HIDAPI_LIBUSB_WHITELIST_DEFAULT SDL_TRUE
@@ -911,10 +914,6 @@ IsInWhitelist(Uint16 vendor, Uint16 product)
 
 static SDL_bool use_libusb_whitelist = SDL_HIDAPI_LIBUSB_WHITELIST_DEFAULT;
 
-#endif /* HAVE_LIBUSB */
-
-#endif /* !SDL_HIDAPI_DISABLED */
-
 /* Shared HIDAPI Implementation */
 
 struct hidapi_backend
@@ -1155,9 +1154,9 @@ int SDL_hid_init(void)
     }
 #endif
 
-#ifdef HAVE_LIBUSB
     use_libusb_whitelist = SDL_GetHintBoolean("SDL_HIDAPI_LIBUSB_WHITELIST",
                                               SDL_HIDAPI_LIBUSB_WHITELIST_DEFAULT);
+#ifdef HAVE_LIBUSB
     if (SDL_getenv("SDL_HIDAPI_DISABLE_LIBUSB") != NULL) {
         SDL_LogDebug(SDL_LOG_CATEGORY_INPUT,
                      "libusb disabled by SDL_HIDAPI_DISABLE_LIBUSB");
@@ -1320,154 +1319,168 @@ Uint32 SDL_hid_device_change_count(void)
     return counter;
 }
 
-struct SDL_hid_device_info *SDL_hid_enumerate(unsigned short vendor_id, unsigned short product_id)
+static void AddDeviceToEnumeration(const char *driver_name, struct hid_device_info *dev, struct SDL_hid_device_info **devs, struct SDL_hid_device_info **last)
 {
-#if defined(HAVE_PLATFORM_BACKEND) || defined(HAVE_DRIVER_BACKEND) || defined(HAVE_LIBUSB)
-#ifdef HAVE_LIBUSB
-    struct hid_device_info *usb_devs = NULL;
-    struct hid_device_info *usb_dev;
-#endif
-#ifdef HAVE_DRIVER_BACKEND
-    struct hid_device_info *driver_devs = NULL;
-    struct hid_device_info *driver_dev;
-#endif
-#ifdef HAVE_PLATFORM_BACKEND
-    struct hid_device_info *raw_devs = NULL;
-    struct hid_device_info *raw_dev;
+    struct SDL_hid_device_info *new_dev;
+
+#ifdef DEBUG_HIDAPI
+    SDL_Log("Adding %s device to enumeration: %ls %ls 0x%.4hx/0x%.4hx/%d",
+            driver_name, dev->manufacturer_string, dev->product_string, dev->vendor_id, dev->product_id, dev->interface_number);
+#else
+    (void)driver_name;
 #endif
-    struct SDL_hid_device_info *devs = NULL, *last = NULL, *new_dev;
 
-    if (SDL_hidapi_refcount == 0 && SDL_hid_init() != 0) {
-        return NULL;
+    new_dev = (struct SDL_hid_device_info *)SDL_malloc(sizeof(struct SDL_hid_device_info));
+    if (new_dev == NULL) {
+        /* Don't bother returning an error, get as many devices as possible */
+        return;
     }
+    CopyHIDDeviceInfo(dev, new_dev);
 
-#ifdef HAVE_LIBUSB
-    if (libusb_ctx.libhandle) {
-        usb_devs = LIBUSB_hid_enumerate(vendor_id, product_id);
+    if ((*last) != NULL) {
+        (*last)->next = new_dev;
+    } else {
+        *devs = new_dev;
+    }
+    *last = new_dev;
+}
+
+static void RemoveDeviceFromEnumeration(const char *driver_name, struct hid_device_info *dev, struct hid_device_info **devs, void (*free_device_info)(struct hid_device_info *))
+{
+    struct hid_device_info *last = NULL, *curr, *next;
+
+    for (curr = *devs; curr; curr = next) {
+        next = curr->next;
+
+        if (dev->vendor_id == curr->vendor_id &&
+            dev->product_id == curr->product_id &&
+            (dev->interface_number < 0 || curr->interface_number < 0 || dev->interface_number == curr->interface_number)) {
 #ifdef DEBUG_HIDAPI
-        SDL_Log("libusb devices found:");
+            SDL_Log("Skipping %s device: %ls %ls 0x%.4hx/0x%.4hx/%d",
+                    driver_name, curr->manufacturer_string, curr->product_string, curr->vendor_id, curr->product_id, curr->interface_number);
+#else
+            (void)driver_name;
 #endif
-        for (usb_dev = usb_devs; usb_dev; usb_dev = usb_dev->next) {
-            if (use_libusb_whitelist) {
-                if (!IsInWhitelist(usb_dev->vendor_id, usb_dev->product_id)) {
-#ifdef DEBUG_HIDAPI
-                    SDL_Log("Device was not in libusb whitelist: %ls %ls 0x%.4hx 0x%.4hx",
-                            usb_dev->manufacturer_string, usb_dev->product_string,
-                            usb_dev->vendor_id, usb_dev->product_id);
-#endif /* DEBUG_HIDAPI */
-                    continue;
-                }
-            }
-            new_dev = (struct SDL_hid_device_info *)SDL_malloc(sizeof(struct SDL_hid_device_info));
-            if (new_dev == NULL) {
-                LIBUSB_hid_free_enumeration(usb_devs);
-                SDL_hid_free_enumeration(devs);
-                SDL_OutOfMemory();
-                return NULL;
+            if (last) {
+                last->next = next;
+            } else {
+                *devs = next;
             }
-            CopyHIDDeviceInfo(usb_dev, new_dev);
+
+            curr->next = NULL;
+            free_device_info(curr);
+            continue;
+        }
+        last = curr;
+    }
+}
+
+#ifdef HAVE_LIBUSB
+static void RemoveNonWhitelistedDevicesFromEnumeration(struct hid_device_info **devs, void (*free_device_info)(struct hid_device_info *))
+{
+    struct hid_device_info *last = NULL, *curr, *next;
+
+    for (curr = *devs; curr; curr = next) {
+        next = curr->next;
+
+        if (!IsInWhitelist(curr->vendor_id, curr->product_id)) {
 #ifdef DEBUG_HIDAPI
-            SDL_Log(" - %ls %ls 0x%.4hx 0x%.4hx",
-                    usb_dev->manufacturer_string, usb_dev->product_string,
-                    usb_dev->vendor_id, usb_dev->product_id);
+            SDL_Log("Device was not in libusb whitelist, skipping: %ls %ls 0x%.4hx/0x%.4hx/%d",
+                    curr->manufacturer_string, curr->product_string, curr->vendor_id, curr->product_id, curr->interface_number);
 #endif
-
-            if (last != NULL) {
-                last->next = new_dev;
+            if (last) {
+                last->next = next;
             } else {
-                devs = new_dev;
+                *devs = next;
             }
-            last = new_dev;
+
+            curr->next = NULL;
+            free_device_info(curr);
+            continue;
         }
+        last = curr;
     }
+}
 #endif /* HAVE_LIBUSB */
 
+struct SDL_hid_device_info *SDL_hid_enumerate(unsigned short vendor_id, unsigned short product_id)
+{
+    struct hid_device_info *driver_devs = NULL;
+    struct hid_device_info *usb_devs = NULL;
+    struct hid_device_info *raw_devs = NULL;
+    struct hid_device_info *dev;
+    struct SDL_hid_device_info *devs = NULL, *last = NULL;
+
+    if (SDL_hidapi_refcount == 0 && SDL_hid_init() != 0) {
+        return NULL;
+    }
+
+    /* Collect the available devices */
 #ifdef HAVE_DRIVER_BACKEND
     driver_devs = DRIVER_hid_enumerate(vendor_id, product_id);
-    for (driver_dev = driver_devs; driver_dev; driver_dev = driver_dev->next) {
-        new_dev = (struct SDL_hid_device_info *)SDL_malloc(sizeof(struct SDL_hid_device_info));
-        CopyHIDDeviceInfo(driver_dev, new_dev);
+#endif
 
-        if (last != NULL) {
-            last->next = new_dev;
-        } else {
-            devs = new_dev;
+#ifdef HAVE_LIBUSB
+    if (libusb_ctx.libhandle) {
+        usb_devs = LIBUSB_hid_enumerate(vendor_id, product_id);
+
+        if (use_libusb_whitelist) {
+            RemoveNonWhitelistedDevicesFromEnumeration(&usb_devs,  LIBUSB_hid_free_enumeration);
         }
-        last = new_dev;
     }
-#endif /* HAVE_DRIVER_BACKEND */
+#endif /* HAVE_LIBUSB */
 
 #ifdef HAVE_PLATFORM_BACKEND
     if (udev_ctx) {
         raw_devs = PLATFORM_hid_enumerate(vendor_id, product_id);
-#ifdef DEBUG_HIDAPI
-        SDL_Log("hidraw devices found:");
-#endif
-        for (raw_dev = raw_devs; raw_dev; raw_dev = raw_dev->next) {
-            SDL_bool bFound = SDL_FALSE;
-#ifdef DEBUG_HIDAPI
-            SDL_Log(" - %ls %ls 0x%.4hx 0x%.4hx",
-                    raw_dev->manufacturer_string, raw_dev->product_string,
-                    raw_dev->vendor_id, raw_dev->product_id);
+    }
 #endif
+
+    /* Highest priority are custom driver devices */
+    for (dev = driver_devs; dev; dev = dev->next) {
+        AddDeviceToEnumeration("driver", dev, &devs, &last);
 #ifdef HAVE_LIBUSB
-            for (usb_dev = usb_devs; usb_dev; usb_dev = usb_dev->next) {
-                if (raw_dev->vendor_id == usb_dev->vendor_id &&
-                    raw_dev->product_id == usb_dev->product_id &&
-                    (raw_dev->interface_number < 0 || raw_dev->interface_number == usb_dev->interface_number)) {
-                    bFound = SDL_TRUE;
-                    break;
-                }
-            }
+        RemoveDeviceFromEnumeration("libusb", dev, &usb_devs, LIBUSB_hid_free_enumeration);
 #endif
-#ifdef HAVE_DRIVER_BACKEND
-            for (driver_dev = driver_devs; driver_dev; driver_dev = driver_dev->next) {
-                if (raw_dev->vendor_id == driver_dev->vendor_id &&
-                    raw_dev->product_id == driver_dev->product_id &&
-                    (raw_dev->interface_number < 0 || raw_dev->interface_number == driver_dev->interface_number)) {
-                    bFound = SDL_TRUE;
-                    break;
-                }
-            }
+#ifdef HAVE_PLATFORM_BACKEND
+        RemoveDeviceFromEnumeration("raw", dev, &raw_devs, PLATFORM_hid_free_enumeration);
 #endif
-            if (!bFound) {
-                new_dev = (struct SDL_hid_device_info *)SDL_malloc(sizeof(struct SDL_hid_device_info));
-                if (new_dev == NULL) {
+    }
+
+    /* If whitelist is in effect, libusb has priority, otherwise raw devices do */
+    if (use_libusb_whitelist) {
+        for (dev = usb_devs; dev; dev = dev->next) {
+            AddDeviceToEnumeration("libusb", dev, &devs, &last);
+#ifdef HAVE_PLATFORM_BACKEND
+            RemoveDeviceFromEnumeration("raw", dev, &raw_devs, PLATFORM_hid_free_enumeration);
+#endif
+        }
+        for (dev = raw_devs; dev; dev = dev->next) {
+            AddDeviceToEnumeration("platform", dev, &devs, &last);
+        }
+    } else {
+        for (dev = raw_devs; dev; dev = dev->next) {
+            AddDeviceToEnumeration("raw", dev, &devs, &last);
 #ifdef HAVE_LIBUSB
-                    if (libusb_ctx.libhandle) {
-                        LIBUSB_hid_free_enumeration(usb_devs);
-                    }
+            RemoveDeviceFromEnumeration("libusb", dev, &usb_devs, LIBUSB_hid_free_enumeration);
 #endif
-                    PLATFORM_hid_free_enumeration(raw_devs);
-                    SDL_hid_free_enumeration(devs);
-                    SDL_OutOfMemory();
-                    return NULL;
-                }
-                CopyHIDDeviceInfo(raw_dev, new_dev);
-                new_dev->next = NULL;
-
-                if (last != NULL) {
-                    last->next = new_dev;
-                } else {
-                    devs = new_dev;
-                }
-                last = new_dev;
-            }
         }
-        PLATFORM_hid_free_enumeration(raw_devs);
+        for (dev = usb_devs; dev; dev = dev->next) {
+            AddDeviceToEnumeration("libusb", dev, &devs, &last);
+        }
     }
-#endif /* HAVE_PLATFORM_BACKEND */
 
+#ifdef HAVE_DRIVER_BACKEND
+    DRIVER_hid_free_enumeration(driver_devs);
+#endif
 #ifdef HAVE_LIBUSB
-    if (libusb_ctx.libhandle) {
-        LIBUSB_hid_free_enumeration(usb_devs);
-    }
+    LIBUSB_hid_free_enumeration(usb_devs);
+#endif
+#ifdef HAVE_PLATFORM_BACKEND
+    PLATFORM_hid_free_enumeration(raw_devs);
 #endif
-    return devs;
 
-#else
-    return NULL;
-#endif /* HAVE_PLATFORM_BACKEND || HAVE_DRIVER_BACKEND || HAVE_LIBUSB */
+    return devs;
 }
 
 void SDL_hid_free_enumeration(struct SDL_hid_device_info *devs)