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