Windows DLL src added, vc2008
[usb-relay-hid.git] / commandline / hiddata_osx.c
CommitLineData
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
25static IOHIDManagerRef g_hidmgr = NULL;
26static int g_hidmgr_open_count = 0;
27
28// Device handle is IOHIDDeviceRef
29static inline IOHIDDeviceRef usbh_to_IOHIDDeviceRef(USBDEVHANDLE usbh)
30{
31 return (IOHIDDeviceRef)(void*)usbh;
32}
33
34
35static 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
45static 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
59static 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
88int 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
154void 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
165int 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
176int 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
188static 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
197int 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
218int 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
249void usbhidSetUsesReportId(USBDEVHANDLE usbh)
250{
251 //TODO Implement if some devices prepend report IDs
252}
253
254
255int 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}