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