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