mirror of
https://github.com/RPCS3/hidapi.git
synced 2026-07-19 21:43:36 -04:00
Mac: Use IOKit IOService:/ paths for devices
Change hid_enumerate() and hid_open_path() to use IOKit IOService:/ paths to uniquely identify devices, instead of creating a path (done by the make_path() function). The path passed to hid_open_path() now must be a valid path to an IOHIDDevice in the IOService plane, like: "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/EHC1@1D,7/ AppleUSBEHCI/PLAYSTATION(R)3 Controller@fd120000/IOUSBInterface@0/IOUSBHIDDriver"
This commit is contained in:
@@ -24,12 +24,14 @@
|
||||
|
||||
#include <IOKit/hid/IOHIDManager.h>
|
||||
#include <IOKit/hid/IOHIDKeys.h>
|
||||
#include <IOKit/IOKitLib.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <wchar.h>
|
||||
#include <locale.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "hidapi.h"
|
||||
|
||||
@@ -330,33 +332,61 @@ static wchar_t *dup_wcs(const wchar_t *s)
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
static int make_path(IOHIDDeviceRef device, char *buf, size_t len)
|
||||
/* 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)
|
||||
{
|
||||
int res;
|
||||
unsigned short vid, pid;
|
||||
char transport[32];
|
||||
int32_t location;
|
||||
static void *iokit_framework = NULL;
|
||||
static io_service_t (*dynamic_IOHIDDeviceGetService)(IOHIDDeviceRef device) = NULL;
|
||||
|
||||
buf[0] = '\0';
|
||||
/* 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/IOKit.framework/IOKit", RTLD_LAZY);
|
||||
|
||||
res = get_string_property_utf8(
|
||||
device, CFSTR(kIOHIDTransportKey),
|
||||
transport, sizeof(transport));
|
||||
if (iokit_framework != NULL)
|
||||
dynamic_IOHIDDeviceGetService = dlsym(iokit_framework, "IOHIDDeviceGetService");
|
||||
}
|
||||
|
||||
if (!res)
|
||||
return -1;
|
||||
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;
|
||||
|
||||
location = get_location_id(device);
|
||||
vid = get_vendor_id(device);
|
||||
pid = get_product_id(device);
|
||||
|
||||
res = snprintf(buf, len, "%s_%04hx_%04hx_%x",
|
||||
transport, vid, pid, location);
|
||||
|
||||
|
||||
buf[len-1] = '\0';
|
||||
return res+1;
|
||||
return tmp->service;
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize the IOHIDManager. Return 0 for success and -1 for failure. */
|
||||
@@ -434,7 +464,6 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
|
||||
unsigned short dev_pid;
|
||||
#define BUF_LEN 256
|
||||
wchar_t buf[BUF_LEN];
|
||||
char cbuf[BUF_LEN];
|
||||
|
||||
IOHIDDeviceRef dev = device_array[i];
|
||||
|
||||
@@ -448,7 +477,9 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
|
||||
if ((vendor_id == 0x0 || vendor_id == dev_vid) &&
|
||||
(product_id == 0x0 || product_id == dev_pid)) {
|
||||
struct hid_device_info *tmp;
|
||||
size_t len;
|
||||
io_object_t iokit_dev;
|
||||
kern_return_t res;
|
||||
io_string_t path;
|
||||
|
||||
/* VID/PID match. Create the record. */
|
||||
tmp = malloc(sizeof(struct hid_device_info));
|
||||
@@ -466,8 +497,14 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
|
||||
|
||||
/* Fill out the record */
|
||||
cur_dev->next = NULL;
|
||||
len = make_path(dev, cbuf, sizeof(cbuf));
|
||||
cur_dev->path = strdup(cbuf);
|
||||
|
||||
/* 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
|
||||
cur_dev->path = strdup("");
|
||||
|
||||
/* Serial Number */
|
||||
get_serial_number(dev, buf, BUF_LEN);
|
||||
@@ -681,11 +718,15 @@ 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"
|
||||
*/
|
||||
hid_device * HID_API_EXPORT hid_open_path(const char *path)
|
||||
{
|
||||
int i;
|
||||
hid_device *dev = NULL;
|
||||
CFIndex num_devices;
|
||||
io_registry_entry_t entry = MACH_PORT_NULL;
|
||||
|
||||
dev = new_hid_device();
|
||||
|
||||
@@ -693,64 +734,61 @@ hid_device * HID_API_EXPORT hid_open_path(const char *path)
|
||||
if (hid_init() < 0)
|
||||
return NULL;
|
||||
|
||||
/* give the IOHIDManager a chance to update itself */
|
||||
process_pending_events();
|
||||
/* Get the IORegistry entry for the given path */
|
||||
entry = IORegistryEntryFromPath(kIOMasterPortDefault, path);
|
||||
if (entry == MACH_PORT_NULL) {
|
||||
/* Path wasn't valid (maybe device was removed?) */
|
||||
goto return_error;
|
||||
}
|
||||
|
||||
CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr);
|
||||
/* Create an IOHIDDevice for the entry */
|
||||
dev->device_handle = IOHIDDeviceCreate(kCFAllocatorDefault, entry);
|
||||
if (dev->device_handle == NULL) {
|
||||
/* Error creating the HID device */
|
||||
goto return_error;
|
||||
}
|
||||
|
||||
num_devices = CFSetGetCount(device_set);
|
||||
IOHIDDeviceRef *device_array = calloc(num_devices, sizeof(IOHIDDeviceRef));
|
||||
CFSetGetValues(device_set, (const void **) device_array);
|
||||
for (i = 0; i < num_devices; i++) {
|
||||
char cbuf[BUF_LEN];
|
||||
size_t len;
|
||||
IOHIDDeviceRef os_dev = device_array[i];
|
||||
/* Open the IOHIDDevice */
|
||||
IOReturn ret = IOHIDDeviceOpen(dev->device_handle, kIOHIDOptionsTypeSeizeDevice);
|
||||
if (ret == kIOReturnSuccess) {
|
||||
char str[32];
|
||||
|
||||
len = make_path(os_dev, cbuf, sizeof(cbuf));
|
||||
if (!strcmp(cbuf, path)) {
|
||||
/* Matched Paths. Open this Device. */
|
||||
IOReturn ret = IOHIDDeviceOpen(os_dev, kIOHIDOptionsTypeSeizeDevice);
|
||||
if (ret == kIOReturnSuccess) {
|
||||
char str[32];
|
||||
/* Create the buffers for receiving data */
|
||||
dev->max_input_report_len = (CFIndex) get_max_report_length(dev->device_handle);
|
||||
dev->input_report_buf = calloc(dev->max_input_report_len, sizeof(uint8_t));
|
||||
|
||||
free(device_array);
|
||||
CFRetain(os_dev);
|
||||
CFRelease(device_set);
|
||||
dev->device_handle = os_dev;
|
||||
/* Create the Run Loop Mode for this device.
|
||||
printing the reference seems to work. */
|
||||
sprintf(str, "HIDAPI_%p", dev->device_handle);
|
||||
dev->run_loop_mode =
|
||||
CFStringCreateWithCString(NULL, str, kCFStringEncodingASCII);
|
||||
|
||||
/* Create the buffers for receiving data */
|
||||
dev->max_input_report_len = (CFIndex) get_max_report_length(os_dev);
|
||||
dev->input_report_buf = calloc(dev->max_input_report_len, sizeof(uint8_t));
|
||||
/* Attach the device to a Run Loop */
|
||||
IOHIDDeviceRegisterInputReportCallback(
|
||||
dev->device_handle, dev->input_report_buf, dev->max_input_report_len,
|
||||
&hid_report_callback, dev);
|
||||
IOHIDDeviceRegisterRemovalCallback(dev->device_handle, hid_device_removal_callback, dev);
|
||||
|
||||
/* Create the Run Loop Mode for this device.
|
||||
printing the reference seems to work. */
|
||||
sprintf(str, "HIDAPI_%p", os_dev);
|
||||
dev->run_loop_mode =
|
||||
CFStringCreateWithCString(NULL, str, kCFStringEncodingASCII);
|
||||
/* Start the read thread */
|
||||
pthread_create(&dev->thread, NULL, read_thread, dev);
|
||||
|
||||
/* Attach the device to a Run Loop */
|
||||
IOHIDDeviceRegisterInputReportCallback(
|
||||
os_dev, dev->input_report_buf, dev->max_input_report_len,
|
||||
&hid_report_callback, dev);
|
||||
IOHIDDeviceRegisterRemovalCallback(dev->device_handle, hid_device_removal_callback, dev);
|
||||
/* Wait here for the read thread to be initialized. */
|
||||
pthread_barrier_wait(&dev->barrier);
|
||||
|
||||
/* Start the read thread */
|
||||
pthread_create(&dev->thread, NULL, read_thread, dev);
|
||||
|
||||
/* Wait here for the read thread to be initialized. */
|
||||
pthread_barrier_wait(&dev->barrier);
|
||||
|
||||
return dev;
|
||||
}
|
||||
else {
|
||||
goto return_error;
|
||||
}
|
||||
}
|
||||
IOObjectRelease(entry);
|
||||
return dev;
|
||||
}
|
||||
else {
|
||||
goto return_error;
|
||||
}
|
||||
|
||||
return_error:
|
||||
free(device_array);
|
||||
CFRelease(device_set);
|
||||
if (dev->device_handle != NULL)
|
||||
CFRelease(dev->device_handle);
|
||||
|
||||
if (entry != MACH_PORT_NULL)
|
||||
IOObjectRelease(entry);
|
||||
|
||||
free_hid_device(dev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user