1 /***********************************************************************
2  HIDAPI - Multi-Platform library for communication with HID devices.
3 
4  hid_mac.cxx
5 
6  Alan Ott
7  Signal 11 Software
8  Copyright 2009, All Rights Reserved.
9 
10  C++ implementation
11  * Copyright 2021
12  * David Freese, W1HKJ
13  * for use in fldigi
14 
15  This software is licensed under the terms of the GNU General Public
16  License v3.
17 
18 ***********************************************************************/
19 
20 /* See Apple Technical Note TN2187 for details on IOHidManager. */
21 /***********************************************************************
22  Tested on both Intel and M1 architecture
23 ***********************************************************************/
24 
25 #include <iostream>
26 
27 #include "hidapi.h"
28 
29 #define UTF8 134217984
30 
wchar2str(char * WC)31 std::string wchar2str( char *WC )
32 {
33 //	size_t count = sizeof(WC);
34 //	char MB[count + 1];
35 //	memset(MB, 0, count + 1);
36 //	size_t ret = wcstombs(MB, WC, count);
37 	static std::string retstr;
38 //	if (ret) retstr = MB;
39 	return retstr;
40 }
41 
pthread_barrier_init(pthread_barrier_t * barrier,const pthread_barrierattr_t * attr,unsigned int count)42 int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count)
43 {
44 	if(count == 0) {
45 		errno = EINVAL;
46 		return -1;
47 	}
48 
49 	if(pthread_mutex_init(&barrier->mutex, 0) < 0) {
50 		return -1;
51 	}
52 	if(pthread_cond_init(&barrier->cond, 0) < 0) {
53 		pthread_mutex_destroy(&barrier->mutex);
54 		return -1;
55 	}
56 	barrier->trip_count = count;
57 	barrier->count = 0;
58 
59 	return 0;
60 }
61 
pthread_barrier_destroy(pthread_barrier_t * barrier)62 int pthread_barrier_destroy(pthread_barrier_t *barrier)
63 {
64 	pthread_cond_destroy(&barrier->cond);
65 	pthread_mutex_destroy(&barrier->mutex);
66 	return 0;
67 }
68 
pthread_barrier_wait(pthread_barrier_t * barrier)69 int pthread_barrier_wait(pthread_barrier_t *barrier)
70 {
71 	pthread_mutex_lock(&barrier->mutex);
72 	++(barrier->count);
73 	if(barrier->count >= barrier->trip_count)
74 	{
75 		barrier->count = 0;
76 		pthread_cond_broadcast(&barrier->cond);
77 		pthread_mutex_unlock(&barrier->mutex);
78 		return 1;
79 	}
80 	else
81 	{
82 		pthread_cond_wait(&barrier->cond, &(barrier->mutex));
83 		pthread_mutex_unlock(&barrier->mutex);
84 		return 0;
85 	}
86 }
87 
88 static	IOHIDManagerRef hid_mgr = 0x0;
89 
90 
register_error(const char * op)91 void hid_device::register_error(const char *op)
92 {
93 }
94 
95 
get_int_property(IOHIDDeviceRef device,CFStringRef key)96 static int32_t get_int_property(IOHIDDeviceRef device, CFStringRef key)
97 {
98 	CFTypeRef ref;
99 	int32_t value;
100 
101 	ref = IOHIDDeviceGetProperty(device, key);
102 	if (ref) {
103 		if (CFGetTypeID(ref) == CFNumberGetTypeID()) {
104 			CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, &value);
105 			return value;
106 		}
107 	}
108 	return 0;
109 }
110 
get_vendor_id(IOHIDDeviceRef device)111 static unsigned short get_vendor_id(IOHIDDeviceRef device)
112 {
113 	return get_int_property(device, CFSTR(kIOHIDVendorIDKey));
114 }
115 
get_product_id(IOHIDDeviceRef device)116 static unsigned short get_product_id(IOHIDDeviceRef device)
117 {
118 	return get_int_property(device, CFSTR(kIOHIDProductIDKey));
119 }
120 
get_max_report_length(IOHIDDeviceRef device)121 static int32_t get_max_report_length(IOHIDDeviceRef device)
122 {
123 	return get_int_property(device, CFSTR(kIOHIDMaxInputReportSizeKey));
124 }
125 
get_string_property(IOHIDDeviceRef device,CFStringRef prop,char * buf,size_t len)126 static int get_string_property(IOHIDDeviceRef device, CFStringRef prop, char *buf, size_t len)
127 {
128 	if (!len)
129 		return 0;
130 
131 	CFStringRef str = reinterpret_cast<const __CFString *>(IOHIDDeviceGetProperty(device, prop));
132 	UniChar ubuf[len];
133 	memset(buf, 0, len);
134 	if (str) {
135 		CFIndex str_len = CFStringGetLength(str);
136 		CFRange range;
137 		range.location = 0;
138 		range.length = ((size_t)str_len > len)? len: (size_t)str_len;
139 
140 		CFStringGetCharacters(
141 			str,
142 			range,
143 			ubuf);
144 		for (int i = 0; i < range.length; i++) buf[i] = ubuf[i];
145 		return 0;
146 	}
147 	else
148 		return -1;
149 
150 }
151 
get_serial_number(IOHIDDeviceRef device,char * buf,size_t len)152 static int get_serial_number(IOHIDDeviceRef device, char *buf, size_t len)
153 {
154 	return get_string_property(device, CFSTR(kIOHIDSerialNumberKey), buf, len);
155 }
156 
get_manufacturer_string(IOHIDDeviceRef device,char * buf,size_t len)157 static int get_manufacturer_string(IOHIDDeviceRef device, char *buf, size_t len)
158 {
159 	return get_string_property(device, CFSTR(kIOHIDManufacturerKey), buf, len);
160 }
161 
get_product_string(IOHIDDeviceRef device,char * buf,size_t len)162 static int get_product_string(IOHIDDeviceRef device, char *buf, size_t len)
163 {
164 	return get_string_property(device, CFSTR(kIOHIDProductKey), buf, len);
165 }
166 
167 /* hidapi_IOHIDDeviceGetService()
168  *
169  * Return the io_service_t corresponding to a given IOHIDDeviceRef, either by:
170  * - on OS X 10.6 and above, calling IOHIDDeviceGetService()
171  * - on OS X 10.5, extract it from the IOHIDDevice struct
172  */
hidapi_IOHIDDeviceGetService(IOHIDDeviceRef device)173 static io_service_t hidapi_IOHIDDeviceGetService(IOHIDDeviceRef device)
174 {
175 	static void *iokit_framework = NULL;
176 	static io_service_t (*dynamic_IOHIDDeviceGetService)(IOHIDDeviceRef device) = NULL;
177 
178 	/* Use dlopen()/dlsym() to get a pointer to IOHIDDeviceGetService() if it exists.
179 	 * If any of these steps fail, dynamic_IOHIDDeviceGetService will be left NULL
180 	 * and the fallback method will be used.
181 	 */
182 	if (iokit_framework == NULL) {
183 		iokit_framework = dlopen("/System/Library/IOKit.framework/IOKit", RTLD_LAZY);
184 
185 		if (iokit_framework != NULL)
186 			dynamic_IOHIDDeviceGetService = reinterpret_cast<unsigned int (*)(__IOHIDDevice *)>(dlsym(iokit_framework, "IOHIDDeviceGetService"));
187 	}
188 
189 	if (dynamic_IOHIDDeviceGetService != NULL) {
190 		/* Running on OS X 10.6 and above: IOHIDDeviceGetService() exists */
191 		return dynamic_IOHIDDeviceGetService(device);
192 	}
193 	else
194 	{
195 		/* Running on OS X 10.5: IOHIDDeviceGetService() doesn't exist.
196 		 *
197 		 * Be naughty and pull the service out of the IOHIDDevice.
198 		 * IOHIDDevice is an opaque struct not exposed to applications, but its
199 		 * layout is stable through all available versions of OS X.
200 		 * Tested and working on OS X 10.5.8 i386, x86_64, and ppc.
201 		 */
202 		struct IOHIDDevice_internal {
203 			/* The first field of the IOHIDDevice struct is a
204 			 * CFRuntimeBase (which is a private CF struct).
205 			 *
206 			 * a, b, and c are the 3 fields that make up a CFRuntimeBase.
207 			 * See http://opensource.apple.com/source/CF/CF-476.18/CFRuntime.h
208 			 *
209 			 * The second field of the IOHIDDevice is the io_service_t we're looking for.
210 			 */
211 			uintptr_t a;
212 			uint8_t b[4];
213 #if __LP64__
214 			uint32_t c;
215 #endif
216 			io_service_t service;
217 		};
218 		struct IOHIDDevice_internal *tmp = (struct IOHIDDevice_internal *)device;
219 
220 		return tmp->service;
221 	}
222 }
223 
224 /* Initialize the IOHIDManager. Return 0 for success and -1 for failure. */
init_hid_manager(void)225 static int init_hid_manager(void)
226 {
227 	/* Initialize all the HID Manager Objects */
228 	hid_mgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
229 	if (hid_mgr) {
230 		IOHIDManagerSetDeviceMatching(hid_mgr, NULL);
231 		IOHIDManagerScheduleWithRunLoop(hid_mgr, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
232 		return 0;
233 	}
234 
235 	return -1;
236 }
237 
238 /* Initialize the IOHIDManager if necessary. This is the public function, and
239    it is safe to call this function repeatedly. Return 0 for success and -1
240    for failure. */
hid_init(void)241 int hid_init(void)
242 {
243 	if (!hid_mgr) {
244 		return init_hid_manager();
245 	}
246 
247 	/* Already initialized. */
248 	return 0;
249 }
250 
hid_exit(void)251 int hid_exit(void)
252 {
253 	if (hid_mgr) {
254 		/* Close the HID manager. */
255 		IOHIDManagerClose(hid_mgr, kIOHIDOptionsTypeNone);
256 		CFRelease(hid_mgr);
257 		hid_mgr = NULL;
258 	}
259 
260 	return 0;
261 }
262 
process_pending_events(void)263 static void process_pending_events(void) {
264 	SInt32 res;
265 	do {
266 		res = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.001, FALSE);
267 	} while(res != kCFRunLoopRunFinished && res != kCFRunLoopRunTimedOut);
268 }
269 
hid_enumerate(unsigned short vendor_id,unsigned short product_id)270 hid_device_info  *hid_enumerate(unsigned short vendor_id, unsigned short product_id)
271 {
272 	hid_device_info *root = NULL; /* return object */
273 	hid_device_info *cur_dev = NULL;
274 	CFIndex num_devices;
275 	int i;
276 
277 	/* Set up the HID Manager if it hasn't been done */
278 	if (hid_init() < 0)
279 		return NULL;
280 
281 	/* give the IOHIDManager a chance to update itself */
282 	process_pending_events();
283 
284 	/* Get a list of the Devices */
285 	IOHIDManagerSetDeviceMatching(hid_mgr, NULL);
286 	CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr);
287 
288 	/* Convert the list into a C array so we can iterate easily. */
289 	num_devices = CFSetGetCount(device_set);
290 	IOHIDDeviceRef *device_array = reinterpret_cast<IOHIDDeviceRef *>(calloc(num_devices, sizeof(IOHIDDeviceRef)));
291 	CFSetGetValues(device_set, (const void **) device_array);
292 
293 	/* Iterate over each device, making an entry for it. */
294 	for (i = 0; i < num_devices; i++) {
295 		unsigned short dev_vid;
296 		unsigned short dev_pid;
297 		#define BUF_LEN 256
298 		char buf[BUF_LEN];
299 
300 		IOHIDDeviceRef dev = device_array[i];
301 
302         if (!dev) {
303             continue;
304         }
305 		dev_vid = get_vendor_id(dev);
306 		dev_pid = get_product_id(dev);
307 
308 		/* Check the VID/PID against the arguments */
309 		if ((vendor_id == 0x0 || vendor_id == dev_vid) &&
310 		    (product_id == 0x0 || product_id == dev_pid)) {
311 			hid_device_info *tmp;
312 			io_object_t iokit_dev;
313 			kern_return_t res;
314 			io_string_t path;
315 
316 			/* VID/PID match. Create the record. */
317 			tmp = new hid_device_info;
318 			if (cur_dev) {
319 				cur_dev->next = tmp;
320 			}
321 			else {
322 				root = tmp;
323 			}
324 			cur_dev = tmp;
325 
326 			/* Get the Usage Page and Usage for this device. */
327 			cur_dev->usage_page = get_int_property(dev, CFSTR(kIOHIDPrimaryUsagePageKey));
328 			cur_dev->usage = get_int_property(dev, CFSTR(kIOHIDPrimaryUsageKey));
329 
330 			/* Fill out the record */
331 			cur_dev->next = NULL;
332 
333 			/* Fill in the path (IOService plane) */
334 			iokit_dev = hidapi_IOHIDDeviceGetService(dev);
335 			res = IORegistryEntryGetPath(iokit_dev, kIOServicePlane, path);
336 			if (res == KERN_SUCCESS)
337 				cur_dev->path = strdup(path);
338 			else
339 				cur_dev->path = strdup("");
340 
341 			/* Serial Number */
342 			get_serial_number(dev, buf, BUF_LEN);
343 			cur_dev->str_serial_number = buf;
344 
345 			/* Manufacturer and Product strings */
346 			get_manufacturer_string(dev, buf, BUF_LEN);
347 			cur_dev->str_manufacturer_string = buf;
348 
349 			get_product_string(dev, buf, BUF_LEN);
350 std::cout << buf << std::endl;
351 
352 			cur_dev->str_product_string = buf;
353 
354 			/* VID/PID */
355 			cur_dev->vendor_id = dev_vid;
356 			cur_dev->product_id = dev_pid;
357 
358 			/* Release Number */
359 			cur_dev->release_number = get_int_property(dev, CFSTR(kIOHIDVersionNumberKey));
360 
361 			/* Interface Number (Unsupported on Mac)*/
362 			cur_dev->interface_number = -1;
363 		}
364 	}
365 
366 	free(device_array);
367 	CFRelease(device_set);
368 
369 	return root;
370 }
371 
hid_free_enumeration(hid_device_info * devs)372 void  hid_free_enumeration(hid_device_info *devs)
373 {
374 	hid_device_info *d = devs;
375 	while (d) {
376 		hid_device_info *next = d->next;
377 		delete d;
378 		d = next;
379 	}
380 }
381 
hid_open(unsigned short vendor_id,unsigned short product_id,std::string serial_number)382 hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, std::string serial_number)
383 {
384 	/* This function is identical to the Linux version. Platform independent. */
385 	hid_device_info *devs, *cur_dev;
386 	std::string path_to_open;
387 	hid_device * handle = NULL;
388 
389 	path_to_open.clear();
390 
391 	devs = hid_enumerate(vendor_id, product_id);
392 	cur_dev = devs;
393 	while (cur_dev) {
394 		if (cur_dev->vendor_id == vendor_id &&
395 		    cur_dev->product_id == product_id) {
396 			if (!serial_number.empty() &&
397 				(cur_dev->str_serial_number == serial_number) ) {
398 				path_to_open = cur_dev->path;
399 				break;
400 			}
401 			else {
402 				path_to_open = cur_dev->path;
403 				break;
404 			}
405 		}
406 		cur_dev = cur_dev->next;
407 	}
408 
409 	if (!path_to_open.empty()) {
410 		/* Open the device */
411 		handle = hid_open_path(path_to_open);
412 	}
413 
414 	hid_free_enumeration(devs);
415 
416 	return handle;
417 }
418 
hid_device_removal_callback(void * context,IOReturn result,void * sender)419 static void hid_device_removal_callback(void *context, IOReturn result,
420                                         void *sender)
421 {
422 	/* Stop the Run Loop for this device. */
423 	hid_device *d = reinterpret_cast<hid_device *>(context);
424 
425 	d->disconnected = 1;
426 	CFRunLoopStop(d->run_loop);
427 }
428 
429 /* The Run Loop calls this function for each input report received.
430    This function puts the data into a linked list to be picked up by
431    hid_read(). */
hid_report_callback(void * context,IOReturn result,void * sender,IOHIDReportType report_type,uint32_t report_id,uint8_t * report,CFIndex report_length)432 static void hid_report_callback(void *context, IOReturn result, void *sender,
433                          IOHIDReportType report_type, uint32_t report_id,
434                          uint8_t *report, CFIndex report_length)
435 {
436 	input_report *rpt;
437 	hid_device *dev = reinterpret_cast<hid_device *>(context);
438 
439 	/* Make a new Input Report object */
440 	rpt = reinterpret_cast<input_report *>(calloc(1, sizeof(input_report)));
441 	rpt->data = reinterpret_cast<unsigned char *>(calloc(1, report_length));
442 	memcpy(rpt->data, report, report_length);
443 	rpt->len = report_length;
444 	rpt->next = NULL;
445 
446 	/* Lock this section */
447 	pthread_mutex_lock(&dev->mutex);
448 
449 	/* Attach the new report object to the end of the list. */
450 	if (dev->input_reports == NULL) {
451 		/* The list is empty. Put it at the root. */
452 		dev->input_reports = rpt;
453 	}
454 	else {
455 		/* Find the end of the list and attach. */
456 		input_report *cur = dev->input_reports;
457 		int num_queued = 0;
458 		while (cur->next != NULL) {
459 			cur = cur->next;
460 			num_queued++;
461 		}
462 		cur->next = rpt;
463 
464 		/* Pop one off if we've reached 30 in the queue. This
465 		   way we don't grow forever if the user never reads
466 		   anything from the device. */
467 		if (num_queued > 30) {
468 			dev->return_data(NULL, 0);
469 		}
470 	}
471 
472 	/* Signal a waiting thread that there is data. */
473 	pthread_cond_signal(&dev->condition);
474 
475 	/* Unlock */
476 	pthread_mutex_unlock(&dev->mutex);
477 
478 }
479 
480 /* This gets called when the read_thread's run loop gets signaled by
481    hid_close(), and serves to stop the read_thread's run loop. */
perform_signal_callback(void * context)482 static void perform_signal_callback(void *context)
483 {
484 	hid_device *dev = reinterpret_cast<hid_device *>(context);
485 	CFRunLoopStop(dev->run_loop); /*TODO: CFRunLoopGetCurrent()*/
486 }
487 
read_thread(void * param)488 static void *read_thread(void *param)
489 {
490 	hid_device *dev = reinterpret_cast<hid_device *>(param);
491 	SInt32 code;
492 
493 	/* Move the device's run loop to this thread. */
494 	IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetCurrent(), dev->run_loop_mode);
495 
496 	/* Create the RunLoopSource which is used to signal the
497 	   event loop to stop when hid_close() is called. */
498 	CFRunLoopSourceContext ctx;
499 	memset(&ctx, 0, sizeof(ctx));
500 	ctx.version = 0;
501 	ctx.info = dev;
502 	ctx.perform = &perform_signal_callback;
503 	dev->source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0/*order*/, &ctx);
504 	CFRunLoopAddSource(CFRunLoopGetCurrent(), dev->source, dev->run_loop_mode);
505 
506 	/* Store off the Run Loop so it can be stopped from hid_close()
507 	   and on device disconnection. */
508 	dev->run_loop = CFRunLoopGetCurrent();
509 
510 	/* Notify the main thread that the read thread is up and running. */
511 	pthread_barrier_wait(&dev->barrier);
512 
513 	/* Run the Event Loop. CFRunLoopRunInMode() will dispatch HID input
514 	   reports into the hid_report_callback(). */
515 	while (!dev->shutdown_thread && !dev->disconnected) {
516 		code = CFRunLoopRunInMode(dev->run_loop_mode, 1000/*sec*/, FALSE);
517 		/* Return if the device has been disconnected */
518 		if (code == kCFRunLoopRunFinished) {
519 			dev->disconnected = 1;
520 			break;
521 		}
522 
523 
524 		/* Break if The Run Loop returns Finished or Stopped. */
525 		if (code != kCFRunLoopRunTimedOut &&
526 		    code != kCFRunLoopRunHandledSource) {
527 			/* There was some kind of error. Setting
528 			   shutdown seems to make sense, but
529 			   there may be something else more appropriate */
530 			dev->shutdown_thread = 1;
531 			break;
532 		}
533 	}
534 
535 	/* Now that the read thread is stopping, Wake any threads which are
536 	   waiting on data (in hid_read_timeout()). Do this under a mutex to
537 	   make sure that a thread which is about to go to sleep waiting on
538 	   the condition actually will go to sleep before the condition is
539 	   signaled. */
540 	pthread_mutex_lock(&dev->mutex);
541 	pthread_cond_broadcast(&dev->condition);
542 	pthread_mutex_unlock(&dev->mutex);
543 
544 	/* Wait here until hid_close() is called and makes it past
545 	   the call to CFRunLoopWakeUp(). This thread still needs to
546 	   be valid when that function is called on the other thread. */
547 	pthread_barrier_wait(&dev->shutdown_barrier);
548 
549 	return NULL;
550 }
551 
552 /* hid_open_path()
553  *
554  * path must be a valid path to an IOHIDDevice in the IOService plane
555  * Example: "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/EHC1@1D,7/AppleUSBEHCI/PLAYSTATION(R)3 Controller@fd120000/IOUSBInterface@0/IOUSBHIDDriver"
556  */
hid_open_path(std::string path)557 hid_device * hid_open_path(std::string path)
558 {
559 	hid_device *dev = NULL;
560 	io_registry_entry_t entry = MACH_PORT_NULL;
561 	IOReturn ret;
562 
563 	dev = new hid_device;
564 
565 	/* Set up the HID Manager if it hasn't been done */
566 	if (hid_init() < 0)
567 		return NULL;
568 
569 	/* Get the IORegistry entry for the given path */
570 	entry = IORegistryEntryFromPath(kIOMasterPortDefault, path.c_str());
571 	if (entry == MACH_PORT_NULL) {
572 		/* Path wasn't valid (maybe device was removed?) */
573 		goto return_error;
574 	}
575 
576 	/* Create an IOHIDDevice for the entry */
577 	dev->device_handle = IOHIDDeviceCreate(kCFAllocatorDefault, entry);
578 	if (dev->device_handle == NULL) {
579 		/* Error creating the HID device */
580 		goto return_error;
581 	}
582 
583 	/* Open the IOHIDDevice */
584 	ret = IOHIDDeviceOpen(dev->device_handle, kIOHIDOptionsTypeSeizeDevice);
585 	if (ret == kIOReturnSuccess) {
586 		char str[32];
587 
588 		/* Create the buffers for receiving data */
589 		dev->max_input_report_len = (CFIndex) get_max_report_length(dev->device_handle);
590 		dev->input_report_buf = reinterpret_cast<uint8_t *>(calloc(dev->max_input_report_len, sizeof(uint8_t)));
591 
592 		/* Create the Run Loop Mode for this device.
593 		   printing the reference seems to work. */
594 		sprintf(str, "HIDAPI_%p", dev->device_handle);
595 		dev->run_loop_mode =
596 			CFStringCreateWithCString(NULL, str, kCFStringEncodingASCII);
597 
598 		/* Attach the device to a Run Loop */
599 		IOHIDDeviceRegisterInputReportCallback(
600 			dev->device_handle, dev->input_report_buf, dev->max_input_report_len,
601 			&hid_report_callback, dev);
602 		IOHIDDeviceRegisterRemovalCallback(dev->device_handle, hid_device_removal_callback, dev);
603 
604 		/* Start the read thread */
605 		pthread_create(&dev->thread, NULL, read_thread, dev);
606 
607 		/* Wait here for the read thread to be initialized. */
608 		pthread_barrier_wait(&dev->barrier);
609 
610 		IOObjectRelease(entry);
611 		return dev;
612 	}
613 	else {
614 		goto return_error;
615 	}
616 
617 return_error:
618 	if (dev->device_handle != NULL)
619 		CFRelease(dev->device_handle);
620 
621 	if (entry != MACH_PORT_NULL)
622 		IOObjectRelease(entry);
623 
624 	delete dev;
625 	return NULL;
626 }
627 
set_report(IOHIDReportType type,const unsigned char * data,size_t length)628 int hid_device::set_report(IOHIDReportType type, const unsigned char *data, size_t length)
629 {
630 	const unsigned char *data_to_send;
631 	size_t length_to_send;
632 	IOReturn res;
633 
634 	/* Return if the device has been disconnected. */
635 	if (disconnected)
636 		return -1;
637 
638 	if (data[0] == 0x0) {
639 		/* Not using numbered Reports.
640 		   Don't send the report number. */
641 		data_to_send = data+1;
642 		length_to_send = length-1;
643 	}
644 	else {
645 		/* Using numbered Reports.
646 		   Send the Report Number */
647 		data_to_send = data;
648 		length_to_send = length;
649 	}
650 
651 	if (!disconnected) {
652 		res = IOHIDDeviceSetReport(device_handle,
653 					   type,
654 					   data[0], /* Report ID*/
655 					   data_to_send, length_to_send);
656 
657 		if (res == kIOReturnSuccess) {
658 			return length;
659 		}
660 		else
661 			return -1;
662 	}
663 
664 	return -1;
665 }
666 
hid_write(const unsigned char * data,size_t length)667 int hid_device::hid_write(const unsigned char *data, size_t length)
668 {
669 	return set_report(kIOHIDReportTypeOutput, data, length);
670 }
671 
672 /* Helper function, so that this isn't duplicated in hid_read(). */
return_data(unsigned char * data,size_t length)673 int hid_device::return_data(unsigned char *data, size_t length)
674 {
675 	/* Copy the data out of the linked list item (rpt) into the
676 	   return buffer (data), and delete the liked list item. */
677 	input_report *rpt = input_reports;
678 	size_t len = (length < rpt->len)? length: rpt->len;
679 	memcpy(data, rpt->data, len);
680 	input_reports = rpt->next;
681 	free(rpt->data);
682 	free(rpt);
683 	return len;
684 }
685 
cond_wait(pthread_cond_t * cond,pthread_mutex_t * mutex)686 int hid_device::cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
687 {
688 	while (!input_reports) {
689 		int res = pthread_cond_wait(cond, mutex);
690 		if (res != 0)
691 			return res;
692 
693 		/* A res of 0 means we may have been signaled or it may
694 		   be a spurious wakeup. Check to see that there's acutally
695 		   data in the queue before returning, and if not, go back
696 		   to sleep. See the pthread_cond_timedwait() man page for
697 		   details. */
698 
699 		if (shutdown_thread || disconnected)
700 			return -1;
701 	}
702 
703 	return 0;
704 }
705 
cond_timedwait(pthread_cond_t * cond,pthread_mutex_t * mutex,const struct timespec * abstime)706 int hid_device::cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
707 {
708 	while (!input_reports) {
709 		int res = pthread_cond_timedwait(cond, mutex, abstime);
710 		if (res != 0)
711 			return res;
712 
713 		/* A res of 0 means we may have been signaled or it may
714 		   be a spurious wakeup. Check to see that there's acutally
715 		   data in the queue before returning, and if not, go back
716 		   to sleep. See the pthread_cond_timedwait() man page for
717 		   details. */
718 
719 		if (shutdown_thread || disconnected)
720 			return -1;
721 	}
722 
723 	return 0;
724 
725 }
726 
hid_read_timeout(unsigned char * data,size_t length,int milliseconds)727 int hid_device::hid_read_timeout(unsigned char *data, size_t length, int milliseconds)
728 {
729 	int bytes_read = -1;
730 
731 	/* Lock the access to the report list. */
732 	pthread_mutex_lock(&mutex);
733 
734 	/* There's an input report queued up. Return it. */
735 	if (input_reports) {
736 		/* Return the first one */
737 		bytes_read = return_data(data, length);
738 		goto ret;
739 	}
740 
741 	/* Return if the device has been disconnected. */
742 	if (disconnected) {
743 		bytes_read = -1;
744 		goto ret;
745 	}
746 
747 	if (shutdown_thread) {
748 		/* This means the device has been closed (or there
749 		   has been an error. An error code of -1 should
750 		   be returned. */
751 		bytes_read = -1;
752 		goto ret;
753 	}
754 
755 	/* There is no data. Go to sleep and wait for data. */
756 
757 	if (milliseconds == -1) {
758 		/* Blocking */
759 		int res;
760 		res = cond_wait(&condition, &mutex);
761 		if (res == 0)
762 			bytes_read = return_data(data, length);
763 		else {
764 			/* There was an error, or a device disconnection. */
765 			bytes_read = -1;
766 		}
767 	}
768 	else if (milliseconds > 0) {
769 		/* Non-blocking, but called with timeout. */
770 		int res;
771 		struct timespec ts;
772 		struct timeval tv;
773 		gettimeofday(&tv, NULL);
774 		TIMEVAL_TO_TIMESPEC(&tv, &ts);
775 		ts.tv_sec += milliseconds / 1000;
776 		ts.tv_nsec += (milliseconds % 1000) * 1000000;
777 		if (ts.tv_nsec >= 1000000000L) {
778 			ts.tv_sec++;
779 			ts.tv_nsec -= 1000000000L;
780 		}
781 
782 		res = cond_timedwait(&condition, &mutex, &ts);
783 		if (res == 0)
784 			bytes_read = return_data(data, length);
785 		else if (res == ETIMEDOUT)
786 			bytes_read = 0;
787 		else
788 			bytes_read = -1;
789 	}
790 	else {
791 		/* Purely non-blocking */
792 		bytes_read = 0;
793 	}
794 
795 ret:
796 	/* Unlock */
797 	pthread_mutex_unlock(&mutex);
798 	return bytes_read;
799 }
800 
hid_read(unsigned char * data,size_t length)801 int hid_device::hid_read(unsigned char *data, size_t length)
802 {
803 	return hid_read_timeout(data, length, (blocking)? -1: 0);
804 }
805 
hid_set_nonblocking(int nonblock)806 int hid_device::hid_set_nonblocking(int nonblock)
807 {
808 	/* All Nonblocking operation is handled by the library. */
809 	blocking = !nonblock;
810 	return 0;
811 }
812 
hid_send_feature_report(const unsigned char * data,size_t length)813 int hid_device::hid_send_feature_report(const unsigned char *data, size_t length)
814 {
815 	return set_report(kIOHIDReportTypeFeature, data, length);
816 }
817 
hid_get_feature_report(unsigned char * data,size_t length)818 int hid_device::hid_get_feature_report(unsigned char *data, size_t length)
819 {
820 	CFIndex len = length;
821 	IOReturn res;
822 
823 	/* Return if the device has been unplugged. */
824 	if (disconnected)
825 		return -1;
826 
827 	res = IOHIDDeviceGetReport(device_handle,
828 	                           kIOHIDReportTypeFeature,
829 	                           data[0], /* Report ID */
830 	                           data, &len);
831 	if (res == kIOReturnSuccess)
832 		return len;
833 	else
834 		return -1;
835 }
836 
837 
hid_close()838 void hid_device::hid_close()
839 {
840 	/* Disconnect the report callback before close. */
841 	if (!disconnected) {
842 		IOHIDDeviceRegisterInputReportCallback(
843 			device_handle, input_report_buf, max_input_report_len,
844 			NULL, this);
845 		IOHIDDeviceRegisterRemovalCallback(device_handle, NULL, this);
846 		IOHIDDeviceUnscheduleFromRunLoop(device_handle, run_loop, run_loop_mode);
847 		IOHIDDeviceScheduleWithRunLoop(device_handle, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
848 	}
849 
850 	/* Cause read_thread() to stop. */
851 	shutdown_thread = 1;
852 
853 	/* Wake up the run thread's event loop so that the thread can exit. */
854 	CFRunLoopSourceSignal(source);
855 	CFRunLoopWakeUp(run_loop);
856 
857 	/* Notify the read thread that it can shut down now. */
858 	pthread_barrier_wait(&shutdown_barrier);
859 
860 	/* Wait for read_thread() to end. */
861 	pthread_join(thread, NULL);
862 
863 	/* Close the OS handle to the device, but only if it's not
864 	   been unplugged. If it's been unplugged, then calling
865 	   IOHIDDeviceClose() will crash. */
866 	if (!disconnected) {
867 		IOHIDDeviceClose(device_handle, kIOHIDOptionsTypeSeizeDevice);
868 	}
869 
870 	/* Clear out the queue of received reports. */
871 	pthread_mutex_lock(&mutex);
872 	while (input_reports) {
873 		return_data(NULL, 0);
874 	}
875 	pthread_mutex_unlock(&mutex);
876 	CFRelease(device_handle);
877 
878 //	delete dev;
879 }
880 
hid_error()881 const char * hid_device::hid_error()
882 {
883 	return "HID error string not implemented";
884 }
885