24
24
25
25
#include <IOKit/hid/IOHIDManager.h>
26
26
#include <IOKit/hid/IOHIDKeys.h>
27
+ #include <IOKit/IOKitLib.h>
27
28
#include <CoreFoundation/CoreFoundation.h>
28
29
#include <wchar.h>
29
30
#include <locale.h>
30
31
#include <pthread.h>
31
32
#include <sys/time.h>
32
33
#include <unistd.h>
34
+ #include <dlfcn.h>
33
35
34
36
#include "hidapi.h"
35
37
@@ -330,33 +332,61 @@ static wchar_t *dup_wcs(const wchar_t *s)
330
332
return ret ;
331
333
}
332
334
333
-
334
- static int make_path (IOHIDDeviceRef device , char * buf , size_t len )
335
+ /* hidapi_IOHIDDeviceGetService()
336
+ *
337
+ * Return the io_service_t corresponding to a given IOHIDDeviceRef, either by:
338
+ * - on OS X 10.6 and above, calling IOHIDDeviceGetService()
339
+ * - on OS X 10.5, extract it from the IOHIDDevice struct
340
+ */
341
+ static io_service_t hidapi_IOHIDDeviceGetService (IOHIDDeviceRef device )
335
342
{
336
- int res ;
337
- unsigned short vid , pid ;
338
- char transport [32 ];
339
- int32_t location ;
340
-
341
- buf [0 ] = '\0' ;
342
-
343
- res = get_string_property_utf8 (
344
- device , CFSTR (kIOHIDTransportKey ),
345
- transport , sizeof (transport ));
346
-
347
- if (!res )
348
- return -1 ;
349
-
350
- location = get_location_id (device );
351
- vid = get_vendor_id (device );
352
- pid = get_product_id (device );
353
-
354
- res = snprintf (buf , len , "%s_%04hx_%04hx_%x" ,
355
- transport , vid , pid , location );
343
+ static void * iokit_framework = NULL ;
344
+ static io_service_t (* dynamic_IOHIDDeviceGetService )(IOHIDDeviceRef device ) = NULL ;
345
+
346
+ /* Use dlopen()/dlsym() to get a pointer to IOHIDDeviceGetService() if it exists.
347
+ * If any of these steps fail, dynamic_IOHIDDeviceGetService will be left NULL
348
+ * and the fallback method will be used.
349
+ */
350
+ if (iokit_framework == NULL ) {
351
+ iokit_framework = dlopen ("/System/Library/IOKit.framework/IOKit" , RTLD_LAZY );
352
+
353
+ if (iokit_framework != NULL )
354
+ dynamic_IOHIDDeviceGetService = dlsym (iokit_framework , "IOHIDDeviceGetService" );
355
+ }
356
356
357
+ if (dynamic_IOHIDDeviceGetService != NULL ) {
358
+ /* Running on OS X 10.6 and above: IOHIDDeviceGetService() exists */
359
+ return dynamic_IOHIDDeviceGetService (device );
360
+ }
361
+ else
362
+ {
363
+ /* Running on OS X 10.5: IOHIDDeviceGetService() doesn't exist.
364
+ *
365
+ * Be naughty and pull the service out of the IOHIDDevice.
366
+ * IOHIDDevice is an opaque struct not exposed to applications, but its
367
+ * layout is stable through all available versions of OS X.
368
+ * Tested and working on OS X 10.5.8 i386, x86_64, and ppc.
369
+ */
370
+ struct IOHIDDevice_internal {
371
+ /* The first field of the IOHIDDevice struct is a
372
+ * CFRuntimeBase (which is a private CF struct).
373
+ *
374
+ * a, b, and c are the 3 fields that make up a CFRuntimeBase.
375
+ * See http://opensource.apple.com/source/CF/CF-476.18/CFRuntime.h
376
+ *
377
+ * The second field of the IOHIDDevice is the io_service_t we're looking for.
378
+ */
379
+ uintptr_t a ;
380
+ uint8_t b [4 ];
381
+ #if __LP64__
382
+ uint32_t c ;
383
+ #endif
384
+ io_service_t service ;
385
+ };
386
+ struct IOHIDDevice_internal * tmp = (struct IOHIDDevice_internal * )device ;
357
387
358
- buf [ len - 1 ] = '\0' ;
359
- return res + 1 ;
388
+ return tmp -> service ;
389
+ }
360
390
}
361
391
362
392
/* 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,
434
464
unsigned short dev_pid ;
435
465
#define BUF_LEN 256
436
466
wchar_t buf [BUF_LEN ];
437
- char cbuf [BUF_LEN ];
438
467
439
468
IOHIDDeviceRef dev = device_array [i ];
440
469
@@ -448,7 +477,9 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
448
477
if ((vendor_id == 0x0 || vendor_id == dev_vid ) &&
449
478
(product_id == 0x0 || product_id == dev_pid )) {
450
479
struct hid_device_info * tmp ;
451
- size_t len ;
480
+ io_object_t iokit_dev ;
481
+ kern_return_t res ;
482
+ io_string_t path ;
452
483
453
484
/* VID/PID match. Create the record. */
454
485
tmp = malloc (sizeof (struct hid_device_info ));
@@ -466,8 +497,14 @@ struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id,
466
497
467
498
/* Fill out the record */
468
499
cur_dev -> next = NULL ;
469
- len = make_path (dev , cbuf , sizeof (cbuf ));
470
- cur_dev -> path = strdup (cbuf );
500
+
501
+ /* Fill in the path (IOService plane) */
502
+ iokit_dev = hidapi_IOHIDDeviceGetService (dev );
503
+ res = IORegistryEntryGetPath (iokit_dev , kIOServicePlane , path );
504
+ if (res == KERN_SUCCESS )
505
+ cur_dev -> path = strdup (path );
506
+ else
507
+ cur_dev -> path = strdup ("" );
471
508
472
509
/* Serial Number */
473
510
get_serial_number (dev , buf , BUF_LEN );
@@ -681,76 +718,77 @@ static void *read_thread(void *param)
681
718
return NULL ;
682
719
}
683
720
721
+ /* hid_open_path()
722
+ *
723
+ * path must be a valid path to an IOHIDDevice in the IOService plane
724
+ * Example: "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/EHC1@1D,7/AppleUSBEHCI/PLAYSTATION(R)3 Controller@fd120000/IOUSBInterface@0/IOUSBHIDDriver"
725
+ */
684
726
hid_device * HID_API_EXPORT hid_open_path (const char * path )
685
727
{
686
- int i ;
687
728
hid_device * dev = NULL ;
688
- CFIndex num_devices ;
729
+ io_registry_entry_t entry = MACH_PORT_NULL ;
689
730
690
731
dev = new_hid_device ();
691
732
692
733
/* Set up the HID Manager if it hasn't been done */
693
734
if (hid_init () < 0 )
694
735
return NULL ;
695
736
696
- /* give the IOHIDManager a chance to update itself */
697
- process_pending_events ();
737
+ /* Get the IORegistry entry for the given path */
738
+ entry = IORegistryEntryFromPath (kIOMasterPortDefault , path );
739
+ if (entry == MACH_PORT_NULL ) {
740
+ /* Path wasn't valid (maybe device was removed?) */
741
+ goto return_error ;
742
+ }
698
743
699
- CFSetRef device_set = IOHIDManagerCopyDevices (hid_mgr );
744
+ /* Create an IOHIDDevice for the entry */
745
+ dev -> device_handle = IOHIDDeviceCreate (kCFAllocatorDefault , entry );
746
+ if (dev -> device_handle == NULL ) {
747
+ /* Error creating the HID device */
748
+ goto return_error ;
749
+ }
700
750
701
- num_devices = CFSetGetCount (device_set );
702
- IOHIDDeviceRef * device_array = calloc (num_devices , sizeof (IOHIDDeviceRef ));
703
- CFSetGetValues (device_set , (const void * * ) device_array );
704
- for (i = 0 ; i < num_devices ; i ++ ) {
705
- char cbuf [BUF_LEN ];
706
- size_t len ;
707
- IOHIDDeviceRef os_dev = device_array [i ];
708
-
709
- len = make_path (os_dev , cbuf , sizeof (cbuf ));
710
- if (!strcmp (cbuf , path )) {
711
- /* Matched Paths. Open this Device. */
712
- IOReturn ret = IOHIDDeviceOpen (os_dev , kIOHIDOptionsTypeSeizeDevice );
713
- if (ret == kIOReturnSuccess ) {
714
- char str [32 ];
715
-
716
- free (device_array );
717
- CFRetain (os_dev );
718
- CFRelease (device_set );
719
- dev -> device_handle = os_dev ;
720
-
721
- /* Create the buffers for receiving data */
722
- dev -> max_input_report_len = (CFIndex ) get_max_report_length (os_dev );
723
- dev -> input_report_buf = calloc (dev -> max_input_report_len , sizeof (uint8_t ));
724
-
725
- /* Create the Run Loop Mode for this device.
726
- printing the reference seems to work. */
727
- sprintf (str , "HIDAPI_%p" , os_dev );
728
- dev -> run_loop_mode =
729
- CFStringCreateWithCString (NULL , str , kCFStringEncodingASCII );
730
-
731
- /* Attach the device to a Run Loop */
732
- IOHIDDeviceRegisterInputReportCallback (
733
- os_dev , dev -> input_report_buf , dev -> max_input_report_len ,
734
- & hid_report_callback , dev );
735
- IOHIDDeviceRegisterRemovalCallback (dev -> device_handle , hid_device_removal_callback , dev );
736
-
737
- /* Start the read thread */
738
- pthread_create (& dev -> thread , NULL , read_thread , dev );
739
-
740
- /* Wait here for the read thread to be initialized. */
741
- pthread_barrier_wait (& dev -> barrier );
742
-
743
- return dev ;
744
- }
745
- else {
746
- goto return_error ;
747
- }
748
- }
751
+ /* Open the IOHIDDevice */
752
+ IOReturn ret = IOHIDDeviceOpen (dev -> device_handle , kIOHIDOptionsTypeSeizeDevice );
753
+ if (ret == kIOReturnSuccess ) {
754
+ char str [32 ];
755
+
756
+ /* Create the buffers for receiving data */
757
+ dev -> max_input_report_len = (CFIndex ) get_max_report_length (dev -> device_handle );
758
+ dev -> input_report_buf = calloc (dev -> max_input_report_len , sizeof (uint8_t ));
759
+
760
+ /* Create the Run Loop Mode for this device.
761
+ printing the reference seems to work. */
762
+ sprintf (str , "HIDAPI_%p" , dev -> device_handle );
763
+ dev -> run_loop_mode =
764
+ CFStringCreateWithCString (NULL , str , kCFStringEncodingASCII );
765
+
766
+ /* Attach the device to a Run Loop */
767
+ IOHIDDeviceRegisterInputReportCallback (
768
+ dev -> device_handle , dev -> input_report_buf , dev -> max_input_report_len ,
769
+ & hid_report_callback , dev );
770
+ IOHIDDeviceRegisterRemovalCallback (dev -> device_handle , hid_device_removal_callback , dev );
771
+
772
+ /* Start the read thread */
773
+ pthread_create (& dev -> thread , NULL , read_thread , dev );
774
+
775
+ /* Wait here for the read thread to be initialized. */
776
+ pthread_barrier_wait (& dev -> barrier );
777
+
778
+ IOObjectRelease (entry );
779
+ return dev ;
780
+ }
781
+ else {
782
+ goto return_error ;
749
783
}
750
784
751
785
return_error :
752
- free (device_array );
753
- CFRelease (device_set );
786
+ if (dev -> device_handle != NULL )
787
+ CFRelease (dev -> device_handle );
788
+
789
+ if (entry != MACH_PORT_NULL )
790
+ IOObjectRelease (entry );
791
+
754
792
free_hid_device (dev );
755
793
return NULL ;
756
794
}
0 commit comments