5b19dc4b |
1 | /** |
2 | * File: hiddata-OSX.c |
3 | * Version for OS/X 10.9+ |
4 | * 05-jan-2014 pa |
5 | * License: ? |
6 | */ |
7 | |
8 | #include "hiddata.h" |
9 | #include <stdio.h> |
10 | #include <string.h> |
11 | #include <stdint.h> |
12 | #include <errno.h> |
13 | |
14 | // OS X: Using IOKit HID functionality |
15 | #include <IOKit/IOKitLib.h> |
16 | #include <IOKit/hid/IOHIDDevice.h> |
17 | |
18 | |
19 | #if 0 //ifdef DEBUG |
20 | #define DEBUG_PRINT(arg) printf arg |
21 | #else |
22 | #define DEBUG_PRINT(arg) |
23 | #endif |
24 | |
25 | static IOHIDManagerRef g_hidmgr = NULL; |
26 | static int g_hidmgr_open_count = 0; |
27 | |
28 | // Device handle is IOHIDDeviceRef |
29 | static inline IOHIDDeviceRef usbh_to_IOHIDDeviceRef(USBDEVHANDLE usbh) |
30 | { |
31 | return (IOHIDDeviceRef)(void*)usbh; |
32 | } |
33 | |
34 | |
35 | static void _hidmgrDeref(void) |
36 | { |
37 | if ( g_hidmgr && (--g_hidmgr_open_count <= 0) ) { |
38 | IOHIDManagerClose(g_hidmgr, kIOHIDOptionsTypeNone); |
39 | CFRelease(g_hidmgr); |
40 | g_hidmgr = NULL; |
41 | } |
42 | } |
43 | |
44 | |
45 | static int32_t get_int_property(IOHIDDeviceRef device, CFStringRef key) |
46 | { |
47 | int32_t value = -1; |
48 | |
49 | CFTypeRef ref = IOHIDDeviceGetProperty(device, key); |
50 | if (ref) { |
51 | if (CFGetTypeID(ref) == CFNumberGetTypeID()) { |
52 | value = 0; |
53 | CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, &value); |
54 | } |
55 | } |
56 | return value; |
57 | } |
58 | |
59 | static int get_string_property_utf8(IOHIDDeviceRef device, CFStringRef prop, char *buf, ssize_t len) |
60 | { |
61 | *buf = 0; |
62 | if (len < 2) |
63 | return 0; |
64 | CFStringRef str = IOHIDDeviceGetProperty(device, prop); |
65 | if (!str) |
66 | return USBHID_ERR_BAD_ARG; |
67 | |
68 | CFIndex str_len = CFStringGetLength(str); |
69 | CFIndex chars_copied = 0; |
70 | if (str_len) { |
71 | CFIndex used_buf_len; |
72 | len--; |
73 | chars_copied = CFStringGetBytes(str, |
74 | CFRangeMake(0, CFStringGetLength(str)), |
75 | kCFStringEncodingUTF8, |
76 | '?', |
77 | FALSE, |
78 | (void*)buf, |
79 | len, |
80 | &used_buf_len); |
81 | buf[used_buf_len] = 0; |
82 | } |
83 | return (int)chars_copied; |
84 | } |
85 | |
86 | |
87 | // Enumerate devices |
88 | int usbhidEnumDevices(int vendor, int product, |
89 | void *context, |
90 | int (*usbhidEnumFunc)(USBDEVHANDLE usbh, void *ctx)) |
91 | |
92 | { |
93 | /* Enumerate thru HID devices */ |
94 | IOHIDManagerRef mgr; |
95 | mgr = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); |
96 | if (!mgr) |
97 | return USBHID_ERR_IO_HID; |
98 | // *** TODO: match by vendor/product right here *** |
99 | IOHIDManagerSetDeviceMatching(mgr, NULL); |
100 | IOReturn ir = IOHIDManagerOpen(mgr, kIOHIDOptionsTypeNone); |
101 | if (ir != kIOReturnSuccess) |
102 | return USBHID_ERR_IO_HID; |
103 | |
104 | CFSetRef device_set = IOHIDManagerCopyDevices(mgr); |
105 | CFIndex num_devices = CFSetGetCount(device_set); |
106 | if (0 == num_devices) |
107 | return USBHID_ERR_NOTFOUND; |
108 | |
109 | g_hidmgr = mgr; |
110 | g_hidmgr_open_count = 1; |
111 | |
112 | IOHIDDeviceRef *device_array = calloc(num_devices, sizeof(IOHIDDeviceRef)); |
113 | CFSetGetValues(device_set, (const void **) device_array); |
114 | |
115 | int i; |
116 | for (i = 0; i < num_devices; i++) { |
117 | IOHIDDeviceRef dev = device_array[i]; |
118 | char buf[32]; |
119 | uint16_t nvid, npid; |
120 | |
121 | if (!dev) |
122 | continue; /* device gone during enumeration? */ |
123 | |
124 | /* Skip non-USB devices */ |
125 | get_string_property_utf8(dev, CFSTR(kIOHIDTransportKey), buf, sizeof(buf)); |
126 | if (0 != strcasecmp(buf, "USB")) |
127 | continue; |
128 | |
129 | nvid = (uint16_t)get_int_property(dev, CFSTR(kIOHIDVendorIDKey)); |
130 | npid = (uint16_t)get_int_property(dev, CFSTR(kIOHIDProductIDKey)); |
131 | |
132 | if (nvid != (uint16_t)vendor || npid != (uint16_t)product) { |
133 | continue; |
134 | } |
135 | //printf("Device: %p\n", dev); |
136 | |
137 | /* the device handle is owned by the callback, it shoud call usbhidCloseDevice() on it */ |
138 | /* No need to open the device only for control pipe io (get/set HID feature) */ |
139 | ++g_hidmgr_open_count; |
140 | if ( 0 == usbhidEnumFunc((USBDEVHANDLE)dev, context) ) |
141 | { |
142 | break; /* stop enumeration */ |
143 | } |
144 | |
145 | } |
146 | |
147 | free(device_array); |
148 | _hidmgrDeref(); |
149 | return 0; |
150 | } |
151 | |
152 | /* ------------------------------------------------------------------------- */ |
153 | |
154 | void usbhidCloseDevice(USBDEVHANDLE usbh) |
155 | { |
156 | IOHIDDeviceRef dev = usbh_to_IOHIDDeviceRef(usbh); |
157 | //printf("close %p\n", dev); |
158 | if (dev) { |
159 | _hidmgrDeref(); |
160 | } |
161 | } |
162 | |
163 | /* ------------------------------------------------------------------------- */ |
164 | |
165 | int usbhidGetVendorString(USBDEVHANDLE usbh, char *buffer, int len) |
166 | { |
167 | IOHIDDeviceRef dev = usbh_to_IOHIDDeviceRef(usbh); |
168 | int len2 = get_string_property_utf8(dev, CFSTR(kIOHIDManufacturerKey), buffer, len); |
169 | if (len2 < 0) { |
170 | fprintf(stderr, "Warning: cannot query vendor for device\n"); |
171 | return USBHID_ERR_IO_HID; |
172 | } |
173 | return 0; |
174 | } |
175 | |
176 | int usbhidGetProductString(USBDEVHANDLE usbh, char *buffer, int len) |
177 | { |
178 | IOHIDDeviceRef dev = usbh_to_IOHIDDeviceRef(usbh); |
179 | int len2 = get_string_property_utf8(dev, CFSTR(kIOHIDProductKey), buffer, len); |
180 | if (len2 < 0) { |
181 | fprintf(stderr, "Warning: cannot query product for device\n"); |
182 | return USBHID_ERR_IO_HID; |
183 | } |
184 | return 0; |
185 | } |
186 | |
187 | |
188 | static inline bool usesReportIDs(USBDEVHANDLE usbh) { |
189 | /* true => the device (or underlying HID layer) returns the report ID |
190 | in the 1st byte of Get report buffer, and expects the id in Set request. |
191 | The v-usb relay devices do not do this. |
192 | */ |
193 | return false; |
194 | } |
195 | |
196 | |
197 | int usbhidSetReport(USBDEVHANDLE usbh, char *buffer, int len) |
198 | { |
199 | IOHIDDeviceRef dev = usbh_to_IOHIDDeviceRef(usbh); |
200 | char reportNumber = buffer[0]; |
201 | |
202 | if ( !usesReportIDs(usbh) ) { |
203 | buffer++; /* skip report ID */ |
204 | len--; |
205 | } |
206 | |
207 | IOReturn |
208 | res = IOHIDDeviceSetReport(dev, kIOHIDReportTypeFeature, (CFIndex)reportNumber, (void*)buffer, (CFIndex)len); |
209 | if (res != kIOReturnSuccess) { |
210 | fprintf(stderr, "Error sending message: %d\n", (int)res); |
211 | return USBHID_ERR_IO_HID; |
212 | } |
213 | return 0; |
214 | } |
215 | |
216 | /* ------------------------------------------------------------------------- */ |
217 | |
218 | int usbhidGetReport(USBDEVHANDLE usbh, int reportNumber, char *buffer, int *len) |
219 | { |
220 | IOHIDDeviceRef dev = usbh_to_IOHIDDeviceRef(usbh); |
221 | int maxLen = *len; |
222 | if (maxLen < 2) |
223 | return USBHID_ERR_BAD_ARG; |
224 | |
225 | if (!usesReportIDs(usbh)) { |
226 | buffer++; /* make room for dummy report ID */ |
227 | maxLen--; |
228 | } |
229 | |
230 | CFIndex rb = maxLen; |
231 | IOReturn |
232 | res = IOHIDDeviceGetReport(dev, kIOHIDReportTypeFeature, (CFIndex)reportNumber, (void*)buffer, &rb); |
233 | |
234 | if (res != kIOReturnSuccess || ((unsigned)rb <= 0)) { |
235 | fprintf(stderr, "Error get report: %d\n", res); |
236 | return USBHID_ERR_IO_HID; |
237 | } |
238 | |
239 | *len = (int)rb; |
240 | |
241 | if (!usesReportIDs(usbh)) { |
242 | buffer[-1] = (char)reportNumber; /* add dummy report ID */ |
243 | (*len)++; |
244 | } |
245 | return 0; |
246 | } |
247 | |
248 | |
249 | void usbhidSetUsesReportId(USBDEVHANDLE usbh) |
250 | { |
251 | //TODO Implement if some devices prepend report IDs |
252 | } |
253 | |
254 | |
255 | int usbhidStrerror_r( int err, char *buf, int len) |
256 | { |
257 | const char *s; |
258 | switch (err) { |
259 | case USBHID_ERR_ACCESS: s = "Access to device denied"; |
260 | case USBHID_ERR_NOTFOUND: s = "The specified device was not found"; |
261 | case USBHID_ERR_IO: s = "Communication error with device"; |
262 | case USBHID_ERR_IO_HID: s = "HID I/O error with device"; |
263 | default: |
264 | s = ""; |
265 | } |
266 | |
267 | return snprintf(buf, len, "%s", s); |
268 | } |