Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/sdl/hidapi/mac/hid.c
9917 views
1
/*******************************************************
2
HIDAPI - Multi-Platform library for
3
communication with HID devices.
4
5
Alan Ott
6
Signal 11 Software
7
8
libusb/hidapi Team
9
10
Copyright 2022, All Rights Reserved.
11
12
At the discretion of the user of this library,
13
this software may be licensed under the terms of the
14
GNU General Public License v3, a BSD-Style license, or the
15
original HIDAPI license as outlined in the LICENSE.txt,
16
LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
17
files located at the root of the source distribution.
18
These files may also be found in the public source
19
code repository located at:
20
https://github.com/libusb/hidapi .
21
********************************************************/
22
23
/* See Apple Technical Note TN2187 for details on IOHidManager. */
24
25
#include <IOKit/hid/IOHIDManager.h>
26
#include <IOKit/hid/IOHIDKeys.h>
27
#include <IOKit/IOKitLib.h>
28
#include <IOKit/usb/USBSpec.h>
29
#include <CoreFoundation/CoreFoundation.h>
30
#include <mach/mach_error.h>
31
#include <stdbool.h>
32
#include <wchar.h>
33
#include <locale.h>
34
#include <pthread.h>
35
#include <sys/time.h>
36
#include <unistd.h>
37
#include <dlfcn.h>
38
39
#include "hidapi_darwin.h"
40
41
/* Barrier implementation because Mac OSX doesn't have pthread_barrier.
42
It also doesn't have clock_gettime(). So much for POSIX and SUSv2.
43
This implementation came from Brent Priddy and was posted on
44
StackOverflow. It is used with his permission. */
45
typedef int pthread_barrierattr_t;
46
typedef struct pthread_barrier {
47
pthread_mutex_t mutex;
48
pthread_cond_t cond;
49
int count;
50
int trip_count;
51
} pthread_barrier_t;
52
53
static int pthread_barrier_init(pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count)
54
{
55
(void) attr;
56
57
if (count == 0) {
58
errno = EINVAL;
59
return -1;
60
}
61
62
if (pthread_mutex_init(&barrier->mutex, 0) < 0) {
63
return -1;
64
}
65
if (pthread_cond_init(&barrier->cond, 0) < 0) {
66
pthread_mutex_destroy(&barrier->mutex);
67
return -1;
68
}
69
barrier->trip_count = count;
70
barrier->count = 0;
71
72
return 0;
73
}
74
75
static int pthread_barrier_destroy(pthread_barrier_t *barrier)
76
{
77
pthread_cond_destroy(&barrier->cond);
78
pthread_mutex_destroy(&barrier->mutex);
79
return 0;
80
}
81
82
static int pthread_barrier_wait(pthread_barrier_t *barrier)
83
{
84
pthread_mutex_lock(&barrier->mutex);
85
++(barrier->count);
86
if (barrier->count >= barrier->trip_count) {
87
barrier->count = 0;
88
pthread_mutex_unlock(&barrier->mutex);
89
pthread_cond_broadcast(&barrier->cond);
90
return 1;
91
}
92
else {
93
do {
94
pthread_cond_wait(&barrier->cond, &(barrier->mutex));
95
}
96
while (barrier->count != 0);
97
98
pthread_mutex_unlock(&barrier->mutex);
99
return 0;
100
}
101
}
102
103
static int return_data(hid_device *dev, unsigned char *data, size_t length);
104
105
/* Linked List of input reports received from the device. */
106
struct input_report {
107
uint8_t *data;
108
size_t len;
109
struct input_report *next;
110
};
111
112
static struct hid_api_version api_version = {
113
.major = HID_API_VERSION_MAJOR,
114
.minor = HID_API_VERSION_MINOR,
115
.patch = HID_API_VERSION_PATCH
116
};
117
118
/* - Run context - */
119
static IOHIDManagerRef hid_mgr = 0x0;
120
static int is_macos_10_10_or_greater = 0;
121
static IOOptionBits device_open_options = 0;
122
static wchar_t *last_global_error_str = NULL;
123
/* --- */
124
125
struct hid_device_ {
126
IOHIDDeviceRef device_handle;
127
IOOptionBits open_options;
128
int blocking;
129
int disconnected;
130
CFStringRef run_loop_mode;
131
CFRunLoopRef run_loop;
132
CFRunLoopSourceRef source;
133
uint8_t *input_report_buf;
134
CFIndex max_input_report_len;
135
struct input_report *input_reports;
136
struct hid_device_info* device_info;
137
138
pthread_t thread;
139
pthread_mutex_t mutex; /* Protects input_reports */
140
pthread_cond_t condition;
141
pthread_barrier_t barrier; /* Ensures correct startup sequence */
142
pthread_barrier_t shutdown_barrier; /* Ensures correct shutdown sequence */
143
int shutdown_thread;
144
wchar_t *last_error_str;
145
};
146
147
static hid_device *new_hid_device(void)
148
{
149
hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device));
150
if (dev == NULL) {
151
return NULL;
152
}
153
154
dev->device_handle = NULL;
155
dev->open_options = device_open_options;
156
dev->blocking = 1;
157
dev->disconnected = 0;
158
dev->run_loop_mode = NULL;
159
dev->run_loop = NULL;
160
dev->source = NULL;
161
dev->input_report_buf = NULL;
162
dev->input_reports = NULL;
163
dev->device_info = NULL;
164
dev->shutdown_thread = 0;
165
dev->last_error_str = NULL;
166
167
/* Thread objects */
168
pthread_mutex_init(&dev->mutex, NULL);
169
pthread_cond_init(&dev->condition, NULL);
170
pthread_barrier_init(&dev->barrier, NULL, 2);
171
pthread_barrier_init(&dev->shutdown_barrier, NULL, 2);
172
173
return dev;
174
}
175
176
static void free_hid_device(hid_device *dev)
177
{
178
if (!dev)
179
return;
180
181
/* Delete any input reports still left over. */
182
struct input_report *rpt = dev->input_reports;
183
while (rpt) {
184
struct input_report *next = rpt->next;
185
free(rpt->data);
186
free(rpt);
187
rpt = next;
188
}
189
190
/* Free the string and the report buffer. The check for NULL
191
is necessary here as CFRelease() doesn't handle NULL like
192
free() and others do. */
193
if (dev->run_loop_mode)
194
CFRelease(dev->run_loop_mode);
195
if (dev->source)
196
CFRelease(dev->source);
197
free(dev->input_report_buf);
198
hid_free_enumeration(dev->device_info);
199
200
/* Clean up the thread objects */
201
pthread_barrier_destroy(&dev->shutdown_barrier);
202
pthread_barrier_destroy(&dev->barrier);
203
pthread_cond_destroy(&dev->condition);
204
pthread_mutex_destroy(&dev->mutex);
205
206
/* Free the structure itself. */
207
free(dev);
208
}
209
210
211
#ifndef HIDAPI_USING_SDL_RUNTIME
212
/* The caller must free the returned string with free(). */
213
static wchar_t *utf8_to_wchar_t(const char *utf8)
214
{
215
wchar_t *ret = NULL;
216
217
if (utf8) {
218
size_t wlen = mbstowcs(NULL, utf8, 0);
219
if ((size_t) -1 == wlen) {
220
return wcsdup(L"");
221
}
222
ret = (wchar_t*) calloc(wlen+1, sizeof(wchar_t));
223
if (ret == NULL) {
224
/* as much as we can do at this point */
225
return NULL;
226
}
227
mbstowcs(ret, utf8, wlen+1);
228
ret[wlen] = 0x0000;
229
}
230
231
return ret;
232
}
233
#endif
234
235
236
/* Makes a copy of the given error message (and decoded according to the
237
* currently locale) into the wide string pointer pointed by error_str.
238
* The last stored error string is freed.
239
* Use register_error_str(NULL) to free the error message completely. */
240
static void register_error_str(wchar_t **error_str, const char *msg)
241
{
242
free(*error_str);
243
#ifdef HIDAPI_USING_SDL_RUNTIME
244
/* Thread-safe error handling */
245
if (msg) {
246
SDL_SetError("%s", msg);
247
} else {
248
SDL_ClearError();
249
}
250
#else
251
*error_str = utf8_to_wchar_t(msg);
252
#endif
253
}
254
255
/* Similar to register_error_str, but allows passing a format string with va_list args into this function. */
256
static void register_error_str_vformat(wchar_t **error_str, const char *format, va_list args)
257
{
258
char msg[1024];
259
vsnprintf(msg, sizeof(msg), format, args);
260
261
register_error_str(error_str, msg);
262
}
263
264
/* Set the last global error to be reported by hid_error(NULL).
265
* The given error message will be copied (and decoded according to the
266
* currently locale, so do not pass in string constants).
267
* The last stored global error message is freed.
268
* Use register_global_error(NULL) to indicate "no error". */
269
static void register_global_error(const char *msg)
270
{
271
register_error_str(&last_global_error_str, msg);
272
}
273
274
/* Similar to register_global_error, but allows passing a format string into this function. */
275
static void register_global_error_format(const char *format, ...)
276
{
277
va_list args;
278
va_start(args, format);
279
register_error_str_vformat(&last_global_error_str, format, args);
280
va_end(args);
281
}
282
283
/* Set the last error for a device to be reported by hid_error(dev).
284
* The given error message will be copied (and decoded according to the
285
* currently locale, so do not pass in string constants).
286
* The last stored device error message is freed.
287
* Use register_device_error(dev, NULL) to indicate "no error". */
288
static void register_device_error(hid_device *dev, const char *msg)
289
{
290
register_error_str(&dev->last_error_str, msg);
291
}
292
293
/* Similar to register_device_error, but you can pass a format string into this function. */
294
static void register_device_error_format(hid_device *dev, const char *format, ...)
295
{
296
va_list args;
297
va_start(args, format);
298
register_error_str_vformat(&dev->last_error_str, format, args);
299
va_end(args);
300
}
301
302
303
static CFArrayRef get_array_property(IOHIDDeviceRef device, CFStringRef key)
304
{
305
CFTypeRef ref = IOHIDDeviceGetProperty(device, key);
306
if (ref != NULL && CFGetTypeID(ref) == CFArrayGetTypeID()) {
307
return (CFArrayRef)ref;
308
} else {
309
return NULL;
310
}
311
}
312
313
static int32_t get_int_property(IOHIDDeviceRef device, CFStringRef key)
314
{
315
CFTypeRef ref;
316
int32_t value = 0;
317
318
ref = IOHIDDeviceGetProperty(device, key);
319
if (ref) {
320
if (CFGetTypeID(ref) == CFNumberGetTypeID()) {
321
CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, &value);
322
return value;
323
}
324
}
325
return 0;
326
}
327
328
static bool try_get_int_property(IOHIDDeviceRef device, CFStringRef key, int32_t *out_val)
329
{
330
bool result = false;
331
CFTypeRef ref;
332
333
ref = IOHIDDeviceGetProperty(device, key);
334
if (ref) {
335
if (CFGetTypeID(ref) == CFNumberGetTypeID()) {
336
result = CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, out_val);
337
}
338
}
339
return result;
340
}
341
342
static bool try_get_ioregistry_int_property(io_service_t service, CFStringRef property, int32_t *out_val)
343
{
344
bool result = false;
345
CFTypeRef ref = IORegistryEntryCreateCFProperty(service, property, kCFAllocatorDefault, 0);
346
347
if (ref) {
348
if (CFGetTypeID(ref) == CFNumberGetTypeID()) {
349
result = CFNumberGetValue(ref, kCFNumberSInt32Type, out_val);
350
}
351
352
CFRelease(ref);
353
}
354
355
return result;
356
}
357
358
static CFArrayRef get_usage_pairs(IOHIDDeviceRef device)
359
{
360
return get_array_property(device, CFSTR(kIOHIDDeviceUsagePairsKey));
361
}
362
363
static unsigned short get_vendor_id(IOHIDDeviceRef device)
364
{
365
return get_int_property(device, CFSTR(kIOHIDVendorIDKey));
366
}
367
368
static unsigned short get_product_id(IOHIDDeviceRef device)
369
{
370
return get_int_property(device, CFSTR(kIOHIDProductIDKey));
371
}
372
373
static int32_t get_max_report_length(IOHIDDeviceRef device)
374
{
375
return get_int_property(device, CFSTR(kIOHIDMaxInputReportSizeKey));
376
}
377
378
static int get_string_property(IOHIDDeviceRef device, CFStringRef prop, wchar_t *buf, size_t len)
379
{
380
CFStringRef str;
381
382
if (!len)
383
return 0;
384
385
str = (CFStringRef) IOHIDDeviceGetProperty(device, prop);
386
387
buf[0] = 0;
388
389
if (str && CFGetTypeID(str) == CFStringGetTypeID()) {
390
CFIndex str_len = CFStringGetLength(str);
391
CFRange range;
392
CFIndex used_buf_len;
393
CFIndex chars_copied;
394
395
len --;
396
397
range.location = 0;
398
range.length = ((size_t) str_len > len)? len: (size_t) str_len;
399
chars_copied = CFStringGetBytes(str,
400
range,
401
kCFStringEncodingUTF32LE,
402
(char) '?',
403
FALSE,
404
(UInt8*)buf,
405
len * sizeof(wchar_t),
406
&used_buf_len);
407
408
if (chars_copied <= 0)
409
buf[0] = 0;
410
else
411
buf[chars_copied] = 0;
412
413
return 0;
414
}
415
else
416
return -1;
417
418
}
419
420
static int get_serial_number(IOHIDDeviceRef device, wchar_t *buf, size_t len)
421
{
422
return get_string_property(device, CFSTR(kIOHIDSerialNumberKey), buf, len);
423
}
424
425
static int get_manufacturer_string(IOHIDDeviceRef device, wchar_t *buf, size_t len)
426
{
427
return get_string_property(device, CFSTR(kIOHIDManufacturerKey), buf, len);
428
}
429
430
static int get_product_string(IOHIDDeviceRef device, wchar_t *buf, size_t len)
431
{
432
return get_string_property(device, CFSTR(kIOHIDProductKey), buf, len);
433
}
434
435
436
/* Implementation of wcsdup() for Mac. */
437
static wchar_t *dup_wcs(const wchar_t *s)
438
{
439
size_t len = wcslen(s);
440
wchar_t *ret = (wchar_t*) malloc((len+1)*sizeof(wchar_t));
441
wcscpy(ret, s);
442
443
return ret;
444
}
445
446
/* Initialize the IOHIDManager. Return 0 for success and -1 for failure. */
447
static int init_hid_manager(void)
448
{
449
/* Initialize all the HID Manager Objects */
450
hid_mgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
451
if (hid_mgr) {
452
IOHIDManagerSetDeviceMatching(hid_mgr, NULL);
453
IOHIDManagerScheduleWithRunLoop(hid_mgr, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
454
return 0;
455
}
456
457
register_global_error("Failed to create IOHIDManager");
458
return -1;
459
}
460
461
HID_API_EXPORT const struct hid_api_version* HID_API_CALL hid_version(void)
462
{
463
return &api_version;
464
}
465
466
HID_API_EXPORT const char* HID_API_CALL hid_version_str(void)
467
{
468
return HID_API_VERSION_STR;
469
}
470
471
/* Initialize the IOHIDManager if necessary. This is the public function, and
472
it is safe to call this function repeatedly. Return 0 for success and -1
473
for failure. */
474
int HID_API_EXPORT hid_init(void)
475
{
476
register_global_error(NULL);
477
478
if (!hid_mgr) {
479
is_macos_10_10_or_greater = (kCFCoreFoundationVersionNumber >= 1151.16); /* kCFCoreFoundationVersionNumber10_10 */
480
hid_darwin_set_open_exclusive(1); /* Backward compatibility */
481
return init_hid_manager();
482
}
483
484
/* Already initialized. */
485
return 0;
486
}
487
488
int HID_API_EXPORT hid_exit(void)
489
{
490
if (hid_mgr) {
491
/* Close the HID manager. */
492
IOHIDManagerClose(hid_mgr, kIOHIDOptionsTypeNone);
493
CFRelease(hid_mgr);
494
hid_mgr = NULL;
495
}
496
497
/* Free global error message */
498
register_global_error(NULL);
499
500
return 0;
501
}
502
503
static void process_pending_events(void) {
504
SInt32 res;
505
do {
506
res = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.001, FALSE);
507
} while(res != kCFRunLoopRunFinished && res != kCFRunLoopRunTimedOut);
508
}
509
510
static int read_usb_interface_from_hid_service_parent(io_service_t hid_service)
511
{
512
int32_t result = -1;
513
bool success = false;
514
io_registry_entry_t current = IO_OBJECT_NULL;
515
kern_return_t res;
516
int parent_number = 0;
517
518
res = IORegistryEntryGetParentEntry(hid_service, kIOServicePlane, &current);
519
while (KERN_SUCCESS == res
520
/* Only search up to 3 parent entries.
521
* With the default driver - the parent-of-interest supposed to be the first one,
522
* but lets assume some custom drivers or so, with deeper tree. */
523
&& parent_number < 3) {
524
io_registry_entry_t parent = IO_OBJECT_NULL;
525
int32_t interface_number = -1;
526
parent_number++;
527
528
success = try_get_ioregistry_int_property(current, CFSTR(kUSBInterfaceNumber), &interface_number);
529
if (success) {
530
result = interface_number;
531
break;
532
}
533
534
res = IORegistryEntryGetParentEntry(current, kIOServicePlane, &parent);
535
if (parent) {
536
IOObjectRelease(current);
537
current = parent;
538
}
539
540
}
541
542
if (current) {
543
IOObjectRelease(current);
544
current = IO_OBJECT_NULL;
545
}
546
547
return result;
548
}
549
550
#ifdef HIDAPI_IGNORE_DEVICE
551
static hid_bus_type get_bus_type(IOHIDDeviceRef dev)
552
{
553
hid_bus_type bus_type = HID_API_BUS_UNKNOWN;
554
555
CFTypeRef transport_prop = IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDTransportKey));
556
557
if (transport_prop != NULL && CFGetTypeID(transport_prop) == CFStringGetTypeID()) {
558
if (CFStringCompare((CFStringRef)transport_prop, CFSTR(kIOHIDTransportUSBValue), 0) == kCFCompareEqualTo) {
559
bus_type = HID_API_BUS_USB;
560
} else if (CFStringHasPrefix((CFStringRef)transport_prop, CFSTR(kIOHIDTransportBluetoothValue))) {
561
bus_type = HID_API_BUS_BLUETOOTH;
562
} else if (CFStringCompare((CFStringRef)transport_prop, CFSTR(kIOHIDTransportI2CValue), 0) == kCFCompareEqualTo) {
563
bus_type = HID_API_BUS_I2C;
564
} else if (CFStringCompare((CFStringRef)transport_prop, CFSTR(kIOHIDTransportSPIValue), 0) == kCFCompareEqualTo) {
565
bus_type = HID_API_BUS_SPI;
566
}
567
}
568
return bus_type;
569
}
570
#endif /* HIDAPI_IGNORE_DEVICE */
571
572
static struct hid_device_info *create_device_info_with_usage(IOHIDDeviceRef dev, int32_t usage_page, int32_t usage)
573
{
574
unsigned short dev_vid;
575
unsigned short dev_pid;
576
int BUF_LEN = 256;
577
wchar_t buf[BUF_LEN];
578
CFTypeRef transport_prop;
579
580
struct hid_device_info *cur_dev;
581
io_service_t hid_service;
582
kern_return_t res;
583
uint64_t entry_id = 0;
584
585
if (dev == NULL) {
586
return NULL;
587
}
588
589
cur_dev = (struct hid_device_info *)calloc(1, sizeof(struct hid_device_info));
590
if (cur_dev == NULL) {
591
return NULL;
592
}
593
594
dev_vid = get_vendor_id(dev);
595
dev_pid = get_product_id(dev);
596
597
#ifdef HIDAPI_IGNORE_DEVICE
598
/* See if there are any devices we should skip in enumeration */
599
if (HIDAPI_IGNORE_DEVICE(get_bus_type(dev), dev_vid, dev_pid, usage_page, usage)) {
600
free(cur_dev);
601
return NULL;
602
}
603
#endif
604
605
cur_dev->usage_page = usage_page;
606
cur_dev->usage = usage;
607
608
/* Fill out the record */
609
cur_dev->next = NULL;
610
611
/* Fill in the path (as a unique ID of the service entry) */
612
cur_dev->path = NULL;
613
hid_service = IOHIDDeviceGetService(dev);
614
if (hid_service != MACH_PORT_NULL) {
615
res = IORegistryEntryGetRegistryEntryID(hid_service, &entry_id);
616
}
617
else {
618
res = KERN_INVALID_ARGUMENT;
619
}
620
621
if (res == KERN_SUCCESS) {
622
/* max value of entry_id(uint64_t) is 18446744073709551615 which is 20 characters long,
623
so for (max) "path" string 'DevSrvsID:18446744073709551615' we would need
624
9+1+20+1=31 bytes buffer, but allocate 32 for simple alignment */
625
const size_t path_len = 32;
626
cur_dev->path = calloc(1, path_len);
627
if (cur_dev->path != NULL) {
628
snprintf(cur_dev->path, path_len, "DevSrvsID:%llu", entry_id);
629
}
630
}
631
632
if (cur_dev->path == NULL) {
633
/* for whatever reason, trying to keep it a non-NULL string */
634
cur_dev->path = strdup("");
635
}
636
637
/* Serial Number */
638
get_serial_number(dev, buf, BUF_LEN);
639
cur_dev->serial_number = dup_wcs(buf);
640
641
/* Manufacturer and Product strings */
642
get_manufacturer_string(dev, buf, BUF_LEN);
643
cur_dev->manufacturer_string = dup_wcs(buf);
644
get_product_string(dev, buf, BUF_LEN);
645
cur_dev->product_string = dup_wcs(buf);
646
647
/* VID/PID */
648
cur_dev->vendor_id = dev_vid;
649
cur_dev->product_id = dev_pid;
650
651
/* Release Number */
652
cur_dev->release_number = get_int_property(dev, CFSTR(kIOHIDVersionNumberKey));
653
654
/* Interface Number.
655
* We can only retrieve the interface number for USB HID devices.
656
* See below */
657
cur_dev->interface_number = -1;
658
659
/* Bus Type */
660
transport_prop = IOHIDDeviceGetProperty(dev, CFSTR(kIOHIDTransportKey));
661
662
if (transport_prop != NULL && CFGetTypeID(transport_prop) == CFStringGetTypeID()) {
663
if (CFStringCompare((CFStringRef)transport_prop, CFSTR(kIOHIDTransportUSBValue), 0) == kCFCompareEqualTo) {
664
int32_t interface_number = -1;
665
cur_dev->bus_type = HID_API_BUS_USB;
666
667
/* A IOHIDDeviceRef used to have this simple property,
668
* until macOS 13.3 - we will try to use it. */
669
if (try_get_int_property(dev, CFSTR(kUSBInterfaceNumber), &interface_number)) {
670
cur_dev->interface_number = interface_number;
671
} else {
672
/* Otherwise fallback to io_service_t property.
673
* (of one of the parent services). */
674
cur_dev->interface_number = read_usb_interface_from_hid_service_parent(hid_service);
675
676
/* If the above doesn't work -
677
* no (known) fallback exists at this point. */
678
}
679
680
/* Match "Bluetooth", "BluetoothLowEnergy" and "Bluetooth Low Energy" strings */
681
} else if (CFStringHasPrefix((CFStringRef)transport_prop, CFSTR(kIOHIDTransportBluetoothValue))) {
682
cur_dev->bus_type = HID_API_BUS_BLUETOOTH;
683
} else if (CFStringCompare((CFStringRef)transport_prop, CFSTR(kIOHIDTransportI2CValue), 0) == kCFCompareEqualTo) {
684
cur_dev->bus_type = HID_API_BUS_I2C;
685
} else if (CFStringCompare((CFStringRef)transport_prop, CFSTR(kIOHIDTransportSPIValue), 0) == kCFCompareEqualTo) {
686
cur_dev->bus_type = HID_API_BUS_SPI;
687
}
688
}
689
690
return cur_dev;
691
}
692
693
static struct hid_device_info *create_device_info(IOHIDDeviceRef device)
694
{
695
const int32_t primary_usage_page = get_int_property(device, CFSTR(kIOHIDPrimaryUsagePageKey));
696
const int32_t primary_usage = get_int_property(device, CFSTR(kIOHIDPrimaryUsageKey));
697
698
/* Primary should always be first, to match previous behavior. */
699
struct hid_device_info *root = create_device_info_with_usage(device, primary_usage_page, primary_usage);
700
struct hid_device_info *cur = root;
701
702
CFArrayRef usage_pairs = get_usage_pairs(device);
703
704
if (usage_pairs != NULL) {
705
struct hid_device_info *next = NULL;
706
for (CFIndex i = 0; i < CFArrayGetCount(usage_pairs); i++) {
707
CFTypeRef dict = CFArrayGetValueAtIndex(usage_pairs, i);
708
if (CFGetTypeID(dict) != CFDictionaryGetTypeID()) {
709
continue;
710
}
711
712
CFTypeRef usage_page_ref, usage_ref;
713
int32_t usage_page, usage;
714
715
if (!CFDictionaryGetValueIfPresent((CFDictionaryRef)dict, CFSTR(kIOHIDDeviceUsagePageKey), &usage_page_ref) ||
716
!CFDictionaryGetValueIfPresent((CFDictionaryRef)dict, CFSTR(kIOHIDDeviceUsageKey), &usage_ref) ||
717
CFGetTypeID(usage_page_ref) != CFNumberGetTypeID() ||
718
CFGetTypeID(usage_ref) != CFNumberGetTypeID() ||
719
!CFNumberGetValue((CFNumberRef)usage_page_ref, kCFNumberSInt32Type, &usage_page) ||
720
!CFNumberGetValue((CFNumberRef)usage_ref, kCFNumberSInt32Type, &usage)) {
721
continue;
722
}
723
if (usage_page == primary_usage_page && usage == primary_usage)
724
continue; /* Already added. */
725
726
next = create_device_info_with_usage(device, usage_page, usage);
727
if (cur) {
728
if (next != NULL) {
729
cur->next = next;
730
cur = next;
731
}
732
} else {
733
root = cur = next;
734
}
735
}
736
}
737
738
return root;
739
}
740
741
struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id)
742
{
743
struct hid_device_info *root = NULL; /* return object */
744
struct hid_device_info *cur_dev = NULL;
745
CFIndex num_devices;
746
int i;
747
748
/* Set up the HID Manager if it hasn't been done */
749
if (hid_init() < 0) {
750
return NULL;
751
}
752
/* register_global_error: global error is set/reset by hid_init */
753
754
/* give the IOHIDManager a chance to update itself */
755
process_pending_events();
756
757
/* Get a list of the Devices */
758
CFMutableDictionaryRef matching = NULL;
759
if (vendor_id != 0 || product_id != 0) {
760
matching = CFDictionaryCreateMutable(kCFAllocatorDefault, kIOHIDOptionsTypeNone, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
761
762
if (matching && vendor_id != 0) {
763
CFNumberRef v = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &vendor_id);
764
CFDictionarySetValue(matching, CFSTR(kIOHIDVendorIDKey), v);
765
CFRelease(v);
766
}
767
768
if (matching && product_id != 0) {
769
CFNumberRef p = CFNumberCreate(kCFAllocatorDefault, kCFNumberShortType, &product_id);
770
CFDictionarySetValue(matching, CFSTR(kIOHIDProductIDKey), p);
771
CFRelease(p);
772
}
773
}
774
IOHIDManagerSetDeviceMatching(hid_mgr, matching);
775
if (matching != NULL) {
776
CFRelease(matching);
777
}
778
779
CFSetRef device_set = IOHIDManagerCopyDevices(hid_mgr);
780
781
IOHIDDeviceRef *device_array = NULL;
782
783
if (device_set != NULL) {
784
/* Convert the list into a C array so we can iterate easily. */
785
num_devices = CFSetGetCount(device_set);
786
device_array = (IOHIDDeviceRef*) calloc(num_devices, sizeof(IOHIDDeviceRef));
787
CFSetGetValues(device_set, (const void **) device_array);
788
} else {
789
num_devices = 0;
790
}
791
792
/* Iterate over each device, making an entry for it. */
793
for (i = 0; i < num_devices; i++) {
794
795
IOHIDDeviceRef dev = device_array[i];
796
if (!dev) {
797
continue;
798
}
799
800
struct hid_device_info *tmp = create_device_info(dev);
801
if (tmp == NULL) {
802
continue;
803
}
804
805
if (cur_dev) {
806
cur_dev->next = tmp;
807
}
808
else {
809
root = tmp;
810
}
811
cur_dev = tmp;
812
813
/* move the pointer to the tail of returned list */
814
while (cur_dev->next != NULL) {
815
cur_dev = cur_dev->next;
816
}
817
}
818
819
free(device_array);
820
if (device_set != NULL)
821
CFRelease(device_set);
822
823
if (root == NULL) {
824
if (vendor_id == 0 && product_id == 0) {
825
register_global_error("No HID devices found in the system.");
826
} else {
827
register_global_error("No HID devices with requested VID/PID found in the system.");
828
}
829
}
830
831
return root;
832
}
833
834
void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs)
835
{
836
/* This function is identical to the Linux version. Platform independent. */
837
struct hid_device_info *d = devs;
838
while (d) {
839
struct hid_device_info *next = d->next;
840
free(d->path);
841
free(d->serial_number);
842
free(d->manufacturer_string);
843
free(d->product_string);
844
free(d);
845
d = next;
846
}
847
}
848
849
hid_device * HID_API_EXPORT hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
850
{
851
/* This function is identical to the Linux version. Platform independent. */
852
853
struct hid_device_info *devs, *cur_dev;
854
const char *path_to_open = NULL;
855
hid_device * handle = NULL;
856
857
/* register_global_error: global error is reset by hid_enumerate/hid_init */
858
devs = hid_enumerate(vendor_id, product_id);
859
if (devs == NULL) {
860
/* register_global_error: global error is already set by hid_enumerate */
861
return NULL;
862
}
863
864
cur_dev = devs;
865
while (cur_dev) {
866
if (cur_dev->vendor_id == vendor_id &&
867
cur_dev->product_id == product_id) {
868
if (serial_number) {
869
if (wcscmp(serial_number, cur_dev->serial_number) == 0) {
870
path_to_open = cur_dev->path;
871
break;
872
}
873
}
874
else {
875
path_to_open = cur_dev->path;
876
break;
877
}
878
}
879
cur_dev = cur_dev->next;
880
}
881
882
if (path_to_open) {
883
handle = hid_open_path(path_to_open);
884
} else {
885
register_global_error("Device with requested VID/PID/(SerialNumber) not found");
886
}
887
888
hid_free_enumeration(devs);
889
890
return handle;
891
}
892
893
static void hid_device_removal_callback(void *context, IOReturn result,
894
void *sender)
895
{
896
(void) result;
897
(void) sender;
898
899
/* Stop the Run Loop for this device. */
900
hid_device *d = (hid_device*) context;
901
902
d->disconnected = 1;
903
CFRunLoopStop(d->run_loop);
904
}
905
906
/* The Run Loop calls this function for each input report received.
907
This function puts the data into a linked list to be picked up by
908
hid_read(). */
909
static void hid_report_callback(void *context, IOReturn result, void *sender,
910
IOHIDReportType report_type, uint32_t report_id,
911
uint8_t *report, CFIndex report_length)
912
{
913
(void) result;
914
(void) sender;
915
(void) report_type;
916
(void) report_id;
917
918
struct input_report *rpt;
919
hid_device *dev = (hid_device*) context;
920
921
/* Make a new Input Report object */
922
rpt = (struct input_report*) calloc(1, sizeof(struct input_report));
923
rpt->data = (uint8_t*) calloc(1, report_length);
924
memcpy(rpt->data, report, report_length);
925
rpt->len = report_length;
926
rpt->next = NULL;
927
928
/* Lock this section */
929
pthread_mutex_lock(&dev->mutex);
930
931
/* Attach the new report object to the end of the list. */
932
if (dev->input_reports == NULL) {
933
/* The list is empty. Put it at the root. */
934
dev->input_reports = rpt;
935
}
936
else {
937
/* Find the end of the list and attach. */
938
struct input_report *cur = dev->input_reports;
939
int num_queued = 0;
940
while (cur->next != NULL) {
941
cur = cur->next;
942
num_queued++;
943
}
944
cur->next = rpt;
945
946
/* Pop one off if we've reached 30 in the queue. This
947
way we don't grow forever if the user never reads
948
anything from the device. */
949
if (num_queued > 30) {
950
return_data(dev, NULL, 0);
951
}
952
}
953
954
/* Signal a waiting thread that there is data. */
955
pthread_cond_signal(&dev->condition);
956
957
/* Unlock */
958
pthread_mutex_unlock(&dev->mutex);
959
960
}
961
962
/* This gets called when the read_thread's run loop gets signaled by
963
hid_close(), and serves to stop the read_thread's run loop. */
964
static void perform_signal_callback(void *context)
965
{
966
hid_device *dev = (hid_device*) context;
967
CFRunLoopStop(dev->run_loop); /*TODO: CFRunLoopGetCurrent()*/
968
}
969
970
static void *read_thread(void *param)
971
{
972
hid_device *dev = (hid_device*) param;
973
SInt32 code;
974
975
/* Move the device's run loop to this thread. */
976
IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetCurrent(), dev->run_loop_mode);
977
978
/* Create the RunLoopSource which is used to signal the
979
event loop to stop when hid_close() is called. */
980
CFRunLoopSourceContext ctx;
981
memset(&ctx, 0, sizeof(ctx));
982
ctx.version = 0;
983
ctx.info = dev;
984
ctx.perform = &perform_signal_callback;
985
dev->source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0/*order*/, &ctx);
986
CFRunLoopAddSource(CFRunLoopGetCurrent(), dev->source, dev->run_loop_mode);
987
988
/* Store off the Run Loop so it can be stopped from hid_close()
989
and on device disconnection. */
990
dev->run_loop = CFRunLoopGetCurrent();
991
992
/* Notify the main thread that the read thread is up and running. */
993
pthread_barrier_wait(&dev->barrier);
994
995
/* Run the Event Loop. CFRunLoopRunInMode() will dispatch HID input
996
reports into the hid_report_callback(). */
997
while (!dev->shutdown_thread && !dev->disconnected) {
998
code = CFRunLoopRunInMode(dev->run_loop_mode, 1000/*sec*/, FALSE);
999
/* Return if the device has been disconnected */
1000
if (code == kCFRunLoopRunFinished || code == kCFRunLoopRunStopped) {
1001
dev->disconnected = 1;
1002
break;
1003
}
1004
1005
1006
/* Break if The Run Loop returns Finished or Stopped. */
1007
if (code != kCFRunLoopRunTimedOut &&
1008
code != kCFRunLoopRunHandledSource) {
1009
/* There was some kind of error. Setting
1010
shutdown seems to make sense, but
1011
there may be something else more appropriate */
1012
dev->shutdown_thread = 1;
1013
break;
1014
}
1015
}
1016
1017
/* Now that the read thread is stopping, Wake any threads which are
1018
waiting on data (in hid_read_timeout()). Do this under a mutex to
1019
make sure that a thread which is about to go to sleep waiting on
1020
the condition actually will go to sleep before the condition is
1021
signaled. */
1022
pthread_mutex_lock(&dev->mutex);
1023
pthread_cond_broadcast(&dev->condition);
1024
pthread_mutex_unlock(&dev->mutex);
1025
1026
/* Wait here until hid_close() is called and makes it past
1027
the call to CFRunLoopWakeUp(). This thread still needs to
1028
be valid when that function is called on the other thread. */
1029
pthread_barrier_wait(&dev->shutdown_barrier);
1030
1031
return NULL;
1032
}
1033
1034
/* \p path must be one of:
1035
- in format 'DevSrvsID:<RegistryEntryID>' (as returned by hid_enumerate);
1036
- a valid path to an IOHIDDevice in the IOService plane (as returned by IORegistryEntryGetPath,
1037
e.g.: "IOService:/AppleACPIPlatformExpert/PCI0@0/AppleACPIPCI/EHC1@1D,7/AppleUSBEHCI/PLAYSTATION(R)3 Controller@fd120000/IOUSBInterface@0/IOUSBHIDDriver");
1038
Second format is for compatibility with paths accepted by older versions of HIDAPI.
1039
*/
1040
static io_registry_entry_t hid_open_service_registry_from_path(const char *path)
1041
{
1042
if (path == NULL)
1043
return MACH_PORT_NULL;
1044
1045
/* Get the IORegistry entry for the given path */
1046
if (strncmp("DevSrvsID:", path, 10) == 0) {
1047
char *endptr;
1048
uint64_t entry_id = strtoull(path + 10, &endptr, 10);
1049
if (*endptr == '\0') {
1050
return IOServiceGetMatchingService((mach_port_t) 0, IORegistryEntryIDMatching(entry_id));
1051
}
1052
}
1053
else {
1054
/* Fallback to older format of the path */
1055
return IORegistryEntryFromPath((mach_port_t) 0, path);
1056
}
1057
1058
return MACH_PORT_NULL;
1059
}
1060
1061
hid_device * HID_API_EXPORT hid_open_path(const char *path)
1062
{
1063
hid_device *dev = NULL;
1064
io_registry_entry_t entry = MACH_PORT_NULL;
1065
IOReturn ret = kIOReturnInvalid;
1066
char str[32];
1067
1068
/* Set up the HID Manager if it hasn't been done */
1069
if (hid_init() < 0) {
1070
return NULL;
1071
}
1072
/* register_global_error: global error is set/reset by hid_init */
1073
1074
dev = new_hid_device();
1075
if (!dev) {
1076
register_global_error("Couldn't allocate memory");
1077
return NULL;
1078
}
1079
1080
/* Get the IORegistry entry for the given path */
1081
entry = hid_open_service_registry_from_path(path);
1082
if (entry == MACH_PORT_NULL) {
1083
/* Path wasn't valid (maybe device was removed?) */
1084
register_global_error("hid_open_path: device mach entry not found with the given path");
1085
goto return_error;
1086
}
1087
1088
/* Create an IOHIDDevice for the entry */
1089
dev->device_handle = IOHIDDeviceCreate(kCFAllocatorDefault, entry);
1090
if (dev->device_handle == NULL) {
1091
/* Error creating the HID device */
1092
register_global_error("hid_open_path: failed to create IOHIDDevice from the mach entry");
1093
goto return_error;
1094
}
1095
1096
/* Open the IOHIDDevice */
1097
ret = IOHIDDeviceOpen(dev->device_handle, dev->open_options);
1098
if (ret != kIOReturnSuccess) {
1099
register_global_error_format("hid_open_path: failed to open IOHIDDevice from mach entry: (0x%08X) %s", ret, mach_error_string(ret));
1100
goto return_error;
1101
}
1102
1103
/* Create the buffers for receiving data */
1104
dev->max_input_report_len = (CFIndex) get_max_report_length(dev->device_handle);
1105
dev->input_report_buf = (uint8_t*) calloc(dev->max_input_report_len, sizeof(uint8_t));
1106
1107
/* Create the Run Loop Mode for this device.
1108
printing the reference seems to work. */
1109
snprintf(str, sizeof(str), "HIDAPI_%p", (void*) dev->device_handle);
1110
dev->run_loop_mode =
1111
CFStringCreateWithCString(NULL, str, kCFStringEncodingASCII);
1112
1113
/* Attach the device to a Run Loop */
1114
IOHIDDeviceRegisterInputReportCallback(
1115
dev->device_handle, dev->input_report_buf, dev->max_input_report_len,
1116
&hid_report_callback, dev);
1117
IOHIDDeviceRegisterRemovalCallback(dev->device_handle, hid_device_removal_callback, dev);
1118
1119
/* Start the read thread */
1120
pthread_create(&dev->thread, NULL, read_thread, dev);
1121
1122
/* Wait here for the read thread to be initialized. */
1123
pthread_barrier_wait(&dev->barrier);
1124
1125
IOObjectRelease(entry);
1126
return dev;
1127
1128
return_error:
1129
if (dev->device_handle != NULL)
1130
CFRelease(dev->device_handle);
1131
1132
if (entry != MACH_PORT_NULL)
1133
IOObjectRelease(entry);
1134
1135
free_hid_device(dev);
1136
return NULL;
1137
}
1138
1139
static int set_report(hid_device *dev, IOHIDReportType type, const unsigned char *data, size_t length)
1140
{
1141
const unsigned char *data_to_send = data;
1142
CFIndex length_to_send = length;
1143
IOReturn res;
1144
unsigned char report_id;
1145
1146
register_device_error(dev, NULL);
1147
1148
if (!data || (length == 0)) {
1149
register_device_error(dev, strerror(EINVAL));
1150
return -1;
1151
}
1152
1153
report_id = data[0];
1154
1155
if (report_id == 0x0) {
1156
/* Not using numbered Reports.
1157
Don't send the report number. */
1158
data_to_send = data+1;
1159
length_to_send = length-1;
1160
}
1161
1162
/* Avoid crash if the device has been unplugged. */
1163
if (dev->disconnected) {
1164
register_device_error(dev, "Device is disconnected");
1165
return -1;
1166
}
1167
1168
res = IOHIDDeviceSetReport(dev->device_handle,
1169
type,
1170
report_id,
1171
data_to_send, length_to_send);
1172
1173
if (res != kIOReturnSuccess) {
1174
register_device_error_format(dev, "IOHIDDeviceSetReport failed: (0x%08X) %s", res, mach_error_string(res));
1175
return -1;
1176
}
1177
1178
return (int) length;
1179
}
1180
1181
static int get_report(hid_device *dev, IOHIDReportType type, unsigned char *data, size_t length)
1182
{
1183
unsigned char *report = data;
1184
CFIndex report_length = length;
1185
IOReturn res = kIOReturnSuccess;
1186
const unsigned char report_id = data[0];
1187
1188
register_device_error(dev, NULL);
1189
1190
if (report_id == 0x0) {
1191
/* Not using numbered Reports.
1192
Don't send the report number. */
1193
report = data+1;
1194
report_length = length-1;
1195
}
1196
1197
/* Avoid crash if the device has been unplugged. */
1198
if (dev->disconnected) {
1199
register_device_error(dev, "Device is disconnected");
1200
return -1;
1201
}
1202
1203
res = IOHIDDeviceGetReport(dev->device_handle,
1204
type,
1205
report_id,
1206
report, &report_length);
1207
1208
if (res != kIOReturnSuccess) {
1209
register_device_error_format(dev, "IOHIDDeviceGetReport failed: (0x%08X) %s", res, mach_error_string(res));
1210
return -1;
1211
}
1212
1213
if (report_id == 0x0) { /* 0 report number still present at the beginning */
1214
report_length++;
1215
}
1216
1217
return (int) report_length;
1218
}
1219
1220
int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length)
1221
{
1222
return set_report(dev, kIOHIDReportTypeOutput, data, length);
1223
}
1224
1225
/* Helper function, so that this isn't duplicated in hid_read(). */
1226
static int return_data(hid_device *dev, unsigned char *data, size_t length)
1227
{
1228
/* Copy the data out of the linked list item (rpt) into the
1229
return buffer (data), and delete the liked list item. */
1230
struct input_report *rpt = dev->input_reports;
1231
size_t len = (length < rpt->len)? length: rpt->len;
1232
if (data != NULL) {
1233
memcpy(data, rpt->data, len);
1234
}
1235
dev->input_reports = rpt->next;
1236
free(rpt->data);
1237
free(rpt);
1238
return (int) len;
1239
}
1240
1241
static int cond_wait(hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex)
1242
{
1243
while (!dev->input_reports) {
1244
int res = pthread_cond_wait(cond, mutex);
1245
if (res != 0)
1246
return res;
1247
1248
/* A res of 0 means we may have been signaled or it may
1249
be a spurious wakeup. Check to see that there's actually
1250
data in the queue before returning, and if not, go back
1251
to sleep. See the pthread_cond_timedwait() man page for
1252
details. */
1253
1254
if (dev->shutdown_thread || dev->disconnected) {
1255
return -1;
1256
}
1257
}
1258
1259
return 0;
1260
}
1261
1262
static int cond_timedwait(hid_device *dev, pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime)
1263
{
1264
while (!dev->input_reports) {
1265
int res = pthread_cond_timedwait(cond, mutex, abstime);
1266
if (res != 0)
1267
return res;
1268
1269
/* A res of 0 means we may have been signaled or it may
1270
be a spurious wakeup. Check to see that there's actually
1271
data in the queue before returning, and if not, go back
1272
to sleep. See the pthread_cond_timedwait() man page for
1273
details. */
1274
1275
if (dev->shutdown_thread || dev->disconnected) {
1276
return -1;
1277
}
1278
}
1279
1280
return 0;
1281
1282
}
1283
1284
int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
1285
{
1286
int bytes_read = -1;
1287
1288
/* Lock the access to the report list. */
1289
pthread_mutex_lock(&dev->mutex);
1290
1291
/* There's an input report queued up. Return it. */
1292
if (dev->input_reports) {
1293
/* Return the first one */
1294
bytes_read = return_data(dev, data, length);
1295
goto ret;
1296
}
1297
1298
/* Return if the device has been disconnected. */
1299
if (dev->disconnected) {
1300
bytes_read = -1;
1301
register_device_error(dev, "hid_read_timeout: device disconnected");
1302
goto ret;
1303
}
1304
1305
if (dev->shutdown_thread) {
1306
/* This means the device has been closed (or there
1307
has been an error. An error code of -1 should
1308
be returned. */
1309
bytes_read = -1;
1310
register_device_error(dev, "hid_read_timeout: thread shutdown");
1311
goto ret;
1312
}
1313
1314
/* There is no data. Go to sleep and wait for data. */
1315
1316
if (milliseconds == -1) {
1317
/* Blocking */
1318
int res;
1319
res = cond_wait(dev, &dev->condition, &dev->mutex);
1320
if (res == 0)
1321
bytes_read = return_data(dev, data, length);
1322
else {
1323
/* There was an error, or a device disconnection. */
1324
register_device_error(dev, "hid_read_timeout: error waiting for more data");
1325
bytes_read = -1;
1326
}
1327
}
1328
else if (milliseconds > 0) {
1329
/* Non-blocking, but called with timeout. */
1330
int res;
1331
struct timespec ts;
1332
struct timeval tv;
1333
gettimeofday(&tv, NULL);
1334
TIMEVAL_TO_TIMESPEC(&tv, &ts);
1335
ts.tv_sec += milliseconds / 1000;
1336
ts.tv_nsec += (milliseconds % 1000) * 1000000;
1337
if (ts.tv_nsec >= 1000000000L) {
1338
ts.tv_sec++;
1339
ts.tv_nsec -= 1000000000L;
1340
}
1341
1342
res = cond_timedwait(dev, &dev->condition, &dev->mutex, &ts);
1343
if (res == 0) {
1344
bytes_read = return_data(dev, data, length);
1345
} else if (res == ETIMEDOUT) {
1346
bytes_read = 0;
1347
} else {
1348
register_device_error(dev, "hid_read_timeout: error waiting for more data");
1349
bytes_read = -1;
1350
}
1351
}
1352
else {
1353
/* Purely non-blocking */
1354
bytes_read = 0;
1355
}
1356
1357
ret:
1358
/* Unlock */
1359
pthread_mutex_unlock(&dev->mutex);
1360
return bytes_read;
1361
}
1362
1363
int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length)
1364
{
1365
return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0);
1366
}
1367
1368
int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock)
1369
{
1370
/* All Nonblocking operation is handled by the library. */
1371
dev->blocking = !nonblock;
1372
1373
return 0;
1374
}
1375
1376
int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length)
1377
{
1378
return set_report(dev, kIOHIDReportTypeFeature, data, length);
1379
}
1380
1381
int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length)
1382
{
1383
return get_report(dev, kIOHIDReportTypeFeature, data, length);
1384
}
1385
1386
int HID_API_EXPORT HID_API_CALL hid_get_input_report(hid_device *dev, unsigned char *data, size_t length)
1387
{
1388
return get_report(dev, kIOHIDReportTypeInput, data, length);
1389
}
1390
1391
void HID_API_EXPORT hid_close(hid_device *dev)
1392
{
1393
if (!dev)
1394
return;
1395
1396
/* Disconnect the report callback before close.
1397
See comment below.
1398
*/
1399
if (is_macos_10_10_or_greater || !dev->disconnected) {
1400
IOHIDDeviceRegisterInputReportCallback(
1401
dev->device_handle, dev->input_report_buf, dev->max_input_report_len,
1402
NULL, dev);
1403
IOHIDDeviceRegisterRemovalCallback(dev->device_handle, NULL, dev);
1404
IOHIDDeviceUnscheduleFromRunLoop(dev->device_handle, dev->run_loop, dev->run_loop_mode);
1405
IOHIDDeviceScheduleWithRunLoop(dev->device_handle, CFRunLoopGetMain(), kCFRunLoopDefaultMode);
1406
}
1407
1408
/* Cause read_thread() to stop. */
1409
dev->shutdown_thread = 1;
1410
1411
/* Wake up the run thread's event loop so that the thread can exit. */
1412
CFRunLoopSourceSignal(dev->source);
1413
CFRunLoopWakeUp(dev->run_loop);
1414
1415
/* Notify the read thread that it can shut down now. */
1416
pthread_barrier_wait(&dev->shutdown_barrier);
1417
1418
/* Wait for read_thread() to end. */
1419
pthread_join(dev->thread, NULL);
1420
1421
/* Close the OS handle to the device, but only if it's not
1422
been unplugged. If it's been unplugged, then calling
1423
IOHIDDeviceClose() will crash.
1424
1425
UPD: The crash part was true in/until some version of macOS.
1426
Starting with macOS 10.15, there is an opposite effect in some environments:
1427
crash happenes if IOHIDDeviceClose() is not called.
1428
Not leaking a resource in all tested environments.
1429
*/
1430
if (is_macos_10_10_or_greater || !dev->disconnected) {
1431
IOHIDDeviceClose(dev->device_handle, dev->open_options);
1432
}
1433
1434
/* Clear out the queue of received reports. */
1435
pthread_mutex_lock(&dev->mutex);
1436
while (dev->input_reports) {
1437
return_data(dev, NULL, 0);
1438
}
1439
pthread_mutex_unlock(&dev->mutex);
1440
CFRelease(dev->device_handle);
1441
1442
free_hid_device(dev);
1443
}
1444
1445
int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen)
1446
{
1447
if (!string || !maxlen)
1448
{
1449
register_device_error(dev, "Zero buffer/length");
1450
return -1;
1451
}
1452
1453
struct hid_device_info *info = hid_get_device_info(dev);
1454
if (!info)
1455
{
1456
// hid_get_device_info will have set an error already
1457
return -1;
1458
}
1459
1460
wcsncpy(string, info->manufacturer_string, maxlen);
1461
string[maxlen - 1] = L'\0';
1462
1463
return 0;
1464
}
1465
1466
int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen)
1467
{
1468
if (!string || !maxlen) {
1469
register_device_error(dev, "Zero buffer/length");
1470
return -1;
1471
}
1472
1473
struct hid_device_info *info = hid_get_device_info(dev);
1474
if (!info) {
1475
// hid_get_device_info will have set an error already
1476
return -1;
1477
}
1478
1479
wcsncpy(string, info->product_string, maxlen);
1480
string[maxlen - 1] = L'\0';
1481
1482
return 0;
1483
}
1484
1485
int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen)
1486
{
1487
if (!string || !maxlen) {
1488
register_device_error(dev, "Zero buffer/length");
1489
return -1;
1490
}
1491
1492
struct hid_device_info *info = hid_get_device_info(dev);
1493
if (!info) {
1494
// hid_get_device_info will have set an error already
1495
return -1;
1496
}
1497
1498
wcsncpy(string, info->serial_number, maxlen);
1499
string[maxlen - 1] = L'\0';
1500
1501
return 0;
1502
}
1503
1504
HID_API_EXPORT struct hid_device_info *HID_API_CALL hid_get_device_info(hid_device *dev) {
1505
if (!dev->device_info) {
1506
dev->device_info = create_device_info(dev->device_handle);
1507
if (!dev->device_info) {
1508
register_device_error(dev, "Failed to create hid_device_info");
1509
}
1510
}
1511
1512
return dev->device_info;
1513
}
1514
1515
int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen)
1516
{
1517
(void) dev;
1518
(void) string_index;
1519
(void) string;
1520
(void) maxlen;
1521
1522
register_device_error(dev, "hid_get_indexed_string: not available on this platform");
1523
return -1;
1524
}
1525
1526
int HID_API_EXPORT_CALL hid_darwin_get_location_id(hid_device *dev, uint32_t *location_id)
1527
{
1528
int res = get_int_property(dev->device_handle, CFSTR(kIOHIDLocationIDKey));
1529
if (res != 0) {
1530
*location_id = (uint32_t) res;
1531
return 0;
1532
} else {
1533
register_device_error(dev, "Failed to get IOHIDLocationID property");
1534
return -1;
1535
}
1536
}
1537
1538
void HID_API_EXPORT_CALL hid_darwin_set_open_exclusive(int open_exclusive)
1539
{
1540
device_open_options = (open_exclusive == 0) ? kIOHIDOptionsTypeNone : kIOHIDOptionsTypeSeizeDevice;
1541
}
1542
1543
int HID_API_EXPORT_CALL hid_darwin_get_open_exclusive(void)
1544
{
1545
return (device_open_options == kIOHIDOptionsTypeSeizeDevice) ? 1 : 0;
1546
}
1547
1548
int HID_API_EXPORT_CALL hid_darwin_is_device_open_exclusive(hid_device *dev)
1549
{
1550
if (!dev)
1551
return -1;
1552
1553
return (dev->open_options == kIOHIDOptionsTypeSeizeDevice) ? 1 : 0;
1554
}
1555
1556
int HID_API_EXPORT_CALL hid_get_report_descriptor(hid_device *dev, unsigned char *buf, size_t buf_size)
1557
{
1558
CFTypeRef ref = IOHIDDeviceGetProperty(dev->device_handle, CFSTR(kIOHIDReportDescriptorKey));
1559
if (ref != NULL && CFGetTypeID(ref) == CFDataGetTypeID()) {
1560
CFDataRef report_descriptor = (CFDataRef) ref;
1561
const UInt8 *descriptor_buf = CFDataGetBytePtr(report_descriptor);
1562
CFIndex descriptor_buf_len = CFDataGetLength(report_descriptor);
1563
size_t copy_len = (size_t) descriptor_buf_len;
1564
1565
if (descriptor_buf == NULL || descriptor_buf_len < 0) {
1566
register_device_error(dev, "Zero buffer/length");
1567
return -1;
1568
}
1569
1570
if (buf_size < copy_len) {
1571
copy_len = buf_size;
1572
}
1573
1574
memcpy(buf, descriptor_buf, copy_len);
1575
return (int)copy_len;
1576
}
1577
else {
1578
register_device_error(dev, "Failed to get kIOHIDReportDescriptorKey property");
1579
return -1;
1580
}
1581
}
1582
1583
HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev)
1584
{
1585
if (dev) {
1586
if (dev->last_error_str == NULL)
1587
return L"Success";
1588
return dev->last_error_str;
1589
}
1590
1591
if (last_global_error_str == NULL)
1592
return L"Success";
1593
return last_global_error_str;
1594
}
1595
1596