rb-blink1 0.0.1

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