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.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/CODE_OF_CONDUCT.md +7 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +21 -0
- data/README.md +43 -0
- data/Rakefile +4 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/ext/rawhid/Makefile +263 -0
- data/ext/rawhid/Makefile.bundled +59 -0
- data/ext/rawhid/extconf.h +4 -0
- data/ext/rawhid/extconf.rb +19 -0
- data/ext/rawhid/hid.h +6 -0
- data/ext/rawhid/hid_LINUX.c +336 -0
- data/ext/rawhid/hid_MACOSX.c +412 -0
- data/ext/rawhid/hid_WINDOWS.c +328 -0
- data/ext/rawhid/rawhid.c +87 -0
- data/ext/rawhid/rawhid_test.c +100 -0
- data/lib/rawhid/version.rb +3 -0
- data/lib/rawhid.rb +2 -0
- data/rawhid.gemspec +30 -0
- metadata +122 -0
@@ -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
|
+
|