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