rb-blink1 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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