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