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 /* Initialize the IOHIDManager. Return 0 for success and -1 for failure. */
init_hid_manager(void)318 static int init_hid_manager(void)
319 {
320 /* Initialize all the HID Manager Objects */
321 hid_mgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
322 if (hid_mgr) {
323 IOHIDManagerSetDeviceMatching(hid_mgr, NULL);
324 IOHIDManagerScheduleWithRunLoop(hid_mgr, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
325 return 0;
326 }
327
328 return -1;
329 }
330
hid_version()331 HID_API_EXPORT const struct hid_api_version* HID_API_CALL hid_version()
332 {
333 return &api_version;
334 }
335
hid_version_str()336 HID_API_EXPORT const char* HID_API_CALL hid_version_str()
337 {
338 return HID_API_VERSION_STR;
339 }
340
341 /* Initialize the IOHIDManager if necessary. This is the public function, and
342 it is safe to call this function repeatedly. Return 0 for success and -1
343 for failure. */
hid_init(void)344 int HID_API_EXPORT hid_init(void)
345 {
346 if (!hid_mgr) {
347 is_macos_10_10_or_greater = (NSAppKitVersionNumber >= 1343); /* NSAppKitVersionNumber10_10 */
348 return init_hid_manager();
349 }
350
351 /* Already initialized. */
352 return 0;
353 }
354
hid_exit(void)355 int HID_API_EXPORT hid_exit(void)
356 {
357 if (hid_mgr) {
358 /* Close the HID manager. */
359 IOHIDManagerClose(hid_mgr, kIOHIDOptionsTypeNone);
360 CFRelease(hid_mgr);
361 hid_mgr = NULL;
362 }
363
364 return 0;
365 }
366
process_pending_events(void)367 static void process_pending_events(void) {
368 SInt32 res;
369 do {
370 res = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.001, FALSE);
371 } while(res != kCFRunLoopRunFinished && res != kCFRunLoopRunTimedOut);
372 }
373
create_device_info_with_usage(IOHIDDeviceRef dev,int32_t usage_page,int32_t usage)374 static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev, int32_t usage_page, int32_t usage)
375 {
376 unsigned short dev_vid;
377 unsigned short dev_pid;
378 int BUF_LEN = 256;
379 wchar_t buf[BUF_LEN];
380
381 struct hid_device_info *cur_dev;
382 io_object_t iokit_dev;
383 kern_return_t res;
384 uint64_t entry_id;
385
386 if (dev == NULL) {
387 return NULL;
388 }
389
390 cur_dev = (struct hid_device_info *)calloc(1, sizeof(struct hid_device_info));
391 if (cur_dev == NULL) {
392 return NULL;
393 }
394
395 dev_vid = get_vendor_id(dev);
396 dev_pid = get_product_id(dev);
397
398 cur_dev->usage_page = usage_page;
399 cur_dev->usage = usage;
400
401 /* Fill out the record */
402 cur_dev->next = NULL;
403
404 /* Fill in the path (as a unique ID of the service entry) */
405 cur_dev->path = NULL;
406 iokit_dev = IOHIDDeviceGetService(dev);
407 if (iokit_dev != MACH_PORT_NULL) {
408 res = IORegistryEntryGetRegistryEntryID(iokit_dev, &entry_id);
409 }
410 else {
411 res = KERN_INVALID_ARGUMENT;
412 }
413
414 if (res == KERN_SUCCESS) {
415 /* max value of entry_id(uint64_t) is 18446744073709551615 which is 20 characters long,
416 so for (max) "path" string 'DevSrvsID:18446744073709551615' we would need
417 9+1+20+1=31 bytes byffer, but allocate 32 for simple alignment */
418 cur_dev->path = calloc(1, 32);
419 if (cur_dev->path != NULL) {
420 sprintf(cur_dev->path, "DevSrvsID:%llu", entry_id);
421 }
422 }
423
424 if (cur_dev->path == NULL) {
425 /* for whatever reason, trying to keep it a non-NULL string */
426 cur_dev->path = strdup("");
427 }
428
429 /* Serial Number */
430 get_serial_number(dev, buf, BUF_LEN);
431 cur_dev->serial_number = dup_wcs(buf);
432
433 /* Manufacturer and Product strings */
434 get_manufacturer_string(dev, buf, BUF_LEN);
435 cur_dev->manufacturer_string = dup_wcs(buf);
436 get_product_string(dev, buf, BUF_LEN);
437 cur_dev->product_string = dup_wcs(buf);
438
439 /* VID/PID */
440 cur_dev->vendor_id = dev_vid;
441 cur_dev->product_id = dev_pid;
442
443 /* Release Number */
444 cur_dev->release_number = get_int_property(dev, CFSTR(kIOHIDVersionNumberKey));
445
446 /* Interface Number */
447 /* We can only retrieve the interface number for USB HID devices.
448 * IOKit always seems to return 0 when querying a standard USB device
449 * for its interface. */
450 int is_usb_hid = get_int_property(dev, CFSTR(kUSBInterfaceClass)) == kUSBHIDClass;
451 if (is_usb_hid) {
452 /* Get the interface number */
453 cur_dev->interface_number = get_int_property(dev, CFSTR(kUSBInterfaceNumber));
454 } else {
455 cur_dev->interface_number = -1;
456 }
457
458 return cur_dev;
459 }
460
create_device_info(IOHIDDeviceRef device)461 static struct hid_device_info *create_device_info(IOHIDDeviceRef device)
462 {
463 const int32_t primary_usage_page = get_int_property(device, CFSTR(kIOHIDPrimaryUsagePageKey));
464 const int32_t primary_usage = get_int_property(device, CFSTR(kIOHIDPrimaryUsageKey));
465
466 /* Primary should always be first, to match previous behavior. */
467 struct hid_device_info *root = create_device_info_with_usage(device, primary_usage_page, primary_usage);
468 struct hid_device_info *cur = root;
469
470 if (!root)
471 return NULL;
472
473 CFArrayRef usage_pairs = get_usage_pairs(device);
474
475 if (usage_pairs != NULL) {
476 struct hid_device_info *next = NULL;
477 for (CFIndex i = 0; i < CFArrayGetCount(usage_pairs); i++) {
478 CFTypeRef dict = CFArrayGetValueAtIndex(usage_pairs, i);
479 if (CFGetTypeID(dict) != CFDictionaryGetTypeID()) {
480 continue;
481 }
482
483 CFTypeRef usage_page_ref, usage_ref;
484 int32_t usage_page, usage;
485
486 if (!CFDictionaryGetValueIfPresent((CFDictionaryRef)dict, CFSTR(kIOHIDDeviceUsagePageKey), &usage_page_ref) ||
487 !CFDictionaryGetValueIfPresent((CFDictionaryRef)dict, CFSTR(kIOHIDDeviceUsageKey), &usage_ref) ||
488 CFGetTypeID(usage_page_ref) != CFNumberGetTypeID() ||
489 CFGetTypeID(usage_ref) != CFNumberGetTypeID() ||
490 !CFNumberGetValue((CFNumberRef)usage_page_ref, kCFNumberSInt32Type, &usage_page) ||
491 !CFNumberGetValue((CFNumberRef)usage_ref, kCFNumberSInt32Type, &usage)) {
492 continue;
493 }
494 if (usage_page == primary_usage_page && usage == primary_usage)
495 continue; /* Already added. */
496
497 next = create_device_info_with_usage(device, usage_page, usage);
498 cur->next = next;
499 if (next != NULL) {
500 cur = next;
501 }
502 }
503 }
504
505 return root;
506 }
507
hid_enumerate(unsigned short vendor_id,unsigned short product_id)508 struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id)
509 {
510 struct hid_device_info *root = NULL; /* return object */
511 struct hid_device_info *cur_dev = NULL;
512 CFIndex num_devices;
513 int i;
514
515 /* Set up the HID Manager if it hasn't been done */
516 if (hid_init() < 0)
517 return NULL;
518
519 /* give the IOHIDManager a chance to update itself */
520 process_pending_events();
521
522 /* Get a list of the Devices */
523 CFMutableDictionaryRef matching = NULL;
524 if (vendor_id != 0 || product_id != 0) {
525 matching = CFDictionaryCreateMutable(kCFAllocatorDefault, kIOHIDOptionsTypeNone, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
526
527 if (matching && vendor_id != 0) {
528 CFNumberRef v = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &vendor_id);
529 CFDictionarySetValue(matching, CFSTR(kIOHIDVendorIDKey), v);
530 CFRelease(v);
531 }
532
533 if (matching && product_id != 0) {
534 CFNumberRef p = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &product_id);
535 CFDictionarySetValue(matching, CFSTR(kIOHIDProductIDKey), p);
536 CFRelease(p);
537 }
538 }
539 IOHIDManagerSetDeviceMatching(hid_mgr, matching);
540 if (matching != NULL) {
541 CFRelease(matching);
542 }
543
544 CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr);
545 if (device_set == NULL) {
546 return NULL;
547 }
548
549 /* Convert the list into a C array so we can iterate easily. */
550 num_devices = CFSetGetCount(device_set);
551 IOHIDDeviceRef *device_array = (IOHIDDeviceRef*) calloc(num_devices, sizeof(IOHIDDeviceRef));
552 CFSetGetValues(device_set, (const void **) device_array);
553
554 /* Iterate over each device, making an entry for it. */
555 for (i = 0; i < num_devices; i++) {
556
557 IOHIDDeviceRef dev = device_array[i];
558 if (!dev) {
559 continue;
560 }
561
562 struct hid_device_info *tmp = create_device_info(dev);
563 if (tmp == NULL) {
564 continue;
565 }
566
567 if (cur_dev) {
568 cur_dev->next = tmp;
569 }
570 else {
571 root = tmp;
572 }
573 cur_dev = tmp;
574
575 /* move the pointer to the tail of returnd list */
576 while (cur_dev->next != NULL) {
577 cur_dev = cur_dev->next;
578 }
579 }
580
581 free(device_array);
582 CFRelease(device_set);
583
584 return root;
585 }
586
hid_free_enumeration(struct hid_device_info * devs)587 void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs)
588 {
589 /* This function is identical to the Linux version. Platform independent. */
590 struct hid_device_info *d = devs;
591 while (d) {
592 struct hid_device_info *next = d->next;
593 free(d->path);
594 free(d->serial_number);
595 free(d->manufacturer_string);
596 free(d->product_string);
597 free(d);
598 d = next;
599 }
600 }
601
hid_open(unsigned short vendor_id,unsigned short product_id,const wchar_t * serial_number)602 hid_device * HID_API_EXPORT hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
603 {
604 /* This function is identical to the Linux version. Platform independent. */
605 struct hid_device_info *devs, *cur_dev;
606 const char *path_to_open = NULL;
607 hid_device * handle = NULL;
608
609 devs = hid_enumerate(vendor_id, product_id);
610 cur_dev = devs;
611 while (cur_dev) {
612 if (cur_dev->vendor_id == vendor_id &&
613 cur_dev->product_id == product_id) {
614 if (serial_number) {
615 if (wcscmp(serial_number, cur_dev->serial_number) == 0) {
616 path_to_open = cur_dev->path;
617 break;
618 }
619 }
620 else {
621 path_to_open = cur_dev->path;
622 break;
623 }
624 }
625 cur_dev = cur_dev->next;
626 }
627
628 if (path_to_open) {
629 /* Open the device */
630 handle = hid_open_path(path_to_open);
631 }
632
633 hid_free_enumeration(devs);
634
635 return handle;
636 }
637
hid_device_removal_callback(void * context,IOReturn result,void * sender)638 static void hid_device_removal_callback(void *context, IOReturn result,
639 void *sender)
640 {
641 (void) result;
642 (void) sender;
643
644 /* Stop the Run Loop for this device. */
645 hid_device *d = (hid_device*) context;
646
647 d->disconnected = 1;
648 CFRunLoopStop(d->run_loop);
649 }
650
651 /* The Run Loop calls this function for each input report received.
652 This function puts the data into a linked list to be picked up by
653 hid_read(). */
hid_report_callback(void * context,IOReturn result,void * sender,IOHIDReportType report_type,uint32_t report_id,uint8_t * report,CFIndex report_length)654 static void hid_report_callback(void *context, IOReturn result, void *sender,
655 IOHIDReportType report_type, uint32_t report_id,
656 uint8_t *report, CFIndex report_length)
657 {
658 (void) result;
659 (void) sender;
660 (void) report_type;
661 (void) report_id;
662
663 struct input_report *rpt;
664 hid_device *dev = (hid_device*) context;
665
666 /* Make a new Input Report object */
667 rpt = (struct input_report*) calloc(1, sizeof(struct input_report));
668 rpt->data = (uint8_t*) calloc(1, report_length);
669 memcpy(rpt->data, report, report_length);
670 rpt->len = report_length;
671 rpt->next = NULL;
672
673 /* Lock this section */
674 pthread_mutex_lock(&dev->mutex);
675
676 /* Attach the new report object to the end of the list. */
677 if (dev->input_reports == NULL) {
678 /* The list is empty. Put it at the root. */
679 dev->input_reports = rpt;
680 }
681 else {
682 /* Find the end of the list and attach. */
683 struct input_report *cur = dev->input_reports;
684 int num_queued = 0;
685 while (cur->next != NULL) {
686 cur = cur->next;
687 num_queued++;
688 }
689 cur->next = rpt;
690
691 /* Pop one off if we've reached 30 in the queue. This
692 way we don't grow forever if the user never reads
693 anything from the device. */
694 if (num_queued > 30) {
695 return_data(dev, NULL, 0);
696 }
697 }
698
699 /* Signal a waiting thread that there is data. */
700 pthread_cond_signal(&dev->condition);
701
702 /* Unlock */
703 pthread_mutex_unlock(&dev->mutex);
704
705 }
706
707 /* This gets called when the read_thread's run loop gets signaled by
708 hid_close(), and serves to stop the read_thread's run loop. */
perform_signal_callback(void * context)709 static void perform_signal_callback(void *context)
710 {
711 hid_device *dev = (hid_device*) context;
712 CFRunLoopStop(dev->run_loop); /*TODO: CFRunLoopGetCurrent()*/
713 }
714
read_thread(void * param)715 static void *read_thread(void *param)
716 {
717 hid_device *dev = (hid_device*) param;
718 SInt32 code;
719
720 /* Move the device's run loop to this thread. */
721 IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetCurrent(), dev->run_loop_mode);
722
723 /* Create the RunLoopSource which is used to signal the
724 event loop to stop when hid_close() is called. */
725 CFRunLoopSourceContext ctx;
726 memset(&ctx, 0, sizeof(ctx));
727 ctx.version = 0;
728 ctx.info = dev;
729 ctx.perform = &perform_signal_callback;
730 dev->source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0/*order*/, &ctx);
731 CFRunLoopAddSource(CFRunLoopGetCurrent(), dev->source, dev->run_loop_mode);
732
733 /* Store off the Run Loop so it can be stopped from hid_close()
734 and on device disconnection. */
735 dev->run_loop = CFRunLoopGetCurrent();
736
737 /* Notify the main thread that the read thread is up and running. */
738 pthread_barrier_wait(&dev->barrier);
739
740 /* Run the Event Loop. CFRunLoopRunInMode() will dispatch HID input
741 reports into the hid_report_callback(). */
742 while (!dev->shutdown_thread && !dev->disconnected) {
743 code = CFRunLoopRunInMode(dev->run_loop_mode, 1000/*sec*/, FALSE);
744 /* Return if the device has been disconnected */
745 if (code == kCFRunLoopRunFinished) {
746 dev->disconnected = 1;
747 break;
748 }
749
750
751 /* Break if The Run Loop returns Finished or Stopped. */
752 if (code != kCFRunLoopRunTimedOut &&
753 code != kCFRunLoopRunHandledSource) {
754 /* There was some kind of error. Setting
755 shutdown seems to make sense, but
756 there may be something else more appropriate */
757 dev->shutdown_thread = 1;
758 break;
759 }
760 }
761
762 /* Now that the read thread is stopping, Wake any threads which are
763 waiting on data (in hid_read_timeout()). Do this under a mutex to
764 make sure that a thread which is about to go to sleep waiting on
765 the condition actually will go to sleep before the condition is
766 signaled. */
767 pthread_mutex_lock(&dev->mutex);
768 pthread_cond_broadcast(&dev->condition);
769 pthread_mutex_unlock(&dev->mutex);
770
771 /* Wait here until hid_close() is called and makes it past
772 the call to CFRunLoopWakeUp(). This thread still needs to
773 be valid when that function is called on the other thread. */
774 pthread_barrier_wait(&dev->shutdown_barrier);
775
776 return NULL;
777 }
778
779 /* \p path must be one of:
780 - in format 'DevSrvsID:<RegistryEntryID>' (as returned by hid_enumerate);
781 - a valid path to an IOHIDDevice in the IOService plane (as returned by IORegistryEntryGetPath,
782 e.g.: "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/EHC1@1D,7/AppleUSBEHCI/PLAYSTATION(R)3 Controller@fd120000/IOUSBInterface@0/IOUSBHIDDriver");
783 Second format is for compatibility with paths accepted by older versions of HIDAPI.
784 */
hid_open_service_registry_from_path(const char * path)785 static io_registry_entry_t hid_open_service_registry_from_path(const char *path)
786 {
787 if (path == NULL)
788 return MACH_PORT_NULL;
789
790 /* Get the IORegistry entry for the given path */
791 if (strncmp("DevSrvsID:", path, 10) == 0) {
792 char *endptr;
793 uint64_t entry_id = strtoull(path + 10, &endptr, 10);
794 if (*endptr == '\0') {
795 return IOServiceGetMatchingService(kIOMasterPortDefault, IORegistryEntryIDMatching(entry_id));
796 }
797 }
798 else {
799 /* Fallback to older format of the path */
800 return IORegistryEntryFromPath(kIOMasterPortDefault, path);
801 }
802
803 return MACH_PORT_NULL;
804 }
805
hid_open_path(const char * path)806 hid_device * HID_API_EXPORT hid_open_path(const char *path)
807 {
808 hid_device *dev = NULL;
809 io_registry_entry_t entry = MACH_PORT_NULL;
810 IOReturn ret = kIOReturnInvalid;
811
812 /* Set up the HID Manager if it hasn't been done */
813 if (hid_init() < 0)
814 goto return_error;
815
816 dev = new_hid_device();
817
818 /* Get the IORegistry entry for the given path */
819 entry = hid_open_service_registry_from_path(path);
820 if (entry == MACH_PORT_NULL) {
821 /* Path wasn't valid (maybe device was removed?) */
822 goto return_error;
823 }
824
825 /* Create an IOHIDDevice for the entry */
826 dev->device_handle = IOHIDDeviceCreate(kCFAllocatorDefault, entry);
827 if (dev->device_handle == NULL) {
828 /* Error creating the HID device */
829 goto return_error;
830 }
831
832 /* Open the IOHIDDevice */
833 ret = IOHIDDeviceOpen(dev->device_handle, kIOHIDOptionsTypeSeizeDevice);
834 if (ret == kIOReturnSuccess) {
835 char str[32];
836
837 /* Create the buffers for receiving data */
838 dev->max_input_report_len = (CFIndex) get_max_report_length(dev->device_handle);
839 dev->input_report_buf = (uint8_t*) calloc(dev->max_input_report_len, sizeof(uint8_t));
840
841 /* Create the Run Loop Mode for this device.
842 printing the reference seems to work. */
843 sprintf(str, "HIDAPI_%p", (void*) dev->device_handle);
844 dev->run_loop_mode =
845 CFStringCreateWithCString(NULL, str, kCFStringEncodingASCII);
846
847 /* Attach the device to a Run Loop */
848 IOHIDDeviceRegisterInputReportCallback(
849 dev->device_handle, dev->input_report_buf, dev->max_input_report_len,
850 &hid_report_callback, dev);
851 IOHIDDeviceRegisterRemovalCallback(dev->device_handle, hid_device_removal_callback, dev);
852
853 /* Start the read thread */
854 pthread_create(&dev->thread, NULL, read_thread, dev);
855
856 /* Wait here for the read thread to be initialized. */
857 pthread_barrier_wait(&dev->barrier);
858
859 IOObjectRelease(entry);
860 return dev;
861 }
862 else {
863 goto return_error;
864 }
865
866 return_error:
867 if (dev->device_handle != NULL)
868 CFRelease(dev->device_handle);
869
870 if (entry != MACH_PORT_NULL)
871 IOObjectRelease(entry);
872
873 free_hid_device(dev);
874 return NULL;
875 }
876
set_report(hid_device * dev,IOHIDReportType type,const unsigned char * data,size_t length)877 static int set_report(hid_device *dev, IOHIDReportType type, const unsigned char *data, size_t length)
878 {
879 const unsigned char *data_to_send = data;
880 CFIndex length_to_send = length;
881 IOReturn res;
882 unsigned char report_id;
883
884 if (!data || (length == 0)) {
885 return -1;
886 }
887
888 report_id = data[0];
889
890 if (report_id == 0x0) {
891 /* Not using numbered Reports.
892 Don't send the report number. */
893 data_to_send = data+1;
894 length_to_send = length-1;
895 }
896
897 /* Avoid crash if the device has been unplugged. */
898 if (dev->disconnected) {
899 return -1;
900 }
901
902 res = IOHIDDeviceSetReport(dev->device_handle,
903 type,
904 report_id,
905 data_to_send, length_to_send);
906
907 if (res == kIOReturnSuccess) {
908 return length;
909 }
910
911 return -1;
912 }
913
get_report(hid_device * dev,IOHIDReportType type,unsigned char * data,size_t length)914 static int get_report(hid_device *dev, IOHIDReportType type, unsigned char *data, size_t length)
915 {
916 unsigned char *report = data;
917 CFIndex report_length = length;
918 IOReturn res = kIOReturnSuccess;
919 const unsigned char report_id = data[0];
920
921 if (report_id == 0x0) {
922 /* Not using numbered Reports.
923 Don't send the report number. */
924 report = data+1;
925 report_length = length-1;
926 }
927
928 /* Avoid crash if the device has been unplugged. */
929 if (dev->disconnected) {
930 return -1;
931 }
932
933 res = IOHIDDeviceGetReport(dev->device_handle,
934 type,
935 report_id,
936 report, &report_length);
937
938 if (res == kIOReturnSuccess) {
939 if (report_id == 0x0) { /* 0 report number still present at the beginning */
940 report_length++;
941 }
942 return report_length;
943 }
944
945 return -1;
946 }
947
hid_write(hid_device * dev,const unsigned char * data,size_t length)948 int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length)
949 {
950 return set_report(dev, kIOHIDReportTypeOutput, data, length);
951 }
952
953 /* Helper function, so that this isn't duplicated in hid_read(). */
return_data(hid_device * dev,unsigned char * data,size_t length)954 static int return_data(hid_device *dev, unsigned char *data, size_t length)
955 {
956 /* Copy the data out of the linked list item (rpt) into the
957 return buffer (data), and delete the liked list item. */
958 struct input_report *rpt = dev->input_reports;
959 size_t len = (length < rpt->len)? length: rpt->len;
960 memcpy(data, rpt->data, len);
961 dev->input_reports = rpt->next;
962 free(rpt->data);
963 free(rpt);
964 return len;
965 }
966
cond_wait(const hid_device * dev,pthread_cond_t * cond,pthread_mutex_t * mutex)967 static int cond_wait(const hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex)
968 {
969 while (!dev->input_reports) {
970 int res = pthread_cond_wait(cond, mutex);
971 if (res != 0)
972 return res;
973
974 /* A res of 0 means we may have been signaled or it may
975 be a spurious wakeup. Check to see that there's actually
976 data in the queue before returning, and if not, go back
977 to sleep. See the pthread_cond_timedwait() man page for
978 details. */
979
980 if (dev->shutdown_thread || dev->disconnected)
981 return -1;
982 }
983
984 return 0;
985 }
986
cond_timedwait(const hid_device * dev,pthread_cond_t * cond,pthread_mutex_t * mutex,const struct timespec * abstime)987 static int cond_timedwait(const hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
988 {
989 while (!dev->input_reports) {
990 int res = pthread_cond_timedwait(cond, mutex, abstime);
991 if (res != 0)
992 return res;
993
994 /* A res of 0 means we may have been signaled or it may
995 be a spurious wakeup. Check to see that there's actually
996 data in the queue before returning, and if not, go back
997 to sleep. See the pthread_cond_timedwait() man page for
998 details. */
999
1000 if (dev->shutdown_thread || dev->disconnected)
1001 return -1;
1002 }
1003
1004 return 0;
1005
1006 }
1007
hid_read_timeout(hid_device * dev,unsigned char * data,size_t length,int milliseconds)1008 int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
1009 {
1010 int bytes_read = -1;
1011
1012 /* Lock the access to the report list. */
1013 pthread_mutex_lock(&dev->mutex);
1014
1015 /* There's an input report queued up. Return it. */
1016 if (dev->input_reports) {
1017 /* Return the first one */
1018 bytes_read = return_data(dev, data, length);
1019 goto ret;
1020 }
1021
1022 /* Return if the device has been disconnected. */
1023 if (dev->disconnected) {
1024 bytes_read = -1;
1025 goto ret;
1026 }
1027
1028 if (dev->shutdown_thread) {
1029 /* This means the device has been closed (or there
1030 has been an error. An error code of -1 should
1031 be returned. */
1032 bytes_read = -1;
1033 goto ret;
1034 }
1035
1036 /* There is no data. Go to sleep and wait for data. */
1037
1038 if (milliseconds == -1) {
1039 /* Blocking */
1040 int res;
1041 res = cond_wait(dev, &dev->condition, &dev->mutex);
1042 if (res == 0)
1043 bytes_read = return_data(dev, data, length);
1044 else {
1045 /* There was an error, or a device disconnection. */
1046 bytes_read = -1;
1047 }
1048 }
1049 else if (milliseconds > 0) {
1050 /* Non-blocking, but called with timeout. */
1051 int res;
1052 struct timespec ts;
1053 struct timeval tv;
1054 gettimeofday(&tv, NULL);
1055 TIMEVAL_TO_TIMESPEC(&tv, &ts);
1056 ts.tv_sec += milliseconds / 1000;
1057 ts.tv_nsec += (milliseconds % 1000) * 1000000;
1058 if (ts.tv_nsec >= 1000000000L) {
1059 ts.tv_sec++;
1060 ts.tv_nsec -= 1000000000L;
1061 }
1062
1063 res = cond_timedwait(dev, &dev->condition, &dev->mutex, &ts);
1064 if (res == 0)
1065 bytes_read = return_data(dev, data, length);
1066 else if (res == ETIMEDOUT)
1067 bytes_read = 0;
1068 else
1069 bytes_read = -1;
1070 }
1071 else {
1072 /* Purely non-blocking */
1073 bytes_read = 0;
1074 }
1075
1076 ret:
1077 /* Unlock */
1078 pthread_mutex_unlock(&dev->mutex);
1079 return bytes_read;
1080 }
1081
hid_read(hid_device * dev,unsigned char * data,size_t length)1082 int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length)
1083 {
1084 return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0);
1085 }
1086
hid_set_nonblocking(hid_device * dev,int nonblock)1087 int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock)
1088 {
1089 /* All Nonblocking operation is handled by the library. */
1090 dev->blocking = !nonblock;
1091
1092 return 0;
1093 }
1094
hid_send_feature_report(hid_device * dev,const unsigned char * data,size_t length)1095 int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length)
1096 {
1097 return set_report(dev, kIOHIDReportTypeFeature, data, length);
1098 }
1099
hid_get_feature_report(hid_device * dev,unsigned char * data,size_t length)1100 int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length)
1101 {
1102 return get_report(dev, kIOHIDReportTypeFeature, data, length);
1103 }
1104
hid_get_input_report(hid_device * dev,unsigned char * data,size_t length)1105 int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length)
1106 {
1107 return get_report(dev, kIOHIDReportTypeInput, data, length);
1108 }
1109
hid_close(hid_device * dev)1110 void HID_API_EXPORT hid_close(hid_device *dev)
1111 {
1112 if (!dev)
1113 return;
1114
1115 /* Disconnect the report callback before close.
1116 See comment below.
1117 */
1118 if (is_macos_10_10_or_greater || !dev->disconnected) {
1119 IOHIDDeviceRegisterInputReportCallback(
1120 dev->device_handle, dev->input_report_buf, dev->max_input_report_len,
1121 NULL, dev);
1122 IOHIDDeviceRegisterRemovalCallback(dev->device_handle, NULL, dev);
1123 IOHIDDeviceUnscheduleFromRunLoop(dev->device_handle, dev->run_loop, dev->run_loop_mode);
1124 IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
1125 }
1126
1127 /* Cause read_thread() to stop. */
1128 dev->shutdown_thread = 1;
1129
1130 /* Wake up the run thread's event loop so that the thread can exit. */
1131 CFRunLoopSourceSignal(dev->source);
1132 CFRunLoopWakeUp(dev->run_loop);
1133
1134 /* Notify the read thread that it can shut down now. */
1135 pthread_barrier_wait(&dev->shutdown_barrier);
1136
1137 /* Wait for read_thread() to end. */
1138 pthread_join(dev->thread, NULL);
1139
1140 /* Close the OS handle to the device, but only if it's not
1141 been unplugged. If it's been unplugged, then calling
1142 IOHIDDeviceClose() will crash.
1143
1144 UPD: The crash part was true in/until some version of macOS.
1145 Starting with macOS 10.15, there is an opposite effect in some environments:
1146 crash happenes if IOHIDDeviceClose() is not called.
1147 Not leaking a resource in all tested environments.
1148 */
1149 if (is_macos_10_10_or_greater || !dev->disconnected) {
1150 IOHIDDeviceClose(dev->device_handle, kIOHIDOptionsTypeSeizeDevice);
1151 }
1152
1153 /* Clear out the queue of received reports. */
1154 pthread_mutex_lock(&dev->mutex);
1155 while (dev->input_reports) {
1156 return_data(dev, NULL, 0);
1157 }
1158 pthread_mutex_unlock(&dev->mutex);
1159 CFRelease(dev->device_handle);
1160
1161 free_hid_device(dev);
1162 }
1163
hid_get_manufacturer_string(hid_device * dev,wchar_t * string,size_t maxlen)1164 int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen)
1165 {
1166 return get_manufacturer_string(dev->device_handle, string, maxlen);
1167 }
1168
hid_get_product_string(hid_device * dev,wchar_t * string,size_t maxlen)1169 int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen)
1170 {
1171 return get_product_string(dev->device_handle, string, maxlen);
1172 }
1173
hid_get_serial_number_string(hid_device * dev,wchar_t * string,size_t maxlen)1174 int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen)
1175 {
1176 return get_serial_number(dev->device_handle, string, maxlen);
1177 }
1178
hid_get_indexed_string(hid_device * dev,int string_index,wchar_t * string,size_t maxlen)1179 int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen)
1180 {
1181 (void) dev;
1182 (void) string_index;
1183 (void) string;
1184 (void) maxlen;
1185
1186 /* TODO: */
1187
1188 return 0;
1189 }
1190
1191
hid_error(hid_device * dev)1192 HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev)
1193 {
1194 (void) dev;
1195 /* TODO: */
1196
1197 return L"hid_error is not implemented yet";
1198 }
1199
1200
1201
1202
1203
1204
1205
1206 #if 0
1207 static int32_t get_location_id(IOHIDDeviceRef device)
1208 {
1209 return get_int_property(device, CFSTR(kIOHIDLocationIDKey));
1210 }
1211
1212 static int32_t get_usage(IOHIDDeviceRef device)
1213 {
1214 int32_t res;
1215 res = get_int_property(device, CFSTR(kIOHIDDeviceUsageKey));
1216 if (!res)
1217 res = get_int_property(device, CFSTR(kIOHIDPrimaryUsageKey));
1218 return res;
1219 }
1220
1221 static int32_t get_usage_page(IOHIDDeviceRef device)
1222 {
1223 int32_t res;
1224 res = get_int_property(device, CFSTR(kIOHIDDeviceUsagePageKey));
1225 if (!res)
1226 res = get_int_property(device, CFSTR(kIOHIDPrimaryUsagePageKey));
1227 return res;
1228 }
1229
1230 static int get_transport(IOHIDDeviceRef device, wchar_t *buf, size_t len)
1231 {
1232 return get_string_property(device, CFSTR(kIOHIDTransportKey), buf, len);
1233 }
1234
1235
1236 int main(void)
1237 {
1238 IOHIDManagerRef mgr;
1239 int i;
1240
1241 mgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
1242 IOHIDManagerSetDeviceMatching(mgr, NULL);
1243 IOHIDManagerOpen(mgr, kIOHIDOptionsTypeNone);
1244
1245 CFSetRef device_set = IOHIDManagerCopyDevices(mgr);
1246
1247 CFIndex num_devices = CFSetGetCount(device_set);
1248 IOHIDDeviceRef *device_array = calloc(num_devices, sizeof(IOHIDDeviceRef));
1249 CFSetGetValues(device_set, (const void **) device_array);
1250
1251 for (i = 0; i < num_devices; i++) {
1252 IOHIDDeviceRef dev = device_array[i];
1253 printf("Device: %p\n", dev);
1254 printf(" %04hx %04hx\n", get_vendor_id(dev), get_product_id(dev));
1255
1256 wchar_t serial[256], buf[256];
1257 char cbuf[256];
1258 get_serial_number(dev, serial, 256);
1259
1260
1261 printf(" Serial: %ls\n", serial);
1262 printf(" Loc: %ld\n", get_location_id(dev));
1263 get_transport(dev, buf, 256);
1264 printf(" Trans: %ls\n", buf);
1265 make_path(dev, cbuf, 256);
1266 printf(" Path: %s\n", cbuf);
1267
1268 }
1269
1270 return 0;
1271 }
1272 #endif
1273