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