| @@ -1,528 +1,528 @@ | | | @@ -1,528 +1,528 @@ |
1 | /* $NetBSD: hid.c,v 1.42 2016/01/10 17:44:48 jakllsch Exp $ */ | | 1 | /* $NetBSD: hid.c,v 1.43 2016/01/23 17:02:25 riastradh Exp $ */ |
2 | /* $FreeBSD: src/sys/dev/usb/hid.c,v 1.11 1999/11/17 22:33:39 n_hibma Exp $ */ | | 2 | /* $FreeBSD: src/sys/dev/usb/hid.c,v 1.11 1999/11/17 22:33:39 n_hibma Exp $ */ |
3 | | | 3 | |
4 | /* | | 4 | /* |
5 | * Copyright (c) 1998 The NetBSD Foundation, Inc. | | 5 | * Copyright (c) 1998 The NetBSD Foundation, Inc. |
6 | * All rights reserved. | | 6 | * All rights reserved. |
7 | * | | 7 | * |
8 | * This code is derived from software contributed to The NetBSD Foundation | | 8 | * This code is derived from software contributed to The NetBSD Foundation |
9 | * by Lennart Augustsson (lennart@augustsson.net) at | | 9 | * by Lennart Augustsson (lennart@augustsson.net) at |
10 | * Carlstedt Research & Technology. | | 10 | * Carlstedt Research & Technology. |
11 | * | | 11 | * |
12 | * Redistribution and use in source and binary forms, with or without | | 12 | * Redistribution and use in source and binary forms, with or without |
13 | * modification, are permitted provided that the following conditions | | 13 | * modification, are permitted provided that the following conditions |
14 | * are met: | | 14 | * are met: |
15 | * 1. Redistributions of source code must retain the above copyright | | 15 | * 1. Redistributions of source code must retain the above copyright |
16 | * notice, this list of conditions and the following disclaimer. | | 16 | * notice, this list of conditions and the following disclaimer. |
17 | * 2. Redistributions in binary form must reproduce the above copyright | | 17 | * 2. Redistributions in binary form must reproduce the above copyright |
18 | * notice, this list of conditions and the following disclaimer in the | | 18 | * notice, this list of conditions and the following disclaimer in the |
19 | * documentation and/or other materials provided with the distribution. | | 19 | * documentation and/or other materials provided with the distribution. |
20 | * | | 20 | * |
21 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS | | 21 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
22 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED | | 22 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
23 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | | 23 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
24 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS | | 24 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
25 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | | 25 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | | 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | | 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | | 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | | 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | | 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
31 | * POSSIBILITY OF SUCH DAMAGE. | | 31 | * POSSIBILITY OF SUCH DAMAGE. |
32 | */ | | 32 | */ |
33 | | | 33 | |
34 | #include <sys/cdefs.h> | | 34 | #include <sys/cdefs.h> |
35 | __KERNEL_RCSID(0, "$NetBSD: hid.c,v 1.42 2016/01/10 17:44:48 jakllsch Exp $"); | | 35 | __KERNEL_RCSID(0, "$NetBSD: hid.c,v 1.43 2016/01/23 17:02:25 riastradh Exp $"); |
36 | | | 36 | |
37 | #include <sys/param.h> | | 37 | #include <sys/param.h> |
38 | #include <sys/systm.h> | | 38 | #include <sys/systm.h> |
39 | #include <sys/kernel.h> | | 39 | #include <sys/kernel.h> |
40 | #include <sys/malloc.h> | | 40 | #include <sys/malloc.h> |
41 | | | 41 | |
42 | #include <dev/usb/usb.h> | | 42 | #include <dev/usb/usb.h> |
43 | #include <dev/usb/usbhid.h> | | 43 | #include <dev/usb/usbhid.h> |
44 | | | 44 | |
45 | #include <dev/usb/hid.h> | | 45 | #include <dev/usb/hid.h> |
46 | | | 46 | |
47 | #ifdef UHIDEV_DEBUG | | 47 | #ifdef UHIDEV_DEBUG |
48 | #define DPRINTF(x) if (uhidevdebug) printf x | | 48 | #define DPRINTF(x) if (uhidevdebug) printf x |
49 | #define DPRINTFN(n,x) if (uhidevdebug>(n)) printf x | | 49 | #define DPRINTFN(n,x) if (uhidevdebug>(n)) printf x |
50 | extern int uhidevdebug; | | 50 | extern int uhidevdebug; |
51 | #else | | 51 | #else |
52 | #define DPRINTF(x) | | 52 | #define DPRINTF(x) |
53 | #define DPRINTFN(n,x) | | 53 | #define DPRINTFN(n,x) |
54 | #endif | | 54 | #endif |
55 | | | 55 | |
56 | Static void hid_clear_local(struct hid_item *); | | 56 | Static void hid_clear_local(struct hid_item *); |
57 | | | 57 | |
58 | #define MAXUSAGE 256 | | 58 | #define MAXUSAGE 256 |
59 | struct hid_data { | | 59 | struct hid_data { |
60 | const u_char *start; | | 60 | const u_char *start; |
61 | const u_char *end; | | 61 | const u_char *end; |
62 | const u_char *p; | | 62 | const u_char *p; |
63 | struct hid_item cur; | | 63 | struct hid_item cur; |
64 | int32_t usages[MAXUSAGE]; | | 64 | int32_t usages[MAXUSAGE]; |
65 | int nu; | | 65 | int nu; |
66 | int minset; | | 66 | int minset; |
67 | int multi; | | 67 | int multi; |
68 | int multimax; | | 68 | int multimax; |
69 | enum hid_kind kind; | | 69 | enum hid_kind kind; |
70 | }; | | 70 | }; |
71 | | | 71 | |
72 | Static void | | 72 | Static void |
73 | hid_clear_local(struct hid_item *c) | | 73 | hid_clear_local(struct hid_item *c) |
74 | { | | 74 | { |
75 | | | 75 | |
76 | DPRINTFN(5,("hid_clear_local\n")); | | 76 | DPRINTFN(5,("hid_clear_local\n")); |
77 | c->usage = 0; | | 77 | c->usage = 0; |
78 | c->usage_minimum = 0; | | 78 | c->usage_minimum = 0; |
79 | c->usage_maximum = 0; | | 79 | c->usage_maximum = 0; |
80 | c->designator_index = 0; | | 80 | c->designator_index = 0; |
81 | c->designator_minimum = 0; | | 81 | c->designator_minimum = 0; |
82 | c->designator_maximum = 0; | | 82 | c->designator_maximum = 0; |
83 | c->string_index = 0; | | 83 | c->string_index = 0; |
84 | c->string_minimum = 0; | | 84 | c->string_minimum = 0; |
85 | c->string_maximum = 0; | | 85 | c->string_maximum = 0; |
86 | c->set_delimiter = 0; | | 86 | c->set_delimiter = 0; |
87 | } | | 87 | } |
88 | | | 88 | |
89 | struct hid_data * | | 89 | struct hid_data * |
90 | hid_start_parse(const void *d, int len, enum hid_kind kind) | | 90 | hid_start_parse(const void *d, int len, enum hid_kind kind) |
91 | { | | 91 | { |
92 | struct hid_data *s; | | 92 | struct hid_data *s; |
93 | | | 93 | |
94 | s = malloc(sizeof *s, M_TEMP, M_WAITOK|M_ZERO); | | 94 | s = malloc(sizeof *s, M_TEMP, M_WAITOK|M_ZERO); |
95 | s->start = s->p = d; | | 95 | s->start = s->p = d; |
96 | s->end = (const char *)d + len; | | 96 | s->end = (const char *)d + len; |
97 | s->kind = kind; | | 97 | s->kind = kind; |
98 | return (s); | | 98 | return (s); |
99 | } | | 99 | } |
100 | | | 100 | |
101 | void | | 101 | void |
102 | hid_end_parse(struct hid_data *s) | | 102 | hid_end_parse(struct hid_data *s) |
103 | { | | 103 | { |
104 | | | 104 | |
105 | while (s->cur.next != NULL) { | | 105 | while (s->cur.next != NULL) { |
106 | struct hid_item *hi = s->cur.next->next; | | 106 | struct hid_item *hi = s->cur.next->next; |
107 | free(s->cur.next, M_TEMP); | | 107 | free(s->cur.next, M_TEMP); |
108 | s->cur.next = hi; | | 108 | s->cur.next = hi; |
109 | } | | 109 | } |
110 | free(s, M_TEMP); | | 110 | free(s, M_TEMP); |
111 | } | | 111 | } |
112 | | | 112 | |
113 | int | | 113 | int |
114 | hid_get_item(struct hid_data *s, struct hid_item *h) | | 114 | hid_get_item(struct hid_data *s, struct hid_item *h) |
115 | { | | 115 | { |
116 | struct hid_item *c = &s->cur; | | 116 | struct hid_item *c = &s->cur; |
117 | unsigned int bTag, bType, bSize; | | 117 | unsigned int bTag, bType, bSize; |
118 | u_int32_t oldpos; | | 118 | u_int32_t oldpos; |
119 | const u_char *data; | | 119 | const u_char *data; |
120 | int32_t dval; | | 120 | int32_t dval; |
121 | const u_char *p; | | 121 | const u_char *p; |
122 | struct hid_item *hi; | | 122 | struct hid_item *hi; |
123 | int i; | | 123 | int i; |
124 | enum hid_kind retkind; | | 124 | enum hid_kind retkind; |
125 | | | 125 | |
126 | top: | | 126 | top: |
127 | DPRINTFN(5,("hid_get_item: multi=%d multimax=%d\n", | | 127 | DPRINTFN(5,("hid_get_item: multi=%d multimax=%d\n", |
128 | s->multi, s->multimax)); | | 128 | s->multi, s->multimax)); |
129 | if (s->multimax != 0) { | | 129 | if (s->multimax != 0) { |
130 | if (s->multi < s->multimax) { | | 130 | if (s->multi < s->multimax) { |
131 | c->usage = s->usages[min(s->multi, s->nu-1)]; | | 131 | c->usage = s->usages[min(s->multi, s->nu-1)]; |
132 | s->multi++; | | 132 | s->multi++; |
133 | *h = *c; | | 133 | *h = *c; |
134 | c->loc.pos += c->loc.size; | | 134 | c->loc.pos += c->loc.size; |
135 | h->next = NULL; | | 135 | h->next = NULL; |
136 | DPRINTFN(5,("return multi\n")); | | 136 | DPRINTFN(5,("return multi\n")); |
137 | return (1); | | 137 | return (1); |
138 | } else { | | 138 | } else { |
139 | c->loc.count = s->multimax; | | 139 | c->loc.count = s->multimax; |
140 | s->multimax = 0; | | 140 | s->multimax = 0; |
141 | s->nu = 0; | | 141 | s->nu = 0; |
142 | hid_clear_local(c); | | 142 | hid_clear_local(c); |
143 | } | | 143 | } |
144 | } | | 144 | } |
145 | for (;;) { | | 145 | for (;;) { |
146 | p = s->p; | | 146 | p = s->p; |
147 | if (p >= s->end) | | 147 | if (p >= s->end) |
148 | return (0); | | 148 | return (0); |
149 | | | 149 | |
150 | bSize = *p++; | | 150 | bSize = *p++; |
151 | if (bSize == 0xfe) { | | 151 | if (bSize == 0xfe) { |
152 | /* long item */ | | 152 | /* long item */ |
153 | bSize = *p++; | | 153 | bSize = *p++; |
154 | bSize |= *p++ << 8; | | 154 | bSize |= *p++ << 8; |
155 | bTag = *p++; | | 155 | bTag = *p++; |
156 | data = p; | | 156 | data = p; |
157 | p += bSize; | | 157 | p += bSize; |
158 | bType = 0xff; /* XXX what should it be */ | | 158 | bType = 0xff; /* XXX what should it be */ |
159 | } else { | | 159 | } else { |
160 | /* short item */ | | 160 | /* short item */ |
161 | bTag = bSize >> 4; | | 161 | bTag = bSize >> 4; |
162 | bType = (bSize >> 2) & 3; | | 162 | bType = (bSize >> 2) & 3; |
163 | bSize &= 3; | | 163 | bSize &= 3; |
164 | if (bSize == 3) bSize = 4; | | 164 | if (bSize == 3) bSize = 4; |
165 | data = p; | | 165 | data = p; |
166 | p += bSize; | | 166 | p += bSize; |
167 | } | | 167 | } |
168 | s->p = p; | | 168 | s->p = p; |
169 | switch(bSize) { | | 169 | switch(bSize) { |
170 | case 0: | | 170 | case 0: |
171 | dval = 0; | | 171 | dval = 0; |
172 | break; | | 172 | break; |
173 | case 1: | | 173 | case 1: |
174 | dval = (int8_t)*data++; | | 174 | dval = (int8_t)*data++; |
175 | break; | | 175 | break; |
176 | case 2: | | 176 | case 2: |
177 | dval = *data++; | | 177 | dval = *data++; |
178 | dval |= *data++ << 8; | | 178 | dval |= *data++ << 8; |
179 | dval = (int16_t)dval; | | 179 | dval = (int16_t)dval; |
180 | break; | | 180 | break; |
181 | case 4: | | 181 | case 4: |
182 | dval = *data++; | | 182 | dval = *data++; |
183 | dval |= *data++ << 8; | | 183 | dval |= *data++ << 8; |
184 | dval |= *data++ << 16; | | 184 | dval |= *data++ << 16; |
185 | dval |= *data++ << 24; | | 185 | dval |= *data++ << 24; |
186 | dval = (int32_t)dval; | | 186 | dval = (int32_t)dval; |
187 | break; | | 187 | break; |
188 | default: | | 188 | default: |
189 | printf("BAD LENGTH %d\n", bSize); | | 189 | printf("BAD LENGTH %d\n", bSize); |
190 | continue; | | 190 | continue; |
191 | } | | 191 | } |
192 | | | 192 | |
193 | DPRINTFN(5,("hid_get_item: bType=%d bTag=%d dval=%d\n", | | 193 | DPRINTFN(5,("hid_get_item: bType=%d bTag=%d dval=%d\n", |
194 | bType, bTag, dval)); | | 194 | bType, bTag, dval)); |
195 | switch (bType) { | | 195 | switch (bType) { |
196 | case 0: /* Main */ | | 196 | case 0: /* Main */ |
197 | switch (bTag) { | | 197 | switch (bTag) { |
198 | case 8: /* Input */ | | 198 | case 8: /* Input */ |
199 | retkind = hid_input; | | 199 | retkind = hid_input; |
200 | ret: | | 200 | ret: |
201 | if (s->kind != retkind) { | | 201 | if (s->kind != retkind) { |
202 | s->minset = 0; | | 202 | s->minset = 0; |
203 | s->nu = 0; | | 203 | s->nu = 0; |
204 | hid_clear_local(c); | | 204 | hid_clear_local(c); |
205 | continue; | | 205 | continue; |
206 | } | | 206 | } |
207 | c->kind = retkind; | | 207 | c->kind = retkind; |
208 | c->flags = dval; | | 208 | c->flags = dval; |
209 | if (c->flags & HIO_VARIABLE) { | | 209 | if (c->flags & HIO_VARIABLE) { |
210 | s->multimax = c->loc.count; | | 210 | s->multimax = c->loc.count; |
211 | s->multi = 0; | | 211 | s->multi = 0; |
212 | c->loc.count = 1; | | 212 | c->loc.count = 1; |
213 | if (s->minset) { | | 213 | if (s->minset) { |
214 | for (i = c->usage_minimum; | | 214 | for (i = c->usage_minimum; |
215 | i <= c->usage_maximum; | | 215 | i <= c->usage_maximum; |
216 | i++) { | | 216 | i++) { |
217 | s->usages[s->nu] = i; | | 217 | s->usages[s->nu] = i; |
218 | if (s->nu < MAXUSAGE-1) | | 218 | if (s->nu < MAXUSAGE-1) |
219 | s->nu++; | | 219 | s->nu++; |
220 | } | | 220 | } |
221 | s->minset = 0; | | 221 | s->minset = 0; |
222 | } | | 222 | } |
223 | goto top; | | 223 | goto top; |
224 | } else { | | 224 | } else { |
225 | if (s->minset) | | 225 | if (s->minset) |
226 | c->usage = c->usage_minimum; | | 226 | c->usage = c->usage_minimum; |
227 | *h = *c; | | 227 | *h = *c; |
228 | h->next = NULL; | | 228 | h->next = NULL; |
229 | c->loc.pos += | | 229 | c->loc.pos += |
230 | c->loc.size * c->loc.count; | | 230 | c->loc.size * c->loc.count; |
231 | s->minset = 0; | | 231 | s->minset = 0; |
232 | s->nu = 0; | | 232 | s->nu = 0; |
233 | hid_clear_local(c); | | 233 | hid_clear_local(c); |
234 | return (1); | | 234 | return (1); |
235 | } | | 235 | } |
236 | case 9: /* Output */ | | 236 | case 9: /* Output */ |
237 | retkind = hid_output; | | 237 | retkind = hid_output; |
238 | goto ret; | | 238 | goto ret; |
239 | case 10: /* Collection */ | | 239 | case 10: /* Collection */ |
240 | c->kind = hid_collection; | | 240 | c->kind = hid_collection; |
241 | c->collection = dval; | | 241 | c->collection = dval; |
242 | c->collevel++; | | 242 | c->collevel++; |
243 | *h = *c; | | 243 | *h = *c; |
244 | hid_clear_local(c); | | 244 | hid_clear_local(c); |
245 | s->nu = 0; | | 245 | s->nu = 0; |
246 | return (1); | | 246 | return (1); |
247 | case 11: /* Feature */ | | 247 | case 11: /* Feature */ |
248 | retkind = hid_feature; | | 248 | retkind = hid_feature; |
249 | goto ret; | | 249 | goto ret; |
250 | case 12: /* End collection */ | | 250 | case 12: /* End collection */ |
251 | c->kind = hid_endcollection; | | 251 | c->kind = hid_endcollection; |
252 | c->collevel--; | | 252 | c->collevel--; |
253 | *h = *c; | | 253 | *h = *c; |
254 | s->nu = 0; | | 254 | s->nu = 0; |
255 | return (1); | | 255 | return (1); |
256 | default: | | 256 | default: |
257 | printf("Main bTag=%d\n", bTag); | | 257 | printf("Main bTag=%d\n", bTag); |
258 | break; | | 258 | break; |
259 | } | | 259 | } |
260 | break; | | 260 | break; |
261 | case 1: /* Global */ | | 261 | case 1: /* Global */ |
262 | switch (bTag) { | | 262 | switch (bTag) { |
263 | case 0: | | 263 | case 0: |
264 | c->_usage_page = dval << 16; | | 264 | c->_usage_page = dval << 16; |
265 | break; | | 265 | break; |
266 | case 1: | | 266 | case 1: |
267 | c->logical_minimum = dval; | | 267 | c->logical_minimum = dval; |
268 | break; | | 268 | break; |
269 | case 2: | | 269 | case 2: |
270 | c->logical_maximum = dval; | | 270 | c->logical_maximum = dval; |
271 | break; | | 271 | break; |
272 | case 3: | | 272 | case 3: |
273 | c->physical_minimum = dval; | | 273 | c->physical_minimum = dval; |
274 | break; | | 274 | break; |
275 | case 4: | | 275 | case 4: |
276 | c->physical_maximum = dval; | | 276 | c->physical_maximum = dval; |
277 | break; | | 277 | break; |
278 | case 5: | | 278 | case 5: |
279 | c->unit_exponent = dval; | | 279 | c->unit_exponent = dval; |
280 | break; | | 280 | break; |
281 | case 6: | | 281 | case 6: |
282 | c->unit = dval; | | 282 | c->unit = dval; |
283 | break; | | 283 | break; |
284 | case 7: | | 284 | case 7: |
285 | c->loc.size = dval; | | 285 | c->loc.size = dval; |
286 | break; | | 286 | break; |
287 | case 8: | | 287 | case 8: |
288 | c->report_ID = dval; | | 288 | c->report_ID = dval; |
289 | c->loc.pos = 0; | | 289 | c->loc.pos = 0; |
290 | break; | | 290 | break; |
291 | case 9: | | 291 | case 9: |
292 | c->loc.count = dval; | | 292 | c->loc.count = dval; |
293 | break; | | 293 | break; |
294 | case 10: /* Push */ | | 294 | case 10: /* Push */ |
295 | hi = malloc(sizeof *hi, M_TEMP, M_WAITOK); | | 295 | hi = malloc(sizeof *hi, M_TEMP, M_WAITOK); |
296 | *hi = *c; | | 296 | *hi = *c; |
297 | c->next = hi; | | 297 | c->next = hi; |
298 | break; | | 298 | break; |
299 | case 11: /* Pop */ | | 299 | case 11: /* Pop */ |
300 | hi = c->next; | | 300 | hi = c->next; |
301 | if (hi == NULL) | | 301 | if (hi == NULL) |
302 | break; | | 302 | break; |
303 | oldpos = c->loc.pos; | | 303 | oldpos = c->loc.pos; |
304 | *c = *hi; | | 304 | *c = *hi; |
305 | c->loc.pos = oldpos; | | 305 | c->loc.pos = oldpos; |
306 | free(hi, M_TEMP); | | 306 | free(hi, M_TEMP); |
307 | break; | | 307 | break; |
308 | default: | | 308 | default: |
309 | printf("Global bTag=%d\n", bTag); | | 309 | printf("Global bTag=%d\n", bTag); |
310 | break; | | 310 | break; |
311 | } | | 311 | } |
312 | break; | | 312 | break; |
313 | case 2: /* Local */ | | 313 | case 2: /* Local */ |
314 | switch (bTag) { | | 314 | switch (bTag) { |
315 | case 0: | | 315 | case 0: |
316 | if (bSize == 1) | | 316 | if (bSize == 1) |
317 | dval = c->_usage_page | (dval&0xff); | | 317 | dval = c->_usage_page | (dval&0xff); |
318 | else if (bSize == 2) | | 318 | else if (bSize == 2) |
319 | dval = c->_usage_page | (dval&0xffff); | | 319 | dval = c->_usage_page | (dval&0xffff); |
320 | c->usage = dval; | | 320 | c->usage = dval; |
321 | if (s->nu < MAXUSAGE) | | 321 | if (s->nu < MAXUSAGE) |
322 | s->usages[s->nu++] = dval; | | 322 | s->usages[s->nu++] = dval; |
323 | /* else XXX */ | | 323 | /* else XXX */ |
324 | break; | | 324 | break; |
325 | case 1: | | 325 | case 1: |
326 | s->minset = 1; | | 326 | s->minset = 1; |
327 | if (bSize == 1) | | 327 | if (bSize == 1) |
328 | dval = c->_usage_page | (dval&0xff); | | 328 | dval = c->_usage_page | (dval&0xff); |
329 | else if (bSize == 2) | | 329 | else if (bSize == 2) |
330 | dval = c->_usage_page | (dval&0xffff); | | 330 | dval = c->_usage_page | (dval&0xffff); |
331 | c->usage_minimum = dval; | | 331 | c->usage_minimum = dval; |
332 | break; | | 332 | break; |
333 | case 2: | | 333 | case 2: |
334 | if (bSize == 1) | | 334 | if (bSize == 1) |
335 | dval = c->_usage_page | (dval&0xff); | | 335 | dval = c->_usage_page | (dval&0xff); |
336 | else if (bSize == 2) | | 336 | else if (bSize == 2) |
337 | dval = c->_usage_page | (dval&0xffff); | | 337 | dval = c->_usage_page | (dval&0xffff); |
338 | c->usage_maximum = dval; | | 338 | c->usage_maximum = dval; |
339 | break; | | 339 | break; |
340 | case 3: | | 340 | case 3: |
341 | c->designator_index = dval; | | 341 | c->designator_index = dval; |
342 | break; | | 342 | break; |
343 | case 4: | | 343 | case 4: |
344 | c->designator_minimum = dval; | | 344 | c->designator_minimum = dval; |
345 | break; | | 345 | break; |
346 | case 5: | | 346 | case 5: |
347 | c->designator_maximum = dval; | | 347 | c->designator_maximum = dval; |
348 | break; | | 348 | break; |
349 | case 7: | | 349 | case 7: |
350 | c->string_index = dval; | | 350 | c->string_index = dval; |
351 | break; | | 351 | break; |
352 | case 8: | | 352 | case 8: |
353 | c->string_minimum = dval; | | 353 | c->string_minimum = dval; |
354 | break; | | 354 | break; |
355 | case 9: | | 355 | case 9: |
356 | c->string_maximum = dval; | | 356 | c->string_maximum = dval; |
357 | break; | | 357 | break; |
358 | case 10: | | 358 | case 10: |
359 | c->set_delimiter = dval; | | 359 | c->set_delimiter = dval; |
360 | break; | | 360 | break; |
361 | default: | | 361 | default: |
362 | printf("Local bTag=%d\n", bTag); | | 362 | printf("Local bTag=%d\n", bTag); |
363 | break; | | 363 | break; |
364 | } | | 364 | } |
365 | break; | | 365 | break; |
366 | default: | | 366 | default: |
367 | printf("default bType=%d\n", bType); | | 367 | printf("default bType=%d\n", bType); |
368 | break; | | 368 | break; |
369 | } | | 369 | } |
370 | } | | 370 | } |
371 | } | | 371 | } |
372 | | | 372 | |
373 | int | | 373 | int |
374 | hid_report_size(const void *buf, int len, enum hid_kind k, u_int8_t id) | | 374 | hid_report_size(const void *buf, int len, enum hid_kind k, u_int8_t id) |
375 | { | | 375 | { |
376 | struct hid_data *d; | | 376 | struct hid_data *d; |
377 | struct hid_item h; | | 377 | struct hid_item h; |
378 | int lo, hi; | | 378 | int lo, hi; |
379 | | | 379 | |
380 | h.report_ID = 0; | | 380 | h.report_ID = 0; |
381 | lo = hi = -1; | | 381 | lo = hi = -1; |
382 | DPRINTFN(2,("hid_report_size: kind=%d id=%d\n", k, id)); | | 382 | DPRINTFN(2,("hid_report_size: kind=%d id=%d\n", k, id)); |
383 | for (d = hid_start_parse(buf, len, k); hid_get_item(d, &h); ) { | | 383 | for (d = hid_start_parse(buf, len, k); hid_get_item(d, &h); ) { |
384 | DPRINTFN(2,("hid_report_size: item kind=%d id=%d pos=%d " | | 384 | DPRINTFN(2,("hid_report_size: item kind=%d id=%d pos=%d " |
385 | "size=%d count=%d\n", | | 385 | "size=%d count=%d\n", |
386 | h.kind, h.report_ID, h.loc.pos, h.loc.size, | | 386 | h.kind, h.report_ID, h.loc.pos, h.loc.size, |
387 | h.loc.count)); | | 387 | h.loc.count)); |
388 | if (h.report_ID == id && h.kind == k) { | | 388 | if (h.report_ID == id && h.kind == k) { |
389 | if (lo < 0) { | | 389 | if (lo < 0) { |
390 | lo = h.loc.pos; | | 390 | lo = h.loc.pos; |
391 | #ifdef DIAGNOSTIC | | 391 | #ifdef DIAGNOSTIC |
392 | if (lo != 0) { | | 392 | if (lo != 0) { |
393 | printf("hid_report_size: lo != 0\n"); | | 393 | printf("hid_report_size: lo != 0\n"); |
394 | } | | 394 | } |
395 | #endif | | 395 | #endif |
396 | } | | 396 | } |
397 | hi = h.loc.pos + h.loc.size * h.loc.count; | | 397 | hi = h.loc.pos + h.loc.size * h.loc.count; |
398 | DPRINTFN(2,("hid_report_size: lo=%d hi=%d\n", lo, hi)); | | 398 | DPRINTFN(2,("hid_report_size: lo=%d hi=%d\n", lo, hi)); |
399 | } | | 399 | } |
400 | } | | 400 | } |
401 | hid_end_parse(d); | | 401 | hid_end_parse(d); |
402 | return ((hi - lo + 7) / 8); | | 402 | return ((hi - lo + 7) / 8); |
403 | } | | 403 | } |
404 | | | 404 | |
405 | int | | 405 | int |
406 | hid_locate(const void *desc, int size, u_int32_t u, u_int8_t id, enum hid_kind k, | | 406 | hid_locate(const void *desc, int size, u_int32_t u, u_int8_t id, enum hid_kind k, |
407 | struct hid_location *loc, u_int32_t *flags) | | 407 | struct hid_location *loc, u_int32_t *flags) |
408 | { | | 408 | { |
409 | struct hid_data *d; | | 409 | struct hid_data *d; |
410 | struct hid_item h; | | 410 | struct hid_item h; |
411 | | | 411 | |
412 | h.report_ID = 0; | | 412 | h.report_ID = 0; |
413 | DPRINTFN(5,("hid_locate: enter usage=0x%x kind=%d id=%d\n", u, k, id)); | | 413 | DPRINTFN(5,("hid_locate: enter usage=0x%x kind=%d id=%d\n", u, k, id)); |
414 | for (d = hid_start_parse(desc, size, k); hid_get_item(d, &h); ) { | | 414 | for (d = hid_start_parse(desc, size, k); hid_get_item(d, &h); ) { |
415 | DPRINTFN(5,("hid_locate: usage=0x%x kind=%d id=%d flags=0x%x\n", | | 415 | DPRINTFN(5,("hid_locate: usage=0x%x kind=%d id=%d flags=0x%x\n", |
416 | h.usage, h.kind, h.report_ID, h.flags)); | | 416 | h.usage, h.kind, h.report_ID, h.flags)); |
417 | if (h.kind == k && !(h.flags & HIO_CONST) && | | 417 | if (h.kind == k && !(h.flags & HIO_CONST) && |
418 | h.usage == u && h.report_ID == id) { | | 418 | h.usage == u && h.report_ID == id) { |
419 | if (loc != NULL) | | 419 | if (loc != NULL) |
420 | *loc = h.loc; | | 420 | *loc = h.loc; |
421 | if (flags != NULL) | | 421 | if (flags != NULL) |
422 | *flags = h.flags; | | 422 | *flags = h.flags; |
423 | hid_end_parse(d); | | 423 | hid_end_parse(d); |
424 | return (1); | | 424 | return (1); |
425 | } | | 425 | } |
426 | } | | 426 | } |
427 | hid_end_parse(d); | | 427 | hid_end_parse(d); |
428 | if (loc != NULL) | | 428 | if (loc != NULL) |
429 | loc->size = 0; | | 429 | loc->size = 0; |
430 | return (0); | | 430 | return (0); |
431 | } | | 431 | } |
432 | | | 432 | |
433 | long | | 433 | long |
434 | hid_get_data(const u_char *buf, const struct hid_location *loc) | | 434 | hid_get_data(const u_char *buf, const struct hid_location *loc) |
435 | { | | 435 | { |
436 | u_int hsize = loc->size; | | 436 | u_int hsize = loc->size; |
437 | u_long data; | | 437 | u_long data; |
438 | | | 438 | |
439 | if (hsize == 0) | | 439 | if (hsize == 0) |
440 | return (0); | | 440 | return (0); |
441 | | | 441 | |
442 | data = hid_get_udata(buf, loc); | | 442 | data = hid_get_udata(buf, loc); |
443 | if (data < (1UL << (hsize - 1)) || hsize == sizeof(data) * NBBY) | | 443 | if (data < (1UL << (hsize - 1)) || hsize == sizeof(data) * NBBY) |
444 | return (data); | | 444 | return (data); |
445 | return data - (1UL << hsize); | | 445 | return data - (1UL << hsize); |
446 | } | | 446 | } |
447 | | | 447 | |
448 | u_long | | 448 | u_long |
449 | hid_get_udata(const u_char *buf, const struct hid_location *loc) | | 449 | hid_get_udata(const u_char *buf, const struct hid_location *loc) |
450 | { | | 450 | { |
451 | u_int hpos = loc->pos; | | 451 | u_int hpos = loc->pos; |
452 | u_int hsize = loc->size; | | 452 | u_int hsize = loc->size; |
453 | u_int i, num, off; | | 453 | u_int i, num, off; |
454 | u_long data; | | 454 | u_long data; |
455 | | | 455 | |
456 | if (hsize == 0) | | 456 | if (hsize == 0) |
457 | return (0); | | 457 | return (0); |
458 | | | 458 | |
459 | data = 0; | | 459 | data = 0; |
460 | off = hpos / 8; | | 460 | off = hpos / 8; |
461 | num = (hpos + hsize + 7) / 8 - off; | | 461 | num = (hpos + hsize + 7) / 8 - off; |
462 | | | 462 | |
463 | for (i = 0; i < num; i++) | | 463 | for (i = 0; i < num; i++) |
464 | data |= buf[off + i] << (i * 8); | | 464 | data |= (unsigned long)buf[off + i] << (i * 8); |
465 | | | 465 | |
466 | data >>= hpos % 8; | | 466 | data >>= hpos % 8; |
467 | if (hsize < sizeof(data) * NBBY) | | 467 | if (hsize < sizeof(data) * NBBY) |
468 | data &= (1UL << hsize) - 1; | | 468 | data &= (1UL << hsize) - 1; |
469 | | | 469 | |
470 | DPRINTFN(10,("hid_get_udata: loc %d/%d = %lu\n", hpos, hsize, data)); | | 470 | DPRINTFN(10,("hid_get_udata: loc %d/%d = %lu\n", hpos, hsize, data)); |
471 | return (data); | | 471 | return (data); |
472 | } | | 472 | } |
473 | | | 473 | |
474 | /* | | 474 | /* |
475 | * hid_is_collection(desc, size, id, usage) | | 475 | * hid_is_collection(desc, size, id, usage) |
476 | * | | 476 | * |
477 | * This function is broken in the following way. | | 477 | * This function is broken in the following way. |
478 | * | | 478 | * |
479 | * It is used to discover if the given 'id' is part of 'usage' collection | | 479 | * It is used to discover if the given 'id' is part of 'usage' collection |
480 | * in the descriptor in order to match report id against device type. | | 480 | * in the descriptor in order to match report id against device type. |
481 | * | | 481 | * |
482 | * The semantics of hid_start_parse() means though, that only a single | | 482 | * The semantics of hid_start_parse() means though, that only a single |
483 | * kind of report is considered. The current HID code that uses this for | | 483 | * kind of report is considered. The current HID code that uses this for |
484 | * matching is actually only looking for input reports, so this works | | 484 | * matching is actually only looking for input reports, so this works |
485 | * for now. | | 485 | * for now. |
486 | * | | 486 | * |
487 | * This function could try all report kinds (input, output and feature) | | 487 | * This function could try all report kinds (input, output and feature) |
488 | * consecutively if necessary, but it may be better to integrate the | | 488 | * consecutively if necessary, but it may be better to integrate the |
489 | * libusbhid code which can consider multiple report kinds simultaneously | | 489 | * libusbhid code which can consider multiple report kinds simultaneously |
490 | * | | 490 | * |
491 | * Needs some thought. | | 491 | * Needs some thought. |
492 | */ | | 492 | */ |
493 | int | | 493 | int |
494 | hid_is_collection(const void *desc, int size, u_int8_t id, u_int32_t usage) | | 494 | hid_is_collection(const void *desc, int size, u_int8_t id, u_int32_t usage) |
495 | { | | 495 | { |
496 | struct hid_data *hd; | | 496 | struct hid_data *hd; |
497 | struct hid_item hi; | | 497 | struct hid_item hi; |
498 | u_int32_t coll_usage = ~0; | | 498 | u_int32_t coll_usage = ~0; |
499 | | | 499 | |
500 | hd = hid_start_parse(desc, size, hid_input); | | 500 | hd = hid_start_parse(desc, size, hid_input); |
501 | if (hd == NULL) | | 501 | if (hd == NULL) |
502 | return (0); | | 502 | return (0); |
503 | | | 503 | |
504 | DPRINTFN(2,("hid_is_collection: id=%d usage=0x%x\n", id, usage)); | | 504 | DPRINTFN(2,("hid_is_collection: id=%d usage=0x%x\n", id, usage)); |
505 | while (hid_get_item(hd, &hi)) { | | 505 | while (hid_get_item(hd, &hi)) { |
506 | DPRINTFN(2,("hid_is_collection: kind=%d id=%d usage=0x%x" | | 506 | DPRINTFN(2,("hid_is_collection: kind=%d id=%d usage=0x%x" |
507 | "(0x%x)\n", | | 507 | "(0x%x)\n", |
508 | hi.kind, hi.report_ID, hi.usage, coll_usage)); | | 508 | hi.kind, hi.report_ID, hi.usage, coll_usage)); |
509 | | | 509 | |
510 | if (hi.kind == hid_collection && | | 510 | if (hi.kind == hid_collection && |
511 | hi.collection == HCOLL_APPLICATION) | | 511 | hi.collection == HCOLL_APPLICATION) |
512 | coll_usage = hi.usage; | | 512 | coll_usage = hi.usage; |
513 | | | 513 | |
514 | if (hi.kind == hid_endcollection) | | 514 | if (hi.kind == hid_endcollection) |
515 | coll_usage = ~0; | | 515 | coll_usage = ~0; |
516 | | | 516 | |
517 | if (hi.kind == hid_input && | | 517 | if (hi.kind == hid_input && |
518 | coll_usage == usage && | | 518 | coll_usage == usage && |
519 | hi.report_ID == id) { | | 519 | hi.report_ID == id) { |
520 | DPRINTFN(2,("hid_is_collection: found\n")); | | 520 | DPRINTFN(2,("hid_is_collection: found\n")); |
521 | hid_end_parse(hd); | | 521 | hid_end_parse(hd); |
522 | return (1); | | 522 | return (1); |
523 | } | | 523 | } |
524 | } | | 524 | } |
525 | DPRINTFN(2,("hid_is_collection: not found\n")); | | 525 | DPRINTFN(2,("hid_is_collection: not found\n")); |
526 | hid_end_parse(hd); | | 526 | hid_end_parse(hd); |
527 | return (0); | | 527 | return (0); |
528 | } | | 528 | } |