From 2af947a9b2c3a9cbd6409da66ccf3155ae44e898 Mon Sep 17 00:00:00 2001 From: Jake Date: Fri, 28 Apr 2017 19:32:22 -0500 Subject: [PATCH 1/7] Implement hid_write_control, so we can use HidD_SetOutputReport on win, all others are just a wrapper until tested --- hidapi/hidapi.h | 5 +++++ libusb/hid.c | 5 +++++ linux/hid.c | 6 ++++++ mac/hid.c | 6 ++++++ windows/hid.c | 17 +++++++++++++++++ 5 files changed, 39 insertions(+) diff --git a/hidapi/hidapi.h b/hidapi/hidapi.h index efba3188..9dd06a5b 100644 --- a/hidapi/hidapi.h +++ b/hidapi/hidapi.h @@ -490,6 +490,11 @@ extern "C" { */ HID_API_EXPORT const char* HID_API_CALL hid_version_str(void); + /** RPCS3 EDIT: This attempts to write the output on the 'control' channel + Otherwise it's the exact same as hid_write + */ + int HID_API_EXPORT HID_API_CALL hid_write_control(hid_device *device, const unsigned char *data, size_t length); + #ifdef __cplusplus } #endif diff --git a/libusb/hid.c b/libusb/hid.c index 0b8aa1e7..c0b9e2ff 100644 --- a/libusb/hid.c +++ b/libusb/hid.c @@ -1022,6 +1022,11 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) } } +int HID_API_EXPORT hid_write_control(hid_device *dev, const unsigned char *data, size_t length) +{ + //RPCS3 TODO: Test if this needs to be changed for control if we ever use it + return hid_write(dev, data, length); +} int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length) { diff --git a/linux/hid.c b/linux/hid.c index 718541e5..c333df40 100644 --- a/linux/hid.c +++ b/linux/hid.c @@ -954,6 +954,12 @@ int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t return bytes_written; } +int HID_API_EXPORT hid_write_control(hid_device *dev, const unsigned char *data, size_t length) +{ + //RPCS3 TODO: Test if this needs to be changed for control if we ever use it + return hid_write(dev, data, length); +} + int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) { diff --git a/mac/hid.c b/mac/hid.c index d88a9ade..68f1a019 100644 --- a/mac/hid.c +++ b/mac/hid.c @@ -958,6 +958,12 @@ static int get_report(hid_device *dev, IOHIDReportType type, unsigned char *data return -1; } +int HID_API_EXPORT hid_write_control(hid_device *dev, const unsigned char *data, size_t length) +{ + //RPCS3 TODO: Test if this needs to be changed for control on mac if we ever use it + return hid_write(dev, data, length); +} + int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length) { return set_report(dev, kIOHIDReportTypeOutput, data, length); diff --git a/windows/hid.c b/windows/hid.c index 57cb78da..c7fecf31 100755 --- a/windows/hid.c +++ b/windows/hid.c @@ -122,6 +122,7 @@ static struct hid_api_version api_version = { typedef BOOLEAN (__stdcall *HidD_FreePreparsedData_)(PHIDP_PREPARSED_DATA preparsed_data); typedef NTSTATUS (__stdcall *HidP_GetCaps_)(PHIDP_PREPARSED_DATA preparsed_data, HIDP_CAPS *caps); typedef BOOLEAN (__stdcall *HidD_SetNumInputBuffers_)(HANDLE handle, ULONG number_buffers); + typedef BOOLEAN(__stdcall *HidD_SetOutputReport_)(HANDLE handle, PVOID data, ULONG length); static HidD_GetAttributes_ HidD_GetAttributes; static HidD_GetSerialNumberString_ HidD_GetSerialNumberString; @@ -135,6 +136,7 @@ static struct hid_api_version api_version = { static HidD_FreePreparsedData_ HidD_FreePreparsedData; static HidP_GetCaps_ HidP_GetCaps; static HidD_SetNumInputBuffers_ HidD_SetNumInputBuffers; + static HidD_SetOutputReport_ HidD_SetOutputReport; static HMODULE lib_handle = NULL; static BOOLEAN initialized = FALSE; @@ -234,6 +236,7 @@ static int lookup_functions() RESOLVE(HidD_FreePreparsedData); RESOLVE(HidP_GetCaps); RESOLVE(HidD_SetNumInputBuffers); + RESOLVE(HidD_SetOutputReport); #undef RESOLVE #if defined(__GNUC__) # pragma GCC diagnostic pop @@ -720,6 +723,20 @@ int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char * return function_result; } +int HID_API_EXPORT HID_API_CALL hid_write_control(hid_device *dev, const unsigned char *data, size_t length) +{ + DWORD bytes_written = length; + BOOL res; + + res = HidD_SetOutputReport(dev->device_handle, (PVOID)data, (ULONG)length); + + if (!res) { + register_error(dev, "SetOutputReport"); + bytes_written = -1; + } + + return length; +} int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) { From 81b4c17d8d1a3155cc7ecfc1c918d0ae2af420ef Mon Sep 17 00:00:00 2001 From: Aurora Date: Mon, 7 Aug 2017 23:56:27 +0200 Subject: [PATCH 2/7] Add CMake file for Mac --- mac/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 mac/CMakeLists.txt diff --git a/mac/CMakeLists.txt b/mac/CMakeLists.txt new file mode 100644 index 00000000..400da03f --- /dev/null +++ b/mac/CMakeLists.txt @@ -0,0 +1 @@ +add_library(hidapi-mac hid.c) \ No newline at end of file From f4849e51358dd23239abd7c26a2a447f51171700 Mon Sep 17 00:00:00 2001 From: Megamouse Date: Tue, 17 Nov 2020 02:23:39 +0100 Subject: [PATCH 3/7] Fix return value in hid_write_control This won't really fix anything, but might spare us some future headaches. --- windows/hid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/hid.c b/windows/hid.c index c7fecf31..63e9bd5d 100755 --- a/windows/hid.c +++ b/windows/hid.c @@ -735,7 +735,7 @@ int HID_API_EXPORT HID_API_CALL hid_write_control(hid_device *dev, const unsigne bytes_written = -1; } - return length; + return bytes_written; } int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) From d154a64eff60ea9c5634c3494fd672ec383f34a5 Mon Sep 17 00:00:00 2001 From: Jake Date: Wed, 26 Apr 2017 11:42:48 -0500 Subject: [PATCH 4/7] Add Cmake support --- libusb/CMakeLists.txt | 1 + linux/CMakeLists.txt | 1 + mac/CMakeLists.txt | 2 +- windows/CMakeLists.txt | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 libusb/CMakeLists.txt create mode 100644 linux/CMakeLists.txt create mode 100644 windows/CMakeLists.txt diff --git a/libusb/CMakeLists.txt b/libusb/CMakeLists.txt new file mode 100644 index 00000000..5bb2c086 --- /dev/null +++ b/libusb/CMakeLists.txt @@ -0,0 +1 @@ +add_library(hidapi-libusb hid.c) diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt new file mode 100644 index 00000000..f7984bd7 --- /dev/null +++ b/linux/CMakeLists.txt @@ -0,0 +1 @@ +add_library(hidapi-hidraw hid.c) diff --git a/mac/CMakeLists.txt b/mac/CMakeLists.txt index 400da03f..c1110cb1 100644 --- a/mac/CMakeLists.txt +++ b/mac/CMakeLists.txt @@ -1 +1 @@ -add_library(hidapi-mac hid.c) \ No newline at end of file +add_library(hidapi-mac hid.c) diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt new file mode 100644 index 00000000..387ac02a --- /dev/null +++ b/windows/CMakeLists.txt @@ -0,0 +1 @@ +add_library(hidapi-hid hid.c) \ No newline at end of file From 907965cc13bdf49ba56f4f88cc94d2c7d43cd1ea Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Mon, 10 Jul 2017 12:47:37 +0200 Subject: [PATCH 5/7] Link with udev While it works fine to link with udev later, the build will break if you use more extreme linker flags with undefined references to libudev. --- linux/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index f7984bd7..60c33c19 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -1 +1,2 @@ add_library(hidapi-hidraw hid.c) +target_link_libraries(hidapi-hidraw udev) From 01f601a1509bf9c67819fbf521df39644bab52d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Janiszewski?= Date: Fri, 30 Nov 2018 21:57:42 +0100 Subject: [PATCH 6/7] Add missing libraries to cmake target --- windows/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/windows/CMakeLists.txt b/windows/CMakeLists.txt index 387ac02a..873216a2 100644 --- a/windows/CMakeLists.txt +++ b/windows/CMakeLists.txt @@ -1 +1,2 @@ -add_library(hidapi-hid hid.c) \ No newline at end of file +add_library(hidapi-hid hid.c) +target_link_libraries(hidapi-hid setupapi) From 8427fa8be12fc42b2075505f44baa07c39e43ca8 Mon Sep 17 00:00:00 2001 From: nastys <@a.a> Date: Sat, 1 Jan 2022 02:37:06 +0100 Subject: [PATCH 7/7] Update macOS hid.c --- mac/hid.c | 164 +++++++++++++++++++++++------------------------------- 1 file changed, 71 insertions(+), 93 deletions(-) diff --git a/mac/hid.c b/mac/hid.c index 68f1a019..0ee5b10c 100644 --- a/mac/hid.c +++ b/mac/hid.c @@ -36,9 +36,6 @@ #include "hidapi.h" -/* As defined in AppKit.h, but we don't need the entire AppKit for a single constant. */ -extern const double NSAppKitVersionNumber; - /* Barrier implementation because Mac OSX doesn't have pthread_barrier. It also doesn't have clock_gettime(). So much for POSIX and SUSv2. This implementation came from Brent Priddy and was posted on @@ -191,7 +188,6 @@ static struct hid_api_version api_version = { }; static IOHIDManagerRef hid_mgr = 0x0; -static int is_macos_10_10_or_greater = 0; #if 0 @@ -314,64 +310,6 @@ static wchar_t *dup_wcs(const wchar_t *s) return ret; } -/* hidapi_IOHIDDeviceGetService() - * - * Return the io_service_t corresponding to a given IOHIDDeviceRef, either by: - * - on OS X 10.6 and above, calling IOHIDDeviceGetService() - * - on OS X 10.5, extract it from the IOHIDDevice struct - */ -static io_service_t hidapi_IOHIDDeviceGetService(IOHIDDeviceRef device) -{ - static void *iokit_framework = NULL; - typedef io_service_t (*dynamic_IOHIDDeviceGetService_t)(IOHIDDeviceRef device); - static dynamic_IOHIDDeviceGetService_t dynamic_IOHIDDeviceGetService = NULL; - - /* Use dlopen()/dlsym() to get a pointer to IOHIDDeviceGetService() if it exists. - * If any of these steps fail, dynamic_IOHIDDeviceGetService will be left NULL - * and the fallback method will be used. - */ - if (iokit_framework == NULL) { - iokit_framework = dlopen("/System/Library/Frameworks/IOKit.framework/IOKit", RTLD_LAZY); - - if (iokit_framework != NULL) - dynamic_IOHIDDeviceGetService = (dynamic_IOHIDDeviceGetService_t) dlsym(iokit_framework, "IOHIDDeviceGetService"); - } - - if (dynamic_IOHIDDeviceGetService != NULL) { - /* Running on OS X 10.6 and above: IOHIDDeviceGetService() exists */ - return dynamic_IOHIDDeviceGetService(device); - } - else - { - /* Running on OS X 10.5: IOHIDDeviceGetService() doesn't exist. - * - * Be naughty and pull the service out of the IOHIDDevice. - * IOHIDDevice is an opaque struct not exposed to applications, but its - * layout is stable through all available versions of OS X. - * Tested and working on OS X 10.5.8 i386, x86_64, and ppc. - */ - struct IOHIDDevice_internal { - /* The first field of the IOHIDDevice struct is a - * CFRuntimeBase (which is a private CF struct). - * - * a, b, and c are the 3 fields that make up a CFRuntimeBase. - * See http://opensource.apple.com/source/CF/CF-476.18/CFRuntime.h - * - * The second field of the IOHIDDevice is the io_service_t we're looking for. - */ - uintptr_t a; - uint8_t b[4]; -#if __LP64__ - uint32_t c; -#endif - io_service_t service; - }; - struct IOHIDDevice_internal *tmp = (struct IOHIDDevice_internal *) device; - - return tmp->service; - } -} - /* Initialize the IOHIDManager. Return 0 for success and -1 for failure. */ static int init_hid_manager(void) { @@ -402,7 +340,6 @@ HID_API_EXPORT const char* HID_API_CALL hid_version_str() int HID_API_EXPORT hid_init(void) { if (!hid_mgr) { - is_macos_10_10_or_greater = (NSAppKitVersionNumber >= 1343); /* NSAppKitVersionNumber10_10 */ return init_hid_manager(); } @@ -439,7 +376,7 @@ static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev, struct hid_device_info *cur_dev; io_object_t iokit_dev; kern_return_t res; - io_string_t path; + uint64_t entry_id; if (dev == NULL) { return NULL; @@ -459,13 +396,30 @@ static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev, /* Fill out the record */ cur_dev->next = NULL; - /* Fill in the path (IOService plane) */ - iokit_dev = hidapi_IOHIDDeviceGetService(dev); - res = IORegistryEntryGetPath(iokit_dev, kIOServicePlane, path); - if (res == KERN_SUCCESS) - cur_dev->path = strdup(path); - else + /* Fill in the path (as a unique ID of the service entry) */ + cur_dev->path = NULL; + iokit_dev = IOHIDDeviceGetService(dev); + if (iokit_dev != MACH_PORT_NULL) { + res = IORegistryEntryGetRegistryEntryID(iokit_dev, &entry_id); + } + else { + res = KERN_INVALID_ARGUMENT; + } + + if (res == KERN_SUCCESS) { + /* max value of entry_id(uint64_t) is 18446744073709551615 which is 20 characters long, + so for (max) "path" string 'DevSrvsID:18446744073709551615' we would need + 9+1+20+1=31 bytes byffer, but allocate 32 for simple alignment */ + cur_dev->path = calloc(1, 32); + if (cur_dev->path != NULL) { + sprintf(cur_dev->path, "DevSrvsID:%llu", entry_id); + } + } + + if (cur_dev->path == NULL) { + /* for whatever reason, trying to keep it a non-NULL string */ cur_dev->path = strdup(""); + } /* Serial Number */ get_serial_number(dev, buf, BUF_LEN); @@ -817,11 +771,33 @@ static void *read_thread(void *param) return NULL; } -/* hid_open_path() - * - * path must be a valid path to an IOHIDDevice in the IOService plane - * Example: "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/EHC1@1D,7/AppleUSBEHCI/PLAYSTATION(R)3 Controller@fd120000/IOUSBInterface@0/IOUSBHIDDriver" - */ +/* \p path must be one of: + - in format 'DevSrvsID:' (as returned by hid_enumerate); + - a valid path to an IOHIDDevice in the IOService plane (as returned by IORegistryEntryGetPath, + e.g.: "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/EHC1@1D,7/AppleUSBEHCI/PLAYSTATION(R)3 Controller@fd120000/IOUSBInterface@0/IOUSBHIDDriver"); + Second format is for compatibility with paths accepted by older versions of HIDAPI. +*/ +static io_registry_entry_t hid_open_service_registry_from_path(const char *path) +{ + if (path == NULL) + return MACH_PORT_NULL; + + /* Get the IORegistry entry for the given path */ + if (strncmp("DevSrvsID:", path, 10) == 0) { + char *endptr; + uint64_t entry_id = strtoull(path + 10, &endptr, 10); + if (*endptr == '\0') { + return IOServiceGetMatchingService(kIOMasterPortDefault, IORegistryEntryIDMatching(entry_id)); + } + } + else { + /* Fallback to older format of the path */ + return IORegistryEntryFromPath(kIOMasterPortDefault, path); + } + + return MACH_PORT_NULL; +} + hid_device * HID_API_EXPORT hid_open_path(const char *path) { hid_device *dev = NULL; @@ -835,7 +811,7 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path) dev = new_hid_device(); /* Get the IORegistry entry for the given path */ - entry = IORegistryEntryFromPath(kIOMasterPortDefault, path); + entry = hid_open_service_registry_from_path(path); if (entry == MACH_PORT_NULL) { /* Path wasn't valid (maybe device was removed?) */ goto return_error; @@ -898,7 +874,13 @@ static int set_report(hid_device *dev, IOHIDReportType type, const unsigned char const unsigned char *data_to_send = data; CFIndex length_to_send = length; IOReturn res; - const unsigned char report_id = data[0]; + unsigned char report_id; + + if (!data || (length == 0)) { + return -1; + } + + report_id = data[0]; if (report_id == 0x0) { /* Not using numbered Reports. @@ -958,15 +940,15 @@ static int get_report(hid_device *dev, IOHIDReportType type, unsigned char *data return -1; } -int HID_API_EXPORT hid_write_control(hid_device *dev, const unsigned char *data, size_t length) +int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length) { - //RPCS3 TODO: Test if this needs to be changed for control on mac if we ever use it - return hid_write(dev, data, length); + return set_report(dev, kIOHIDReportTypeOutput, data, length); } -int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length) +int HID_API_EXPORT hid_write_control(hid_device *dev, const unsigned char *data, size_t length) { - return set_report(dev, kIOHIDReportTypeOutput, data, length); + //RPCS3 TODO: Test if this needs to be changed for control on mac if we ever use it + return hid_write(dev, data, length); } /* Helper function, so that this isn't duplicated in hid_read(). */ @@ -1134,14 +1116,12 @@ void HID_API_EXPORT hid_close(hid_device *dev) /* Disconnect the report callback before close. See comment below. */ - if (is_macos_10_10_or_greater || !dev->disconnected) { - IOHIDDeviceRegisterInputReportCallback( - dev->device_handle, dev->input_report_buf, dev->max_input_report_len, - NULL, dev); - IOHIDDeviceRegisterRemovalCallback(dev->device_handle, NULL, dev); - IOHIDDeviceUnscheduleFromRunLoop(dev->device_handle, dev->run_loop, dev->run_loop_mode); - IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetMain(), kCFRunLoopDefaultMode); - } + IOHIDDeviceRegisterInputReportCallback( + dev->device_handle, dev->input_report_buf, dev->max_input_report_len, + NULL, dev); + IOHIDDeviceRegisterRemovalCallback(dev->device_handle, NULL, dev); + IOHIDDeviceUnscheduleFromRunLoop(dev->device_handle, dev->run_loop, dev->run_loop_mode); + IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetMain(), kCFRunLoopDefaultMode); /* Cause read_thread() to stop. */ dev->shutdown_thread = 1; @@ -1165,9 +1145,7 @@ void HID_API_EXPORT hid_close(hid_device *dev) crash happenes if IOHIDDeviceClose() is not called. Not leaking a resource in all tested environments. */ - if (is_macos_10_10_or_greater || !dev->disconnected) { - IOHIDDeviceClose(dev->device_handle, kIOHIDOptionsTypeSeizeDevice); - } + IOHIDDeviceClose(dev->device_handle, kIOHIDOptionsTypeSeizeDevice); /* Clear out the queue of received reports. */ pthread_mutex_lock(&dev->mutex);