rawhid 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+