diff --git a/include/SDL3/SDL_hidapi.h b/include/SDL3/SDL_hidapi.h index 987621b6ab..87673255e6 100644 --- a/include/SDL3/SDL_hidapi.h +++ b/include/SDL3/SDL_hidapi.h @@ -76,6 +76,37 @@ extern "C" { struct SDL_hid_device_; typedef struct SDL_hid_device_ SDL_hid_device; /**< opaque hidapi structure */ +/** + * \brief HID underlying bus types. + */ +typedef enum { + /** Unknown bus type */ + SDL_HID_API_BUS_UNKNOWN = 0x00, + + /** USB bus + Specifications: + https://usb.org/hid */ + SDL_HID_API_BUS_USB = 0x01, + + /** Bluetooth or Bluetooth LE bus + Specifications: + https://www.bluetooth.com/specifications/specs/human-interface-device-profile-1-1-1/ + https://www.bluetooth.com/specifications/specs/hid-service-1-0/ + https://www.bluetooth.com/specifications/specs/hid-over-gatt-profile-1-0/ */ + SDL_HID_API_BUS_BLUETOOTH = 0x02, + + /** I2C bus + Specifications: + https://docs.microsoft.com/previous-versions/windows/hardware/design/dn642101(v=vs.85) */ + SDL_HID_API_BUS_I2C = 0x03, + + /** SPI bus + Specifications: + https://www.microsoft.com/download/details.aspx?id=103325 */ + SDL_HID_API_BUS_SPI = 0x04, + +} SDL_hid_bus_type; + /** hidapi info structure */ /** * \brief Information about a connected HID device @@ -98,17 +129,17 @@ typedef struct SDL_hid_device_info /** Product string */ wchar_t *product_string; /** Usage Page for this Device/Interface - (Windows/Mac only). */ + (Windows/Mac/hidraw only) */ unsigned short usage_page; /** Usage for this Device/Interface - (Windows/Mac only).*/ + (Windows/Mac/hidraw only) */ unsigned short usage; /** The USB interface which this logical device represents. - * Valid on both Linux implementations in all cases. - * Valid on the Windows implementation only if the device - contains more than one interface. */ + Valid only if the device is a USB HID device. + Set to -1 in all other cases. + */ int interface_number; /** Additional information about the USB interface. @@ -117,8 +148,12 @@ typedef struct SDL_hid_device_info int interface_subclass; int interface_protocol; + /** Underlying bus type */ + SDL_hid_bus_type bus_type; + /** Pointer to the next device */ struct SDL_hid_device_info *next; + } SDL_hid_device_info; @@ -187,8 +222,8 @@ extern DECLSPEC Uint32 SDLCALL SDL_hid_device_change_count(void); * matches. If `vendor_id` and `product_id` are both set to 0, then all HID * devices will be returned. * - * \param vendor_id The Vendor ID (VID) of the types of device to open. - * \param product_id The Product ID (PID) of the types of device to open. + * \param vendor_id The Vendor ID (VID) of the types of device to open, or 0 to match any vendor. + * \param product_id The Product ID (PID) of the types of device to open, or 0 to match any product. * \returns a pointer to a linked list of type SDL_hid_device_info, containing * information about the HID devices attached to the system, or NULL * in the case of failure. Free this linked list by calling @@ -237,13 +272,12 @@ extern DECLSPEC SDL_hid_device * SDLCALL SDL_hid_open(unsigned short vendor_id, * platform-specific path name can be used (eg: /dev/hidraw0 on Linux). * * \param path The path name of the device to open - * \param bExclusive Open device in exclusive mode (Windows only) * \returns a pointer to a SDL_hid_device object on success or NULL on * failure. * * \since This function is available since SDL 3.0.0. */ -extern DECLSPEC SDL_hid_device * SDLCALL SDL_hid_open_path(const char *path, int bExclusive /* = false */); +extern DECLSPEC SDL_hid_device * SDLCALL SDL_hid_open_path(const char *path); /** * Write an Output report to a HID device. @@ -377,6 +411,28 @@ extern DECLSPEC int SDLCALL SDL_hid_send_feature_report(SDL_hid_device *dev, con */ extern DECLSPEC int SDLCALL SDL_hid_get_feature_report(SDL_hid_device *dev, unsigned char *data, size_t length); +/** + * Get an input report from a HID device. + * + * Set the first byte of `data` to the Report ID of the report to be read. + * Make sure to allow space for this extra byte in `data`. Upon return, the + * first byte will still contain the Report ID, and the report data will start + * in data[1]. + * + * \param dev A device handle returned from SDL_hid_open(). + * \param data A buffer to put the read data into, including the Report ID. + * Set the first byte of `data` to the Report ID of the report to + * be read, or set it to zero if your device does not use numbered + * reports. + * \param length The number of bytes to read, including an extra byte for the + * report ID. The buffer can be longer than the actual report. + * \returns the number of bytes read plus one for the report ID (which is + * still in the first byte), or -1 on error. + * + * \since This function is available since SDL 3.0.0. +*/ +extern DECLSPEC int SDLCALL SDL_hid_get_input_report(SDL_hid_device *dev, unsigned char *data, size_t length); + /** * Close a HID device. * @@ -441,6 +497,26 @@ extern DECLSPEC int SDLCALL SDL_hid_get_serial_number_string(SDL_hid_device *dev */ extern DECLSPEC int SDLCALL SDL_hid_get_indexed_string(SDL_hid_device *dev, int string_index, wchar_t *string, size_t maxlen); +/** + * Get the device info from a HID device. + * + * \param dev A device handle returned from SDL_hid_open(). + * \returns a pointer to the SDL_hid_device_info for this hid_device, or NULL in the case of failure; call SDL_GetError() for more information. This struct is valid until the device is closed with SDL_hid_close(). + */ +extern DECLSPEC SDL_hid_device_info * SDLCALL SDL_hid_get_device_info(SDL_hid_device *dev); + +/** + * Get a report descriptor from a HID device. + * + * User has to provide a preallocated buffer where descriptor will be copied to. The recommended size for a preallocated buffer is 4096 bytes. + * + * \param dev A device handle returned from SDL_hid_open(). + * \param buf The buffer to copy descriptor into. + * \param buf_size The size of the buffer in bytes. + * \returns the number of bytes actually copied, or -1 on error; call SDL_GetError() for more information. + */ +extern DECLSPEC int SDLCALL SDL_hid_get_report_descriptor(SDL_hid_device *dev, unsigned char *buf, size_t buf_size); + /** * Start or stop a BLE scan on iOS and tvOS to pair Steam Controllers * diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym index c306f532f7..4956c8411e 100644 --- a/src/dynapi/SDL_dynapi.sym +++ b/src/dynapi/SDL_dynapi.sym @@ -861,6 +861,9 @@ SDL3_0.0.0 { SDL_wcstol; SDL_swprintf; SDL_vswprintf; + SDL_hid_get_input_report; + SDL_hid_get_device_info; + SDL_hid_get_report_descriptor; # extra symbols go here (don't modify this line) local: *; }; diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h index 7da9a97e57..ebd6a45d85 100644 --- a/src/dynapi/SDL_dynapi_overrides.h +++ b/src/dynapi/SDL_dynapi_overrides.h @@ -887,3 +887,6 @@ #define SDL_wcstol SDL_wcstol_REAL #define SDL_swprintf SDL_swprintf_REAL #define SDL_vswprintf SDL_vswprintf_REAL +#define SDL_hid_get_input_report SDL_hid_get_input_report_REAL +#define SDL_hid_get_device_info SDL_hid_get_device_info_REAL +#define SDL_hid_get_report_descriptor SDL_hid_get_report_descriptor_REAL diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h index 9722e2169f..8d65e1febd 100644 --- a/src/dynapi/SDL_dynapi_procs.h +++ b/src/dynapi/SDL_dynapi_procs.h @@ -797,7 +797,7 @@ SDL_DYNAPI_PROC(int,SDL_hid_get_product_string,(SDL_hid_device *a, wchar_t *b, s SDL_DYNAPI_PROC(int,SDL_hid_get_serial_number_string,(SDL_hid_device *a, wchar_t *b, size_t c),(a,b,c),return) SDL_DYNAPI_PROC(int,SDL_hid_init,(void),(),return) SDL_DYNAPI_PROC(SDL_hid_device*,SDL_hid_open,(unsigned short a, unsigned short b, const wchar_t *c),(a,b,c),return) -SDL_DYNAPI_PROC(SDL_hid_device*,SDL_hid_open_path,(const char *a, int b),(a,b),return) +SDL_DYNAPI_PROC(SDL_hid_device*,SDL_hid_open_path,(const char *a),(a),return) SDL_DYNAPI_PROC(int,SDL_hid_read,(SDL_hid_device *a, unsigned char *b, size_t c),(a,b,c),return) SDL_DYNAPI_PROC(int,SDL_hid_read_timeout,(SDL_hid_device *a, unsigned char *b, size_t c, int d),(a,b,c,d),return) SDL_DYNAPI_PROC(int,SDL_hid_send_feature_report,(SDL_hid_device *a, const unsigned char *b, size_t c),(a,b,c),return) @@ -932,3 +932,6 @@ SDL_DYNAPI_PROC(float,SDL_GetWindowDisplayScale,(SDL_Window *a),(a),return) SDL_DYNAPI_PROC(float,SDL_GetWindowPixelDensity,(SDL_Window *a),(a),return) SDL_DYNAPI_PROC(long,SDL_wcstol,(const wchar_t *a, wchar_t **b, int c),(a,b,c),return) SDL_DYNAPI_PROC(int,SDL_vswprintf,(wchar_t *a, size_t b, const wchar_t *c, va_list d),(a,b,c,d),return) +SDL_DYNAPI_PROC(int,SDL_hid_get_input_report,(SDL_hid_device *a, unsigned char *b, size_t c),(a,b,c),return) +SDL_DYNAPI_PROC(SDL_hid_device_info*,SDL_hid_get_device_info,(SDL_hid_device *a),(a),return) +SDL_DYNAPI_PROC(int,SDL_hid_get_report_descriptor,(SDL_hid_device *a, unsigned char *b, size_t c),(a,b,c),return) diff --git a/src/hidapi/SDL_hidapi.c b/src/hidapi/SDL_hidapi.c index d8eefb2742..a15526b8f1 100644 --- a/src/hidapi/SDL_hidapi.c +++ b/src/hidapi/SDL_hidapi.c @@ -31,6 +31,9 @@ #include "SDL_hidapi_c.h" +/* Initial type declarations */ +#include "hidapi/hidapi.h" + #ifndef SDL_HIDAPI_DISABLED #if defined(__WIN32__) || defined(__WINGDK__) @@ -540,6 +543,7 @@ typedef struct PLATFORM_hid_device_ PLATFORM_hid_device; #define hid_get_device_info PLATFORM_hid_get_device_info #define hid_get_feature_report PLATFORM_hid_get_feature_report #define hid_get_indexed_string PLATFORM_hid_get_indexed_string +#define hid_get_input_report PLATFORM_hid_get_input_report #define hid_get_manufacturer_string PLATFORM_hid_get_manufacturer_string #define hid_get_product_string PLATFORM_hid_get_product_string #define hid_get_report_descriptor PLATFORM_hid_get_report_descriptor @@ -560,7 +564,9 @@ typedef struct PLATFORM_hid_device_ PLATFORM_hid_device; #define read_thread PLATFORM_read_thread #define return_data PLATFORM_return_data +/* Allow hidapi.h to be included in the platform implementation */ #undef HIDAPI_H__ + #ifdef __LINUX__ #ifdef SDL_USE_LIBUDEV @@ -633,6 +639,7 @@ static const SDL_UDEV_Symbols *udev_ctx = NULL; #undef hid_get_device_info #undef hid_get_feature_report #undef hid_get_indexed_string +#undef hid_get_input_report #undef hid_get_manufacturer_string #undef hid_get_product_string #undef hid_get_report_descriptor @@ -671,8 +678,10 @@ typedef struct DRIVER_hid_device_ DRIVER_hid_device; #define hid_error DRIVER_hid_error #define hid_exit DRIVER_hid_exit #define hid_free_enumeration DRIVER_hid_free_enumeration +#define hid_get_device_info DRIVER_hid_device_info #define hid_get_feature_report DRIVER_hid_get_feature_report #define hid_get_indexed_string DRIVER_hid_get_indexed_string +#define hid_get_input_report DRIVER_hid_get_input_report #define hid_get_manufacturer_string DRIVER_hid_get_manufacturer_string #define hid_get_product_string DRIVER_hid_get_product_string #define hid_get_report_descriptor DRIVER_hid_get_report_descriptor @@ -700,8 +709,10 @@ typedef struct DRIVER_hid_device_ DRIVER_hid_device; #undef hid_error #undef hid_exit #undef hid_free_enumeration +#undef hid_get_device_info #undef hid_get_feature_report #undef hid_get_indexed_string +#undef hid_get_input_report #undef hid_get_manufacturer_string #undef hid_get_product_string #undef hid_get_report_descriptor @@ -824,9 +835,10 @@ typedef struct LIBUSB_hid_device_ LIBUSB_hid_device; #define hid_error LIBUSB_hid_error #define hid_exit LIBUSB_hid_exit #define hid_free_enumeration LIBUSB_hid_free_enumeration +#define hid_get_device_info LIBUSB_hid_get_device_info #define hid_get_feature_report LIBUSB_hid_get_feature_report -#define hid_get_input_report LIBUSB_hid_get_input_report #define hid_get_indexed_string LIBUSB_hid_get_indexed_string +#define hid_get_input_report LIBUSB_hid_get_input_report #define hid_get_manufacturer_string LIBUSB_hid_get_manufacturer_string #define hid_get_product_string LIBUSB_hid_get_product_string #define hid_get_report_descriptor LIBUSB_hid_get_report_descriptor @@ -900,9 +912,10 @@ static int SDL_libusb_get_string_descriptor(libusb_device_handle *dev, #undef hid_error #undef hid_exit #undef hid_free_enumeration +#undef hid_get_device_info #undef hid_get_feature_report -#undef hid_get_input_report #undef hid_get_indexed_string +#undef hid_get_input_report #undef hid_get_manufacturer_string #undef hid_get_product_string #undef hid_get_report_descriptor @@ -935,11 +948,14 @@ struct hidapi_backend int (*hid_set_nonblocking)(void *device, int nonblock); int (*hid_send_feature_report)(void *device, const unsigned char *data, size_t length); int (*hid_get_feature_report)(void *device, unsigned char *data, size_t length); + int (*hid_get_input_report)(void *device, unsigned char *data, size_t length); void (*hid_close)(void *device); int (*hid_get_manufacturer_string)(void *device, wchar_t *string, size_t maxlen); int (*hid_get_product_string)(void *device, wchar_t *string, size_t maxlen); int (*hid_get_serial_number_string)(void *device, wchar_t *string, size_t maxlen); int (*hid_get_indexed_string)(void *device, int string_index, wchar_t *string, size_t maxlen); + struct hid_device_info *(*hid_get_device_info)(void *device); + int (*hid_get_report_descriptor)(void *device, unsigned char *buf, size_t buf_size); const wchar_t *(*hid_error)(void *device); }; @@ -951,11 +967,14 @@ static const struct hidapi_backend PLATFORM_Backend = { (void *)PLATFORM_hid_set_nonblocking, (void *)PLATFORM_hid_send_feature_report, (void *)PLATFORM_hid_get_feature_report, + (void *)PLATFORM_hid_get_input_report, (void *)PLATFORM_hid_close, (void *)PLATFORM_hid_get_manufacturer_string, (void *)PLATFORM_hid_get_product_string, (void *)PLATFORM_hid_get_serial_number_string, (void *)PLATFORM_hid_get_indexed_string, + (void *)PLATFORM_hid_get_device_info, + (void *)PLATFORM_hid_get_report_descriptor, (void *)PLATFORM_hid_error }; #endif /* HAVE_PLATFORM_BACKEND */ @@ -968,11 +987,14 @@ static const struct hidapi_backend DRIVER_Backend = { (void *)DRIVER_hid_set_nonblocking, (void *)DRIVER_hid_send_feature_report, (void *)DRIVER_hid_get_feature_report, + (void *)DRIVER_hid_get_input_report, (void *)DRIVER_hid_close, (void *)DRIVER_hid_get_manufacturer_string, (void *)DRIVER_hid_get_product_string, (void *)DRIVER_hid_get_serial_number_string, (void *)DRIVER_hid_get_indexed_string, + (void *)DRIVER_hid_get_device_info, + (void *)DRIVER_hid_get_report_descriptor, (void *)DRIVER_hid_error }; #endif /* HAVE_DRIVER_BACKEND */ @@ -985,11 +1007,14 @@ static const struct hidapi_backend LIBUSB_Backend = { (void *)LIBUSB_hid_set_nonblocking, (void *)LIBUSB_hid_send_feature_report, (void *)LIBUSB_hid_get_feature_report, + (void *)LIBUSB_hid_get_input_report, (void *)LIBUSB_hid_close, (void *)LIBUSB_hid_get_manufacturer_string, (void *)LIBUSB_hid_get_product_string, (void *)LIBUSB_hid_get_serial_number_string, (void *)LIBUSB_hid_get_indexed_string, + (void *)LIBUSB_hid_get_device_info, + (void *)LIBUSB_hid_get_report_descriptor, (void *)LIBUSB_hid_error }; #endif /* HAVE_LIBUSB */ @@ -999,6 +1024,7 @@ struct SDL_hid_device_ const void *magic; void *device; const struct hidapi_backend *backend; + SDL_hid_device_info info; }; static char device_magic; @@ -1010,15 +1036,19 @@ static SDL_hid_device *CreateHIDDeviceWrapper(void *device, const struct hidapi_ wrapper->magic = &device_magic; wrapper->device = device; wrapper->backend = backend; + SDL_zero(wrapper->info); return wrapper; } #endif /* HAVE_PLATFORM_BACKEND || HAVE_DRIVER_BACKEND || HAVE_LIBUSB */ -static void -DeleteHIDDeviceWrapper(SDL_hid_device *device) +static void DeleteHIDDeviceWrapper(SDL_hid_device *device) { device->magic = NULL; + SDL_free(device->info.path); + SDL_free(device->info.serial_number); + SDL_free(device->info.manufacturer_string); + SDL_free(device->info.product_string); SDL_free(device); } @@ -1028,9 +1058,6 @@ DeleteHIDDeviceWrapper(SDL_hid_device *device) return retval; \ } -#ifndef SDL_HIDAPI_DISABLED -#if defined(HAVE_PLATFORM_BACKEND) || defined(HAVE_DRIVER_BACKEND) || defined(HAVE_LIBUSB) - #define COPY_IF_EXISTS(var) \ if (pSrc->var != NULL) { \ pDst->var = SDL_strdup(pSrc->var); \ @@ -1044,8 +1071,7 @@ DeleteHIDDeviceWrapper(SDL_hid_device *device) pDst->var = NULL; \ } -static void -CopyHIDDeviceInfo(struct hid_device_info *pSrc, struct SDL_hid_device_info *pDst) +static void CopyHIDDeviceInfo(struct hid_device_info *pSrc, struct SDL_hid_device_info *pDst) { COPY_IF_EXISTS(path) pDst->vendor_id = pSrc->vendor_id; @@ -1060,15 +1086,13 @@ CopyHIDDeviceInfo(struct hid_device_info *pSrc, struct SDL_hid_device_info *pDst pDst->interface_class = pSrc->interface_class; pDst->interface_subclass = pSrc->interface_subclass; pDst->interface_protocol = pSrc->interface_protocol; + pDst->bus_type = (SDL_hid_bus_type)pSrc->bus_type; pDst->next = NULL; } #undef COPY_IF_EXISTS #undef WCOPY_IF_EXISTS -#endif /* HAVE_PLATFORM_BACKEND || HAVE_DRIVER_BACKEND || HAVE_LIBUSB */ -#endif /* !SDL_HIDAPI_DISABLED */ - static int SDL_hidapi_refcount = 0; static void SDL_SetHIDAPIError(const wchar_t *error) @@ -1451,7 +1475,7 @@ SDL_hid_device *SDL_hid_open(unsigned short vendor_id, unsigned short product_id return NULL; } -SDL_hid_device *SDL_hid_open_path(const char *path, int bExclusive /* = false */) +SDL_hid_device *SDL_hid_open_path(const char *path) { #if defined(HAVE_PLATFORM_BACKEND) || defined(HAVE_DRIVER_BACKEND) || defined(HAVE_LIBUSB) void *pDevice = NULL; @@ -1568,6 +1592,19 @@ int SDL_hid_get_feature_report(SDL_hid_device *device, unsigned char *data, size return result; } +int SDL_hid_get_input_report(SDL_hid_device *device, unsigned char *data, size_t length) +{ + int result; + + CHECK_DEVICE_MAGIC(device, -1); + + result = device->backend->hid_get_input_report(device->device, data, length); + if (result < 0) { + SDL_SetHIDAPIError(device->backend->hid_error(device->device)); + } + return result; +} + int SDL_hid_close(SDL_hid_device *device) { CHECK_DEVICE_MAGIC(device, -1); @@ -1629,6 +1666,35 @@ int SDL_hid_get_indexed_string(SDL_hid_device *device, int string_index, wchar_t return result; } +SDL_hid_device_info *SDL_hid_get_device_info(SDL_hid_device *device) +{ + struct hid_device_info *info; + + CHECK_DEVICE_MAGIC(device, NULL); + + info = device->backend->hid_get_device_info(device->device); + if (info) { + CopyHIDDeviceInfo(info, &device->info); + return &device->info; + } else { + SDL_SetHIDAPIError(device->backend->hid_error(device->device)); + return NULL; + } +} + +int SDL_hid_get_report_descriptor(SDL_hid_device *device, unsigned char *buf, size_t buf_size) +{ + int result; + + CHECK_DEVICE_MAGIC(device, -1); + + result = device->backend->hid_get_report_descriptor(device->device, buf, buf_size); + if (result < 0) { + SDL_SetHIDAPIError(device->backend->hid_error(device->device)); + } + return result; +} + void SDL_hid_ble_scan(SDL_bool active) { #if !defined(SDL_HIDAPI_DISABLED) && (defined(__IOS__) || defined(__TVOS__)) diff --git a/src/joystick/hidapi/SDL_hidapijoystick.c b/src/joystick/hidapi/SDL_hidapijoystick.c index daea81cd56..a7ae20eacf 100644 --- a/src/joystick/hidapi/SDL_hidapijoystick.c +++ b/src/joystick/hidapi/SDL_hidapijoystick.c @@ -459,7 +459,7 @@ static void HIDAPI_SetupDeviceDriver(SDL_HIDAPI_Device *device, SDL_bool *remove SDL_UnlockJoysticks(); } - dev = SDL_hid_open_path(path, 0); + dev = SDL_hid_open_path(path); while (lock_count > 0) { --lock_count; @@ -490,7 +490,7 @@ static void HIDAPI_SetupDeviceDriver(SDL_HIDAPI_Device *device, SDL_bool *remove * * See https://github.com/libsdl-org/SDL/issues/7304 for details. */ - dev = SDL_hid_open_path(device->path, 0); + dev = SDL_hid_open_path(device->path); #endif if (dev == NULL) {