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