Atlas - hid.c

Home / ext / SDL / src / hidapi / mac Lines: 1 | Size: 45872 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 unsigned short dev_version; 577 int BUF_LEN = 256; 578 wchar_t buf[BUF_LEN]; 579 CFTypeRef transport_prop; 580 581 struct hid_device_info *cur_dev; 582 io_service_t hid_service; 583 kern_return_t res; 584 uint64_t entry_id = 0; 585 586 if (dev == NULL) { 587 return NULL; 588 } 589 590 cur_dev = (struct hid_device_info *)calloc(1, sizeof(struct hid_device_info)); 591 if (cur_dev == NULL) { 592 return NULL; 593 } 594 595 dev_vid = get_vendor_id(dev); 596 dev_pid = get_product_id(dev); 597 dev_version = get_int_property(dev, CFSTR(kIOHIDVersionNumberKey)); 598 599#ifdef HIDAPI_IGNORE_DEVICE 600 /* See if there are any devices we should skip in enumeration */ 601 if (HIDAPI_IGNORE_DEVICE(get_bus_type(dev), dev_vid, dev_pid, usage_page, usage, false)) { 602 free(cur_dev); 603 return NULL; 604 } 605#endif 606 607#ifdef HIDAPI_USING_SDL_RUNTIME 608 if (IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDVirtualHIDevice)) == kCFBooleanTrue) { 609 /* Steam virtual gamepads always have kIOHIDVirtualHIDevice property unlike real devices */ 610 if (SDL_IsJoystickSteamVirtualGamepad(dev_vid, dev_pid, dev_version)) { 611 const char *allow_steam_virtual_gamepad = SDL_getenv_unsafe("SDL_GAMECONTROLLER_ALLOW_STEAM_VIRTUAL_GAMEPAD"); 612 if (!SDL_GetStringBoolean(allow_steam_virtual_gamepad, false)) { 613 free(cur_dev); 614 return NULL; 615 } 616 } 617 } 618#endif 619 620 cur_dev->usage_page = usage_page; 621 cur_dev->usage = usage; 622 623 /* Fill out the record */ 624 cur_dev->next = NULL; 625 626 /* Fill in the path (as a unique ID of the service entry) */ 627 cur_dev->path = NULL; 628 hid_service = IOHIDDeviceGetService(dev); 629 if (hid_service != MACH_PORT_NULL) { 630 res = IORegistryEntryGetRegistryEntryID(hid_service, &entry_id); 631 } 632 else { 633 res = KERN_INVALID_ARGUMENT; 634 } 635 636 if (res == KERN_SUCCESS) { 637 /* max value of entry_id(uint64_t) is 18446744073709551615 which is 20 characters long, 638 so for (max) "path" string 'DevSrvsID:18446744073709551615' we would need 639 9+1+20+1=31 bytes buffer, but allocate 32 for simple alignment */ 640 const size_t path_len = 32; 641 cur_dev->path = calloc(1, path_len); 642 if (cur_dev->path != NULL) { 643 snprintf(cur_dev->path, path_len, "DevSrvsID:%llu", entry_id); 644 } 645 } 646 647 if (cur_dev->path == NULL) { 648 /* for whatever reason, trying to keep it a non-NULL string */ 649 cur_dev->path = strdup(""); 650 } 651 652 /* Serial Number */ 653 get_serial_number(dev, buf, BUF_LEN); 654 cur_dev->serial_number = dup_wcs(buf); 655 656 /* Manufacturer and Product strings */ 657 get_manufacturer_string(dev, buf, BUF_LEN); 658 cur_dev->manufacturer_string = dup_wcs(buf); 659 get_product_string(dev, buf, BUF_LEN); 660 cur_dev->product_string = dup_wcs(buf); 661 662 /* VID/PID */ 663 cur_dev->vendor_id = dev_vid; 664 cur_dev->product_id = dev_pid; 665 666 /* Release Number */ 667 cur_dev->release_number = dev_version; 668 669 /* Interface Number. 670 * We can only retrieve the interface number for USB HID devices. 671 * See below */ 672 cur_dev->interface_number = -1; 673 674 /* Bus Type */ 675 transport_prop = IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDTransportKey)); 676 677 if (transport_prop != NULL && CFGetTypeID(transport_prop) == CFStringGetTypeID()) { 678 if (CFStringCompare((CFStringRef)transport_prop, CFSTR(kIOHIDTransportUSBValue), 0) == kCFCompareEqualTo) { 679 int32_t interface_number = -1; 680 cur_dev->bus_type = HID_API_BUS_USB; 681 682 /* A IOHIDDeviceRef used to have this simple property, 683 * until macOS 13.3 - we will try to use it. */ 684 if (try_get_int_property(dev, CFSTR(kUSBInterfaceNumber), &interface_number)) { 685 cur_dev->interface_number = interface_number; 686 } else { 687 /* Otherwise fallback to io_service_t property. 688 * (of one of the parent services). */ 689 cur_dev->interface_number = read_usb_interface_from_hid_service_parent(hid_service); 690 691 /* If the above doesn't work - 692 * no (known) fallback exists at this point. */ 693 } 694 695 /* Match "Bluetooth", "BluetoothLowEnergy" and "Bluetooth Low Energy" strings */ 696 } else if (CFStringHasPrefix((CFStringRef)transport_prop, CFSTR(kIOHIDTransportBluetoothValue))) { 697 cur_dev->bus_type = HID_API_BUS_BLUETOOTH; 698 } else if (CFStringCompare((CFStringRef)transport_prop, CFSTR(kIOHIDTransportI2CValue), 0) == kCFCompareEqualTo) { 699 cur_dev->bus_type = HID_API_BUS_I2C; 700 } else if (CFStringCompare((CFStringRef)transport_prop, CFSTR(kIOHIDTransportSPIValue), 0) == kCFCompareEqualTo) { 701 cur_dev->bus_type = HID_API_BUS_SPI; 702 } 703 } 704 705 return cur_dev; 706} 707 708static struct hid_device_info *create_device_info(IOHIDDeviceRef device) 709{ 710 const int32_t primary_usage_page = get_int_property(device, CFSTR(kIOHIDPrimaryUsagePageKey)); 711 const int32_t primary_usage = get_int_property(device, CFSTR(kIOHIDPrimaryUsageKey)); 712 713 /* Primary should always be first, to match previous behavior. */ 714 struct hid_device_info *root = create_device_info_with_usage(device, primary_usage_page, primary_usage); 715 struct hid_device_info *cur = root; 716 717 CFArrayRef usage_pairs = get_usage_pairs(device); 718 719 if (usage_pairs != NULL) { 720 struct hid_device_info *next = NULL; 721 for (CFIndex i = 0; i < CFArrayGetCount(usage_pairs); i++) { 722 CFTypeRef dict = CFArrayGetValueAtIndex(usage_pairs, i); 723 if (CFGetTypeID(dict) != CFDictionaryGetTypeID()) { 724 continue; 725 } 726 727 CFTypeRef usage_page_ref, usage_ref; 728 int32_t usage_page, usage; 729 730 if (!CFDictionaryGetValueIfPresent((CFDictionaryRef)dict, CFSTR(kIOHIDDeviceUsagePageKey), &usage_page_ref) || 731 !CFDictionaryGetValueIfPresent((CFDictionaryRef)dict, CFSTR(kIOHIDDeviceUsageKey), &usage_ref) || 732 CFGetTypeID(usage_page_ref) != CFNumberGetTypeID() || 733 CFGetTypeID(usage_ref) != CFNumberGetTypeID() || 734 !CFNumberGetValue((CFNumberRef)usage_page_ref, kCFNumberSInt32Type, &usage_page) || 735 !CFNumberGetValue((CFNumberRef)usage_ref, kCFNumberSInt32Type, &usage)) { 736 continue; 737 } 738 if (usage_page == primary_usage_page && usage == primary_usage) 739 continue; /* Already added. */ 740 741 next = create_device_info_with_usage(device, usage_page, usage); 742 if (cur) { 743 if (next != NULL) { 744 cur->next = next; 745 cur = next; 746 } 747 } else { 748 root = cur = next; 749 } 750 } 751 } 752 753 return root; 754} 755 756struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id) 757{ 758 struct hid_device_info *root = NULL; /* return object */ 759 struct hid_device_info *cur_dev = NULL; 760 CFIndex num_devices; 761 int i; 762 763 /* Set up the HID Manager if it hasn't been done */ 764 if (hid_init() < 0) { 765 return NULL; 766 } 767 /* register_global_error: global error is set/reset by hid_init */ 768 769 /* give the IOHIDManager a chance to update itself */ 770 process_pending_events(); 771 772 /* Get a list of the Devices */ 773 CFMutableDictionaryRef matching = NULL; 774 if (vendor_id != 0 || product_id != 0) { 775 matching = CFDictionaryCreateMutable(kCFAllocatorDefault, kIOHIDOptionsTypeNone, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 776 777 if (matching && vendor_id != 0) { 778 CFNumberRef v = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &vendor_id); 779 CFDictionarySetValue(matching, CFSTR(kIOHIDVendorIDKey), v); 780 CFRelease(v); 781 } 782 783 if (matching && product_id != 0) { 784 CFNumberRef p = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &product_id); 785 CFDictionarySetValue(matching, CFSTR(kIOHIDProductIDKey), p); 786 CFRelease(p); 787 } 788 } 789 IOHIDManagerSetDeviceMatching(hid_mgr, matching); 790 if (matching != NULL) { 791 CFRelease(matching); 792 } 793 794 CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr); 795 796 IOHIDDeviceRef *device_array = NULL; 797 798 if (device_set != NULL) { 799 /* Convert the list into a C array so we can iterate easily. */ 800 num_devices = CFSetGetCount(device_set); 801 device_array = (IOHIDDeviceRef*) calloc(num_devices, sizeof(IOHIDDeviceRef)); 802 CFSetGetValues(device_set, (const void **) device_array); 803 } else { 804 num_devices = 0; 805 } 806 807 /* Iterate over each device, making an entry for it. */ 808 for (i = 0; i < num_devices; i++) { 809 810 IOHIDDeviceRef dev = device_array[i]; 811 if (!dev) { 812 continue; 813 } 814 815 struct hid_device_info *tmp = create_device_info(dev); 816 if (tmp == NULL) { 817 continue; 818 } 819 820 if (cur_dev) { 821 cur_dev->next = tmp; 822 } 823 else { 824 root = tmp; 825 } 826 cur_dev = tmp; 827 828 /* move the pointer to the tail of returned list */ 829 while (cur_dev->next != NULL) { 830 cur_dev = cur_dev->next; 831 } 832 } 833 834 free(device_array); 835 if (device_set != NULL) 836 CFRelease(device_set); 837 838 if (root == NULL) { 839 if (vendor_id == 0 && product_id == 0) { 840 register_global_error("No HID devices found in the system."); 841 } else { 842 register_global_error("No HID devices with requested VID/PID found in the system."); 843 } 844 } 845 846 return root; 847} 848 849void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs) 850{ 851 /* This function is identical to the Linux version. Platform independent. */ 852 struct hid_device_info *d = devs; 853 while (d) { 854 struct hid_device_info *next = d->next; 855 free(d->path); 856 free(d->serial_number); 857 free(d->manufacturer_string); 858 free(d->product_string); 859 free(d); 860 d = next; 861 } 862} 863 864hid_device * HID_API_EXPORT hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number) 865{ 866 /* This function is identical to the Linux version. Platform independent. */ 867 868 struct hid_device_info *devs, *cur_dev; 869 const char *path_to_open = NULL; 870 hid_device * handle = NULL; 871 872 /* register_global_error: global error is reset by hid_enumerate/hid_init */ 873 devs = hid_enumerate(vendor_id, product_id); 874 if (devs == NULL) { 875 /* register_global_error: global error is already set by hid_enumerate */ 876 return NULL; 877 } 878 879 cur_dev = devs; 880 while (cur_dev) { 881 if (cur_dev->vendor_id == vendor_id && 882 cur_dev->product_id == product_id) { 883 if (serial_number) { 884 if (wcscmp(serial_number, cur_dev->serial_number) == 0) { 885 path_to_open = cur_dev->path; 886 break; 887 } 888 } 889 else { 890 path_to_open = cur_dev->path; 891 break; 892 } 893 } 894 cur_dev = cur_dev->next; 895 } 896 897 if (path_to_open) { 898 handle = hid_open_path(path_to_open); 899 } else { 900 register_global_error("Device with requested VID/PID/(SerialNumber) not found"); 901 } 902 903 hid_free_enumeration(devs); 904 905 return handle; 906} 907 908static void hid_device_removal_callback(void *context, IOReturn result, 909 void *sender) 910{ 911 (void) result; 912 (void) sender; 913 914 /* Stop the Run Loop for this device. */ 915 hid_device *d = (hid_device*) context; 916 917 d->disconnected = 1; 918 CFRunLoopStop(d->run_loop); 919} 920 921/* The Run Loop calls this function for each input report received. 922 This function puts the data into a linked list to be picked up by 923 hid_read(). */ 924static void hid_report_callback(void *context, IOReturn result, void *sender, 925 IOHIDReportType report_type, uint32_t report_id, 926 uint8_t *report, CFIndex report_length) 927{ 928 (void) result; 929 (void) sender; 930 (void) report_type; 931 (void) report_id; 932 933 struct input_report *rpt; 934 hid_device *dev = (hid_device*) context; 935 936 /* Make a new Input Report object */ 937 rpt = (struct input_report*) calloc(1, sizeof(struct input_report)); 938 rpt->data = (uint8_t*) calloc(1, report_length); 939 memcpy(rpt->data, report, report_length); 940 rpt->len = report_length; 941 rpt->next = NULL; 942 943 /* Lock this section */ 944 pthread_mutex_lock(&dev->mutex); 945 946 /* Attach the new report object to the end of the list. */ 947 if (dev->input_reports == NULL) { 948 /* The list is empty. Put it at the root. */ 949 dev->input_reports = rpt; 950 } 951 else { 952 /* Find the end of the list and attach. */ 953 struct input_report *cur = dev->input_reports; 954 int num_queued = 0; 955 while (cur->next != NULL) { 956 cur = cur->next; 957 num_queued++; 958 } 959 cur->next = rpt; 960 961 /* Pop one off if we've reached 30 in the queue. This 962 way we don't grow forever if the user never reads 963 anything from the device. */ 964 if (num_queued > 30) { 965 return_data(dev, NULL, 0); 966 } 967 } 968 969 /* Signal a waiting thread that there is data. */ 970 pthread_cond_signal(&dev->condition); 971 972 /* Unlock */ 973 pthread_mutex_unlock(&dev->mutex); 974 975} 976 977/* This gets called when the read_thread's run loop gets signaled by 978 hid_close(), and serves to stop the read_thread's run loop. */ 979static void perform_signal_callback(void *context) 980{ 981 hid_device *dev = (hid_device*) context; 982 CFRunLoopStop(dev->run_loop); /*TODO: CFRunLoopGetCurrent()*/ 983} 984 985static void *read_thread(void *param) 986{ 987 hid_device *dev = (hid_device*) param; 988 SInt32 code; 989 990 /* Move the device's run loop to this thread. */ 991 IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetCurrent(), dev->run_loop_mode); 992 993 /* Create the RunLoopSource which is used to signal the 994 event loop to stop when hid_close() is called. */ 995 CFRunLoopSourceContext ctx; 996 memset(&ctx, 0, sizeof(ctx)); 997 ctx.version = 0; 998 ctx.info = dev; 999 ctx.perform = &perform_signal_callback; 1000 dev->source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0/*order*/, &ctx); 1001 CFRunLoopAddSource(CFRunLoopGetCurrent(), dev->source, dev->run_loop_mode); 1002 1003 /* Store off the Run Loop so it can be stopped from hid_close() 1004 and on device disconnection. */ 1005 dev->run_loop = CFRunLoopGetCurrent(); 1006 1007 /* Notify the main thread that the read thread is up and running. */ 1008 pthread_barrier_wait(&dev->barrier); 1009 1010 /* Run the Event Loop. CFRunLoopRunInMode() will dispatch HID input 1011 reports into the hid_report_callback(). */ 1012 while (!dev->shutdown_thread && !dev->disconnected) { 1013 code = CFRunLoopRunInMode(dev->run_loop_mode, 1000/*sec*/, FALSE); 1014 /* Return if the device has been disconnected */ 1015 if (code == kCFRunLoopRunFinished || code == kCFRunLoopRunStopped) { 1016 dev->disconnected = 1; 1017 break; 1018 } 1019 1020 1021 /* Break if The Run Loop returns Finished or Stopped. */ 1022 if (code != kCFRunLoopRunTimedOut && 1023 code != kCFRunLoopRunHandledSource) { 1024 /* There was some kind of error. Setting 1025 shutdown seems to make sense, but 1026 there may be something else more appropriate */ 1027 dev->shutdown_thread = 1; 1028 break; 1029 } 1030 } 1031 1032 /* Now that the read thread is stopping, Wake any threads which are 1033 waiting on data (in hid_read_timeout()). Do this under a mutex to 1034 make sure that a thread which is about to go to sleep waiting on 1035 the condition actually will go to sleep before the condition is 1036 signaled. */ 1037 pthread_mutex_lock(&dev->mutex); 1038 pthread_cond_broadcast(&dev->condition); 1039 pthread_mutex_unlock(&dev->mutex); 1040 1041 /* Wait here until hid_close() is called and makes it past 1042 the call to CFRunLoopWakeUp(). This thread still needs to 1043 be valid when that function is called on the other thread. */ 1044 pthread_barrier_wait(&dev->shutdown_barrier); 1045 1046 return NULL; 1047} 1048 1049/* \p path must be one of: 1050 - in format 'DevSrvsID:<RegistryEntryID>' (as returned by hid_enumerate); 1051 - a valid path to an IOHIDDevice in the IOService plane (as returned by IORegistryEntryGetPath, 1052 e.g.: "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/EHC1@1D,7/AppleUSBEHCI/PLAYSTATION(R)3 Controller@fd120000/IOUSBInterface@0/IOUSBHIDDriver"); 1053 Second format is for compatibility with paths accepted by older versions of HIDAPI. 1054*/ 1055static io_registry_entry_t hid_open_service_registry_from_path(const char *path) 1056{ 1057 if (path == NULL) 1058 return MACH_PORT_NULL; 1059 1060 /* Get the IORegistry entry for the given path */ 1061 if (strncmp("DevSrvsID:", path, 10) == 0) { 1062 char *endptr; 1063 uint64_t entry_id = strtoull(path + 10, &endptr, 10); 1064 if (*endptr == '\0') { 1065 return IOServiceGetMatchingService((mach_port_t) 0, IORegistryEntryIDMatching(entry_id)); 1066 } 1067 } 1068 else { 1069 /* Fallback to older format of the path */ 1070 return IORegistryEntryFromPath((mach_port_t) 0, path); 1071 } 1072 1073 return MACH_PORT_NULL; 1074} 1075 1076hid_device * HID_API_EXPORT hid_open_path(const char *path) 1077{ 1078 hid_device *dev = NULL; 1079 io_registry_entry_t entry = MACH_PORT_NULL; 1080 IOReturn ret = kIOReturnInvalid; 1081 char str[32]; 1082 1083 /* Set up the HID Manager if it hasn't been done */ 1084 if (hid_init() < 0) { 1085 return NULL; 1086 } 1087 /* register_global_error: global error is set/reset by hid_init */ 1088 1089 dev = new_hid_device(); 1090 if (!dev) { 1091 register_global_error("Couldn't allocate memory"); 1092 return NULL; 1093 } 1094 1095 /* Get the IORegistry entry for the given path */ 1096 entry = hid_open_service_registry_from_path(path); 1097 if (entry == MACH_PORT_NULL) { 1098 /* Path wasn't valid (maybe device was removed?) */ 1099 register_global_error("hid_open_path: device mach entry not found with the given path"); 1100 goto return_error; 1101 } 1102 1103 /* Create an IOHIDDevice for the entry */ 1104 dev->device_handle = IOHIDDeviceCreate(kCFAllocatorDefault, entry); 1105 if (dev->device_handle == NULL) { 1106 /* Error creating the HID device */ 1107 register_global_error("hid_open_path: failed to create IOHIDDevice from the mach entry"); 1108 goto return_error; 1109 } 1110 1111 /* Open the IOHIDDevice */ 1112 ret = IOHIDDeviceOpen(dev->device_handle, dev->open_options); 1113 if (ret != kIOReturnSuccess) { 1114 register_global_error_format("hid_open_path: failed to open IOHIDDevice from mach entry: (0x%08X) %s", ret, mach_error_string(ret)); 1115 goto return_error; 1116 } 1117 1118 /* Create the buffers for receiving data */ 1119 dev->max_input_report_len = (CFIndex) get_max_report_length(dev->device_handle); 1120 dev->input_report_buf = (uint8_t*) calloc(dev->max_input_report_len, sizeof(uint8_t)); 1121 1122 /* Create the Run Loop Mode for this device. 1123 printing the reference seems to work. */ 1124 snprintf(str, sizeof(str), "HIDAPI_%p", (void*) dev->device_handle); 1125 dev->run_loop_mode = 1126 CFStringCreateWithCString(NULL, str, kCFStringEncodingASCII); 1127 1128 /* Attach the device to a Run Loop */ 1129 IOHIDDeviceRegisterInputReportCallback( 1130 dev->device_handle, dev->input_report_buf, dev->max_input_report_len, 1131 &hid_report_callback, dev); 1132 IOHIDDeviceRegisterRemovalCallback(dev->device_handle, hid_device_removal_callback, dev); 1133 1134 /* Start the read thread */ 1135 pthread_create(&dev->thread, NULL, read_thread, dev); 1136 1137 /* Wait here for the read thread to be initialized. */ 1138 pthread_barrier_wait(&dev->barrier); 1139 1140 IOObjectRelease(entry); 1141 return dev; 1142 1143return_error: 1144 if (dev->device_handle != NULL) 1145 CFRelease(dev->device_handle); 1146 1147 if (entry != MACH_PORT_NULL) 1148 IOObjectRelease(entry); 1149 1150 free_hid_device(dev); 1151 return NULL; 1152} 1153 1154static int set_report(hid_device *dev, IOHIDReportType type, const unsigned char *data, size_t length) 1155{ 1156 const char *pass_through_magic = "MAGIC0"; 1157 size_t pass_through_magic_length = strlen(pass_through_magic); 1158 const unsigned char *data_to_send = data; 1159 CFIndex length_to_send = length; 1160 IOReturn res; 1161 unsigned char report_id; 1162 1163 register_device_error(dev, NULL); 1164 1165 if (!data || (length == 0)) { 1166 register_device_error(dev, strerror(EINVAL)); 1167 return -1; 1168 } 1169 1170 report_id = data[0]; 1171 1172 if (report_id == 0x0) { 1173 /* Not using numbered Reports. 1174 Don't send the report number. */ 1175 data_to_send = data+1; 1176 length_to_send = length-1; 1177 } 1178 else if (length > 6 && memcmp(data, pass_through_magic, pass_through_magic_length) == 0) { 1179 report_id = data[pass_through_magic_length]; 1180 data_to_send = data+pass_through_magic_length; 1181 length_to_send = length-pass_through_magic_length; 1182 } 1183 1184 /* Avoid crash if the device has been unplugged. */ 1185 if (dev->disconnected) { 1186 register_device_error(dev, "Device is disconnected"); 1187 return -1; 1188 } 1189 1190 res = IOHIDDeviceSetReport(dev->device_handle, 1191 type, 1192 report_id, 1193 data_to_send, length_to_send); 1194 1195 if (res != kIOReturnSuccess) { 1196 register_device_error_format(dev, "IOHIDDeviceSetReport failed: (0x%08X) %s", res, mach_error_string(res)); 1197 return -1; 1198 } 1199 1200 return (int) length; 1201} 1202 1203static int get_report(hid_device *dev, IOHIDReportType type, unsigned char *data, size_t length) 1204{ 1205 unsigned char *report = data; 1206 CFIndex report_length = length; 1207 IOReturn res = kIOReturnSuccess; 1208 const unsigned char report_id = data[0]; 1209 1210 register_device_error(dev, NULL); 1211 1212 if (report_id == 0x0) { 1213 /* Not using numbered Reports. 1214 Don't send the report number. */ 1215 report = data+1; 1216 report_length = length-1; 1217 } 1218 1219 /* Avoid crash if the device has been unplugged. */ 1220 if (dev->disconnected) { 1221 register_device_error(dev, "Device is disconnected"); 1222 return -1; 1223 } 1224 1225 res = IOHIDDeviceGetReport(dev->device_handle, 1226 type, 1227 report_id, 1228 report, &report_length); 1229 1230 if (res != kIOReturnSuccess) { 1231 register_device_error_format(dev, "IOHIDDeviceGetReport failed: (0x%08X) %s", res, mach_error_string(res)); 1232 return -1; 1233 } 1234 1235 if (report_id == 0x0) { /* 0 report number still present at the beginning */ 1236 report_length++; 1237 } 1238 1239 return (int) report_length; 1240} 1241 1242int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length) 1243{ 1244 return set_report(dev, kIOHIDReportTypeOutput, data, length); 1245} 1246 1247/* Helper function, so that this isn't duplicated in hid_read(). */ 1248static int return_data(hid_device *dev, unsigned char *data, size_t length) 1249{ 1250 /* Copy the data out of the linked list item (rpt) into the 1251 return buffer (data), and delete the liked list item. */ 1252 struct input_report *rpt = dev->input_reports; 1253 size_t len = (length < rpt->len)? length: rpt->len; 1254 if (data != NULL) { 1255 memcpy(data, rpt->data, len); 1256 } 1257 dev->input_reports = rpt->next; 1258 free(rpt->data); 1259 free(rpt); 1260 return (int) len; 1261} 1262 1263static int cond_wait(hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex) 1264{ 1265 while (!dev->input_reports) { 1266 int res = pthread_cond_wait(cond, mutex); 1267 if (res != 0) 1268 return res; 1269 1270 /* A res of 0 means we may have been signaled or it may 1271 be a spurious wakeup. Check to see that there's actually 1272 data in the queue before returning, and if not, go back 1273 to sleep. See the pthread_cond_timedwait() man page for 1274 details. */ 1275 1276 if (dev->shutdown_thread || dev->disconnected) { 1277 return -1; 1278 } 1279 } 1280 1281 return 0; 1282} 1283 1284static int cond_timedwait(hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime) 1285{ 1286 while (!dev->input_reports) { 1287 int res = pthread_cond_timedwait(cond, mutex, abstime); 1288 if (res != 0) 1289 return res; 1290 1291 /* A res of 0 means we may have been signaled or it may 1292 be a spurious wakeup. Check to see that there's actually 1293 data in the queue before returning, and if not, go back 1294 to sleep. See the pthread_cond_timedwait() man page for 1295 details. */ 1296 1297 if (dev->shutdown_thread || dev->disconnected) { 1298 return -1; 1299 } 1300 } 1301 1302 return 0; 1303 1304} 1305 1306int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds) 1307{ 1308 int bytes_read = -1; 1309 1310 /* Lock the access to the report list. */ 1311 pthread_mutex_lock(&dev->mutex); 1312 1313 /* There's an input report queued up. Return it. */ 1314 if (dev->input_reports) { 1315 /* Return the first one */ 1316 bytes_read = return_data(dev, data, length); 1317 goto ret; 1318 } 1319 1320 /* Return if the device has been disconnected. */ 1321 if (dev->disconnected) { 1322 bytes_read = -1; 1323 register_device_error(dev, "hid_read_timeout: device disconnected"); 1324 goto ret; 1325 } 1326 1327 if (dev->shutdown_thread) { 1328 /* This means the device has been closed (or there 1329 has been an error. An error code of -1 should 1330 be returned. */ 1331 bytes_read = -1; 1332 register_device_error(dev, "hid_read_timeout: thread shutdown"); 1333 goto ret; 1334 } 1335 1336 /* There is no data. Go to sleep and wait for data. */ 1337 1338 if (milliseconds == -1) { 1339 /* Blocking */ 1340 int res; 1341 res = cond_wait(dev, &dev->condition, &dev->mutex); 1342 if (res == 0) 1343 bytes_read = return_data(dev, data, length); 1344 else { 1345 /* There was an error, or a device disconnection. */ 1346 register_device_error(dev, "hid_read_timeout: error waiting for more data"); 1347 bytes_read = -1; 1348 } 1349 } 1350 else if (milliseconds > 0) { 1351 /* Non-blocking, but called with timeout. */ 1352 int res; 1353 struct timespec ts; 1354 struct timeval tv; 1355 gettimeofday(&tv, NULL); 1356 TIMEVAL_TO_TIMESPEC(&tv, &ts); 1357 ts.tv_sec += milliseconds / 1000; 1358 ts.tv_nsec += (milliseconds % 1000) * 1000000; 1359 if (ts.tv_nsec >= 1000000000L) { 1360 ts.tv_sec++; 1361 ts.tv_nsec -= 1000000000L; 1362 } 1363 1364 res = cond_timedwait(dev, &dev->condition, &dev->mutex, &ts); 1365 if (res == 0) { 1366 bytes_read = return_data(dev, data, length); 1367 } else if (res == ETIMEDOUT) { 1368 bytes_read = 0; 1369 } else { 1370 register_device_error(dev, "hid_read_timeout: error waiting for more data"); 1371 bytes_read = -1; 1372 } 1373 } 1374 else { 1375 /* Purely non-blocking */ 1376 bytes_read = 0; 1377 } 1378 1379ret: 1380 /* Unlock */ 1381 pthread_mutex_unlock(&dev->mutex); 1382 return bytes_read; 1383} 1384 1385int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length) 1386{ 1387 return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0); 1388} 1389 1390int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock) 1391{ 1392 /* All Nonblocking operation is handled by the library. */ 1393 dev->blocking = !nonblock; 1394 1395 return 0; 1396} 1397 1398int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length) 1399{ 1400 return set_report(dev, kIOHIDReportTypeFeature, data, length); 1401} 1402 1403int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length) 1404{ 1405 return get_report(dev, kIOHIDReportTypeFeature, data, length); 1406} 1407 1408int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length) 1409{ 1410 return get_report(dev, kIOHIDReportTypeInput, data, length); 1411} 1412 1413void HID_API_EXPORT hid_close(hid_device *dev) 1414{ 1415 if (!dev) 1416 return; 1417 1418 /* Disconnect the report callback before close. 1419 See comment below. 1420 */ 1421 if (is_macos_10_10_or_greater || !dev->disconnected) { 1422 IOHIDDeviceRegisterInputReportCallback( 1423 dev->device_handle, dev->input_report_buf, dev->max_input_report_len, 1424 NULL, dev); 1425 IOHIDDeviceRegisterRemovalCallback(dev->device_handle, NULL, dev); 1426 IOHIDDeviceUnscheduleFromRunLoop(dev->device_handle, dev->run_loop, dev->run_loop_mode); 1427 IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetMain(), kCFRunLoopDefaultMode); 1428 } 1429 1430 /* Cause read_thread() to stop. */ 1431 dev->shutdown_thread = 1; 1432 1433 /* Wake up the run thread's event loop so that the thread can exit. */ 1434 CFRunLoopSourceSignal(dev->source); 1435 CFRunLoopWakeUp(dev->run_loop); 1436 1437 /* Notify the read thread that it can shut down now. */ 1438 pthread_barrier_wait(&dev->shutdown_barrier); 1439 1440 /* Wait for read_thread() to end. */ 1441 pthread_join(dev->thread, NULL); 1442 1443 /* Close the OS handle to the device, but only if it's not 1444 been unplugged. If it's been unplugged, then calling 1445 IOHIDDeviceClose() will crash. 1446 1447 UPD: The crash part was true in/until some version of macOS. 1448 Starting with macOS 10.15, there is an opposite effect in some environments: 1449 crash happens if IOHIDDeviceClose() is not called. 1450 Not leaking a resource in all tested environments. 1451 */ 1452 if (is_macos_10_10_or_greater || !dev->disconnected) { 1453 IOHIDDeviceClose(dev->device_handle, dev->open_options); 1454 } 1455 1456 /* Clear out the queue of received reports. */ 1457 pthread_mutex_lock(&dev->mutex); 1458 while (dev->input_reports) { 1459 return_data(dev, NULL, 0); 1460 } 1461 pthread_mutex_unlock(&dev->mutex); 1462 CFRelease(dev->device_handle); 1463 1464 free_hid_device(dev); 1465} 1466 1467int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen) 1468{ 1469 if (!string || !maxlen) 1470 { 1471 register_device_error(dev, "Zero buffer/length"); 1472 return -1; 1473 } 1474 1475 struct hid_device_info *info = hid_get_device_info(dev); 1476 if (!info) 1477 { 1478 // hid_get_device_info will have set an error already 1479 return -1; 1480 } 1481 1482 wcsncpy(string, info->manufacturer_string, maxlen); 1483 string[maxlen - 1] = L'\0'; 1484 1485 return 0; 1486} 1487 1488int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen) 1489{ 1490 if (!string || !maxlen) { 1491 register_device_error(dev, "Zero buffer/length"); 1492 return -1; 1493 } 1494 1495 struct hid_device_info *info = hid_get_device_info(dev); 1496 if (!info) { 1497 // hid_get_device_info will have set an error already 1498 return -1; 1499 } 1500 1501 wcsncpy(string, info->product_string, maxlen); 1502 string[maxlen - 1] = L'\0'; 1503 1504 return 0; 1505} 1506 1507int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen) 1508{ 1509 if (!string || !maxlen) { 1510 register_device_error(dev, "Zero buffer/length"); 1511 return -1; 1512 } 1513 1514 struct hid_device_info *info = hid_get_device_info(dev); 1515 if (!info) { 1516 // hid_get_device_info will have set an error already 1517 return -1; 1518 } 1519 1520 wcsncpy(string, info->serial_number, maxlen); 1521 string[maxlen - 1] = L'\0'; 1522 1523 return 0; 1524} 1525 1526HID_API_EXPORT struct hid_device_info *HID_API_CALL hid_get_device_info(hid_device *dev) { 1527 if (!dev->device_info) { 1528 dev->device_info = create_device_info(dev->device_handle); 1529 if (!dev->device_info) { 1530 register_device_error(dev, "Failed to create hid_device_info"); 1531 } 1532 } 1533 1534 return dev->device_info; 1535} 1536 1537int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen) 1538{ 1539 (void) dev; 1540 (void) string_index; 1541 (void) string; 1542 (void) maxlen; 1543 1544 register_device_error(dev, "hid_get_indexed_string: not available on this platform"); 1545 return -1; 1546} 1547 1548int HID_API_EXPORT_CALL hid_darwin_get_location_id(hid_device *dev, uint32_t *location_id) 1549{ 1550 int res = get_int_property(dev->device_handle, CFSTR(kIOHIDLocationIDKey)); 1551 if (res != 0) { 1552 *location_id = (uint32_t) res; 1553 return 0; 1554 } else { 1555 register_device_error(dev, "Failed to get IOHIDLocationID property"); 1556 return -1; 1557 } 1558} 1559 1560void HID_API_EXPORT_CALL hid_darwin_set_open_exclusive(int open_exclusive) 1561{ 1562 device_open_options = (open_exclusive == 0) ? kIOHIDOptionsTypeNone : kIOHIDOptionsTypeSeizeDevice; 1563} 1564 1565int HID_API_EXPORT_CALL hid_darwin_get_open_exclusive(void) 1566{ 1567 return (device_open_options == kIOHIDOptionsTypeSeizeDevice) ? 1 : 0; 1568} 1569 1570int HID_API_EXPORT_CALL hid_darwin_is_device_open_exclusive(hid_device *dev) 1571{ 1572 if (!dev) 1573 return -1; 1574 1575 return (dev->open_options == kIOHIDOptionsTypeSeizeDevice) ? 1 : 0; 1576} 1577 1578int HID_API_EXPORT_CALL hid_get_report_descriptor(hid_device *dev, unsigned char *buf, size_t buf_size) 1579{ 1580 CFTypeRef ref = IOHIDDeviceGetProperty(dev->device_handle, CFSTR(kIOHIDReportDescriptorKey)); 1581 if (ref != NULL && CFGetTypeID(ref) == CFDataGetTypeID()) { 1582 CFDataRef report_descriptor = (CFDataRef) ref; 1583 const UInt8 *descriptor_buf = CFDataGetBytePtr(report_descriptor); 1584 CFIndex descriptor_buf_len = CFDataGetLength(report_descriptor); 1585 size_t copy_len = (size_t) descriptor_buf_len; 1586 1587 if (descriptor_buf == NULL || descriptor_buf_len < 0) { 1588 register_device_error(dev, "Zero buffer/length"); 1589 return -1; 1590 } 1591 1592 if (buf_size < copy_len) { 1593 copy_len = buf_size; 1594 } 1595 1596 memcpy(buf, descriptor_buf, copy_len); 1597 return (int)copy_len; 1598 } 1599 else { 1600 register_device_error(dev, "Failed to get kIOHIDReportDescriptorKey property"); 1601 return -1; 1602 } 1603} 1604 1605HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev) 1606{ 1607 if (dev) { 1608 if (dev->last_error_str == NULL) 1609 return L"Success"; 1610 return dev->last_error_str; 1611 } 1612 1613 if (last_global_error_str == NULL) 1614 return L"Success"; 1615 return last_global_error_str; 1616} 1617
[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.