Atlas - hid.c

Home / ext / SDL / src / hidapi / mac Lines: 1 | Size: 44934 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 libusb/hidapi Team 9 10 Copyright 2022, All Rights Reserved. 11 12 At the discretion of the user of this library, 13 this software may be licensed under the terms of the 14 GNU General Public License v3, a BSD-Style license, or the 15 original HIDAPI license as outlined in the LICENSE.txt, 16 LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt 17 files located at the root of the source distribution. 18 These files may also be found in the public source 19 code repository located at: 20 https://github.com/libusb/hidapi . 21********************************************************/ 22 23/* See Apple Technical Note TN2187 for details on IOHidManager. */ 24 25#include <IOKit/hid/IOHIDManager.h> 26#include <IOKit/hid/IOHIDKeys.h> 27#include <IOKit/IOKitLib.h> 28#include <IOKit/usb/USBSpec.h> 29#include <CoreFoundation/CoreFoundation.h> 30#include <mach/mach_error.h> 31#include <stdbool.h> 32#include <wchar.h> 33#include <locale.h> 34#include <pthread.h> 35#include <sys/time.h> 36#include <unistd.h> 37#include <dlfcn.h> 38 39#include "hidapi_darwin.h" 40 41/* Barrier implementation because Mac OSX doesn't have pthread_barrier. 42 It also doesn't have clock_gettime(). So much for POSIX and SUSv2. 43 This implementation came from Brent Priddy and was posted on 44 StackOverflow. It is used with his permission. */ 45typedef int pthread_barrierattr_t; 46typedef struct pthread_barrier { 47 pthread_mutex_t mutex; 48 pthread_cond_t cond; 49 int count; 50 int trip_count; 51} pthread_barrier_t; 52 53static int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count) 54{ 55 (void) attr; 56 57 if (count == 0) { 58 errno = EINVAL; 59 return -1; 60 } 61 62 if (pthread_mutex_init(&barrier->mutex, 0) < 0) { 63 return -1; 64 } 65 if (pthread_cond_init(&barrier->cond, 0) < 0) { 66 pthread_mutex_destroy(&barrier->mutex); 67 return -1; 68 } 69 barrier->trip_count = count; 70 barrier->count = 0; 71 72 return 0; 73} 74 75static int pthread_barrier_destroy(pthread_barrier_t *barrier) 76{ 77 pthread_cond_destroy(&barrier->cond); 78 pthread_mutex_destroy(&barrier->mutex); 79 return 0; 80} 81 82static int pthread_barrier_wait(pthread_barrier_t *barrier) 83{ 84 pthread_mutex_lock(&barrier->mutex); 85 ++(barrier->count); 86 if (barrier->count >= barrier->trip_count) { 87 barrier->count = 0; 88 pthread_mutex_unlock(&barrier->mutex); 89 pthread_cond_broadcast(&barrier->cond); 90 return 1; 91 } 92 else { 93 do { 94 pthread_cond_wait(&barrier->cond, &(barrier->mutex)); 95 } 96 while (barrier->count != 0); 97 98 pthread_mutex_unlock(&barrier->mutex); 99 return 0; 100 } 101} 102 103static int return_data(hid_device *dev, unsigned char *data, size_t length); 104 105/* Linked List of input reports received from the device. */ 106struct input_report { 107 uint8_t *data; 108 size_t len; 109 struct input_report *next; 110}; 111 112static struct hid_api_version api_version = { 113 .major = HID_API_VERSION_MAJOR, 114 .minor = HID_API_VERSION_MINOR, 115 .patch = HID_API_VERSION_PATCH 116}; 117 118/* - Run context - */ 119static IOHIDManagerRef hid_mgr = 0x0; 120static int is_macos_10_10_or_greater = 0; 121static IOOptionBits device_open_options = 0; 122static wchar_t *last_global_error_str = NULL; 123/* --- */ 124 125struct hid_device_ { 126 IOHIDDeviceRef device_handle; 127 IOOptionBits open_options; 128 int blocking; 129 int disconnected; 130 CFStringRef run_loop_mode; 131 CFRunLoopRef run_loop; 132 CFRunLoopSourceRef source; 133 uint8_t *input_report_buf; 134 CFIndex max_input_report_len; 135 struct input_report *input_reports; 136 struct hid_device_info* device_info; 137 138 pthread_t thread; 139 pthread_mutex_t mutex; /* Protects input_reports */ 140 pthread_cond_t condition; 141 pthread_barrier_t barrier; /* Ensures correct startup sequence */ 142 pthread_barrier_t shutdown_barrier; /* Ensures correct shutdown sequence */ 143 int shutdown_thread; 144 wchar_t *last_error_str; 145}; 146 147static hid_device *new_hid_device(void) 148{ 149 hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device)); 150 if (dev == NULL) { 151 return NULL; 152 } 153 154 dev->device_handle = NULL; 155 dev->open_options = device_open_options; 156 dev->blocking = 1; 157 dev->disconnected = 0; 158 dev->run_loop_mode = NULL; 159 dev->run_loop = NULL; 160 dev->source = NULL; 161 dev->input_report_buf = NULL; 162 dev->input_reports = NULL; 163 dev->device_info = NULL; 164 dev->shutdown_thread = 0; 165 dev->last_error_str = NULL; 166 167 /* Thread objects */ 168 pthread_mutex_init(&dev->mutex, NULL); 169 pthread_cond_init(&dev->condition, NULL); 170 pthread_barrier_init(&dev->barrier, NULL, 2); 171 pthread_barrier_init(&dev->shutdown_barrier, NULL, 2); 172 173 return dev; 174} 175 176static void free_hid_device(hid_device *dev) 177{ 178 if (!dev) 179 return; 180 181 /* Delete any input reports still left over. */ 182 struct input_report *rpt = dev->input_reports; 183 while (rpt) { 184 struct input_report *next = rpt->next; 185 free(rpt->data); 186 free(rpt); 187 rpt = next; 188 } 189 190 /* Free the string and the report buffer. The check for NULL 191 is necessary here as CFRelease() doesn't handle NULL like 192 free() and others do. */ 193 if (dev->run_loop_mode) 194 CFRelease(dev->run_loop_mode); 195 if (dev->source) 196 CFRelease(dev->source); 197 free(dev->input_report_buf); 198 hid_free_enumeration(dev->device_info); 199 200 /* Clean up the thread objects */ 201 pthread_barrier_destroy(&dev->shutdown_barrier); 202 pthread_barrier_destroy(&dev->barrier); 203 pthread_cond_destroy(&dev->condition); 204 pthread_mutex_destroy(&dev->mutex); 205 206 /* Free the structure itself. */ 207 free(dev); 208} 209 210 211#ifndef HIDAPI_USING_SDL_RUNTIME 212/* The caller must free the returned string with free(). */ 213static wchar_t *utf8_to_wchar_t(const char *utf8) 214{ 215 wchar_t *ret = NULL; 216 217 if (utf8) { 218 size_t wlen = mbstowcs(NULL, utf8, 0); 219 if ((size_t) -1 == wlen) { 220 return wcsdup(L""); 221 } 222 ret = (wchar_t*) calloc(wlen+1, sizeof(wchar_t)); 223 if (ret == NULL) { 224 /* as much as we can do at this point */ 225 return NULL; 226 } 227 mbstowcs(ret, utf8, wlen+1); 228 ret[wlen] = 0x0000; 229 } 230 231 return ret; 232} 233#endif 234 235 236/* Makes a copy of the given error message (and decoded according to the 237 * currently locale) into the wide string pointer pointed by error_str. 238 * The last stored error string is freed. 239 * Use register_error_str(NULL) to free the error message completely. */ 240static void register_error_str(wchar_t **error_str, const char *msg) 241{ 242 free(*error_str); 243#ifdef HIDAPI_USING_SDL_RUNTIME 244 /* Thread-safe error handling */ 245 if (msg) { 246 SDL_SetError("%s", msg); 247 } else { 248 SDL_ClearError(); 249 } 250#else 251 *error_str = utf8_to_wchar_t(msg); 252#endif 253} 254 255/* Similar to register_error_str, but allows passing a format string with va_list args into this function. */ 256static void register_error_str_vformat(wchar_t **error_str, const char *format, va_list args) 257{ 258 char msg[1024]; 259 vsnprintf(msg, sizeof(msg), format, args); 260 261 register_error_str(error_str, msg); 262} 263 264/* Set the last global error to be reported by hid_error(NULL). 265 * The given error message will be copied (and decoded according to the 266 * currently locale, so do not pass in string constants). 267 * The last stored global error message is freed. 268 * Use register_global_error(NULL) to indicate "no error". */ 269static void register_global_error(const char *msg) 270{ 271 register_error_str(&last_global_error_str, msg); 272} 273 274/* Similar to register_global_error, but allows passing a format string into this function. */ 275static void register_global_error_format(const char *format, ...) 276{ 277 va_list args; 278 va_start(args, format); 279 register_error_str_vformat(&last_global_error_str, format, args); 280 va_end(args); 281} 282 283/* Set the last error for a device to be reported by hid_error(dev). 284 * The given error message will be copied (and decoded according to the 285 * currently locale, so do not pass in string constants). 286 * The last stored device error message is freed. 287 * Use register_device_error(dev, NULL) to indicate "no error". */ 288static void register_device_error(hid_device *dev, const char *msg) 289{ 290 register_error_str(&dev->last_error_str, msg); 291} 292 293/* Similar to register_device_error, but you can pass a format string into this function. */ 294static void register_device_error_format(hid_device *dev, const char *format, ...) 295{ 296 va_list args; 297 va_start(args, format); 298 register_error_str_vformat(&dev->last_error_str, format, args); 299 va_end(args); 300} 301 302 303static CFArrayRef get_array_property(IOHIDDeviceRef device, CFStringRef key) 304{ 305 CFTypeRef ref = IOHIDDeviceGetProperty(device, key); 306 if (ref != NULL && CFGetTypeID(ref) == CFArrayGetTypeID()) { 307 return (CFArrayRef)ref; 308 } else { 309 return NULL; 310 } 311} 312 313static int32_t get_int_property(IOHIDDeviceRef device, CFStringRef key) 314{ 315 CFTypeRef ref; 316 int32_t value = 0; 317 318 ref = IOHIDDeviceGetProperty(device, key); 319 if (ref) { 320 if (CFGetTypeID(ref) == CFNumberGetTypeID()) { 321 CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, &value); 322 return value; 323 } 324 } 325 return 0; 326} 327 328static bool try_get_int_property(IOHIDDeviceRef device, CFStringRef key, int32_t *out_val) 329{ 330 bool result = false; 331 CFTypeRef ref; 332 333 ref = IOHIDDeviceGetProperty(device, key); 334 if (ref) { 335 if (CFGetTypeID(ref) == CFNumberGetTypeID()) { 336 result = CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, out_val); 337 } 338 } 339 return result; 340} 341 342static bool try_get_ioregistry_int_property(io_service_t service, CFStringRef property, int32_t *out_val) 343{ 344 bool result = false; 345 CFTypeRef ref = IORegistryEntryCreateCFProperty(service, property, kCFAllocatorDefault, 0); 346 347 if (ref) { 348 if (CFGetTypeID(ref) == CFNumberGetTypeID()) { 349 result = CFNumberGetValue(ref, kCFNumberSInt32Type, out_val); 350 } 351 352 CFRelease(ref); 353 } 354 355 return result; 356} 357 358static CFArrayRef get_usage_pairs(IOHIDDeviceRef device) 359{ 360 return get_array_property(device, CFSTR(kIOHIDDeviceUsagePairsKey)); 361} 362 363static unsigned short get_vendor_id(IOHIDDeviceRef device) 364{ 365 return get_int_property(device, CFSTR(kIOHIDVendorIDKey)); 366} 367 368static unsigned short get_product_id(IOHIDDeviceRef device) 369{ 370 return get_int_property(device, CFSTR(kIOHIDProductIDKey)); 371} 372 373static int32_t get_max_report_length(IOHIDDeviceRef device) 374{ 375 return get_int_property(device, CFSTR(kIOHIDMaxInputReportSizeKey)); 376} 377 378static int get_string_property(IOHIDDeviceRef device, CFStringRef prop, wchar_t *buf, size_t len) 379{ 380 CFStringRef str; 381 382 if (!len) 383 return 0; 384 385 str = (CFStringRef) IOHIDDeviceGetProperty(device, prop); 386 387 buf[0] = 0; 388 389 if (str && CFGetTypeID(str) == CFStringGetTypeID()) { 390 CFIndex str_len = CFStringGetLength(str); 391 CFRange range; 392 CFIndex used_buf_len; 393 CFIndex chars_copied; 394 395 len --; 396 397 range.location = 0; 398 range.length = ((size_t) str_len > len)? len: (size_t) str_len; 399 chars_copied = CFStringGetBytes(str, 400 range, 401 kCFStringEncodingUTF32LE, 402 (char) '?', 403 FALSE, 404 (UInt8*)buf, 405 len * sizeof(wchar_t), 406 &used_buf_len); 407 408 if (chars_copied <= 0) 409 buf[0] = 0; 410 else 411 buf[chars_copied] = 0; 412 413 return 0; 414 } 415 else 416 return -1; 417 418} 419 420static int get_serial_number(IOHIDDeviceRef device, wchar_t *buf, size_t len) 421{ 422 return get_string_property(device, CFSTR(kIOHIDSerialNumberKey), buf, len); 423} 424 425static int get_manufacturer_string(IOHIDDeviceRef device, wchar_t *buf, size_t len) 426{ 427 return get_string_property(device, CFSTR(kIOHIDManufacturerKey), buf, len); 428} 429 430static int get_product_string(IOHIDDeviceRef device, wchar_t *buf, size_t len) 431{ 432 return get_string_property(device, CFSTR(kIOHIDProductKey), buf, len); 433} 434 435 436/* Implementation of wcsdup() for Mac. */ 437static wchar_t *dup_wcs(const wchar_t *s) 438{ 439 size_t len = wcslen(s); 440 wchar_t *ret = (wchar_t*) malloc((len+1)*sizeof(wchar_t)); 441 wcscpy(ret, s); 442 443 return ret; 444} 445 446/* Initialize the IOHIDManager. Return 0 for success and -1 for failure. */ 447static int init_hid_manager(void) 448{ 449 /* Initialize all the HID Manager Objects */ 450 hid_mgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); 451 if (hid_mgr) { 452 IOHIDManagerSetDeviceMatching(hid_mgr, NULL); 453 IOHIDManagerScheduleWithRunLoop(hid_mgr, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); 454 return 0; 455 } 456 457 register_global_error("Failed to create IOHIDManager"); 458 return -1; 459} 460 461HID_API_EXPORT const struct hid_api_version* HID_API_CALL hid_version(void) 462{ 463 return &api_version; 464} 465 466HID_API_EXPORT const char* HID_API_CALL hid_version_str(void) 467{ 468 return HID_API_VERSION_STR; 469} 470 471/* Initialize the IOHIDManager if necessary. This is the public function, and 472 it is safe to call this function repeatedly. Return 0 for success and -1 473 for failure. */ 474int HID_API_EXPORT hid_init(void) 475{ 476 register_global_error(NULL); 477 478 if (!hid_mgr) { 479 is_macos_10_10_or_greater = (kCFCoreFoundationVersionNumber >= 1151.16); /* kCFCoreFoundationVersionNumber10_10 */ 480 hid_darwin_set_open_exclusive(1); /* Backward compatibility */ 481 return init_hid_manager(); 482 } 483 484 /* Already initialized. */ 485 return 0; 486} 487 488int HID_API_EXPORT hid_exit(void) 489{ 490 if (hid_mgr) { 491 /* Close the HID manager. */ 492 IOHIDManagerClose(hid_mgr, kIOHIDOptionsTypeNone); 493 CFRelease(hid_mgr); 494 hid_mgr = NULL; 495 } 496 497 /* Free global error message */ 498 register_global_error(NULL); 499 500 return 0; 501} 502 503static void process_pending_events(void) { 504 SInt32 res; 505 do { 506 res = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.001, FALSE); 507 } while(res != kCFRunLoopRunFinished && res != kCFRunLoopRunTimedOut); 508} 509 510static int read_usb_interface_from_hid_service_parent(io_service_t hid_service) 511{ 512 int32_t result = -1; 513 bool success = false; 514 io_registry_entry_t current = IO_OBJECT_NULL; 515 kern_return_t res; 516 int parent_number = 0; 517 518 res = IORegistryEntryGetParentEntry(hid_service, kIOServicePlane, &current); 519 while (KERN_SUCCESS == res 520 /* Only search up to 3 parent entries. 521 * With the default driver - the parent-of-interest supposed to be the first one, 522 * but lets assume some custom drivers or so, with deeper tree. */ 523 && parent_number < 3) { 524 io_registry_entry_t parent = IO_OBJECT_NULL; 525 int32_t interface_number = -1; 526 parent_number++; 527 528 success = try_get_ioregistry_int_property(current, CFSTR(kUSBInterfaceNumber), &interface_number); 529 if (success) { 530 result = interface_number; 531 break; 532 } 533 534 res = IORegistryEntryGetParentEntry(current, kIOServicePlane, &parent); 535 if (parent) { 536 IOObjectRelease(current); 537 current = parent; 538 } 539 540 } 541 542 if (current) { 543 IOObjectRelease(current); 544 current = IO_OBJECT_NULL; 545 } 546 547 return result; 548} 549 550#ifdef HIDAPI_IGNORE_DEVICE 551static hid_bus_type get_bus_type(IOHIDDeviceRef dev) 552{ 553 hid_bus_type bus_type = HID_API_BUS_UNKNOWN; 554 555 CFTypeRef transport_prop = IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDTransportKey)); 556 557 if (transport_prop != NULL && CFGetTypeID(transport_prop) == CFStringGetTypeID()) { 558 if (CFStringCompare((CFStringRef)transport_prop, CFSTR(kIOHIDTransportUSBValue), 0) == kCFCompareEqualTo) { 559 bus_type = HID_API_BUS_USB; 560 } else if (CFStringHasPrefix((CFStringRef)transport_prop, CFSTR(kIOHIDTransportBluetoothValue))) { 561 bus_type = HID_API_BUS_BLUETOOTH; 562 } else if (CFStringCompare((CFStringRef)transport_prop, CFSTR(kIOHIDTransportI2CValue), 0) == kCFCompareEqualTo) { 563 bus_type = HID_API_BUS_I2C; 564 } else if (CFStringCompare((CFStringRef)transport_prop, CFSTR(kIOHIDTransportSPIValue), 0) == kCFCompareEqualTo) { 565 bus_type = HID_API_BUS_SPI; 566 } 567 } 568 return bus_type; 569} 570#endif /* HIDAPI_IGNORE_DEVICE */ 571 572static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev, int32_t usage_page, int32_t usage) 573{ 574 unsigned short dev_vid; 575 unsigned short dev_pid; 576 int BUF_LEN = 256; 577 wchar_t buf[BUF_LEN]; 578 CFTypeRef transport_prop; 579 580 struct hid_device_info *cur_dev; 581 io_service_t hid_service; 582 kern_return_t res; 583 uint64_t entry_id = 0; 584 585 if (dev == NULL) { 586 return NULL; 587 } 588 589 cur_dev = (struct hid_device_info *)calloc(1, sizeof(struct hid_device_info)); 590 if (cur_dev == NULL) { 591 return NULL; 592 } 593 594 dev_vid = get_vendor_id(dev); 595 dev_pid = get_product_id(dev); 596 597#ifdef HIDAPI_IGNORE_DEVICE 598 /* See if there are any devices we should skip in enumeration */ 599 if (HIDAPI_IGNORE_DEVICE(get_bus_type(dev), dev_vid, dev_pid, usage_page, usage)) { 600 free(cur_dev); 601 return NULL; 602 } 603#endif 604 605 cur_dev->usage_page = usage_page; 606 cur_dev->usage = usage; 607 608 /* Fill out the record */ 609 cur_dev->next = NULL; 610 611 /* Fill in the path (as a unique ID of the service entry) */ 612 cur_dev->path = NULL; 613 hid_service = IOHIDDeviceGetService(dev); 614 if (hid_service != MACH_PORT_NULL) { 615 res = IORegistryEntryGetRegistryEntryID(hid_service, &entry_id); 616 } 617 else { 618 res = KERN_INVALID_ARGUMENT; 619 } 620 621 if (res == KERN_SUCCESS) { 622 /* max value of entry_id(uint64_t) is 18446744073709551615 which is 20 characters long, 623 so for (max) "path" string 'DevSrvsID:18446744073709551615' we would need 624 9+1+20+1=31 bytes buffer, but allocate 32 for simple alignment */ 625 const size_t path_len = 32; 626 cur_dev->path = calloc(1, path_len); 627 if (cur_dev->path != NULL) { 628 snprintf(cur_dev->path, path_len, "DevSrvsID:%llu", entry_id); 629 } 630 } 631 632 if (cur_dev->path == NULL) { 633 /* for whatever reason, trying to keep it a non-NULL string */ 634 cur_dev->path = strdup(""); 635 } 636 637 /* Serial Number */ 638 get_serial_number(dev, buf, BUF_LEN); 639 cur_dev->serial_number = dup_wcs(buf); 640 641 /* Manufacturer and Product strings */ 642 get_manufacturer_string(dev, buf, BUF_LEN); 643 cur_dev->manufacturer_string = dup_wcs(buf); 644 get_product_string(dev, buf, BUF_LEN); 645 cur_dev->product_string = dup_wcs(buf); 646 647 /* VID/PID */ 648 cur_dev->vendor_id = dev_vid; 649 cur_dev->product_id = dev_pid; 650 651 /* Release Number */ 652 cur_dev->release_number = get_int_property(dev, CFSTR(kIOHIDVersionNumberKey)); 653 654 /* Interface Number. 655 * We can only retrieve the interface number for USB HID devices. 656 * See below */ 657 cur_dev->interface_number = -1; 658 659 /* Bus Type */ 660 transport_prop = IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDTransportKey)); 661 662 if (transport_prop != NULL && CFGetTypeID(transport_prop) == CFStringGetTypeID()) { 663 if (CFStringCompare((CFStringRef)transport_prop, CFSTR(kIOHIDTransportUSBValue), 0) == kCFCompareEqualTo) { 664 int32_t interface_number = -1; 665 cur_dev->bus_type = HID_API_BUS_USB; 666 667 /* A IOHIDDeviceRef used to have this simple property, 668 * until macOS 13.3 - we will try to use it. */ 669 if (try_get_int_property(dev, CFSTR(kUSBInterfaceNumber), &interface_number)) { 670 cur_dev->interface_number = interface_number; 671 } else { 672 /* Otherwise fallback to io_service_t property. 673 * (of one of the parent services). */ 674 cur_dev->interface_number = read_usb_interface_from_hid_service_parent(hid_service); 675 676 /* If the above doesn't work - 677 * no (known) fallback exists at this point. */ 678 } 679 680 /* Match "Bluetooth", "BluetoothLowEnergy" and "Bluetooth Low Energy" strings */ 681 } else if (CFStringHasPrefix((CFStringRef)transport_prop, CFSTR(kIOHIDTransportBluetoothValue))) { 682 cur_dev->bus_type = HID_API_BUS_BLUETOOTH; 683 } else if (CFStringCompare((CFStringRef)transport_prop, CFSTR(kIOHIDTransportI2CValue), 0) == kCFCompareEqualTo) { 684 cur_dev->bus_type = HID_API_BUS_I2C; 685 } else if (CFStringCompare((CFStringRef)transport_prop, CFSTR(kIOHIDTransportSPIValue), 0) == kCFCompareEqualTo) { 686 cur_dev->bus_type = HID_API_BUS_SPI; 687 } 688 } 689 690 return cur_dev; 691} 692 693static struct hid_device_info *create_device_info(IOHIDDeviceRef device) 694{ 695 const int32_t primary_usage_page = get_int_property(device, CFSTR(kIOHIDPrimaryUsagePageKey)); 696 const int32_t primary_usage = get_int_property(device, CFSTR(kIOHIDPrimaryUsageKey)); 697 698 /* Primary should always be first, to match previous behavior. */ 699 struct hid_device_info *root = create_device_info_with_usage(device, primary_usage_page, primary_usage); 700 struct hid_device_info *cur = root; 701 702 CFArrayRef usage_pairs = get_usage_pairs(device); 703 704 if (usage_pairs != NULL) { 705 struct hid_device_info *next = NULL; 706 for (CFIndex i = 0; i < CFArrayGetCount(usage_pairs); i++) { 707 CFTypeRef dict = CFArrayGetValueAtIndex(usage_pairs, i); 708 if (CFGetTypeID(dict) != CFDictionaryGetTypeID()) { 709 continue; 710 } 711 712 CFTypeRef usage_page_ref, usage_ref; 713 int32_t usage_page, usage; 714 715 if (!CFDictionaryGetValueIfPresent((CFDictionaryRef)dict, CFSTR(kIOHIDDeviceUsagePageKey), &usage_page_ref) || 716 !CFDictionaryGetValueIfPresent((CFDictionaryRef)dict, CFSTR(kIOHIDDeviceUsageKey), &usage_ref) || 717 CFGetTypeID(usage_page_ref) != CFNumberGetTypeID() || 718 CFGetTypeID(usage_ref) != CFNumberGetTypeID() || 719 !CFNumberGetValue((CFNumberRef)usage_page_ref, kCFNumberSInt32Type, &usage_page) || 720 !CFNumberGetValue((CFNumberRef)usage_ref, kCFNumberSInt32Type, &usage)) { 721 continue; 722 } 723 if (usage_page == primary_usage_page && usage == primary_usage) 724 continue; /* Already added. */ 725 726 next = create_device_info_with_usage(device, usage_page, usage); 727 if (cur) { 728 if (next != NULL) { 729 cur->next = next; 730 cur = next; 731 } 732 } else { 733 root = cur = next; 734 } 735 } 736 } 737 738 return root; 739} 740 741struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) 742{ 743 struct hid_device_info *root = NULL; /* return object */ 744 struct hid_device_info *cur_dev = NULL; 745 CFIndex num_devices; 746 int i; 747 748 /* Set up the HID Manager if it hasn't been done */ 749 if (hid_init() < 0) { 750 return NULL; 751 } 752 /* register_global_error: global error is set/reset by hid_init */ 753 754 /* give the IOHIDManager a chance to update itself */ 755 process_pending_events(); 756 757 /* Get a list of the Devices */ 758 CFMutableDictionaryRef matching = NULL; 759 if (vendor_id != 0 || product_id != 0) { 760 matching = CFDictionaryCreateMutable(kCFAllocatorDefault, kIOHIDOptionsTypeNone, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 761 762 if (matching && vendor_id != 0) { 763 CFNumberRef v = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &vendor_id); 764 CFDictionarySetValue(matching, CFSTR(kIOHIDVendorIDKey), v); 765 CFRelease(v); 766 } 767 768 if (matching && product_id != 0) { 769 CFNumberRef p = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &product_id); 770 CFDictionarySetValue(matching, CFSTR(kIOHIDProductIDKey), p); 771 CFRelease(p); 772 } 773 } 774 IOHIDManagerSetDeviceMatching(hid_mgr, matching); 775 if (matching != NULL) { 776 CFRelease(matching); 777 } 778 779 CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr); 780 781 IOHIDDeviceRef *device_array = NULL; 782 783 if (device_set != NULL) { 784 /* Convert the list into a C array so we can iterate easily. */ 785 num_devices = CFSetGetCount(device_set); 786 device_array = (IOHIDDeviceRef*) calloc(num_devices, sizeof(IOHIDDeviceRef)); 787 CFSetGetValues(device_set, (const void **) device_array); 788 } else { 789 num_devices = 0; 790 } 791 792 /* Iterate over each device, making an entry for it. */ 793 for (i = 0; i < num_devices; i++) { 794 795 IOHIDDeviceRef dev = device_array[i]; 796 if (!dev) { 797 continue; 798 } 799 800 struct hid_device_info *tmp = create_device_info(dev); 801 if (tmp == NULL) { 802 continue; 803 } 804 805 if (cur_dev) { 806 cur_dev->next = tmp; 807 } 808 else { 809 root = tmp; 810 } 811 cur_dev = tmp; 812 813 /* move the pointer to the tail of returned list */ 814 while (cur_dev->next != NULL) { 815 cur_dev = cur_dev->next; 816 } 817 } 818 819 free(device_array); 820 if (device_set != NULL) 821 CFRelease(device_set); 822 823 if (root == NULL) { 824 if (vendor_id == 0 && product_id == 0) { 825 register_global_error("No HID devices found in the system."); 826 } else { 827 register_global_error("No HID devices with requested VID/PID found in the system."); 828 } 829 } 830 831 return root; 832} 833 834void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs) 835{ 836 /* This function is identical to the Linux version. Platform independent. */ 837 struct hid_device_info *d = devs; 838 while (d) { 839 struct hid_device_info *next = d->next; 840 free(d->path); 841 free(d->serial_number); 842 free(d->manufacturer_string); 843 free(d->product_string); 844 free(d); 845 d = next; 846 } 847} 848 849hid_device * HID_API_EXPORT hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) 850{ 851 /* This function is identical to the Linux version. Platform independent. */ 852 853 struct hid_device_info *devs, *cur_dev; 854 const char *path_to_open = NULL; 855 hid_device * handle = NULL; 856 857 /* register_global_error: global error is reset by hid_enumerate/hid_init */ 858 devs = hid_enumerate(vendor_id, product_id); 859 if (devs == NULL) { 860 /* register_global_error: global error is already set by hid_enumerate */ 861 return NULL; 862 } 863 864 cur_dev = devs; 865 while (cur_dev) { 866 if (cur_dev->vendor_id == vendor_id && 867 cur_dev->product_id == product_id) { 868 if (serial_number) { 869 if (wcscmp(serial_number, cur_dev->serial_number) == 0) { 870 path_to_open = cur_dev->path; 871 break; 872 } 873 } 874 else { 875 path_to_open = cur_dev->path; 876 break; 877 } 878 } 879 cur_dev = cur_dev->next; 880 } 881 882 if (path_to_open) { 883 handle = hid_open_path(path_to_open); 884 } else { 885 register_global_error("Device with requested VID/PID/(SerialNumber) not found"); 886 } 887 888 hid_free_enumeration(devs); 889 890 return handle; 891} 892 893static void hid_device_removal_callback(void *context, IOReturn result, 894 void *sender) 895{ 896 (void) result; 897 (void) sender; 898 899 /* Stop the Run Loop for this device. */ 900 hid_device *d = (hid_device*) context; 901 902 d->disconnected = 1; 903 CFRunLoopStop(d->run_loop); 904} 905 906/* The Run Loop calls this function for each input report received. 907 This function puts the data into a linked list to be picked up by 908 hid_read(). */ 909static void hid_report_callback(void *context, IOReturn result, void *sender, 910 IOHIDReportType report_type, uint32_t report_id, 911 uint8_t *report, CFIndex report_length) 912{ 913 (void) result; 914 (void) sender; 915 (void) report_type; 916 (void) report_id; 917 918 struct input_report *rpt; 919 hid_device *dev = (hid_device*) context; 920 921 /* Make a new Input Report object */ 922 rpt = (struct input_report*) calloc(1, sizeof(struct input_report)); 923 rpt->data = (uint8_t*) calloc(1, report_length); 924 memcpy(rpt->data, report, report_length); 925 rpt->len = report_length; 926 rpt->next = NULL; 927 928 /* Lock this section */ 929 pthread_mutex_lock(&dev->mutex); 930 931 /* Attach the new report object to the end of the list. */ 932 if (dev->input_reports == NULL) { 933 /* The list is empty. Put it at the root. */ 934 dev->input_reports = rpt; 935 } 936 else { 937 /* Find the end of the list and attach. */ 938 struct input_report *cur = dev->input_reports; 939 int num_queued = 0; 940 while (cur->next != NULL) { 941 cur = cur->next; 942 num_queued++; 943 } 944 cur->next = rpt; 945 946 /* Pop one off if we've reached 30 in the queue. This 947 way we don't grow forever if the user never reads 948 anything from the device. */ 949 if (num_queued > 30) { 950 return_data(dev, NULL, 0); 951 } 952 } 953 954 /* Signal a waiting thread that there is data. */ 955 pthread_cond_signal(&dev->condition); 956 957 /* Unlock */ 958 pthread_mutex_unlock(&dev->mutex); 959 960} 961 962/* This gets called when the read_thread's run loop gets signaled by 963 hid_close(), and serves to stop the read_thread's run loop. */ 964static void perform_signal_callback(void *context) 965{ 966 hid_device *dev = (hid_device*) context; 967 CFRunLoopStop(dev->run_loop); /*TODO: CFRunLoopGetCurrent()*/ 968} 969 970static void *read_thread(void *param) 971{ 972 hid_device *dev = (hid_device*) param; 973 SInt32 code; 974 975 /* Move the device's run loop to this thread. */ 976 IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetCurrent(), dev->run_loop_mode); 977 978 /* Create the RunLoopSource which is used to signal the 979 event loop to stop when hid_close() is called. */ 980 CFRunLoopSourceContext ctx; 981 memset(&ctx, 0, sizeof(ctx)); 982 ctx.version = 0; 983 ctx.info = dev; 984 ctx.perform = &perform_signal_callback; 985 dev->source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0/*order*/, &ctx); 986 CFRunLoopAddSource(CFRunLoopGetCurrent(), dev->source, dev->run_loop_mode); 987 988 /* Store off the Run Loop so it can be stopped from hid_close() 989 and on device disconnection. */ 990 dev->run_loop = CFRunLoopGetCurrent(); 991 992 /* Notify the main thread that the read thread is up and running. */ 993 pthread_barrier_wait(&dev->barrier); 994 995 /* Run the Event Loop. CFRunLoopRunInMode() will dispatch HID input 996 reports into the hid_report_callback(). */ 997 while (!dev->shutdown_thread && !dev->disconnected) { 998 code = CFRunLoopRunInMode(dev->run_loop_mode, 1000/*sec*/, FALSE); 999 /* Return if the device has been disconnected */ 1000 if (code == kCFRunLoopRunFinished || code == kCFRunLoopRunStopped) { 1001 dev->disconnected = 1; 1002 break; 1003 } 1004 1005 1006 /* Break if The Run Loop returns Finished or Stopped. */ 1007 if (code != kCFRunLoopRunTimedOut && 1008 code != kCFRunLoopRunHandledSource) { 1009 /* There was some kind of error. Setting 1010 shutdown seems to make sense, but 1011 there may be something else more appropriate */ 1012 dev->shutdown_thread = 1; 1013 break; 1014 } 1015 } 1016 1017 /* Now that the read thread is stopping, Wake any threads which are 1018 waiting on data (in hid_read_timeout()). Do this under a mutex to 1019 make sure that a thread which is about to go to sleep waiting on 1020 the condition actually will go to sleep before the condition is 1021 signaled. */ 1022 pthread_mutex_lock(&dev->mutex); 1023 pthread_cond_broadcast(&dev->condition); 1024 pthread_mutex_unlock(&dev->mutex); 1025 1026 /* Wait here until hid_close() is called and makes it past 1027 the call to CFRunLoopWakeUp(). This thread still needs to 1028 be valid when that function is called on the other thread. */ 1029 pthread_barrier_wait(&dev->shutdown_barrier); 1030 1031 return NULL; 1032} 1033 1034/* \p path must be one of: 1035 - in format 'DevSrvsID:<RegistryEntryID>' (as returned by hid_enumerate); 1036 - a valid path to an IOHIDDevice in the IOService plane (as returned by IORegistryEntryGetPath, 1037 e.g.: "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/EHC1@1D,7/AppleUSBEHCI/PLAYSTATION(R)3 Controller@fd120000/IOUSBInterface@0/IOUSBHIDDriver"); 1038 Second format is for compatibility with paths accepted by older versions of HIDAPI. 1039*/ 1040static io_registry_entry_t hid_open_service_registry_from_path(const char *path) 1041{ 1042 if (path == NULL) 1043 return MACH_PORT_NULL; 1044 1045 /* Get the IORegistry entry for the given path */ 1046 if (strncmp("DevSrvsID:", path, 10) == 0) { 1047 char *endptr; 1048 uint64_t entry_id = strtoull(path + 10, &endptr, 10); 1049 if (*endptr == '\0') { 1050 return IOServiceGetMatchingService((mach_port_t) 0, IORegistryEntryIDMatching(entry_id)); 1051 } 1052 } 1053 else { 1054 /* Fallback to older format of the path */ 1055 return IORegistryEntryFromPath((mach_port_t) 0, path); 1056 } 1057 1058 return MACH_PORT_NULL; 1059} 1060 1061hid_device * HID_API_EXPORT hid_open_path(const char *path) 1062{ 1063 hid_device *dev = NULL; 1064 io_registry_entry_t entry = MACH_PORT_NULL; 1065 IOReturn ret = kIOReturnInvalid; 1066 char str[32]; 1067 1068 /* Set up the HID Manager if it hasn't been done */ 1069 if (hid_init() < 0) { 1070 return NULL; 1071 } 1072 /* register_global_error: global error is set/reset by hid_init */ 1073 1074 dev = new_hid_device(); 1075 if (!dev) { 1076 register_global_error("Couldn't allocate memory"); 1077 return NULL; 1078 } 1079 1080 /* Get the IORegistry entry for the given path */ 1081 entry = hid_open_service_registry_from_path(path); 1082 if (entry == MACH_PORT_NULL) { 1083 /* Path wasn't valid (maybe device was removed?) */ 1084 register_global_error("hid_open_path: device mach entry not found with the given path"); 1085 goto return_error; 1086 } 1087 1088 /* Create an IOHIDDevice for the entry */ 1089 dev->device_handle = IOHIDDeviceCreate(kCFAllocatorDefault, entry); 1090 if (dev->device_handle == NULL) { 1091 /* Error creating the HID device */ 1092 register_global_error("hid_open_path: failed to create IOHIDDevice from the mach entry"); 1093 goto return_error; 1094 } 1095 1096 /* Open the IOHIDDevice */ 1097 ret = IOHIDDeviceOpen(dev->device_handle, dev->open_options); 1098 if (ret != kIOReturnSuccess) { 1099 register_global_error_format("hid_open_path: failed to open IOHIDDevice from mach entry: (0x%08X) %s", ret, mach_error_string(ret)); 1100 goto return_error; 1101 } 1102 1103 /* Create the buffers for receiving data */ 1104 dev->max_input_report_len = (CFIndex) get_max_report_length(dev->device_handle); 1105 dev->input_report_buf = (uint8_t*) calloc(dev->max_input_report_len, sizeof(uint8_t)); 1106 1107 /* Create the Run Loop Mode for this device. 1108 printing the reference seems to work. */ 1109 snprintf(str, sizeof(str), "HIDAPI_%p", (void*) dev->device_handle); 1110 dev->run_loop_mode = 1111 CFStringCreateWithCString(NULL, str, kCFStringEncodingASCII); 1112 1113 /* Attach the device to a Run Loop */ 1114 IOHIDDeviceRegisterInputReportCallback( 1115 dev->device_handle, dev->input_report_buf, dev->max_input_report_len, 1116 &hid_report_callback, dev); 1117 IOHIDDeviceRegisterRemovalCallback(dev->device_handle, hid_device_removal_callback, dev); 1118 1119 /* Start the read thread */ 1120 pthread_create(&dev->thread, NULL, read_thread, dev); 1121 1122 /* Wait here for the read thread to be initialized. */ 1123 pthread_barrier_wait(&dev->barrier); 1124 1125 IOObjectRelease(entry); 1126 return dev; 1127 1128return_error: 1129 if (dev->device_handle != NULL) 1130 CFRelease(dev->device_handle); 1131 1132 if (entry != MACH_PORT_NULL) 1133 IOObjectRelease(entry); 1134 1135 free_hid_device(dev); 1136 return NULL; 1137} 1138 1139static int set_report(hid_device *dev, IOHIDReportType type, const unsigned char *data, size_t length) 1140{ 1141 const unsigned char *data_to_send = data; 1142 CFIndex length_to_send = length; 1143 IOReturn res; 1144 unsigned char report_id; 1145 1146 register_device_error(dev, NULL); 1147 1148 if (!data || (length == 0)) { 1149 register_device_error(dev, strerror(EINVAL)); 1150 return -1; 1151 } 1152 1153 report_id = data[0]; 1154 1155 if (report_id == 0x0) { 1156 /* Not using numbered Reports. 1157 Don't send the report number. */ 1158 data_to_send = data+1; 1159 length_to_send = length-1; 1160 } 1161 1162 /* Avoid crash if the device has been unplugged. */ 1163 if (dev->disconnected) { 1164 register_device_error(dev, "Device is disconnected"); 1165 return -1; 1166 } 1167 1168 res = IOHIDDeviceSetReport(dev->device_handle, 1169 type, 1170 report_id, 1171 data_to_send, length_to_send); 1172 1173 if (res != kIOReturnSuccess) { 1174 register_device_error_format(dev, "IOHIDDeviceSetReport failed: (0x%08X) %s", res, mach_error_string(res)); 1175 return -1; 1176 } 1177 1178 return (int) length; 1179} 1180 1181static int get_report(hid_device *dev, IOHIDReportType type, unsigned char *data, size_t length) 1182{ 1183 unsigned char *report = data; 1184 CFIndex report_length = length; 1185 IOReturn res = kIOReturnSuccess; 1186 const unsigned char report_id = data[0]; 1187 1188 register_device_error(dev, NULL); 1189 1190 if (report_id == 0x0) { 1191 /* Not using numbered Reports. 1192 Don't send the report number. */ 1193 report = data+1; 1194 report_length = length-1; 1195 } 1196 1197 /* Avoid crash if the device has been unplugged. */ 1198 if (dev->disconnected) { 1199 register_device_error(dev, "Device is disconnected"); 1200 return -1; 1201 } 1202 1203 res = IOHIDDeviceGetReport(dev->device_handle, 1204 type, 1205 report_id, 1206 report, &report_length); 1207 1208 if (res != kIOReturnSuccess) { 1209 register_device_error_format(dev, "IOHIDDeviceGetReport failed: (0x%08X) %s", res, mach_error_string(res)); 1210 return -1; 1211 } 1212 1213 if (report_id == 0x0) { /* 0 report number still present at the beginning */ 1214 report_length++; 1215 } 1216 1217 return (int) report_length; 1218} 1219 1220int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length) 1221{ 1222 return set_report(dev, kIOHIDReportTypeOutput, data, length); 1223} 1224 1225/* Helper function, so that this isn't duplicated in hid_read(). */ 1226static int return_data(hid_device *dev, unsigned char *data, size_t length) 1227{ 1228 /* Copy the data out of the linked list item (rpt) into the 1229 return buffer (data), and delete the liked list item. */ 1230 struct input_report *rpt = dev->input_reports; 1231 size_t len = (length < rpt->len)? length: rpt->len; 1232 if (data != NULL) { 1233 memcpy(data, rpt->data, len); 1234 } 1235 dev->input_reports = rpt->next; 1236 free(rpt->data); 1237 free(rpt); 1238 return (int) len; 1239} 1240 1241static int cond_wait(hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex) 1242{ 1243 while (!dev->input_reports) { 1244 int res = pthread_cond_wait(cond, mutex); 1245 if (res != 0) 1246 return res; 1247 1248 /* A res of 0 means we may have been signaled or it may 1249 be a spurious wakeup. Check to see that there's actually 1250 data in the queue before returning, and if not, go back 1251 to sleep. See the pthread_cond_timedwait() man page for 1252 details. */ 1253 1254 if (dev->shutdown_thread || dev->disconnected) { 1255 return -1; 1256 } 1257 } 1258 1259 return 0; 1260} 1261 1262static int cond_timedwait(hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime) 1263{ 1264 while (!dev->input_reports) { 1265 int res = pthread_cond_timedwait(cond, mutex, abstime); 1266 if (res != 0) 1267 return res; 1268 1269 /* A res of 0 means we may have been signaled or it may 1270 be a spurious wakeup. Check to see that there's actually 1271 data in the queue before returning, and if not, go back 1272 to sleep. See the pthread_cond_timedwait() man page for 1273 details. */ 1274 1275 if (dev->shutdown_thread || dev->disconnected) { 1276 return -1; 1277 } 1278 } 1279 1280 return 0; 1281 1282} 1283 1284int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) 1285{ 1286 int bytes_read = -1; 1287 1288 /* Lock the access to the report list. */ 1289 pthread_mutex_lock(&dev->mutex); 1290 1291 /* There's an input report queued up. Return it. */ 1292 if (dev->input_reports) { 1293 /* Return the first one */ 1294 bytes_read = return_data(dev, data, length); 1295 goto ret; 1296 } 1297 1298 /* Return if the device has been disconnected. */ 1299 if (dev->disconnected) { 1300 bytes_read = -1; 1301 register_device_error(dev, "hid_read_timeout: device disconnected"); 1302 goto ret; 1303 } 1304 1305 if (dev->shutdown_thread) { 1306 /* This means the device has been closed (or there 1307 has been an error. An error code of -1 should 1308 be returned. */ 1309 bytes_read = -1; 1310 register_device_error(dev, "hid_read_timeout: thread shutdown"); 1311 goto ret; 1312 } 1313 1314 /* There is no data. Go to sleep and wait for data. */ 1315 1316 if (milliseconds == -1) { 1317 /* Blocking */ 1318 int res; 1319 res = cond_wait(dev, &dev->condition, &dev->mutex); 1320 if (res == 0) 1321 bytes_read = return_data(dev, data, length); 1322 else { 1323 /* There was an error, or a device disconnection. */ 1324 register_device_error(dev, "hid_read_timeout: error waiting for more data"); 1325 bytes_read = -1; 1326 } 1327 } 1328 else if (milliseconds > 0) { 1329 /* Non-blocking, but called with timeout. */ 1330 int res; 1331 struct timespec ts; 1332 struct timeval tv; 1333 gettimeofday(&tv, NULL); 1334 TIMEVAL_TO_TIMESPEC(&tv, &ts); 1335 ts.tv_sec += milliseconds / 1000; 1336 ts.tv_nsec += (milliseconds % 1000) * 1000000; 1337 if (ts.tv_nsec >= 1000000000L) { 1338 ts.tv_sec++; 1339 ts.tv_nsec -= 1000000000L; 1340 } 1341 1342 res = cond_timedwait(dev, &dev->condition, &dev->mutex, &ts); 1343 if (res == 0) { 1344 bytes_read = return_data(dev, data, length); 1345 } else if (res == ETIMEDOUT) { 1346 bytes_read = 0; 1347 } else { 1348 register_device_error(dev, "hid_read_timeout: error waiting for more data"); 1349 bytes_read = -1; 1350 } 1351 } 1352 else { 1353 /* Purely non-blocking */ 1354 bytes_read = 0; 1355 } 1356 1357ret: 1358 /* Unlock */ 1359 pthread_mutex_unlock(&dev->mutex); 1360 return bytes_read; 1361} 1362 1363int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length) 1364{ 1365 return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0); 1366} 1367 1368int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock) 1369{ 1370 /* All Nonblocking operation is handled by the library. */ 1371 dev->blocking = !nonblock; 1372 1373 return 0; 1374} 1375 1376int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) 1377{ 1378 return set_report(dev, kIOHIDReportTypeFeature, data, length); 1379} 1380 1381int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) 1382{ 1383 return get_report(dev, kIOHIDReportTypeFeature, data, length); 1384} 1385 1386int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length) 1387{ 1388 return get_report(dev, kIOHIDReportTypeInput, data, length); 1389} 1390 1391void HID_API_EXPORT hid_close(hid_device *dev) 1392{ 1393 if (!dev) 1394 return; 1395 1396 /* Disconnect the report callback before close. 1397 See comment below. 1398 */ 1399 if (is_macos_10_10_or_greater || !dev->disconnected) { 1400 IOHIDDeviceRegisterInputReportCallback( 1401 dev->device_handle, dev->input_report_buf, dev->max_input_report_len, 1402 NULL, dev); 1403 IOHIDDeviceRegisterRemovalCallback(dev->device_handle, NULL, dev); 1404 IOHIDDeviceUnscheduleFromRunLoop(dev->device_handle, dev->run_loop, dev->run_loop_mode); 1405 IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetMain(), kCFRunLoopDefaultMode); 1406 } 1407 1408 /* Cause read_thread() to stop. */ 1409 dev->shutdown_thread = 1; 1410 1411 /* Wake up the run thread's event loop so that the thread can exit. */ 1412 CFRunLoopSourceSignal(dev->source); 1413 CFRunLoopWakeUp(dev->run_loop); 1414 1415 /* Notify the read thread that it can shut down now. */ 1416 pthread_barrier_wait(&dev->shutdown_barrier); 1417 1418 /* Wait for read_thread() to end. */ 1419 pthread_join(dev->thread, NULL); 1420 1421 /* Close the OS handle to the device, but only if it's not 1422 been unplugged. If it's been unplugged, then calling 1423 IOHIDDeviceClose() will crash. 1424 1425 UPD: The crash part was true in/until some version of macOS. 1426 Starting with macOS 10.15, there is an opposite effect in some environments: 1427 crash happens if IOHIDDeviceClose() is not called. 1428 Not leaking a resource in all tested environments. 1429 */ 1430 if (is_macos_10_10_or_greater || !dev->disconnected) { 1431 IOHIDDeviceClose(dev->device_handle, dev->open_options); 1432 } 1433 1434 /* Clear out the queue of received reports. */ 1435 pthread_mutex_lock(&dev->mutex); 1436 while (dev->input_reports) { 1437 return_data(dev, NULL, 0); 1438 } 1439 pthread_mutex_unlock(&dev->mutex); 1440 CFRelease(dev->device_handle); 1441 1442 free_hid_device(dev); 1443} 1444 1445int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) 1446{ 1447 if (!string || !maxlen) 1448 { 1449 register_device_error(dev, "Zero buffer/length"); 1450 return -1; 1451 } 1452 1453 struct hid_device_info *info = hid_get_device_info(dev); 1454 if (!info) 1455 { 1456 // hid_get_device_info will have set an error already 1457 return -1; 1458 } 1459 1460 wcsncpy(string, info->manufacturer_string, maxlen); 1461 string[maxlen - 1] = L'\0'; 1462 1463 return 0; 1464} 1465 1466int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) 1467{ 1468 if (!string || !maxlen) { 1469 register_device_error(dev, "Zero buffer/length"); 1470 return -1; 1471 } 1472 1473 struct hid_device_info *info = hid_get_device_info(dev); 1474 if (!info) { 1475 // hid_get_device_info will have set an error already 1476 return -1; 1477 } 1478 1479 wcsncpy(string, info->product_string, maxlen); 1480 string[maxlen - 1] = L'\0'; 1481 1482 return 0; 1483} 1484 1485int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) 1486{ 1487 if (!string || !maxlen) { 1488 register_device_error(dev, "Zero buffer/length"); 1489 return -1; 1490 } 1491 1492 struct hid_device_info *info = hid_get_device_info(dev); 1493 if (!info) { 1494 // hid_get_device_info will have set an error already 1495 return -1; 1496 } 1497 1498 wcsncpy(string, info->serial_number, maxlen); 1499 string[maxlen - 1] = L'\0'; 1500 1501 return 0; 1502} 1503 1504HID_API_EXPORT struct hid_device_info *HID_API_CALL hid_get_device_info(hid_device *dev) { 1505 if (!dev->device_info) { 1506 dev->device_info = create_device_info(dev->device_handle); 1507 if (!dev->device_info) { 1508 register_device_error(dev, "Failed to create hid_device_info"); 1509 } 1510 } 1511 1512 return dev->device_info; 1513} 1514 1515int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) 1516{ 1517 (void) dev; 1518 (void) string_index; 1519 (void) string; 1520 (void) maxlen; 1521 1522 register_device_error(dev, "hid_get_indexed_string: not available on this platform"); 1523 return -1; 1524} 1525 1526int HID_API_EXPORT_CALL hid_darwin_get_location_id(hid_device *dev, uint32_t *location_id) 1527{ 1528 int res = get_int_property(dev->device_handle, CFSTR(kIOHIDLocationIDKey)); 1529 if (res != 0) { 1530 *location_id = (uint32_t) res; 1531 return 0; 1532 } else { 1533 register_device_error(dev, "Failed to get IOHIDLocationID property"); 1534 return -1; 1535 } 1536} 1537 1538void HID_API_EXPORT_CALL hid_darwin_set_open_exclusive(int open_exclusive) 1539{ 1540 device_open_options = (open_exclusive == 0) ? kIOHIDOptionsTypeNone : kIOHIDOptionsTypeSeizeDevice; 1541} 1542 1543int HID_API_EXPORT_CALL hid_darwin_get_open_exclusive(void) 1544{ 1545 return (device_open_options == kIOHIDOptionsTypeSeizeDevice) ? 1 : 0; 1546} 1547 1548int HID_API_EXPORT_CALL hid_darwin_is_device_open_exclusive(hid_device *dev) 1549{ 1550 if (!dev) 1551 return -1; 1552 1553 return (dev->open_options == kIOHIDOptionsTypeSeizeDevice) ? 1 : 0; 1554} 1555 1556int HID_API_EXPORT_CALL hid_get_report_descriptor(hid_device *dev, unsigned char *buf, size_t buf_size) 1557{ 1558 CFTypeRef ref = IOHIDDeviceGetProperty(dev->device_handle, CFSTR(kIOHIDReportDescriptorKey)); 1559 if (ref != NULL && CFGetTypeID(ref) == CFDataGetTypeID()) { 1560 CFDataRef report_descriptor = (CFDataRef) ref; 1561 const UInt8 *descriptor_buf = CFDataGetBytePtr(report_descriptor); 1562 CFIndex descriptor_buf_len = CFDataGetLength(report_descriptor); 1563 size_t copy_len = (size_t) descriptor_buf_len; 1564 1565 if (descriptor_buf == NULL || descriptor_buf_len < 0) { 1566 register_device_error(dev, "Zero buffer/length"); 1567 return -1; 1568 } 1569 1570 if (buf_size < copy_len) { 1571 copy_len = buf_size; 1572 } 1573 1574 memcpy(buf, descriptor_buf, copy_len); 1575 return (int)copy_len; 1576 } 1577 else { 1578 register_device_error(dev, "Failed to get kIOHIDReportDescriptorKey property"); 1579 return -1; 1580 } 1581} 1582 1583HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) 1584{ 1585 if (dev) { 1586 if (dev->last_error_str == NULL) 1587 return L"Success"; 1588 return dev->last_error_str; 1589 } 1590 1591 if (last_global_error_str == NULL) 1592 return L"Success"; 1593 return last_global_error_str; 1594} 1595
[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.