xref: /netbsd/sys/dev/hid/hid.c (revision 299cc587)
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