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