├── .gitignore ├── Makefile ├── README.md └── src ├── hid.c ├── hidapi.h └── tp.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | tpkb 2 | hid.o -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(PREFIX),) 2 | PREFIX := /usr/local 3 | endif 4 | 5 | tpkb: src/tp.cpp hid.o src/hidapi.h 6 | g++ -o tpkb src/tp.cpp hid.o -framework CoreFoundation -framework IOKit 7 | 8 | hid.o: src/hid.c src/hidapi.h 9 | gcc -c -o hid.o src/hid.c 10 | 11 | .PHONY: install 12 | install: tpkb 13 | install -m 0755 $? $(PREFIX)/bin/tpkb 14 | 15 | .PHONY: clean 16 | clean: 17 | git clean -Xf 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Trackpoint-Config-OSX 2 | 3 | Config Utility for ThinkPad TrackPoint Bluetooth Keyboards for OSX 4 | 5 | ## Features 6 | * Maximize Trackpoint Sensitivity 7 | * Disable middle-click (only scrolling with Trackpoint) 8 | 9 | ## Support Models 10 | - ThinkPad Compact Bluetooth Keyboard with TrackPoint (0B47189) 11 | - ThinkPad TrackPoint Keyboard II 12 | 13 | ## Install 14 | 1. `make install` 15 | 2. `sudo tpkb` 16 | 17 | ## Tuning/Customize 18 | ### Mouse Speed 19 | `defaults write -g com.apple.mouse.scaling 9` (Re-login required) 20 | replace 9 to any you want, the max you can set from SystemPreferce GUI is 3 21 | 22 | ### Trackpoint 23 | modify file `src/tp.cpp`, replace `0x09` to any you want 24 | ``` cpp 25 | // sensitivity 26 | // 18 02 xx # xx Sensitivity, 00-FF 27 | buf[0] = 0x18; 28 | buf[1] = 0x02; 29 | buf[2] = 0x09; 30 | hid_write(dev, buf, 3); 31 | ``` 32 | 33 | ## Adapted from 34 | - https://github.com/unknownzerx/tpkb 35 | - https://github.com/lentinj/tp-compact-keyboard 36 | 37 | 38 | enjoy :) -------------------------------------------------------------------------------- /src/hid.c: -------------------------------------------------------------------------------- 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 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 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_barrierattr_t *attr, unsigned int count) 49 | { 50 | if(count == 0) { 51 | errno = EINVAL; 52 | return -1; 53 | } 54 | 55 | if(pthread_mutex_init(&barrier->mutex, 0) < 0) { 56 | return -1; 57 | } 58 | if(pthread_cond_init(&barrier->cond, 0) < 0) { 59 | pthread_mutex_destroy(&barrier->mutex); 60 | return -1; 61 | } 62 | barrier->trip_count = count; 63 | barrier->count = 0; 64 | 65 | return 0; 66 | } 67 | 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, char *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 len) 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(), kCFRunLoopDefaultMode); 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(kIOHIDPrimaryUsagePageKey)); 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(kIOHIDVersionNumberKey)); 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 product_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_removal_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_mutex_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(), kCFRunLoopDefaultMode); 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 *string, 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 *string, 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 1113 | -------------------------------------------------------------------------------- /src/hidapi.h: -------------------------------------------------------------------------------- 1 | /******************************************************* 2 | HIDAPI - Multi-Platform library for 3 | communication with HID devices. 4 | 5 | Alan Ott 6 | Signal 11 Software 7 | 8 | 8/22/2009 9 | 10 | Copyright 2009, 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 | /** @file 24 | * @defgroup API hidapi API 25 | */ 26 | 27 | #ifndef HIDAPI_H__ 28 | #define HIDAPI_H__ 29 | 30 | #include 31 | 32 | #ifdef _WIN32 33 | #define HID_API_EXPORT __declspec(dllexport) 34 | #define HID_API_CALL 35 | #else 36 | #define HID_API_EXPORT /**< API export macro */ 37 | #define HID_API_CALL /**< API call macro */ 38 | #endif 39 | 40 | #define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/ 41 | 42 | #ifdef __cplusplus 43 | extern "C" { 44 | #endif 45 | struct hid_device_; 46 | typedef struct hid_device_ hid_device; /**< opaque hidapi structure */ 47 | 48 | /** hidapi info structure */ 49 | struct hid_device_info { 50 | /** Platform-specific device path */ 51 | char *path; 52 | /** Device Vendor ID */ 53 | unsigned short vendor_id; 54 | /** Device Product ID */ 55 | unsigned short product_id; 56 | /** Serial Number */ 57 | wchar_t *serial_number; 58 | /** Device Release Number in binary-coded decimal, 59 | also known as Device Version Number */ 60 | unsigned short release_number; 61 | /** Manufacturer String */ 62 | wchar_t *manufacturer_string; 63 | /** Product string */ 64 | wchar_t *product_string; 65 | /** Usage Page for this Device/Interface 66 | (Windows/Mac only). */ 67 | unsigned short usage_page; 68 | /** Usage for this Device/Interface 69 | (Windows/Mac only).*/ 70 | unsigned short usage; 71 | /** The USB interface which this logical device 72 | represents. Valid on both Linux implementations 73 | in all cases, and valid on the Windows implementation 74 | only if the device contains more than one interface. */ 75 | int interface_number; 76 | 77 | /** Pointer to the next device */ 78 | struct hid_device_info *next; 79 | }; 80 | 81 | 82 | /** @brief Initialize the HIDAPI library. 83 | 84 | This function initializes the HIDAPI library. Calling it is not 85 | strictly necessary, as it will be called automatically by 86 | hid_enumerate() and any of the hid_open_*() functions if it is 87 | needed. This function should be called at the beginning of 88 | execution however, if there is a chance of HIDAPI handles 89 | being opened by different threads simultaneously. 90 | 91 | @ingroup API 92 | 93 | @returns 94 | This function returns 0 on success and -1 on error. 95 | */ 96 | int HID_API_EXPORT HID_API_CALL hid_init(void); 97 | 98 | /** @brief Finalize the HIDAPI library. 99 | 100 | This function frees all of the static data associated with 101 | HIDAPI. It should be called at the end of execution to avoid 102 | memory leaks. 103 | 104 | @ingroup API 105 | 106 | @returns 107 | This function returns 0 on success and -1 on error. 108 | */ 109 | int HID_API_EXPORT HID_API_CALL hid_exit(void); 110 | 111 | /** @brief Enumerate the HID Devices. 112 | 113 | This function returns a linked list of all the HID devices 114 | attached to the system which match vendor_id and product_id. 115 | If @p vendor_id is set to 0 then any vendor matches. 116 | If @p product_id is set to 0 then any product matches. 117 | If @p vendor_id and @p product_id are both set to 0, then 118 | all HID devices will be returned. 119 | 120 | @ingroup API 121 | @param vendor_id The Vendor ID (VID) of the types of device 122 | to open. 123 | @param product_id The Product ID (PID) of the types of 124 | device to open. 125 | 126 | @returns 127 | This function returns a pointer to a linked list of type 128 | struct #hid_device, containing information about the HID devices 129 | attached to the system, or NULL in the case of failure. Free 130 | this linked list by calling hid_free_enumeration(). 131 | */ 132 | struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id); 133 | 134 | /** @brief Free an enumeration Linked List 135 | 136 | This function frees a linked list created by hid_enumerate(). 137 | 138 | @ingroup API 139 | @param devs Pointer to a list of struct_device returned from 140 | hid_enumerate(). 141 | */ 142 | void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs); 143 | 144 | /** @brief Open a HID device using a Vendor ID (VID), Product ID 145 | (PID) and optionally a serial number. 146 | 147 | If @p serial_number is NULL, the first device with the 148 | specified VID and PID is opened. 149 | 150 | @ingroup API 151 | @param vendor_id The Vendor ID (VID) of the device to open. 152 | @param product_id The Product ID (PID) of the device to open. 153 | @param serial_number The Serial Number of the device to open 154 | (Optionally NULL). 155 | 156 | @returns 157 | This function returns a pointer to a #hid_device object on 158 | success or NULL on failure. 159 | */ 160 | HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number); 161 | 162 | /** @brief Open a HID device by its path name. 163 | 164 | The path name be determined by calling hid_enumerate(), or a 165 | platform-specific path name can be used (eg: /dev/hidraw0 on 166 | Linux). 167 | 168 | @ingroup API 169 | @param path The path name of the device to open 170 | 171 | @returns 172 | This function returns a pointer to a #hid_device object on 173 | success or NULL on failure. 174 | */ 175 | HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path); 176 | 177 | /** @brief Write an Output report to a HID device. 178 | 179 | The first byte of @p data[] must contain the Report ID. For 180 | devices which only support a single report, this must be set 181 | to 0x0. The remaining bytes contain the report data. Since 182 | the Report ID is mandatory, calls to hid_write() will always 183 | contain one more byte than the report contains. For example, 184 | if a hid report is 16 bytes long, 17 bytes must be passed to 185 | hid_write(), the Report ID (or 0x0, for devices with a 186 | single report), followed by the report data (16 bytes). In 187 | this example, the length passed in would be 17. 188 | 189 | hid_write() will send the data on the first OUT endpoint, if 190 | one exists. If it does not, it will send the data through 191 | the Control Endpoint (Endpoint 0). 192 | 193 | @ingroup API 194 | @param device A device handle returned from hid_open(). 195 | @param data The data to send, including the report number as 196 | the first byte. 197 | @param length The length in bytes of the data to send. 198 | 199 | @returns 200 | This function returns the actual number of bytes written and 201 | -1 on error. 202 | */ 203 | int HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length); 204 | 205 | /** @brief Read an Input report from a HID device with timeout. 206 | 207 | Input reports are returned 208 | to the host through the INTERRUPT IN endpoint. The first byte will 209 | contain the Report number if the device uses numbered reports. 210 | 211 | @ingroup API 212 | @param device A device handle returned from hid_open(). 213 | @param data A buffer to put the read data into. 214 | @param length The number of bytes to read. For devices with 215 | multiple reports, make sure to read an extra byte for 216 | the report number. 217 | @param milliseconds timeout in milliseconds or -1 for blocking wait. 218 | 219 | @returns 220 | This function returns the actual number of bytes read and 221 | -1 on error. 222 | */ 223 | int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds); 224 | 225 | /** @brief Read an Input report from a HID device. 226 | 227 | Input reports are returned 228 | to the host through the INTERRUPT IN endpoint. The first byte will 229 | contain the Report number if the device uses numbered reports. 230 | 231 | @ingroup API 232 | @param device A device handle returned from hid_open(). 233 | @param data A buffer to put the read data into. 234 | @param length The number of bytes to read. For devices with 235 | multiple reports, make sure to read an extra byte for 236 | the report number. 237 | 238 | @returns 239 | This function returns the actual number of bytes read and 240 | -1 on error. 241 | */ 242 | int HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length); 243 | 244 | /** @brief Set the device handle to be non-blocking. 245 | 246 | In non-blocking mode calls to hid_read() will return 247 | immediately with a value of 0 if there is no data to be 248 | read. In blocking mode, hid_read() will wait (block) until 249 | there is data to read before returning. 250 | 251 | Nonblocking can be turned on and off at any time. 252 | 253 | @ingroup API 254 | @param device A device handle returned from hid_open(). 255 | @param nonblock enable or not the nonblocking reads 256 | - 1 to enable nonblocking 257 | - 0 to disable nonblocking. 258 | 259 | @returns 260 | This function returns 0 on success and -1 on error. 261 | */ 262 | int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock); 263 | 264 | /** @brief Send a Feature report to the device. 265 | 266 | Feature reports are sent over the Control endpoint as a 267 | Set_Report transfer. The first byte of @p data[] must 268 | contain the Report ID. For devices which only support a 269 | single report, this must be set to 0x0. The remaining bytes 270 | contain the report data. Since the Report ID is mandatory, 271 | calls to hid_send_feature_report() will always contain one 272 | more byte than the report contains. For example, if a hid 273 | report is 16 bytes long, 17 bytes must be passed to 274 | hid_send_feature_report(): the Report ID (or 0x0, for 275 | devices which do not use numbered reports), followed by the 276 | report data (16 bytes). In this example, the length passed 277 | in would be 17. 278 | 279 | @ingroup API 280 | @param device A device handle returned from hid_open(). 281 | @param data The data to send, including the report number as 282 | the first byte. 283 | @param length The length in bytes of the data to send, including 284 | the report number. 285 | 286 | @returns 287 | This function returns the actual number of bytes written and 288 | -1 on error. 289 | */ 290 | int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length); 291 | 292 | /** @brief Get a feature report from a HID device. 293 | 294 | Make sure to set the first byte of @p data[] to the Report 295 | ID of the report to be read. Make sure to allow space for 296 | this extra byte in @p data[]. 297 | 298 | @ingroup API 299 | @param device A device handle returned from hid_open(). 300 | @param data A buffer to put the read data into, including 301 | the Report ID. Set the first byte of @p data[] to the 302 | Report ID of the report to be read. 303 | @param length The number of bytes to read, including an 304 | extra byte for the report ID. The buffer can be longer 305 | than the actual report. 306 | 307 | @returns 308 | This function returns the number of bytes read and 309 | -1 on error. 310 | */ 311 | int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length); 312 | 313 | /** @brief Close a HID device. 314 | 315 | @ingroup API 316 | @param device A device handle returned from hid_open(). 317 | */ 318 | void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device); 319 | 320 | /** @brief Get The Manufacturer String from a HID device. 321 | 322 | @ingroup API 323 | @param device A device handle returned from hid_open(). 324 | @param string A wide string buffer to put the data into. 325 | @param maxlen The length of the buffer in multiples of wchar_t. 326 | 327 | @returns 328 | This function returns 0 on success and -1 on error. 329 | */ 330 | int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen); 331 | 332 | /** @brief Get The Product String from a HID device. 333 | 334 | @ingroup API 335 | @param device A device handle returned from hid_open(). 336 | @param string A wide string buffer to put the data into. 337 | @param maxlen The length of the buffer in multiples of wchar_t. 338 | 339 | @returns 340 | This function returns 0 on success and -1 on error. 341 | */ 342 | int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen); 343 | 344 | /** @brief Get The Serial Number String from a HID device. 345 | 346 | @ingroup API 347 | @param device A device handle returned from hid_open(). 348 | @param string A wide string buffer to put the data into. 349 | @param maxlen The length of the buffer in multiples of wchar_t. 350 | 351 | @returns 352 | This function returns 0 on success and -1 on error. 353 | */ 354 | int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen); 355 | 356 | /** @brief Get a string from a HID device, based on its string index. 357 | 358 | @ingroup API 359 | @param device A device handle returned from hid_open(). 360 | @param string_index The index of the string to get. 361 | @param string A wide string buffer to put the data into. 362 | @param maxlen The length of the buffer in multiples of wchar_t. 363 | 364 | @returns 365 | This function returns 0 on success and -1 on error. 366 | */ 367 | int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen); 368 | 369 | /** @brief Get a string describing the last error which occurred. 370 | 371 | @ingroup API 372 | @param device A device handle returned from hid_open(). 373 | 374 | @returns 375 | This function returns a string containing the last error 376 | which occurred or NULL if none has occurred. 377 | */ 378 | HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device); 379 | 380 | #ifdef __cplusplus 381 | } 382 | #endif 383 | 384 | #endif 385 | 386 | -------------------------------------------------------------------------------- /src/tp.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "hidapi.h" 6 | 7 | int main() 8 | { 9 | const uint16_t vendor_id = 0x17EF; 10 | const uint16_t product_ids[] = {0x6048, 0x60E1}; 11 | 12 | if (hid_init()) { 13 | return -1; 14 | } 15 | 16 | hid_device *dev = 0; 17 | for (int i = 0; i < sizeof(product_ids)/sizeof(uint16_t); ++i) { 18 | dev = hid_open(vendor_id, product_ids[i], NULL); 19 | if (dev != 0) { 20 | break; 21 | } 22 | } 23 | 24 | if (dev == 0) { 25 | printf("TP Keyboard not found. Possible solutions:\n" 26 | " * Have you run tpkb with 'sudo'?\n" 27 | " * Make sure you don't have keyboard customizers running (for example Karabiner)\n"); 28 | 29 | //Debug support 30 | printf("Detected devices:\n"); 31 | hid_device_info *devs = hid_enumerate(0,0); 32 | for (;devs; devs = devs->next) { 33 | printf("VID: 0x%04X\tPID: 0x%04X\tManufacturer: %ls\n", 34 | devs->vendor_id, 35 | devs->product_id, 36 | devs->manufacturer_string); 37 | } 38 | hid_free_enumeration(devs); 39 | devs = NULL; 40 | 41 | return 0; 42 | } 43 | 44 | unsigned char buf[4]; 45 | 46 | // sensitivity 47 | // 18 02 xx # xx Sensitivity, 00-FF 48 | buf[0] = 0x18; 49 | buf[1] = 0x02; 50 | buf[2] = 0x09; 51 | hid_write(dev, buf, 3); 52 | 53 | // Middle button 54 | buf[0] = 0x18; 55 | buf[1] = 0x09; 56 | buf[2] = 0x01; 57 | hid_write(dev, buf, 3); 58 | 59 | hid_close(dev); 60 | return 0; 61 | } 62 | --------------------------------------------------------------------------------