Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update macOS hid.c #8

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions hidapi/hidapi.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions libusb/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
add_library(hidapi-libusb hid.c)
5 changes: 5 additions & 0 deletions libusb/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
2 changes: 2 additions & 0 deletions linux/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
add_library(hidapi-hidraw hid.c)
target_link_libraries(hidapi-hidraw udev)
6 changes: 6 additions & 0 deletions linux/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
1 change: 1 addition & 0 deletions mac/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
add_library(hidapi-mac hid.c)
160 changes: 72 additions & 88 deletions mac/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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();
}

Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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:<RegistryEntryID>' (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;
Expand All @@ -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;
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -963,6 +945,12 @@ int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t
return set_report(dev, kIOHIDReportTypeOutput, data, length);
}

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);
}

/* Helper function, so that this isn't duplicated in hid_read(). */
static int return_data(hid_device *dev, unsigned char *data, size_t length)
{
Expand Down Expand Up @@ -1128,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;
Expand All @@ -1159,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);
Expand Down
2 changes: 2 additions & 0 deletions windows/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
add_library(hidapi-hid hid.c)
target_link_libraries(hidapi-hid setupapi)
17 changes: 17 additions & 0 deletions windows/hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 bytes_written;
}

int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
{
Expand Down