1 /*******************************************************
2 HIDAPI - Multi-Platform library for
3 communication with HID devices.
4
5 Alan Ott
6 Signal 11 Software
7
8 2010-07-03
9
10 Copyright 2010, All Rights Reserved.
11
12 At the discretion of the user of this library,
13 this software may be licensed under the terms of the
14 GNU General Public License v3, a BSD-Style license, or the
15 original HIDAPI license as outlined in the LICENSE.txt,
16 LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
17 files located at the root of the source distribution.
18 These files may also be found in the public source
19 code repository located at:
20 https://github.com/libusb/hidapi .
21 ********************************************************/
22
23 /* See Apple Technical Note TN2187 for details on IOHidManager. */
24
25 #include <IOKit/hid/IOHIDManager.h>
26 #include <IOKit/hid/IOHIDKeys.h>
27 #include <IOKit/IOKitLib.h>
28 #include <IOKit/usb/USBSpec.h>
29 #include <CoreFoundation/CoreFoundation.h>
30 #include <wchar.h>
31 #include <locale.h>
32 #include <pthread.h>
33 #include <sys/time.h>
34 #include <unistd.h>
35 #include <dlfcn.h>
36
37 #include "hidapi.h"
38
39 /* As defined in AppKit.h, but we don't need the entire AppKit for a single constant. */
40 extern const double NSAppKitVersionNumber;
41
42 /* Barrier implementation because Mac OSX doesn't have pthread_barrier.
43 It also doesn't have clock_gettime(). So much for POSIX and SUSv2.
44 This implementation came from Brent Priddy and was posted on
45 StackOverflow. It is used with his permission. */
46 typedef int pthread_barrierattr_t;
47 typedef struct pthread_barrier {
48 pthread_mutex_t mutex;
49 pthread_cond_t cond;
50 int count;
51 int trip_count;
52 } pthread_barrier_t;
53
pthread_barrier_init(pthread_barrier_t * barrier,const pthread_barrierattr_t * attr,unsigned int count)54 static int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count)
55 {
56 (void) attr;
57
58 if(count == 0) {
59 errno = EINVAL;
60 return -1;
61 }
62
63 if(pthread_mutex_init(&barrier->mutex, 0) < 0) {
64 return -1;
65 }
66 if(pthread_cond_init(&barrier->cond, 0) < 0) {
67 pthread_mutex_destroy(&barrier->mutex);
68 return -1;
69 }
70 barrier->trip_count = count;
71 barrier->count = 0;
72
73 return 0;
74 }
75
pthread_barrier_destroy(pthread_barrier_t * barrier)76 static int pthread_barrier_destroy(pthread_barrier_t *barrier)
77 {
78 pthread_cond_destroy(&barrier->cond);
79 pthread_mutex_destroy(&barrier->mutex);
80 return 0;
81 }
82
pthread_barrier_wait(pthread_barrier_t * barrier)83 static int pthread_barrier_wait(pthread_barrier_t *barrier)
84 {
85 pthread_mutex_lock(&barrier->mutex);
86 ++(barrier->count);
87 if(barrier->count >= barrier->trip_count)
88 {
89 barrier->count = 0;
90 pthread_cond_broadcast(&barrier->cond);
91 pthread_mutex_unlock(&barrier->mutex);
92 return 1;
93 }
94 else
95 {
96 pthread_cond_wait(&barrier->cond, &(barrier->mutex));
97 pthread_mutex_unlock(&barrier->mutex);
98 return 0;
99 }
100 }
101
102 static int return_data(hid_device *dev, unsigned char *data, size_t length);
103
104 /* Linked List of input reports received from the device. */
105 struct input_report {
106 uint8_t *data;
107 size_t len;
108 struct input_report *next;
109 };
110
111 struct hid_device_ {
112 IOHIDDeviceRef device_handle;
113 int blocking;
114 int uses_numbered_reports;
115 int disconnected;
116 CFStringRef run_loop_mode;
117 CFRunLoopRef run_loop;
118 CFRunLoopSourceRef source;
119 uint8_t *input_report_buf;
120 CFIndex max_input_report_len;
121 struct input_report *input_reports;
122
123 pthread_t thread;
124 pthread_mutex_t mutex; /* Protects input_reports */
125 pthread_cond_t condition;
126 pthread_barrier_t barrier; /* Ensures correct startup sequence */
127 pthread_barrier_t shutdown_barrier; /* Ensures correct shutdown sequence */
128 int shutdown_thread;
129 };
130
new_hid_device(void)131 static hid_device *new_hid_device(void)
132 {
133 hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device));
134 dev->device_handle = NULL;
135 dev->blocking = 1;
136 dev->uses_numbered_reports = 0;
137 dev->disconnected = 0;
138 dev->run_loop_mode = NULL;
139 dev->run_loop = NULL;
140 dev->source = NULL;
141 dev->input_report_buf = NULL;
142 dev->input_reports = NULL;
143 dev->shutdown_thread = 0;
144
145 /* Thread objects */
146 pthread_mutex_init(&dev->mutex, NULL);
147 pthread_cond_init(&dev->condition, NULL);
148 pthread_barrier_init(&dev->barrier, NULL, 2);
149 pthread_barrier_init(&dev->shutdown_barrier, NULL, 2);
150
151 return dev;
152 }
153
free_hid_device(hid_device * dev)154 static void free_hid_device(hid_device *dev)
155 {
156 if (!dev)
157 return;
158
159 /* Delete any input reports still left over. */
160 struct input_report *rpt = dev->input_reports;
161 while (rpt) {
162 struct input_report *next = rpt->next;
163 free(rpt->data);
164 free(rpt);
165 rpt = next;
166 }
167
168 /* Free the string and the report buffer. The check for NULL
169 is necessary here as CFRelease() doesn't handle NULL like
170 free() and others do. */
171 if (dev->run_loop_mode)
172 CFRelease(dev->run_loop_mode);
173 if (dev->source)
174 CFRelease(dev->source);
175 free(dev->input_report_buf);
176
177 /* Clean up the thread objects */
178 pthread_barrier_destroy(&dev->shutdown_barrier);
179 pthread_barrier_destroy(&dev->barrier);
180 pthread_cond_destroy(&dev->condition);
181 pthread_mutex_destroy(&dev->mutex);
182
183 /* Free the structure itself. */
184 free(dev);
185 }
186
187 static struct hid_api_version api_version = {
188 .major = HID_API_VERSION_MAJOR,
189 .minor = HID_API_VERSION_MINOR,
190 .patch = HID_API_VERSION_PATCH
191 };
192
193 static IOHIDManagerRef hid_mgr = 0x0;
194 static int is_macos_10_10_or_greater = 0;
195
196
197 #if 0
198 static void register_error(hid_device *dev, const char *op)
199 {
200
201 }
202 #endif
203
get_array_property(IOHIDDeviceRef device,CFStringRef key)204 static CFArrayRef get_array_property(IOHIDDeviceRef device, CFStringRef key)
205 {
206 CFTypeRef ref = IOHIDDeviceGetProperty(device, key);
207 if (ref != NULL && CFGetTypeID(ref) == CFArrayGetTypeID()) {
208 return (CFArrayRef)ref;
209 } else {
210 return NULL;
211 }
212 }
213
get_int_property(IOHIDDeviceRef device,CFStringRef key)214 static int32_t get_int_property(IOHIDDeviceRef device, CFStringRef key)
215 {
216 CFTypeRef ref;
217 int32_t value;
218
219 ref = IOHIDDeviceGetProperty(device, key);
220 if (ref) {
221 if (CFGetTypeID(ref) == CFNumberGetTypeID()) {
222 CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, &value);
223 return value;
224 }
225 }
226 return 0;
227 }
228
get_usage_pairs(IOHIDDeviceRef device)229 static CFArrayRef get_usage_pairs(IOHIDDeviceRef device)
230 {
231 return get_array_property(device, CFSTR(kIOHIDDeviceUsagePairsKey));
232 }
233
get_vendor_id(IOHIDDeviceRef device)234 static unsigned short get_vendor_id(IOHIDDeviceRef device)
235 {
236 return get_int_property(device, CFSTR(kIOHIDVendorIDKey));
237 }
238
get_product_id(IOHIDDeviceRef device)239 static unsigned short get_product_id(IOHIDDeviceRef device)
240 {
241 return get_int_property(device, CFSTR(kIOHIDProductIDKey));
242 }
243
get_max_report_length(IOHIDDeviceRef device)244 static int32_t get_max_report_length(IOHIDDeviceRef device)
245 {
246 return get_int_property(device, CFSTR(kIOHIDMaxInputReportSizeKey));
247 }
248
get_string_property(IOHIDDeviceRef device,CFStringRef prop,wchar_t * buf,size_t len)249 static int get_string_property(IOHIDDeviceRef device, CFStringRef prop, wchar_t *buf, size_t len)
250 {
251 CFStringRef str;
252
253 if (!len)
254 return 0;
255
256 str = (CFStringRef) IOHIDDeviceGetProperty(device, prop);
257
258 buf[0] = 0;
259
260 if (str) {
261 CFIndex str_len = CFStringGetLength(str);
262 CFRange range;
263 CFIndex used_buf_len;
264 CFIndex chars_copied;
265
266 len --;
267
268 range.location = 0;
269 range.length = ((size_t) str_len > len)? len: (size_t) str_len;
270 chars_copied = CFStringGetBytes(str,
271 range,
272 kCFStringEncodingUTF32LE,
273 (char) '?',
274 FALSE,
275 (UInt8*)buf,
276 len * sizeof(wchar_t),
277 &used_buf_len);
278
279 if (chars_copied <= 0)
280 buf[0] = 0;
281 else
282 buf[chars_copied] = 0;
283
284 return 0;
285 }
286 else
287 return -1;
288
289 }
290
get_serial_number(IOHIDDeviceRef device,wchar_t * buf,size_t len)291 static int get_serial_number(IOHIDDeviceRef device, wchar_t *buf, size_t len)
292 {
293 return get_string_property(device, CFSTR(kIOHIDSerialNumberKey), buf, len);
294 }
295
get_manufacturer_string(IOHIDDeviceRef device,wchar_t * buf,size_t len)296 static int get_manufacturer_string(IOHIDDeviceRef device, wchar_t *buf, size_t len)
297 {
298 return get_string_property(device, CFSTR(kIOHIDManufacturerKey), buf, len);
299 }
300
get_product_string(IOHIDDeviceRef device,wchar_t * buf,size_t len)301 static int get_product_string(IOHIDDeviceRef device, wchar_t *buf, size_t len)
302 {
303 return get_string_property(device, CFSTR(kIOHIDProductKey), buf, len);
304 }
305
306
307 /* Implementation of wcsdup() for Mac. */
dup_wcs(const wchar_t * s)308 static wchar_t *dup_wcs(const wchar_t *s)
309 {
310 size_t len = wcslen(s);
311 wchar_t *ret = (wchar_t*) malloc((len+1)*sizeof(wchar_t));
312 wcscpy(ret, s);
313
314 return ret;
315 }
316
317 /* hidapi_IOHIDDeviceGetService()
318 *
319 * Return the io_service_t corresponding to a given IOHIDDeviceRef, either by:
320 * - on OS X 10.6 and above, calling IOHIDDeviceGetService()
321 * - on OS X 10.5, extract it from the IOHIDDevice struct
322 */
hidapi_IOHIDDeviceGetService(IOHIDDeviceRef device)323 static io_service_t hidapi_IOHIDDeviceGetService(IOHIDDeviceRef device)
324 {
325 static void *iokit_framework = NULL;
326 typedef io_service_t (*dynamic_IOHIDDeviceGetService_t)(IOHIDDeviceRef device);
327 static dynamic_IOHIDDeviceGetService_t dynamic_IOHIDDeviceGetService = NULL;
328
329 /* Use dlopen()/dlsym() to get a pointer to IOHIDDeviceGetService() if it exists.
330 * If any of these steps fail, dynamic_IOHIDDeviceGetService will be left NULL
331 * and the fallback method will be used.
332 */
333 if (iokit_framework == NULL) {
334 iokit_framework = dlopen("/System/Library/Frameworks/IOKit.framework/IOKit", RTLD_LAZY);
335
336 if (iokit_framework != NULL)
337 dynamic_IOHIDDeviceGetService = (dynamic_IOHIDDeviceGetService_t) dlsym(iokit_framework, "IOHIDDeviceGetService");
338 }
339
340 if (dynamic_IOHIDDeviceGetService != NULL) {
341 /* Running on OS X 10.6 and above: IOHIDDeviceGetService() exists */
342 return dynamic_IOHIDDeviceGetService(device);
343 }
344 else
345 {
346 /* Running on OS X 10.5: IOHIDDeviceGetService() doesn't exist.
347 *
348 * Be naughty and pull the service out of the IOHIDDevice.
349 * IOHIDDevice is an opaque struct not exposed to applications, but its
350 * layout is stable through all available versions of OS X.
351 * Tested and working on OS X 10.5.8 i386, x86_64, and ppc.
352 */
353 struct IOHIDDevice_internal {
354 /* The first field of the IOHIDDevice struct is a
355 * CFRuntimeBase (which is a private CF struct).
356 *
357 * a, b, and c are the 3 fields that make up a CFRuntimeBase.
358 * See http://opensource.apple.com/source/CF/CF-476.18/CFRuntime.h
359 *
360 * The second field of the IOHIDDevice is the io_service_t we're looking for.
361 */
362 uintptr_t a;
363 uint8_t b[4];
364 #if __LP64__
365 uint32_t c;
366 #endif
367 io_service_t service;
368 };
369 struct IOHIDDevice_internal *tmp = (struct IOHIDDevice_internal *) device;
370
371 return tmp->service;
372 }
373 }
374
375 /* Initialize the IOHIDManager. Return 0 for success and -1 for failure. */
init_hid_manager(void)376 static int init_hid_manager(void)
377 {
378 /* Initialize all the HID Manager Objects */
379 hid_mgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
380 if (hid_mgr) {
381 IOHIDManagerSetDeviceMatching(hid_mgr, NULL);
382 IOHIDManagerScheduleWithRunLoop(hid_mgr, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
383 return 0;
384 }
385
386 return -1;
387 }
388
hid_version()389 HID_API_EXPORT const struct hid_api_version* HID_API_CALL hid_version()
390 {
391 return &api_version;
392 }
393
hid_version_str()394 HID_API_EXPORT const char* HID_API_CALL hid_version_str()
395 {
396 return HID_API_VERSION_STR;
397 }
398
399 /* Initialize the IOHIDManager if necessary. This is the public function, and
400 it is safe to call this function repeatedly. Return 0 for success and -1
401 for failure. */
hid_init(void)402 int HID_API_EXPORT hid_init(void)
403 {
404 if (!hid_mgr) {
405 is_macos_10_10_or_greater = (NSAppKitVersionNumber >= 1343); /* NSAppKitVersionNumber10_10 */
406 return init_hid_manager();
407 }
408
409 /* Already initialized. */
410 return 0;
411 }
412
hid_exit(void)413 int HID_API_EXPORT hid_exit(void)
414 {
415 if (hid_mgr) {
416 /* Close the HID manager. */
417 IOHIDManagerClose(hid_mgr, kIOHIDOptionsTypeNone);
418 CFRelease(hid_mgr);
419 hid_mgr = NULL;
420 }
421
422 return 0;
423 }
424
process_pending_events(void)425 static void process_pending_events(void) {
426 SInt32 res;
427 do {
428 res = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.001, FALSE);
429 } while(res != kCFRunLoopRunFinished && res != kCFRunLoopRunTimedOut);
430 }
431
create_device_info_with_usage(IOHIDDeviceRef dev,int32_t usage_page,int32_t usage)432 static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev, int32_t usage_page, int32_t usage)
433 {
434 unsigned short dev_vid;
435 unsigned short dev_pid;
436 int BUF_LEN = 256;
437 wchar_t buf[BUF_LEN];
438
439 struct hid_device_info *cur_dev;
440 io_object_t iokit_dev;
441 kern_return_t res;
442 io_string_t path;
443
444 if (dev == NULL) {
445 return NULL;
446 }
447
448 cur_dev = (struct hid_device_info *)calloc(1, sizeof(struct hid_device_info));
449 if (cur_dev == NULL) {
450 return NULL;
451 }
452
453 dev_vid = get_vendor_id(dev);
454 dev_pid = get_product_id(dev);
455
456 cur_dev->usage_page = usage_page;
457 cur_dev->usage = usage;
458
459 /* Fill out the record */
460 cur_dev->next = NULL;
461
462 /* Fill in the path (IOService plane) */
463 iokit_dev = hidapi_IOHIDDeviceGetService(dev);
464 res = IORegistryEntryGetPath(iokit_dev, kIOServicePlane, path);
465 if (res == KERN_SUCCESS)
466 cur_dev->path = strdup(path);
467 else
468 cur_dev->path = strdup("");
469
470 /* Serial Number */
471 get_serial_number(dev, buf, BUF_LEN);
472 cur_dev->serial_number = dup_wcs(buf);
473
474 /* Manufacturer and Product strings */
475 get_manufacturer_string(dev, buf, BUF_LEN);
476 cur_dev->manufacturer_string = dup_wcs(buf);
477 get_product_string(dev, buf, BUF_LEN);
478 cur_dev->product_string = dup_wcs(buf);
479
480 /* VID/PID */
481 cur_dev->vendor_id = dev_vid;
482 cur_dev->product_id = dev_pid;
483
484 /* Release Number */
485 cur_dev->release_number = get_int_property(dev, CFSTR(kIOHIDVersionNumberKey));
486
487 /* Interface Number */
488 /* We can only retrieve the interface number for USB HID devices.
489 * IOKit always seems to return 0 when querying a standard USB device
490 * for its interface. */
491 int is_usb_hid = get_int_property(dev, CFSTR(kUSBInterfaceClass)) == kUSBHIDClass;
492 if (is_usb_hid) {
493 /* Get the interface number */
494 cur_dev->interface_number = get_int_property(dev, CFSTR(kUSBInterfaceNumber));
495 } else {
496 cur_dev->interface_number = -1;
497 }
498
499 return cur_dev;
500 }
501
create_device_info(IOHIDDeviceRef device)502 static struct hid_device_info *create_device_info(IOHIDDeviceRef device)
503 {
504 const int32_t primary_usage_page = get_int_property(device, CFSTR(kIOHIDPrimaryUsagePageKey));
505 const int32_t primary_usage = get_int_property(device, CFSTR(kIOHIDPrimaryUsageKey));
506
507 /* Primary should always be first, to match previous behavior. */
508 struct hid_device_info *root = create_device_info_with_usage(device, primary_usage_page, primary_usage);
509 struct hid_device_info *cur = root;
510
511 if (!root)
512 return NULL;
513
514 CFArrayRef usage_pairs = get_usage_pairs(device);
515
516 if (usage_pairs != NULL) {
517 struct hid_device_info *next = NULL;
518 for (CFIndex i = 0; i < CFArrayGetCount(usage_pairs); i++) {
519 CFTypeRef dict = CFArrayGetValueAtIndex(usage_pairs, i);
520 if (CFGetTypeID(dict) != CFDictionaryGetTypeID()) {
521 continue;
522 }
523
524 CFTypeRef usage_page_ref, usage_ref;
525 int32_t usage_page, usage;
526
527 if (!CFDictionaryGetValueIfPresent((CFDictionaryRef)dict, CFSTR(kIOHIDDeviceUsagePageKey), &usage_page_ref) ||
528 !CFDictionaryGetValueIfPresent((CFDictionaryRef)dict, CFSTR(kIOHIDDeviceUsageKey), &usage_ref) ||
529 CFGetTypeID(usage_page_ref) != CFNumberGetTypeID() ||
530 CFGetTypeID(usage_ref) != CFNumberGetTypeID() ||
531 !CFNumberGetValue((CFNumberRef)usage_page_ref, kCFNumberSInt32Type, &usage_page) ||
532 !CFNumberGetValue((CFNumberRef)usage_ref, kCFNumberSInt32Type, &usage)) {
533 continue;
534 }
535 if (usage_page == primary_usage_page && usage == primary_usage)
536 continue; /* Already added. */
537
538 next = create_device_info_with_usage(device, usage_page, usage);
539 cur->next = next;
540 if (next != NULL) {
541 cur = next;
542 }
543 }
544 }
545
546 return root;
547 }
548
hid_enumerate(unsigned short vendor_id,unsigned short product_id)549 struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id)
550 {
551 struct hid_device_info *root = NULL; /* return object */
552 struct hid_device_info *cur_dev = NULL;
553 CFIndex num_devices;
554 int i;
555
556 /* Set up the HID Manager if it hasn't been done */
557 if (hid_init() < 0)
558 return NULL;
559
560 /* give the IOHIDManager a chance to update itself */
561 process_pending_events();
562
563 /* Get a list of the Devices */
564 CFMutableDictionaryRef matching = NULL;
565 if (vendor_id != 0 || product_id != 0) {
566 matching = CFDictionaryCreateMutable(kCFAllocatorDefault, kIOHIDOptionsTypeNone, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
567
568 if (matching && vendor_id != 0) {
569 CFNumberRef v = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &vendor_id);
570 CFDictionarySetValue(matching, CFSTR(kIOHIDVendorIDKey), v);
571 CFRelease(v);
572 }
573
574 if (matching && product_id != 0) {
575 CFNumberRef p = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &product_id);
576 CFDictionarySetValue(matching, CFSTR(kIOHIDProductIDKey), p);
577 CFRelease(p);
578 }
579 }
580 IOHIDManagerSetDeviceMatching(hid_mgr, matching);
581 if (matching != NULL) {
582 CFRelease(matching);
583 }
584
585 CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr);
586 if (device_set == NULL) {
587 return NULL;
588 }
589
590 /* Convert the list into a C array so we can iterate easily. */
591 num_devices = CFSetGetCount(device_set);
592 IOHIDDeviceRef *device_array = (IOHIDDeviceRef*) calloc(num_devices, sizeof(IOHIDDeviceRef));
593 CFSetGetValues(device_set, (const void **) device_array);
594
595 /* Iterate over each device, making an entry for it. */
596 for (i = 0; i < num_devices; i++) {
597
598 IOHIDDeviceRef dev = device_array[i];
599 if (!dev) {
600 continue;
601 }
602
603 struct hid_device_info *tmp = create_device_info(dev);
604 if (tmp == NULL) {
605 continue;
606 }
607
608 if (cur_dev) {
609 cur_dev->next = tmp;
610 }
611 else {
612 root = tmp;
613 }
614 cur_dev = tmp;
615
616 /* move the pointer to the tail of returnd list */
617 while (cur_dev->next != NULL) {
618 cur_dev = cur_dev->next;
619 }
620 }
621
622 free(device_array);
623 CFRelease(device_set);
624
625 return root;
626 }
627
hid_free_enumeration(struct hid_device_info * devs)628 void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs)
629 {
630 /* This function is identical to the Linux version. Platform independent. */
631 struct hid_device_info *d = devs;
632 while (d) {
633 struct hid_device_info *next = d->next;
634 free(d->path);
635 free(d->serial_number);
636 free(d->manufacturer_string);
637 free(d->product_string);
638 free(d);
639 d = next;
640 }
641 }
642
hid_open(unsigned short vendor_id,unsigned short product_id,const wchar_t * serial_number)643 hid_device * HID_API_EXPORT hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
644 {
645 /* This function is identical to the Linux version. Platform independent. */
646 struct hid_device_info *devs, *cur_dev;
647 const char *path_to_open = NULL;
648 hid_device * handle = NULL;
649
650 devs = hid_enumerate(vendor_id, product_id);
651 cur_dev = devs;
652 while (cur_dev) {
653 if (cur_dev->vendor_id == vendor_id &&
654 cur_dev->product_id == product_id) {
655 if (serial_number) {
656 if (wcscmp(serial_number, cur_dev->serial_number) == 0) {
657 path_to_open = cur_dev->path;
658 break;
659 }
660 }
661 else {
662 path_to_open = cur_dev->path;
663 break;
664 }
665 }
666 cur_dev = cur_dev->next;
667 }
668
669 if (path_to_open) {
670 /* Open the device */
671 handle = hid_open_path(path_to_open);
672 }
673
674 hid_free_enumeration(devs);
675
676 return handle;
677 }
678
hid_device_removal_callback(void * context,IOReturn result,void * sender)679 static void hid_device_removal_callback(void *context, IOReturn result,
680 void *sender)
681 {
682 (void) result;
683 (void) sender;
684
685 /* Stop the Run Loop for this device. */
686 hid_device *d = (hid_device*) context;
687
688 d->disconnected = 1;
689 CFRunLoopStop(d->run_loop);
690 }
691
692 /* The Run Loop calls this function for each input report received.
693 This function puts the data into a linked list to be picked up by
694 hid_read(). */
hid_report_callback(void * context,IOReturn result,void * sender,IOHIDReportType report_type,uint32_t report_id,uint8_t * report,CFIndex report_length)695 static void hid_report_callback(void *context, IOReturn result, void *sender,
696 IOHIDReportType report_type, uint32_t report_id,
697 uint8_t *report, CFIndex report_length)
698 {
699 (void) result;
700 (void) sender;
701 (void) report_type;
702 (void) report_id;
703
704 struct input_report *rpt;
705 hid_device *dev = (hid_device*) context;
706
707 /* Make a new Input Report object */
708 rpt = (struct input_report*) calloc(1, sizeof(struct input_report));
709 rpt->data = (uint8_t*) calloc(1, report_length);
710 memcpy(rpt->data, report, report_length);
711 rpt->len = report_length;
712 rpt->next = NULL;
713
714 /* Lock this section */
715 pthread_mutex_lock(&dev->mutex);
716
717 /* Attach the new report object to the end of the list. */
718 if (dev->input_reports == NULL) {
719 /* The list is empty. Put it at the root. */
720 dev->input_reports = rpt;
721 }
722 else {
723 /* Find the end of the list and attach. */
724 struct input_report *cur = dev->input_reports;
725 int num_queued = 0;
726 while (cur->next != NULL) {
727 cur = cur->next;
728 num_queued++;
729 }
730 cur->next = rpt;
731
732 /* Pop one off if we've reached 30 in the queue. This
733 way we don't grow forever if the user never reads
734 anything from the device. */
735 if (num_queued > 30) {
736 return_data(dev, NULL, 0);
737 }
738 }
739
740 /* Signal a waiting thread that there is data. */
741 pthread_cond_signal(&dev->condition);
742
743 /* Unlock */
744 pthread_mutex_unlock(&dev->mutex);
745
746 }
747
748 /* This gets called when the read_thread's run loop gets signaled by
749 hid_close(), and serves to stop the read_thread's run loop. */
perform_signal_callback(void * context)750 static void perform_signal_callback(void *context)
751 {
752 hid_device *dev = (hid_device*) context;
753 CFRunLoopStop(dev->run_loop); /*TODO: CFRunLoopGetCurrent()*/
754 }
755
read_thread(void * param)756 static void *read_thread(void *param)
757 {
758 hid_device *dev = (hid_device*) param;
759 SInt32 code;
760
761 /* Move the device's run loop to this thread. */
762 IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetCurrent(), dev->run_loop_mode);
763
764 /* Create the RunLoopSource which is used to signal the
765 event loop to stop when hid_close() is called. */
766 CFRunLoopSourceContext ctx;
767 memset(&ctx, 0, sizeof(ctx));
768 ctx.version = 0;
769 ctx.info = dev;
770 ctx.perform = &perform_signal_callback;
771 dev->source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0/*order*/, &ctx);
772 CFRunLoopAddSource(CFRunLoopGetCurrent(), dev->source, dev->run_loop_mode);
773
774 /* Store off the Run Loop so it can be stopped from hid_close()
775 and on device disconnection. */
776 dev->run_loop = CFRunLoopGetCurrent();
777
778 /* Notify the main thread that the read thread is up and running. */
779 pthread_barrier_wait(&dev->barrier);
780
781 /* Run the Event Loop. CFRunLoopRunInMode() will dispatch HID input
782 reports into the hid_report_callback(). */
783 while (!dev->shutdown_thread && !dev->disconnected) {
784 code = CFRunLoopRunInMode(dev->run_loop_mode, 1000/*sec*/, FALSE);
785 /* Return if the device has been disconnected */
786 if (code == kCFRunLoopRunFinished) {
787 dev->disconnected = 1;
788 break;
789 }
790
791
792 /* Break if The Run Loop returns Finished or Stopped. */
793 if (code != kCFRunLoopRunTimedOut &&
794 code != kCFRunLoopRunHandledSource) {
795 /* There was some kind of error. Setting
796 shutdown seems to make sense, but
797 there may be something else more appropriate */
798 dev->shutdown_thread = 1;
799 break;
800 }
801 }
802
803 /* Now that the read thread is stopping, Wake any threads which are
804 waiting on data (in hid_read_timeout()). Do this under a mutex to
805 make sure that a thread which is about to go to sleep waiting on
806 the condition actually will go to sleep before the condition is
807 signaled. */
808 pthread_mutex_lock(&dev->mutex);
809 pthread_cond_broadcast(&dev->condition);
810 pthread_mutex_unlock(&dev->mutex);
811
812 /* Wait here until hid_close() is called and makes it past
813 the call to CFRunLoopWakeUp(). This thread still needs to
814 be valid when that function is called on the other thread. */
815 pthread_barrier_wait(&dev->shutdown_barrier);
816
817 return NULL;
818 }
819
820 /* hid_open_path()
821 *
822 * path must be a valid path to an IOHIDDevice in the IOService plane
823 * Example: "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/EHC1@1D,7/AppleUSBEHCI/PLAYSTATION(R)3 Controller@fd120000/IOUSBInterface@0/IOUSBHIDDriver"
824 */
hid_open_path(const char * path)825 hid_device * HID_API_EXPORT hid_open_path(const char *path)
826 {
827 hid_device *dev = NULL;
828 io_registry_entry_t entry = MACH_PORT_NULL;
829 IOReturn ret = kIOReturnInvalid;
830
831 /* Set up the HID Manager if it hasn't been done */
832 if (hid_init() < 0)
833 goto return_error;
834
835 dev = new_hid_device();
836
837 /* Get the IORegistry entry for the given path */
838 entry = IORegistryEntryFromPath(kIOMasterPortDefault, path);
839 if (entry == MACH_PORT_NULL) {
840 /* Path wasn't valid (maybe device was removed?) */
841 goto return_error;
842 }
843
844 /* Create an IOHIDDevice for the entry */
845 dev->device_handle = IOHIDDeviceCreate(kCFAllocatorDefault, entry);
846 if (dev->device_handle == NULL) {
847 /* Error creating the HID device */
848 goto return_error;
849 }
850
851 /* Open the IOHIDDevice */
852 ret = IOHIDDeviceOpen(dev->device_handle, kIOHIDOptionsTypeSeizeDevice);
853 if (ret == kIOReturnSuccess) {
854 char str[32];
855
856 /* Create the buffers for receiving data */
857 dev->max_input_report_len = (CFIndex) get_max_report_length(dev->device_handle);
858 dev->input_report_buf = (uint8_t*) calloc(dev->max_input_report_len, sizeof(uint8_t));
859
860 /* Create the Run Loop Mode for this device.
861 printing the reference seems to work. */
862 sprintf(str, "HIDAPI_%p", (void*) dev->device_handle);
863 dev->run_loop_mode =
864 CFStringCreateWithCString(NULL, str, kCFStringEncodingASCII);
865
866 /* Attach the device to a Run Loop */
867 IOHIDDeviceRegisterInputReportCallback(
868 dev->device_handle, dev->input_report_buf, dev->max_input_report_len,
869 &hid_report_callback, dev);
870 IOHIDDeviceRegisterRemovalCallback(dev->device_handle, hid_device_removal_callback, dev);
871
872 /* Start the read thread */
873 pthread_create(&dev->thread, NULL, read_thread, dev);
874
875 /* Wait here for the read thread to be initialized. */
876 pthread_barrier_wait(&dev->barrier);
877
878 IOObjectRelease(entry);
879 return dev;
880 }
881 else {
882 goto return_error;
883 }
884
885 return_error:
886 if (dev->device_handle != NULL)
887 CFRelease(dev->device_handle);
888
889 if (entry != MACH_PORT_NULL)
890 IOObjectRelease(entry);
891
892 free_hid_device(dev);
893 return NULL;
894 }
895
set_report(hid_device * dev,IOHIDReportType type,const unsigned char * data,size_t length)896 static int set_report(hid_device *dev, IOHIDReportType type, const unsigned char *data, size_t length)
897 {
898 const unsigned char *data_to_send = data;
899 CFIndex length_to_send = length;
900 IOReturn res;
901 const unsigned char report_id = data[0];
902
903 if (report_id == 0x0) {
904 /* Not using numbered Reports.
905 Don't send the report number. */
906 data_to_send = data+1;
907 length_to_send = length-1;
908 }
909
910 /* Avoid crash if the device has been unplugged. */
911 if (dev->disconnected) {
912 return -1;
913 }
914
915 res = IOHIDDeviceSetReport(dev->device_handle,
916 type,
917 report_id,
918 data_to_send, length_to_send);
919
920 if (res == kIOReturnSuccess) {
921 return length;
922 }
923
924 return -1;
925 }
926
get_report(hid_device * dev,IOHIDReportType type,unsigned char * data,size_t length)927 static int get_report(hid_device *dev, IOHIDReportType type, unsigned char *data, size_t length)
928 {
929 unsigned char *report = data;
930 CFIndex report_length = length;
931 IOReturn res = kIOReturnSuccess;
932 const unsigned char report_id = data[0];
933
934 if (report_id == 0x0) {
935 /* Not using numbered Reports.
936 Don't send the report number. */
937 report = data+1;
938 report_length = length-1;
939 }
940
941 /* Avoid crash if the device has been unplugged. */
942 if (dev->disconnected) {
943 return -1;
944 }
945
946 res = IOHIDDeviceGetReport(dev->device_handle,
947 type,
948 report_id,
949 report, &report_length);
950
951 if (res == kIOReturnSuccess) {
952 if (report_id == 0x0) { /* 0 report number still present at the beginning */
953 report_length++;
954 }
955 return report_length;
956 }
957
958 return -1;
959 }
960
hid_write(hid_device * dev,const unsigned char * data,size_t length)961 int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length)
962 {
963 return set_report(dev, kIOHIDReportTypeOutput, data, length);
964 }
965
966 /* Helper function, so that this isn't duplicated in hid_read(). */
return_data(hid_device * dev,unsigned char * data,size_t length)967 static int return_data(hid_device *dev, unsigned char *data, size_t length)
968 {
969 /* Copy the data out of the linked list item (rpt) into the
970 return buffer (data), and delete the liked list item. */
971 struct input_report *rpt = dev->input_reports;
972 size_t len = (length < rpt->len)? length: rpt->len;
973 memcpy(data, rpt->data, len);
974 dev->input_reports = rpt->next;
975 free(rpt->data);
976 free(rpt);
977 return len;
978 }
979
cond_wait(const hid_device * dev,pthread_cond_t * cond,pthread_mutex_t * mutex)980 static int cond_wait(const hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex)
981 {
982 while (!dev->input_reports) {
983 int res = pthread_cond_wait(cond, mutex);
984 if (res != 0)
985 return res;
986
987 /* A res of 0 means we may have been signaled or it may
988 be a spurious wakeup. Check to see that there's actually
989 data in the queue before returning, and if not, go back
990 to sleep. See the pthread_cond_timedwait() man page for
991 details. */
992
993 if (dev->shutdown_thread || dev->disconnected)
994 return -1;
995 }
996
997 return 0;
998 }
999
cond_timedwait(const hid_device * dev,pthread_cond_t * cond,pthread_mutex_t * mutex,const struct timespec * abstime)1000 static int cond_timedwait(const hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
1001 {
1002 while (!dev->input_reports) {
1003 int res = pthread_cond_timedwait(cond, mutex, abstime);
1004 if (res != 0)
1005 return res;
1006
1007 /* A res of 0 means we may have been signaled or it may
1008 be a spurious wakeup. Check to see that there's actually
1009 data in the queue before returning, and if not, go back
1010 to sleep. See the pthread_cond_timedwait() man page for
1011 details. */
1012
1013 if (dev->shutdown_thread || dev->disconnected)
1014 return -1;
1015 }
1016
1017 return 0;
1018
1019 }
1020
hid_read_timeout(hid_device * dev,unsigned char * data,size_t length,int milliseconds)1021 int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
1022 {
1023 int bytes_read = -1;
1024
1025 /* Lock the access to the report list. */
1026 pthread_mutex_lock(&dev->mutex);
1027
1028 /* There's an input report queued up. Return it. */
1029 if (dev->input_reports) {
1030 /* Return the first one */
1031 bytes_read = return_data(dev, data, length);
1032 goto ret;
1033 }
1034
1035 /* Return if the device has been disconnected. */
1036 if (dev->disconnected) {
1037 bytes_read = -1;
1038 goto ret;
1039 }
1040
1041 if (dev->shutdown_thread) {
1042 /* This means the device has been closed (or there
1043 has been an error. An error code of -1 should
1044 be returned. */
1045 bytes_read = -1;
1046 goto ret;
1047 }
1048
1049 /* There is no data. Go to sleep and wait for data. */
1050
1051 if (milliseconds == -1) {
1052 /* Blocking */
1053 int res;
1054 res = cond_wait(dev, &dev->condition, &dev->mutex);
1055 if (res == 0)
1056 bytes_read = return_data(dev, data, length);
1057 else {
1058 /* There was an error, or a device disconnection. */
1059 bytes_read = -1;
1060 }
1061 }
1062 else if (milliseconds > 0) {
1063 /* Non-blocking, but called with timeout. */
1064 int res;
1065 struct timespec ts;
1066 struct timeval tv;
1067 gettimeofday(&tv, NULL);
1068 TIMEVAL_TO_TIMESPEC(&tv, &ts);
1069 ts.tv_sec += milliseconds / 1000;
1070 ts.tv_nsec += (milliseconds % 1000) * 1000000;
1071 if (ts.tv_nsec >= 1000000000L) {
1072 ts.tv_sec++;
1073 ts.tv_nsec -= 1000000000L;
1074 }
1075
1076 res = cond_timedwait(dev, &dev->condition, &dev->mutex, &ts);
1077 if (res == 0)
1078 bytes_read = return_data(dev, data, length);
1079 else if (res == ETIMEDOUT)
1080 bytes_read = 0;
1081 else
1082 bytes_read = -1;
1083 }
1084 else {
1085 /* Purely non-blocking */
1086 bytes_read = 0;
1087 }
1088
1089 ret:
1090 /* Unlock */
1091 pthread_mutex_unlock(&dev->mutex);
1092 return bytes_read;
1093 }
1094
hid_read(hid_device * dev,unsigned char * data,size_t length)1095 int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length)
1096 {
1097 return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0);
1098 }
1099
hid_set_nonblocking(hid_device * dev,int nonblock)1100 int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock)
1101 {
1102 /* All Nonblocking operation is handled by the library. */
1103 dev->blocking = !nonblock;
1104
1105 return 0;
1106 }
1107
hid_send_feature_report(hid_device * dev,const unsigned char * data,size_t length)1108 int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length)
1109 {
1110 return set_report(dev, kIOHIDReportTypeFeature, data, length);
1111 }
1112
hid_get_feature_report(hid_device * dev,unsigned char * data,size_t length)1113 int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length)
1114 {
1115 return get_report(dev, kIOHIDReportTypeFeature, data, length);
1116 }
1117
hid_get_input_report(hid_device * dev,unsigned char * data,size_t length)1118 int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length)
1119 {
1120 return get_report(dev, kIOHIDReportTypeInput, data, length);
1121 }
1122
hid_close(hid_device * dev)1123 void HID_API_EXPORT hid_close(hid_device *dev)
1124 {
1125 if (!dev)
1126 return;
1127
1128 /* Disconnect the report callback before close.
1129 See comment below.
1130 */
1131 if (is_macos_10_10_or_greater || !dev->disconnected) {
1132 IOHIDDeviceRegisterInputReportCallback(
1133 dev->device_handle, dev->input_report_buf, dev->max_input_report_len,
1134 NULL, dev);
1135 IOHIDDeviceRegisterRemovalCallback(dev->device_handle, NULL, dev);
1136 IOHIDDeviceUnscheduleFromRunLoop(dev->device_handle, dev->run_loop, dev->run_loop_mode);
1137 IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
1138 }
1139
1140 /* Cause read_thread() to stop. */
1141 dev->shutdown_thread = 1;
1142
1143 /* Wake up the run thread's event loop so that the thread can exit. */
1144 CFRunLoopSourceSignal(dev->source);
1145 CFRunLoopWakeUp(dev->run_loop);
1146
1147 /* Notify the read thread that it can shut down now. */
1148 pthread_barrier_wait(&dev->shutdown_barrier);
1149
1150 /* Wait for read_thread() to end. */
1151 pthread_join(dev->thread, NULL);
1152
1153 /* Close the OS handle to the device, but only if it's not
1154 been unplugged. If it's been unplugged, then calling
1155 IOHIDDeviceClose() will crash.
1156
1157 UPD: The crash part was true in/until some version of macOS.
1158 Starting with macOS 10.15, there is an opposite effect in some environments:
1159 crash happenes if IOHIDDeviceClose() is not called.
1160 Not leaking a resource in all tested environments.
1161 */
1162 if (is_macos_10_10_or_greater || !dev->disconnected) {
1163 IOHIDDeviceClose(dev->device_handle, kIOHIDOptionsTypeSeizeDevice);
1164 }
1165
1166 /* Clear out the queue of received reports. */
1167 pthread_mutex_lock(&dev->mutex);
1168 while (dev->input_reports) {
1169 return_data(dev, NULL, 0);
1170 }
1171 pthread_mutex_unlock(&dev->mutex);
1172 CFRelease(dev->device_handle);
1173
1174 free_hid_device(dev);
1175 }
1176
hid_get_manufacturer_string(hid_device * dev,wchar_t * string,size_t maxlen)1177 int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen)
1178 {
1179 return get_manufacturer_string(dev->device_handle, string, maxlen);
1180 }
1181
hid_get_product_string(hid_device * dev,wchar_t * string,size_t maxlen)1182 int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen)
1183 {
1184 return get_product_string(dev->device_handle, string, maxlen);
1185 }
1186
hid_get_serial_number_string(hid_device * dev,wchar_t * string,size_t maxlen)1187 int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen)
1188 {
1189 return get_serial_number(dev->device_handle, string, maxlen);
1190 }
1191
hid_get_indexed_string(hid_device * dev,int string_index,wchar_t * string,size_t maxlen)1192 int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen)
1193 {
1194 (void) dev;
1195 (void) string_index;
1196 (void) string;
1197 (void) maxlen;
1198
1199 /* TODO: */
1200
1201 return 0;
1202 }
1203
1204
hid_error(hid_device * dev)1205 HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev)
1206 {
1207 (void) dev;
1208 /* TODO: */
1209
1210 return L"hid_error is not implemented yet";
1211 }
1212
1213
1214
1215
1216
1217
1218
1219 #if 0
1220 static int32_t get_location_id(IOHIDDeviceRef device)
1221 {
1222 return get_int_property(device, CFSTR(kIOHIDLocationIDKey));
1223 }
1224
1225 static int32_t get_usage(IOHIDDeviceRef device)
1226 {
1227 int32_t res;
1228 res = get_int_property(device, CFSTR(kIOHIDDeviceUsageKey));
1229 if (!res)
1230 res = get_int_property(device, CFSTR(kIOHIDPrimaryUsageKey));
1231 return res;
1232 }
1233
1234 static int32_t get_usage_page(IOHIDDeviceRef device)
1235 {
1236 int32_t res;
1237 res = get_int_property(device, CFSTR(kIOHIDDeviceUsagePageKey));
1238 if (!res)
1239 res = get_int_property(device, CFSTR(kIOHIDPrimaryUsagePageKey));
1240 return res;
1241 }
1242
1243 static int get_transport(IOHIDDeviceRef device, wchar_t *buf, size_t len)
1244 {
1245 return get_string_property(device, CFSTR(kIOHIDTransportKey), buf, len);
1246 }
1247
1248
1249 int main(void)
1250 {
1251 IOHIDManagerRef mgr;
1252 int i;
1253
1254 mgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
1255 IOHIDManagerSetDeviceMatching(mgr, NULL);
1256 IOHIDManagerOpen(mgr, kIOHIDOptionsTypeNone);
1257
1258 CFSetRef device_set = IOHIDManagerCopyDevices(mgr);
1259
1260 CFIndex num_devices = CFSetGetCount(device_set);
1261 IOHIDDeviceRef *device_array = calloc(num_devices, sizeof(IOHIDDeviceRef));
1262 CFSetGetValues(device_set, (const void **) device_array);
1263
1264 for (i = 0; i < num_devices; i++) {
1265 IOHIDDeviceRef dev = device_array[i];
1266 printf("Device: %p\n", dev);
1267 printf(" %04hx %04hx\n", get_vendor_id(dev), get_product_id(dev));
1268
1269 wchar_t serial[256], buf[256];
1270 char cbuf[256];
1271 get_serial_number(dev, serial, 256);
1272
1273
1274 printf(" Serial: %ls\n", serial);
1275 printf(" Loc: %ld\n", get_location_id(dev));
1276 get_transport(dev, buf, 256);
1277 printf(" Trans: %ls\n", buf);
1278 make_path(dev, cbuf, 256);
1279 printf(" Path: %s\n", cbuf);
1280
1281 }
1282
1283 return 0;
1284 }
1285 #endif
1286