Atlas - hid.c

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