From 6e7769cde1e29b65f2e89688fe0d990e755a7017 Mon Sep 17 00:00:00 2001 From: Sam Lantinga Date: Mon, 5 Jun 2023 18:02:37 -0700 Subject: [PATCH] Fixed detecting Bluetooth Steam Controllers --- src/hidapi/SDL_hidapi.c | 12 +++--- src/hidapi/SDL_hidapi_c.h | 2 +- src/hidapi/android/hid.cpp | 2 +- src/hidapi/ios/hid.m | 2 +- src/hidapi/libusb/hid.c | 2 +- src/hidapi/linux/hid.c | 2 +- src/hidapi/mac/hid.c | 25 +++++++++++- src/hidapi/windows/hid.c | 78 +++++++++++++++++++++++++++++++++++++- 8 files changed, 113 insertions(+), 12 deletions(-) diff --git a/src/hidapi/SDL_hidapi.c b/src/hidapi/SDL_hidapi.c index 84a4b9eb4bb14..6d780cd9510fe 100644 --- a/src/hidapi/SDL_hidapi.c +++ b/src/hidapi/SDL_hidapi.c @@ -531,8 +531,8 @@ static void HIDAPI_ShutdownDiscovery(void) /* Platform HIDAPI Implementation */ #define HIDAPI_USING_SDL_RUNTIME -#define HIDAPI_IGNORE_DEVICE(VID, PID, USAGE_PAGE, USAGE) \ - SDL_HIDAPI_ShouldIgnoreDevice(VID, PID, USAGE_PAGE, USAGE) +#define HIDAPI_IGNORE_DEVICE(BUS, VID, PID, USAGE_PAGE, USAGE) \ + SDL_HIDAPI_ShouldIgnoreDevice(BUS, VID, PID, USAGE_PAGE, USAGE) struct PLATFORM_hid_device_; typedef struct PLATFORM_hid_device_ PLATFORM_hid_device; @@ -1053,13 +1053,15 @@ static void SDLCALL IgnoredDevicesChanged(void *userdata, const char *name, cons } } -SDL_bool SDL_HIDAPI_ShouldIgnoreDevice(Uint16 vendor_id, Uint16 product_id, Uint16 usage_page, Uint16 usage) +SDL_bool SDL_HIDAPI_ShouldIgnoreDevice(int bus, Uint16 vendor_id, Uint16 product_id, Uint16 usage_page, Uint16 usage) { /* See if there are any devices we should skip in enumeration */ if (SDL_hidapi_only_controllers && usage_page) { if (vendor_id == USB_VENDOR_VALVE) { - /* Ignore the mouse/keyboard interface on Steam Controllers */ - if (usage == USB_USAGE_GENERIC_KEYBOARD || usage == USB_USAGE_GENERIC_MOUSE) { + /* Ignore the USB mouse/keyboard interface on Steam Controllers */ + if (bus == HID_API_BUS_USB && + usage_page == USB_USAGEPAGE_GENERIC_DESKTOP && + (usage == USB_USAGE_GENERIC_KEYBOARD || usage == USB_USAGE_GENERIC_MOUSE)) { return SDL_TRUE; } } else if (usage_page == USB_USAGEPAGE_GENERIC_DESKTOP && diff --git a/src/hidapi/SDL_hidapi_c.h b/src/hidapi/SDL_hidapi_c.h index 0b2fe70ebf810..59e4f3e8123bb 100644 --- a/src/hidapi/SDL_hidapi_c.h +++ b/src/hidapi/SDL_hidapi_c.h @@ -22,7 +22,7 @@ /* Return true if the HIDAPI should ignore a device during enumeration */ -extern SDL_bool SDL_HIDAPI_ShouldIgnoreDevice(Uint16 vendor_id, Uint16 product_id, Uint16 usage_page, Uint16 usage); +extern SDL_bool SDL_HIDAPI_ShouldIgnoreDevice(int bus_type, Uint16 vendor_id, Uint16 product_id, Uint16 usage_page, Uint16 usage); #ifdef SDL_JOYSTICK_HIDAPI #ifdef HAVE_LIBUSB diff --git a/src/hidapi/android/hid.cpp b/src/hidapi/android/hid.cpp index 75b0d4fd39a50..1f4d8c6a0076c 100644 --- a/src/hidapi/android/hid.cpp +++ b/src/hidapi/android/hid.cpp @@ -1074,7 +1074,7 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor const hid_device_info *info = pDevice->GetDeviceInfo(); /* See if there are any devices we should skip in enumeration */ - if (SDL_HIDAPI_ShouldIgnoreDevice(info->vendor_id, info->product_id, 0, 0)) { + if (SDL_HIDAPI_ShouldIgnoreDevice(HID_API_BUS_UNKNOWN, info->vendor_id, info->product_id, 0, 0)) { continue; } diff --git a/src/hidapi/ios/hid.m b/src/hidapi/ios/hid.m index be0b327ff7a1d..1091196027c62 100644 --- a/src/hidapi/ios/hid.m +++ b/src/hidapi/ios/hid.m @@ -859,7 +859,7 @@ int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock) struct hid_device_info *root = NULL; /* See if there are any devices we should skip in enumeration */ - if (SDL_HIDAPI_ShouldIgnoreDevice(VALVE_USB_VID, D0G_BLE2_PID, 0, 0)) { + if (SDL_HIDAPI_ShouldIgnoreDevice(HID_API_BUS_BLUETOOTH, VALVE_USB_VID, D0G_BLE2_PID, 0, 0)) { return NULL; } diff --git a/src/hidapi/libusb/hid.c b/src/hidapi/libusb/hid.c index 65899fe7d2167..0d3b4726d4b4e 100644 --- a/src/hidapi/libusb/hid.c +++ b/src/hidapi/libusb/hid.c @@ -943,7 +943,7 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, #ifdef HIDAPI_IGNORE_DEVICE /* See if there are any devices we should skip in enumeration */ - if (HIDAPI_IGNORE_DEVICE(dev_vid, dev_pid, 0, 0)) { + if (HIDAPI_IGNORE_DEVICE(HID_API_BUS_USB, dev_vid, dev_pid, 0, 0)) { continue; } #endif diff --git a/src/hidapi/linux/hid.c b/src/hidapi/linux/hid.c index a364ac2e08402..89d6ae16f6562 100644 --- a/src/hidapi/linux/hid.c +++ b/src/hidapi/linux/hid.c @@ -984,7 +984,7 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, if (get_hid_report_descriptor_from_sysfs(sysfs_path, &report_desc) >= 0) { get_next_hid_usage(report_desc.value, report_desc.size, &pos, &page, &usage); } - if (HIDAPI_IGNORE_DEVICE(dev_vid, dev_pid, page, usage)) { + if (HIDAPI_IGNORE_DEVICE(bus_type, dev_vid, dev_pid, page, usage)) { continue; } #endif diff --git a/src/hidapi/mac/hid.c b/src/hidapi/mac/hid.c index 779c2c9e1a808..a6719824c548a 100644 --- a/src/hidapi/mac/hid.c +++ b/src/hidapi/mac/hid.c @@ -548,6 +548,28 @@ static int read_usb_interface_from_hid_service_parent(io_service_t hid_service) return result; } +#ifdef HIDAPI_IGNORE_DEVICE +static hid_bus_type get_bus_type(IOHIDDeviceRef dev) +{ + hid_bus_type bus_type = HID_API_BUS_UNKNOWN; + + CFTypeRef transport_prop = IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDTransportKey)); + + if (transport_prop != NULL && CFGetTypeID(transport_prop) == CFStringGetTypeID()) { + if (CFStringCompare((CFStringRef)transport_prop, CFSTR(kIOHIDTransportUSBValue), 0) == kCFCompareEqualTo) { + bus_type = HID_API_BUS_USB; + } else if (CFStringHasPrefix((CFStringRef)transport_prop, CFSTR(kIOHIDTransportBluetoothValue))) { + bus_type = HID_API_BUS_BLUETOOTH; + } else if (CFStringCompare((CFStringRef)transport_prop, CFSTR(kIOHIDTransportI2CValue), 0) == kCFCompareEqualTo) { + bus_type = HID_API_BUS_I2C; + } else if (CFStringCompare((CFStringRef)transport_prop, CFSTR(kIOHIDTransportSPIValue), 0) == kCFCompareEqualTo) { + bus_type = HID_API_BUS_SPI; + } + } + return bus_type; +} +#endif /* HIDAPI_IGNORE_DEVICE */ + static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev, int32_t usage_page, int32_t usage) { unsigned short dev_vid; @@ -769,11 +791,12 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, #ifdef HIDAPI_IGNORE_DEVICE /* See if there are any devices we should skip in enumeration */ + hid_bus_type bus_type = get_bus_type(dev); unsigned short dev_vid = get_vendor_id(dev); unsigned short dev_pid = get_product_id(dev); unsigned short usage_page = get_int_property(dev, CFSTR(kIOHIDPrimaryUsagePageKey)); unsigned short usage = get_int_property(dev, CFSTR(kIOHIDPrimaryUsageKey)); - if (HIDAPI_IGNORE_DEVICE(dev_vid, dev_pid, usage_page, usage)) { + if (HIDAPI_IGNORE_DEVICE(bus_type, dev_vid, dev_pid, usage_page, usage)) { continue; } #endif diff --git a/src/hidapi/windows/hid.c b/src/hidapi/windows/hid.c index bf989b77e05ef..81b36729ad420 100644 --- a/src/hidapi/windows/hid.c +++ b/src/hidapi/windows/hid.c @@ -644,6 +644,81 @@ static void hid_internal_get_ble_info(struct hid_device_info* dev, DEVINST dev_n } } +#ifdef HIDAPI_IGNORE_DEVICE +static hid_bus_type get_bus_type(const wchar_t* interface_path) +{ + wchar_t *device_id = NULL, *compatible_ids = NULL; + CONFIGRET cr; + DEVINST dev_node; + hid_bus_type bus_type = HID_API_BUS_UNKNOWN; + + /* Get the device id from interface path */ + device_id = hid_internal_get_device_interface_property(interface_path, &DEVPKEY_Device_InstanceId, DEVPROP_TYPE_STRING); + if (!device_id) + goto end; + + /* Open devnode from device id */ + cr = CM_Locate_DevNodeW(&dev_node, (DEVINSTID_W)device_id, CM_LOCATE_DEVNODE_NORMAL); + if (cr != CR_SUCCESS) + goto end; + + /* Get devnode parent */ + cr = CM_Get_Parent(&dev_node, dev_node, 0); + if (cr != CR_SUCCESS) + goto end; + + /* Get the compatible ids from parent devnode */ + compatible_ids = hid_internal_get_devnode_property(dev_node, &DEVPKEY_Device_CompatibleIds, DEVPROP_TYPE_STRING_LIST); + if (!compatible_ids) + goto end; + + /* Now we can parse parent's compatible IDs to find out the device bus type */ + for (wchar_t* compatible_id = compatible_ids; *compatible_id; compatible_id += wcslen(compatible_id) + 1) { + /* Normalize to upper case */ + hid_internal_towupper(compatible_id); + + /* USB devices + https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-support + https://docs.microsoft.com/windows-hardware/drivers/install/standard-usb-identifiers */ + if (wcsstr(compatible_id, L"USB") != NULL) { + bus_type = HID_API_BUS_USB; + break; + } + + /* Bluetooth devices + https://docs.microsoft.com/windows-hardware/drivers/bluetooth/installing-a-bluetooth-device */ + if (wcsstr(compatible_id, L"BTHENUM") != NULL) { + bus_type = HID_API_BUS_BLUETOOTH; + break; + } + + /* Bluetooth LE devices */ + if (wcsstr(compatible_id, L"BTHLEDEVICE") != NULL) { + bus_type = HID_API_BUS_BLUETOOTH; + break; + } + + /* I2C devices + https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-support-and-power-management */ + if (wcsstr(compatible_id, L"PNP0C50") != NULL) { + bus_type = HID_API_BUS_I2C; + break; + } + + /* SPI devices + https://docs.microsoft.com/windows-hardware/drivers/hid/plug-and-play-for-spi */ + if (wcsstr(compatible_id, L"PNP0C51") != NULL) { + bus_type = HID_API_BUS_SPI; + break; + } + } +end: + free(device_id); + free(compatible_ids); + return bus_type; +} +#endif /* HIDAPI_IGNORE_DEVICE */ + static void hid_internal_get_info(const wchar_t* interface_path, struct hid_device_info* dev) { wchar_t *device_id = NULL, *compatible_ids = NULL; @@ -906,13 +981,14 @@ struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned shor #ifdef HIDAPI_IGNORE_DEVICE /* See if there are any devices we should skip in enumeration */ + hid_bus_type bus_type = get_bus_type(device_interface); PHIDP_PREPARSED_DATA pp_data = NULL; HIDP_CAPS caps = { 0 }; if (HidD_GetPreparsedData(device_handle, &pp_data)) { HidP_GetCaps(pp_data, &caps); HidD_FreePreparsedData(pp_data); } - if (HIDAPI_IGNORE_DEVICE(attrib.VendorID, attrib.ProductID, caps.UsagePage, caps.Usage)) { + if (HIDAPI_IGNORE_DEVICE(bus_type, attrib.VendorID, attrib.ProductID, caps.UsagePage, caps.Usage)) { goto cont_close; } #endif