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