rawhid 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,412 @@
1
+ /* Simple Raw HID functions for Linux - for use with Teensy RawHID example
2
+ * http://www.pjrc.com/teensy/rawhid.html
3
+ * Copyright (c) 2009 PJRC.COM, LLC
4
+ *
5
+ * rawhid_open - open 1 or more devices
6
+ * rawhid_recv - receive a packet
7
+ * rawhid_send - send a packet
8
+ * rawhid_close - close a device
9
+ *
10
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ * of this software and associated documentation files (the "Software"), to deal
12
+ * in the Software without restriction, including without limitation the rights
13
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ * copies of the Software, and to permit persons to whom the Software is
15
+ * furnished to do so, subject to the following conditions:
16
+ *
17
+ * The above description, website URL and copyright notice and this permission
18
+ * notice shall be included in all copies or substantial portions of the Software.
19
+ *
20
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26
+ * THE SOFTWARE.
27
+ *
28
+ * Version 1.0: Initial Release
29
+ */
30
+
31
+ #include <stdio.h>
32
+ #include <stdlib.h>
33
+ #include <stdint.h>
34
+ #include <unistd.h>
35
+ #include <IOKit/IOKitLib.h>
36
+ #include <IOKit/hid/IOHIDLib.h>
37
+
38
+ #include "hid.h"
39
+
40
+ #define BUFFER_SIZE 64
41
+
42
+ #define printf(...) // comment this out to get lots of info printed
43
+
44
+
45
+ // a list of all opened HID devices, so the caller can
46
+ // simply refer to them by number
47
+ typedef struct hid_struct hid_t;
48
+ typedef struct buffer_struct buffer_t;
49
+ static hid_t *first_hid = NULL;
50
+ static hid_t *last_hid = NULL;
51
+ struct hid_struct {
52
+ IOHIDDeviceRef ref;
53
+ int open;
54
+ uint8_t buffer[BUFFER_SIZE];
55
+ buffer_t *first_buffer;
56
+ buffer_t *last_buffer;
57
+ struct hid_struct *prev;
58
+ struct hid_struct *next;
59
+ };
60
+ struct buffer_struct {
61
+ struct buffer_struct *next;
62
+ uint32_t len;
63
+ uint8_t buf[BUFFER_SIZE];
64
+ };
65
+
66
+ // private functions, not intended to be used from outside this file
67
+ static void add_hid(hid_t *);
68
+ static hid_t * get_hid(int);
69
+ static void free_all_hid(void);
70
+ static void hid_close(hid_t *);
71
+ static void attach_callback(void *, IOReturn, void *, IOHIDDeviceRef);
72
+ static void detach_callback(void *, IOReturn, void *hid_mgr, IOHIDDeviceRef dev);
73
+ static void timeout_callback(CFRunLoopTimerRef, void *);
74
+ static void input_callback(void *, IOReturn, void *, IOHIDReportType,
75
+ uint32_t, uint8_t *, CFIndex);
76
+
77
+
78
+
79
+ // rawhid_recv - receive a packet
80
+ // Inputs:
81
+ // num = device to receive from (zero based)
82
+ // buf = buffer to receive packet
83
+ // len = buffer's size
84
+ // timeout = time to wait, in milliseconds
85
+ // Output:
86
+ // number of bytes received, or -1 on error
87
+ //
88
+ int rawhid_recv(int num, void *buf, int len, int timeout)
89
+ {
90
+ hid_t *hid;
91
+ buffer_t *b;
92
+ CFRunLoopTimerRef timer=NULL;
93
+ CFRunLoopTimerContext context;
94
+ int ret=0, timeout_occurred=0;
95
+
96
+ if (len < 1) return 0;
97
+ hid = get_hid(num);
98
+ if (!hid || !hid->open) return -1;
99
+ if ((b = hid->first_buffer) != NULL) {
100
+ if (len > b->len) len = b->len;
101
+ memcpy(buf, b->buf, len);
102
+ hid->first_buffer = b->next;
103
+ free(b);
104
+ return len;
105
+ }
106
+ memset(&context, 0, sizeof(context));
107
+ context.info = &timeout_occurred;
108
+ timer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent() +
109
+ (double)timeout / 1000.0, 0, 0, 0, timeout_callback, &context);
110
+ CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopDefaultMode);
111
+ while (1) {
112
+ CFRunLoopRun();
113
+ if ((b = hid->first_buffer) != NULL) {
114
+ if (len > b->len) len = b->len;
115
+ memcpy(buf, b->buf, len);
116
+ hid->first_buffer = b->next;
117
+ free(b);
118
+ ret = len;
119
+ break;
120
+ }
121
+ if (!hid->open) {
122
+ printf("rawhid_recv, device not open\n");
123
+ ret = -1;
124
+ break;
125
+ }
126
+ if (timeout_occurred) break;
127
+ }
128
+ CFRunLoopTimerInvalidate(timer);
129
+ CFRelease(timer);
130
+ return ret;
131
+ }
132
+
133
+ static void input_callback(void *context, IOReturn ret, void *sender,
134
+ IOHIDReportType type, uint32_t id, uint8_t *data, CFIndex len)
135
+ {
136
+ buffer_t *n;
137
+ hid_t *hid;
138
+
139
+ printf("input_callback\n");
140
+ if (ret != kIOReturnSuccess || len < 1) return;
141
+ hid = context;
142
+ if (!hid || hid->ref != sender) return;
143
+ n = (buffer_t *)malloc(sizeof(buffer_t));
144
+ if (!n) return;
145
+ if (len > BUFFER_SIZE) len = BUFFER_SIZE;
146
+ memcpy(n->buf, data, len);
147
+ n->len = len;
148
+ n->next = NULL;
149
+ if (!hid->first_buffer || !hid->last_buffer) {
150
+ hid->first_buffer = hid->last_buffer = n;
151
+ } else {
152
+ hid->last_buffer->next = n;
153
+ hid->last_buffer = n;
154
+ }
155
+ CFRunLoopStop(CFRunLoopGetCurrent());
156
+ }
157
+
158
+ static void timeout_callback(CFRunLoopTimerRef timer, void *info)
159
+ {
160
+ printf("timeout_callback\n");
161
+ *(int *)info = 1;
162
+ CFRunLoopStop(CFRunLoopGetCurrent());
163
+ }
164
+
165
+
166
+ void output_callback(void *context, IOReturn ret, void *sender,
167
+ IOHIDReportType type, uint32_t id, uint8_t *data, CFIndex len)
168
+ {
169
+ printf("output_callback, r=%d\n", ret);
170
+ if (ret == kIOReturnSuccess) {
171
+ *(int *)context = len;
172
+ } else {
173
+ // timeout if not success?
174
+ *(int *)context = 0;
175
+ }
176
+ CFRunLoopStop(CFRunLoopGetCurrent());
177
+ }
178
+
179
+
180
+ // rawhid_send - send a packet
181
+ // Inputs:
182
+ // num = device to transmit to (zero based)
183
+ // buf = buffer containing packet to send
184
+ // len = number of bytes to transmit
185
+ // timeout = time to wait, in milliseconds
186
+ // Output:
187
+ // number of bytes sent, or -1 on error
188
+ //
189
+ int rawhid_send(int num, void *buf, int len, int timeout)
190
+ {
191
+ hid_t *hid;
192
+ int result=-100;
193
+
194
+ hid = get_hid(num);
195
+ if (!hid || !hid->open) return -1;
196
+ #if 1
197
+ #warning "Send timeout not implemented on MACOSX"
198
+ IOReturn ret = IOHIDDeviceSetReport(hid->ref, kIOHIDReportTypeOutput, 0, buf, len);
199
+ result = (ret == kIOReturnSuccess) ? len : -1;
200
+ #endif
201
+ #if 0
202
+ // No matter what I tried this never actually sends an output
203
+ // report and output_callback never gets called. Why??
204
+ // Did I miss something? This is exactly the same params as
205
+ // the sync call that works. Is it an Apple bug?
206
+ // (submitted to Apple on 22-sep-2009, problem ID 7245050)
207
+ //
208
+ IOHIDDeviceScheduleWithRunLoop(hid->ref, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
209
+ // should already be scheduled with run loop by attach_callback,
210
+ // sadly this doesn't make any difference either way
211
+
212
+ // could this be related?
213
+ // http://lists.apple.com/archives/usb/2008/Aug/msg00021.html
214
+ //
215
+ IOHIDDeviceSetReportWithCallback(hid->ref, kIOHIDReportTypeOutput,
216
+ 0, buf, len, (double)timeout / 1000.0, output_callback, &result);
217
+ while (1) {
218
+ printf("enter run loop (send)\n");
219
+ CFRunLoopRun();
220
+ printf("leave run loop (send)\n");
221
+ if (result > -100) break;
222
+ if (!hid->open) {
223
+ result = -1;
224
+ break;
225
+ }
226
+ }
227
+ #endif
228
+ return result;
229
+ }
230
+
231
+
232
+ // rawhid_open - open 1 or more devices
233
+ //
234
+ // Inputs:
235
+ // max = maximum number of devices to open
236
+ // vid = Vendor ID, or -1 if any
237
+ // pid = Product ID, or -1 if any
238
+ // usage_page = top level usage page, or -1 if any
239
+ // usage = top level usage number, or -1 if any
240
+ // Output:
241
+ // actual number of devices opened
242
+ //
243
+ int rawhid_open(int max, int vid, int pid, int usage_page, int usage)
244
+ {
245
+ static IOHIDManagerRef hid_manager=NULL;
246
+ CFMutableDictionaryRef dict;
247
+ CFNumberRef num;
248
+ IOReturn ret;
249
+ hid_t *p;
250
+ int count=0;
251
+
252
+ if (first_hid) free_all_hid();
253
+ printf("rawhid_open, max=%d\n", max);
254
+ if (max < 1) return 0;
255
+ // Start the HID Manager
256
+ // http://developer.apple.com/technotes/tn2007/tn2187.html
257
+ if (!hid_manager) {
258
+ hid_manager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
259
+ if (hid_manager == NULL || CFGetTypeID(hid_manager) != IOHIDManagerGetTypeID()) {
260
+ if (hid_manager) CFRelease(hid_manager);
261
+ return 0;
262
+ }
263
+ }
264
+ if (vid > 0 || pid > 0 || usage_page > 0 || usage > 0) {
265
+ // Tell the HID Manager what type of devices we want
266
+ dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
267
+ &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
268
+ if (!dict) return 0;
269
+ if (vid > 0) {
270
+ num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &vid);
271
+ CFDictionarySetValue(dict, CFSTR(kIOHIDVendorIDKey), num);
272
+ CFRelease(num);
273
+ }
274
+ if (pid > 0) {
275
+ num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &pid);
276
+ CFDictionarySetValue(dict, CFSTR(kIOHIDProductIDKey), num);
277
+ CFRelease(num);
278
+ }
279
+ if (usage_page > 0) {
280
+ num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage_page);
281
+ CFDictionarySetValue(dict, CFSTR(kIOHIDPrimaryUsagePageKey), num);
282
+ CFRelease(num);
283
+ }
284
+ if (usage > 0) {
285
+ num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &usage);
286
+ CFDictionarySetValue(dict, CFSTR(kIOHIDPrimaryUsageKey), num);
287
+ CFRelease(num);
288
+ }
289
+ IOHIDManagerSetDeviceMatching(hid_manager, dict);
290
+ CFRelease(dict);
291
+ } else {
292
+ IOHIDManagerSetDeviceMatching(hid_manager, NULL);
293
+ }
294
+ // set up a callbacks for device attach & detach
295
+ IOHIDManagerScheduleWithRunLoop(hid_manager, CFRunLoopGetCurrent(),
296
+ kCFRunLoopDefaultMode);
297
+ IOHIDManagerRegisterDeviceMatchingCallback(hid_manager, attach_callback, NULL);
298
+ IOHIDManagerRegisterDeviceRemovalCallback(hid_manager, detach_callback, NULL);
299
+ ret = IOHIDManagerOpen(hid_manager, kIOHIDOptionsTypeNone);
300
+ if (ret != kIOReturnSuccess) {
301
+ IOHIDManagerUnscheduleFromRunLoop(hid_manager,
302
+ CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
303
+ CFRelease(hid_manager);
304
+ return 0;
305
+ }
306
+ printf("run loop\n");
307
+ // let it do the callback for all devices
308
+ while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, true) == kCFRunLoopRunHandledSource) ;
309
+ // count up how many were added by the callback
310
+ for (p = first_hid; p; p = p->next) count++;
311
+ return count;
312
+ }
313
+
314
+
315
+ // rawhid_close - close a device
316
+ //
317
+ // Inputs:
318
+ // num = device to close (zero based)
319
+ // Output
320
+ // (nothing)
321
+ //
322
+ void rawhid_close(int num)
323
+ {
324
+ hid_t *hid;
325
+
326
+ hid = get_hid(num);
327
+ if (!hid || !hid->open) return;
328
+ hid_close(hid);
329
+ hid->open = 0;
330
+ }
331
+
332
+
333
+ static void add_hid(hid_t *h)
334
+ {
335
+ if (!first_hid || !last_hid) {
336
+ first_hid = last_hid = h;
337
+ h->next = h->prev = NULL;
338
+ return;
339
+ }
340
+ last_hid->next = h;
341
+ h->prev = last_hid;
342
+ h->next = NULL;
343
+ last_hid = h;
344
+ }
345
+
346
+
347
+ static hid_t * get_hid(int num)
348
+ {
349
+ hid_t *p;
350
+ for (p = first_hid; p && num > 0; p = p->next, num--) ;
351
+ return p;
352
+ }
353
+
354
+
355
+ static void free_all_hid(void)
356
+ {
357
+ hid_t *p, *q;
358
+
359
+ for (p = first_hid; p; p = p->next) {
360
+ hid_close(p);
361
+ }
362
+ p = first_hid;
363
+ while (p) {
364
+ q = p;
365
+ p = p->next;
366
+ free(q);
367
+ }
368
+ first_hid = last_hid = NULL;
369
+ }
370
+
371
+
372
+ static void hid_close(hid_t *hid)
373
+ {
374
+ if (!hid || !hid->open || !hid->ref) return;
375
+ IOHIDDeviceUnscheduleFromRunLoop(hid->ref, CFRunLoopGetCurrent( ), kCFRunLoopDefaultMode);
376
+ IOHIDDeviceClose(hid->ref, kIOHIDOptionsTypeNone);
377
+ hid->ref = NULL;
378
+ }
379
+
380
+ static void detach_callback(void *context, IOReturn r, void *hid_mgr, IOHIDDeviceRef dev)
381
+ {
382
+ hid_t *p;
383
+
384
+ printf("detach callback\n");
385
+ for (p = first_hid; p; p = p->next) {
386
+ if (p->ref == dev) {
387
+ p->open = 0;
388
+ CFRunLoopStop(CFRunLoopGetCurrent());
389
+ return;
390
+ }
391
+ }
392
+ }
393
+
394
+
395
+ static void attach_callback(void *context, IOReturn r, void *hid_mgr, IOHIDDeviceRef dev)
396
+ {
397
+ struct hid_struct *h;
398
+
399
+ printf("attach callback\n");
400
+ if (IOHIDDeviceOpen(dev, kIOHIDOptionsTypeNone) != kIOReturnSuccess) return;
401
+ h = (hid_t *)malloc(sizeof(hid_t));
402
+ if (!h) return;
403
+ memset(h, 0, sizeof(hid_t));
404
+ IOHIDDeviceScheduleWithRunLoop(dev, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
405
+ IOHIDDeviceRegisterInputReportCallback(dev, h->buffer, sizeof(h->buffer),
406
+ input_callback, h);
407
+ h->ref = dev;
408
+ h->open = 1;
409
+ add_hid(h);
410
+ }
411
+
412
+