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,336 @@
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
+ /* modified for Ruby by slact in 2017 */
32
+
33
+ #include <stdio.h>
34
+ #include <stdlib.h>
35
+ #include <stdint.h>
36
+ #include <usb.h>
37
+
38
+ #include "hid.h"
39
+
40
+
41
+ // On Linux there are several options to access HID devices.
42
+ //
43
+ // libusb 0.1 - the only way that works well on all distributions
44
+ // libusb 1.0 - someday will become standard on most distributions
45
+ // hidraw driver - relatively new, not supported on many distributions (yet)
46
+ // hiddev driver - old, ubuntu, fedora, others dropping support
47
+ // usbfs - low level usb API: http://www.kernel.org/doc/htmldocs/usb.html#usbfs
48
+ //
49
+ // This code uses libusb 0.1, which is well supported on all linux distributions
50
+ // and very stable and widely used by many programs. libusb 0.1 only provides a
51
+ // simple synchronous interface, basically the same as this code needs. However,
52
+ // if you want non-blocking I/O, libusb 0.1 simply does not provide that. There
53
+ // is also no kernel-level buffering, so performance is poor.
54
+ //
55
+ // UPDATE: As of November 2011, hidraw support seems to be working well in all
56
+ // major linux distributions. Embedded and "small" distros, used on ARM boards,
57
+ // routers and embedded hardware stil seem to omit the hidraw driver.
58
+ //
59
+ // The hidraw driver is a great solution. However, it has only been supported
60
+ // by relatively recent (in 2009) kernels. Here is a quick survey of the status
61
+ // of hidraw support in various distributions as of Sept 2009:
62
+ //
63
+ // Fedora 11: works, kernel 2.6.29.4
64
+ // Mandiva 2009.1: works, kernel 2.6.29.1
65
+ // Ubuntu 9.10-alpha6: works, kernel 2.6.31
66
+ // Ubuntu 9.04: sysfs attrs chain broken (hidraw root only), 2.6.28 kernel
67
+ // openSUSE 11.1: sysfs attrs chain broken (hidraw root only), 2.6.27.7 kernel
68
+ // Debian Live, Lenny 5.0.2: sysfs attrs chain broken (hidraw root only), 2.6.26
69
+ // SimplyMEPIS 8.0.10: sysfs attrs chain broken (hidraw root only), 2.6.27
70
+ // Mint 7: sysfs attrs chain broken (hidraw root only), 2.6.28 kernel
71
+ // Gentoo 2008.0-r1: sysfs attrs chain broken (hidraw root only), 2.6.24 kernel
72
+ // Centos 5: no hidraw or hiddev devices, 2.6.18 kernel
73
+ // Slitaz 2.0: no hidraw devices (has hiddev), 2.6.25.5 kernel
74
+ // Puppy 4.3: no hidraw devices (has hiddev), 2.6.30.5 kernel
75
+ // Damn Small 4.4.10: (would not boot)
76
+ // Gentoo 10.0-test20090926: (would not boot)
77
+ // PCLinuxOS 2009.2: (would not boot)
78
+ // Slackware: (no live cd available? www.slackware-live.org dead)
79
+
80
+
81
+
82
+ #define printf(...) // comment this out for lots of info
83
+
84
+
85
+ // a list of all opened HID devices, so the caller can
86
+ // simply refer to them by number
87
+ typedef struct hid_struct hid_t;
88
+ struct hid_struct {
89
+ usb_dev_handle *usb;
90
+ int open;
91
+ int iface;
92
+ int ep_in;
93
+ int ep_out;
94
+ struct hid_struct *prev;
95
+ struct hid_struct *next;
96
+ };
97
+
98
+
99
+ // private functions, not intended to be used from outside this file
100
+ static void hid_close(hid_t *hid);
101
+ static int hid_parse_item(uint32_t *val, uint8_t **data, const uint8_t *end);
102
+
103
+
104
+ // rawhid_recv - receive a packet
105
+ // Inputs:
106
+ // dev = pointer to device to receive from
107
+ // buf = buffer to receive packet
108
+ // len = buffer's size
109
+ // timeout = time to wait, in milliseconds
110
+ // Output:
111
+ // number of bytes received, or -1 on error
112
+ //
113
+ int rawhid_recv(void *dev, void *buf, int len, int timeout)
114
+ {
115
+ hid_t *hid = dev;
116
+ int r;
117
+
118
+ if (!hid || !hid->open) return -1;
119
+ r = usb_interrupt_read(hid->usb, hid->ep_in, buf, len, timeout);
120
+ if (r >= 0) return r;
121
+ if (r == -110) return 0; // timeout
122
+ return -1;
123
+ }
124
+
125
+ // rawhid_send - send a packet
126
+ // Inputs:
127
+ // dev = device to transmit to
128
+ // buf = buffer containing packet to send
129
+ // len = number of bytes to transmit
130
+ // timeout = time to wait, in milliseconds
131
+ // Output:
132
+ // number of bytes sent, or -1 on error
133
+ //
134
+ int rawhid_send(void *dev, void *buf, int len, int timeout)
135
+ {
136
+ hid_t *hid = dev;
137
+
138
+ if (!hid || !hid->open) return -1;
139
+ if (hid->ep_out) {
140
+ return usb_interrupt_write(hid->usb, hid->ep_out, buf, len, timeout);
141
+ } else {
142
+ return usb_control_msg(hid->usb, 0x21, 9, 0, hid->iface, buf, len, timeout);
143
+ }
144
+ }
145
+
146
+ // rawhid_open - open 1 or more devices
147
+ //
148
+ // Inputs:
149
+ // max = maximum number of devices to open
150
+ // dev = pointer to array of pointers to devices (at least of size 'max')
151
+ // vid = Vendor ID, or -1 if any
152
+ // pid = Product ID, or -1 if any
153
+ // usage_page = top level usage page, or -1 if any
154
+ // usage = top level usage number, or -1 if any
155
+ // Output:
156
+ // actual number of devices opened
157
+ //
158
+ int rawhid_open(int max, void **devs, int vid, int pid, int usage_page, int usage)
159
+ {
160
+ struct usb_bus *bus;
161
+ struct usb_device *dev;
162
+ struct usb_interface *iface;
163
+ struct usb_interface_descriptor *desc;
164
+ struct usb_endpoint_descriptor *ep;
165
+ usb_dev_handle *u;
166
+ uint8_t buf[1024], *p;
167
+ int i, n, len, tag, ep_in, ep_out, count=0, claimed;
168
+ uint32_t val=0, parsed_usage, parsed_usage_page;
169
+ hid_t *hid;
170
+
171
+ printf("rawhid_open, max=%d\n", max);
172
+ if (max < 1) return 0;
173
+ usb_init();
174
+ usb_find_busses();
175
+ usb_find_devices();
176
+ for (bus = usb_get_busses(); bus; bus = bus->next) {
177
+ for (dev = bus->devices; dev; dev = dev->next) {
178
+ if (vid > 0 && dev->descriptor.idVendor != vid) continue;
179
+ if (pid > 0 && dev->descriptor.idProduct != pid) continue;
180
+ if (!dev->config) continue;
181
+ if (dev->config->bNumInterfaces < 1) continue;
182
+ printf("device: vid=%04X, pic=%04X, with %d iface\n",
183
+ dev->descriptor.idVendor,
184
+ dev->descriptor.idProduct,
185
+ dev->config->bNumInterfaces);
186
+ iface = dev->config->interface;
187
+ u = NULL;
188
+ claimed = 0;
189
+ for (i=0; i<dev->config->bNumInterfaces && iface; i++, iface++) {
190
+ desc = iface->altsetting;
191
+ if (!desc) continue;
192
+ printf(" type %d, %d, %d\n", desc->bInterfaceClass,
193
+ desc->bInterfaceSubClass, desc->bInterfaceProtocol);
194
+ if (desc->bInterfaceClass != 3) continue;
195
+ if (desc->bInterfaceSubClass != 0) continue;
196
+ if (desc->bInterfaceProtocol != 0) continue;
197
+ ep = desc->endpoint;
198
+ ep_in = ep_out = 0;
199
+ for (n = 0; n < desc->bNumEndpoints; n++, ep++) {
200
+ if (ep->bEndpointAddress & 0x80) {
201
+ if (!ep_in) ep_in = ep->bEndpointAddress & 0x7F;
202
+ printf(" IN endpoint %d\n", ep_in);
203
+ } else {
204
+ if (!ep_out) ep_out = ep->bEndpointAddress;
205
+ printf(" OUT endpoint %d\n", ep_out);
206
+ }
207
+ }
208
+ if (!ep_in) continue;
209
+ if (!u) {
210
+ u = usb_open(dev);
211
+ if (!u) {
212
+ printf(" unable to open device\n");
213
+ break;
214
+ }
215
+ }
216
+ printf(" hid interface (generic)\n");
217
+ if (usb_get_driver_np(u, i, (char *)buf, sizeof(buf)) >= 0) {
218
+ printf(" in use by driver \"%s\"\n", buf);
219
+ if (usb_detach_kernel_driver_np(u, i) < 0) {
220
+ printf(" unable to detach from kernel\n");
221
+ continue;
222
+ }
223
+ }
224
+ if (usb_claim_interface(u, i) < 0) {
225
+ printf(" unable claim interface %d\n", i);
226
+ continue;
227
+ }
228
+ len = usb_control_msg(u, 0x81, 6, 0x2200, i, (char *)buf, sizeof(buf), 250);
229
+ printf(" descriptor, len=%d\n", len);
230
+ if (len < 2) {
231
+ usb_release_interface(u, i);
232
+ continue;
233
+ }
234
+ p = buf;
235
+ parsed_usage_page = parsed_usage = 0;
236
+ while ((tag = hid_parse_item(&val, &p, buf + len)) >= 0) {
237
+ printf(" tag: %X, val %X\n", tag, val);
238
+ if (tag == 4) parsed_usage_page = val;
239
+ if (tag == 8) parsed_usage = val;
240
+ if (parsed_usage_page && parsed_usage) break;
241
+ }
242
+ if ((!parsed_usage_page) || (!parsed_usage) ||
243
+ (usage_page > 0 && parsed_usage_page != usage_page) ||
244
+ (usage > 0 && parsed_usage != usage)) {
245
+ usb_release_interface(u, i);
246
+ continue;
247
+ }
248
+ hid = (struct hid_struct *)malloc(sizeof(struct hid_struct));
249
+ if (!hid) {
250
+ usb_release_interface(u, i);
251
+ continue;
252
+ }
253
+ hid->usb = u;
254
+ hid->iface = i;
255
+ hid->ep_in = ep_in;
256
+ hid->ep_out = ep_out;
257
+ hid->open = 1;
258
+
259
+ *devs = hid;
260
+ devs++;
261
+
262
+ claimed++;
263
+ count++;
264
+ if (count >= max) return count;
265
+ }
266
+ if (u && !claimed) usb_close(u);
267
+ }
268
+ }
269
+ return count;
270
+ }
271
+
272
+
273
+ // rawhid_close - close a device, free associated memory
274
+ //
275
+ // Inputs:
276
+ // dev = pointer to device to close
277
+ // Output
278
+ // (nothing)
279
+ //
280
+ void rawhid_close(void *dev)
281
+ {
282
+ hid_t *hid = dev;
283
+
284
+ if (!hid || !hid->open) return;
285
+ free(dev);
286
+ }
287
+
288
+ // Chuck Robey wrote a real HID report parser
289
+ // (chuckr@telenix.org) chuckr@chuckr.org
290
+ // http://people.freebsd.org/~chuckr/code/python/uhidParser-0.2.tbz
291
+ // this tiny thing only needs to extract the top-level usage page
292
+ // and usage, and even then is may not be truly correct, but it does
293
+ // work with the Teensy Raw HID example.
294
+ static int hid_parse_item(uint32_t *val, uint8_t **data, const uint8_t *end)
295
+ {
296
+ const uint8_t *p = *data;
297
+ uint8_t tag;
298
+ int table[4] = {0, 1, 2, 4};
299
+ int len;
300
+
301
+ if (p >= end) return -1;
302
+ if (p[0] == 0xFE) {
303
+ // long item, HID 1.11, 6.2.2.3, page 27
304
+ if (p + 5 >= end || p + p[1] >= end) return -1;
305
+ tag = p[2];
306
+ *val = 0;
307
+ len = p[1] + 5;
308
+ } else {
309
+ // short item, HID 1.11, 6.2.2.2, page 26
310
+ tag = p[0] & 0xFC;
311
+ len = table[p[0] & 0x03];
312
+ if (p + len + 1 >= end) return -1;
313
+ switch (p[0] & 0x03) {
314
+ case 3: *val = p[1] | (p[2] << 8) | (p[3] << 16) | (p[4] << 24); break;
315
+ case 2: *val = p[1] | (p[2] << 8); break;
316
+ case 1: *val = p[1]; break;
317
+ case 0: *val = 0; break;
318
+ }
319
+ }
320
+ *data += len + 1;
321
+ return tag;
322
+ }
323
+
324
+
325
+ static void hid_close(hid_t *hid)
326
+ {
327
+ hid_t *p;
328
+ int others=0;
329
+
330
+ usb_release_interface(hid->usb, hid->iface);
331
+ usb_close(hid->usb);
332
+ hid->usb = NULL;
333
+ }
334
+
335
+
336
+