usb_hid.c revision 188942
1184610Salfred/*	$NetBSD: hid.c,v 1.17 2001/11/13 06:24:53 lukem Exp $	*/
2184610Salfred
3184610Salfred
4184610Salfred#include <sys/cdefs.h>
5184610Salfred__FBSDID("$FreeBSD: head/sys/dev/usb/usb_hid.c 188942 2009-02-23 18:31:00Z thompsa $");
6184610Salfred/*-
7184610Salfred * Copyright (c) 1998 The NetBSD Foundation, Inc.
8184610Salfred * All rights reserved.
9184610Salfred *
10184610Salfred * This code is derived from software contributed to The NetBSD Foundation
11184610Salfred * by Lennart Augustsson (lennart@augustsson.net) at
12184610Salfred * Carlstedt Research & Technology.
13184610Salfred *
14184610Salfred * Redistribution and use in source and binary forms, with or without
15184610Salfred * modification, are permitted provided that the following conditions
16184610Salfred * are met:
17184610Salfred * 1. Redistributions of source code must retain the above copyright
18184610Salfred *    notice, this list of conditions and the following disclaimer.
19184610Salfred * 2. Redistributions in binary form must reproduce the above copyright
20184610Salfred *    notice, this list of conditions and the following disclaimer in the
21184610Salfred *    documentation and/or other materials provided with the distribution.
22184610Salfred * 3. All advertising materials mentioning features or use of this software
23184610Salfred *    must display the following acknowledgement:
24184610Salfred *        This product includes software developed by the NetBSD
25184610Salfred *        Foundation, Inc. and its contributors.
26184610Salfred * 4. Neither the name of The NetBSD Foundation nor the names of its
27184610Salfred *    contributors may be used to endorse or promote products derived
28184610Salfred *    from this software without specific prior written permission.
29184610Salfred *
30184610Salfred * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
31184610Salfred * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
32184610Salfred * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
33184610Salfred * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
34184610Salfred * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
35184610Salfred * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
36184610Salfred * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
37184610Salfred * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
38184610Salfred * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
39184610Salfred * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
40184610Salfred * POSSIBILITY OF SUCH DAMAGE.
41184610Salfred */
42184610Salfred
43188942Sthompsa#include <dev/usb/usb.h>
44188942Sthompsa#include <dev/usb/usb_mfunc.h>
45188942Sthompsa#include <dev/usb/usb_error.h>
46188942Sthompsa#include <dev/usb/usb_defs.h>
47188942Sthompsa#include <dev/usb/usbhid.h>
48184610Salfred
49184610Salfred#define	USB_DEBUG_VAR usb2_debug
50184610Salfred
51188942Sthompsa#include <dev/usb/usb_core.h>
52188942Sthompsa#include <dev/usb/usb_debug.h>
53188942Sthompsa#include <dev/usb/usb_parse.h>
54188942Sthompsa#include <dev/usb/usb_process.h>
55188942Sthompsa#include <dev/usb/usb_device.h>
56188942Sthompsa#include <dev/usb/usb_request.h>
57188942Sthompsa#include <dev/usb/usb_hid.h>
58184610Salfred
59184610Salfredstatic void hid_clear_local(struct hid_item *);
60184610Salfred
61184610Salfred#define	MAXUSAGE 100
62184610Salfredstruct hid_data {
63184610Salfred	const uint8_t *start;
64184610Salfred	const uint8_t *end;
65184610Salfred	const uint8_t *p;
66184610Salfred	struct hid_item cur;
67184610Salfred	int32_t	usages[MAXUSAGE];
68184610Salfred	int	nu;
69184610Salfred	int	minset;
70184610Salfred	int	multi;
71184610Salfred	int	multimax;
72184610Salfred	int	kindset;
73184610Salfred};
74184610Salfred
75184610Salfred/*------------------------------------------------------------------------*
76184610Salfred *	hid_clear_local
77184610Salfred *------------------------------------------------------------------------*/
78184610Salfredstatic void
79184610Salfredhid_clear_local(struct hid_item *c)
80184610Salfred{
81184610Salfred
82184610Salfred	c->usage = 0;
83184610Salfred	c->usage_minimum = 0;
84184610Salfred	c->usage_maximum = 0;
85184610Salfred	c->designator_index = 0;
86184610Salfred	c->designator_minimum = 0;
87184610Salfred	c->designator_maximum = 0;
88184610Salfred	c->string_index = 0;
89184610Salfred	c->string_minimum = 0;
90184610Salfred	c->string_maximum = 0;
91184610Salfred	c->set_delimiter = 0;
92184610Salfred}
93184610Salfred
94184610Salfred/*------------------------------------------------------------------------*
95184610Salfred *	hid_start_parse
96184610Salfred *------------------------------------------------------------------------*/
97184610Salfredstruct hid_data *
98184610Salfredhid_start_parse(const void *d, int len, int kindset)
99184610Salfred{
100184610Salfred	struct hid_data *s;
101184610Salfred
102184610Salfred	s = malloc(sizeof *s, M_TEMP, M_WAITOK | M_ZERO);
103184610Salfred	s->start = s->p = d;
104184610Salfred	s->end = ((const uint8_t *)d) + len;
105184610Salfred	s->kindset = kindset;
106184610Salfred	return (s);
107184610Salfred}
108184610Salfred
109184610Salfred/*------------------------------------------------------------------------*
110184610Salfred *	hid_end_parse
111184610Salfred *------------------------------------------------------------------------*/
112184610Salfredvoid
113184610Salfredhid_end_parse(struct hid_data *s)
114184610Salfred{
115184610Salfred
116184610Salfred	while (s->cur.next != NULL) {
117184610Salfred		struct hid_item *hi = s->cur.next->next;
118184610Salfred
119184610Salfred		free(s->cur.next, M_TEMP);
120184610Salfred		s->cur.next = hi;
121184610Salfred	}
122184610Salfred	free(s, M_TEMP);
123184610Salfred}
124184610Salfred
125184610Salfred/*------------------------------------------------------------------------*
126184610Salfred *	hid_get_item
127184610Salfred *------------------------------------------------------------------------*/
128184610Salfredint
129184610Salfredhid_get_item(struct hid_data *s, struct hid_item *h)
130184610Salfred{
131184610Salfred	struct hid_item *c = &s->cur;
132184610Salfred	unsigned int bTag, bType, bSize;
133184610Salfred	uint32_t oldpos;
134184610Salfred	const uint8_t *data;
135184610Salfred	int32_t dval;
136184610Salfred	const uint8_t *p;
137184610Salfred	struct hid_item *hi;
138184610Salfred	int i;
139184610Salfred
140184610Salfredtop:
141184610Salfred	if (s->multimax != 0) {
142184610Salfred		if (s->multi < s->multimax) {
143184610Salfred			c->usage = s->usages[MIN(s->multi, s->nu - 1)];
144184610Salfred			s->multi++;
145184610Salfred			*h = *c;
146184610Salfred			c->loc.pos += c->loc.size;
147184610Salfred			h->next = 0;
148184610Salfred			return (1);
149184610Salfred		} else {
150184610Salfred			c->loc.count = s->multimax;
151184610Salfred			s->multimax = 0;
152184610Salfred			s->nu = 0;
153184610Salfred			hid_clear_local(c);
154184610Salfred		}
155184610Salfred	}
156184610Salfred	for (;;) {
157184610Salfred		p = s->p;
158184610Salfred		if (p >= s->end)
159184610Salfred			return (0);
160184610Salfred
161184610Salfred		bSize = *p++;
162184610Salfred		if (bSize == 0xfe) {
163184610Salfred			/* long item */
164184610Salfred			bSize = *p++;
165184610Salfred			bSize |= *p++ << 8;
166184610Salfred			bTag = *p++;
167184610Salfred			data = p;
168184610Salfred			p += bSize;
169184610Salfred			bType = 0xff;	/* XXX what should it be */
170184610Salfred		} else {
171184610Salfred			/* short item */
172184610Salfred			bTag = bSize >> 4;
173184610Salfred			bType = (bSize >> 2) & 3;
174184610Salfred			bSize &= 3;
175184610Salfred			if (bSize == 3)
176184610Salfred				bSize = 4;
177184610Salfred			data = p;
178184610Salfred			p += bSize;
179184610Salfred		}
180184610Salfred		s->p = p;
181184610Salfred		switch (bSize) {
182184610Salfred		case 0:
183184610Salfred			dval = 0;
184184610Salfred			break;
185184610Salfred		case 1:
186184610Salfred			dval = (int8_t)*data++;
187184610Salfred			break;
188184610Salfred		case 2:
189184610Salfred			dval = *data++;
190184610Salfred			dval |= *data++ << 8;
191184610Salfred			dval = (int16_t)dval;
192184610Salfred			break;
193184610Salfred		case 4:
194184610Salfred			dval = *data++;
195184610Salfred			dval |= *data++ << 8;
196184610Salfred			dval |= *data++ << 16;
197184610Salfred			dval |= *data++ << 24;
198184610Salfred			break;
199184610Salfred		default:
200184610Salfred			printf("BAD LENGTH %d\n", bSize);
201184610Salfred			continue;
202184610Salfred		}
203184610Salfred
204184610Salfred		switch (bType) {
205184610Salfred		case 0:		/* Main */
206184610Salfred			switch (bTag) {
207184610Salfred			case 8:	/* Input */
208184610Salfred				if (!(s->kindset & (1 << hid_input))) {
209184610Salfred					if (s->nu > 0)
210184610Salfred						s->nu--;
211184610Salfred					continue;
212184610Salfred				}
213184610Salfred				c->kind = hid_input;
214184610Salfred				c->flags = dval;
215184610Salfred		ret:
216184610Salfred				if (c->flags & HIO_VARIABLE) {
217184610Salfred					s->multimax = c->loc.count;
218184610Salfred					s->multi = 0;
219184610Salfred					c->loc.count = 1;
220184610Salfred					if (s->minset) {
221184610Salfred						for (i = c->usage_minimum;
222184610Salfred						    i <= c->usage_maximum;
223184610Salfred						    i++) {
224184610Salfred							s->usages[s->nu] = i;
225184610Salfred							if (s->nu < MAXUSAGE - 1)
226184610Salfred								s->nu++;
227184610Salfred						}
228184610Salfred						s->minset = 0;
229184610Salfred					}
230184610Salfred					goto top;
231184610Salfred				} else {
232184610Salfred					*h = *c;
233184610Salfred					h->next = 0;
234184610Salfred					c->loc.pos +=
235184610Salfred					    c->loc.size * c->loc.count;
236184610Salfred					hid_clear_local(c);
237184610Salfred					s->minset = 0;
238184610Salfred					return (1);
239184610Salfred				}
240184610Salfred			case 9:	/* Output */
241184610Salfred				if (!(s->kindset & (1 << hid_output))) {
242184610Salfred					if (s->nu > 0)
243184610Salfred						s->nu--;
244184610Salfred					continue;
245184610Salfred				}
246184610Salfred				c->kind = hid_output;
247184610Salfred				c->flags = dval;
248184610Salfred				goto ret;
249184610Salfred			case 10:	/* Collection */
250184610Salfred				c->kind = hid_collection;
251184610Salfred				c->collection = dval;
252184610Salfred				c->collevel++;
253184610Salfred				*h = *c;
254184610Salfred				hid_clear_local(c);
255184610Salfred				s->nu = 0;
256184610Salfred				return (1);
257184610Salfred			case 11:	/* Feature */
258184610Salfred				if (!(s->kindset & (1 << hid_feature))) {
259184610Salfred					if (s->nu > 0)
260184610Salfred						s->nu--;
261184610Salfred					continue;
262184610Salfred				}
263184610Salfred				c->kind = hid_feature;
264184610Salfred				c->flags = dval;
265184610Salfred				goto ret;
266184610Salfred			case 12:	/* End collection */
267184610Salfred				c->kind = hid_endcollection;
268184610Salfred				c->collevel--;
269184610Salfred				*h = *c;
270184610Salfred				hid_clear_local(c);
271184610Salfred				s->nu = 0;
272184610Salfred				return (1);
273184610Salfred			default:
274184610Salfred				printf("Main bTag=%d\n", bTag);
275184610Salfred				break;
276184610Salfred			}
277184610Salfred			break;
278184610Salfred		case 1:		/* Global */
279184610Salfred			switch (bTag) {
280184610Salfred			case 0:
281184610Salfred				c->_usage_page = dval << 16;
282184610Salfred				break;
283184610Salfred			case 1:
284184610Salfred				c->logical_minimum = dval;
285184610Salfred				break;
286184610Salfred			case 2:
287184610Salfred				c->logical_maximum = dval;
288184610Salfred				break;
289184610Salfred			case 3:
290184610Salfred				c->physical_minimum = dval;
291184610Salfred				break;
292184610Salfred			case 4:
293184610Salfred				c->physical_maximum = dval;
294184610Salfred				break;
295184610Salfred			case 5:
296184610Salfred				c->unit_exponent = dval;
297184610Salfred				break;
298184610Salfred			case 6:
299184610Salfred				c->unit = dval;
300184610Salfred				break;
301184610Salfred			case 7:
302184610Salfred				c->loc.size = dval;
303184610Salfred				break;
304184610Salfred			case 8:
305184610Salfred				c->report_ID = dval;
306184610Salfred				break;
307184610Salfred			case 9:
308184610Salfred				c->loc.count = dval;
309184610Salfred				break;
310184610Salfred			case 10:	/* Push */
311184610Salfred				hi = malloc(sizeof *hi, M_TEMP, M_WAITOK);
312184610Salfred				*hi = s->cur;
313184610Salfred				c->next = hi;
314184610Salfred				break;
315184610Salfred			case 11:	/* Pop */
316184610Salfred				hi = c->next;
317184610Salfred				oldpos = c->loc.pos;
318184610Salfred				s->cur = *hi;
319184610Salfred				c->loc.pos = oldpos;
320184610Salfred				free(hi, M_TEMP);
321184610Salfred				break;
322184610Salfred			default:
323184610Salfred				printf("Global bTag=%d\n", bTag);
324184610Salfred				break;
325184610Salfred			}
326184610Salfred			break;
327184610Salfred		case 2:		/* Local */
328184610Salfred			switch (bTag) {
329184610Salfred			case 0:
330184610Salfred				if (bSize == 1)
331184610Salfred					dval = c->_usage_page | (dval & 0xff);
332184610Salfred				else if (bSize == 2)
333184610Salfred					dval = c->_usage_page | (dval & 0xffff);
334184610Salfred				c->usage = dval;
335184610Salfred				if (s->nu < MAXUSAGE)
336184610Salfred					s->usages[s->nu++] = dval;
337184610Salfred				/* else XXX */
338184610Salfred				break;
339184610Salfred			case 1:
340184610Salfred				s->minset = 1;
341184610Salfred				if (bSize == 1)
342184610Salfred					dval = c->_usage_page | (dval & 0xff);
343184610Salfred				else if (bSize == 2)
344184610Salfred					dval = c->_usage_page | (dval & 0xffff);
345184610Salfred				c->usage_minimum = dval;
346184610Salfred				break;
347184610Salfred			case 2:
348184610Salfred				if (bSize == 1)
349184610Salfred					dval = c->_usage_page | (dval & 0xff);
350184610Salfred				else if (bSize == 2)
351184610Salfred					dval = c->_usage_page | (dval & 0xffff);
352184610Salfred				c->usage_maximum = dval;
353184610Salfred				break;
354184610Salfred			case 3:
355184610Salfred				c->designator_index = dval;
356184610Salfred				break;
357184610Salfred			case 4:
358184610Salfred				c->designator_minimum = dval;
359184610Salfred				break;
360184610Salfred			case 5:
361184610Salfred				c->designator_maximum = dval;
362184610Salfred				break;
363184610Salfred			case 7:
364184610Salfred				c->string_index = dval;
365184610Salfred				break;
366184610Salfred			case 8:
367184610Salfred				c->string_minimum = dval;
368184610Salfred				break;
369184610Salfred			case 9:
370184610Salfred				c->string_maximum = dval;
371184610Salfred				break;
372184610Salfred			case 10:
373184610Salfred				c->set_delimiter = dval;
374184610Salfred				break;
375184610Salfred			default:
376184610Salfred				printf("Local bTag=%d\n", bTag);
377184610Salfred				break;
378184610Salfred			}
379184610Salfred			break;
380184610Salfred		default:
381184610Salfred			printf("default bType=%d\n", bType);
382184610Salfred			break;
383184610Salfred		}
384184610Salfred	}
385184610Salfred}
386184610Salfred
387184610Salfred/*------------------------------------------------------------------------*
388184610Salfred *	hid_report_size
389184610Salfred *------------------------------------------------------------------------*/
390184610Salfredint
391184610Salfredhid_report_size(const void *buf, int len, enum hid_kind k, uint8_t *idp)
392184610Salfred{
393184610Salfred	struct hid_data *d;
394184610Salfred	struct hid_item h;
395184610Salfred	int hi, lo, size, id;
396184610Salfred
397184610Salfred	id = 0;
398184610Salfred	hi = lo = -1;
399184610Salfred	for (d = hid_start_parse(buf, len, 1 << k); hid_get_item(d, &h);)
400184610Salfred		if (h.kind == k) {
401184610Salfred			if (h.report_ID != 0 && !id)
402184610Salfred				id = h.report_ID;
403184610Salfred			if (h.report_ID == id) {
404184610Salfred				if (lo < 0)
405184610Salfred					lo = h.loc.pos;
406184610Salfred				hi = h.loc.pos + h.loc.size * h.loc.count;
407184610Salfred			}
408184610Salfred		}
409184610Salfred	hid_end_parse(d);
410184610Salfred	size = hi - lo;
411184610Salfred	if (id != 0) {
412184610Salfred		size += 8;
413184610Salfred		*idp = id;		/* XXX wrong */
414184610Salfred	} else
415184610Salfred		*idp = 0;
416184610Salfred	return ((size + 7) / 8);
417184610Salfred}
418184610Salfred
419184610Salfred/*------------------------------------------------------------------------*
420184610Salfred *	hid_locate
421184610Salfred *------------------------------------------------------------------------*/
422184610Salfredint
423184610Salfredhid_locate(const void *desc, int size, uint32_t u, enum hid_kind k,
424184610Salfred    struct hid_location *loc, uint32_t *flags)
425184610Salfred{
426184610Salfred	struct hid_data *d;
427184610Salfred	struct hid_item h;
428184610Salfred
429184610Salfred	for (d = hid_start_parse(desc, size, 1 << k); hid_get_item(d, &h);) {
430184610Salfred		if (h.kind == k && !(h.flags & HIO_CONST) && h.usage == u) {
431184610Salfred			if (loc != NULL)
432184610Salfred				*loc = h.loc;
433184610Salfred			if (flags != NULL)
434184610Salfred				*flags = h.flags;
435184610Salfred			hid_end_parse(d);
436184610Salfred			return (1);
437184610Salfred		}
438184610Salfred	}
439184610Salfred	hid_end_parse(d);
440184610Salfred	loc->size = 0;
441184610Salfred	return (0);
442184610Salfred}
443184610Salfred
444184610Salfred/*------------------------------------------------------------------------*
445184610Salfred *	hid_get_data
446184610Salfred *------------------------------------------------------------------------*/
447184610Salfreduint32_t
448184610Salfredhid_get_data(const uint8_t *buf, uint32_t len, struct hid_location *loc)
449184610Salfred{
450184610Salfred	uint32_t hpos = loc->pos;
451184610Salfred	uint32_t hsize = loc->size;
452184610Salfred	uint32_t data;
453184610Salfred	int i, s, t;
454184610Salfred
455184610Salfred	DPRINTFN(11, "hid_get_data: loc %d/%d\n", hpos, hsize);
456184610Salfred
457184610Salfred	if (hsize == 0)
458184610Salfred		return (0);
459184610Salfred
460184610Salfred	data = 0;
461184610Salfred	s = hpos / 8;
462184610Salfred	for (i = hpos; i < (hpos + hsize); i += 8) {
463184610Salfred		t = (i / 8);
464184610Salfred		if (t < len) {
465184610Salfred			data |= buf[t] << ((t - s) * 8);
466184610Salfred		}
467184610Salfred	}
468184610Salfred	data >>= hpos % 8;
469184610Salfred	data &= (1 << hsize) - 1;
470184610Salfred	hsize = 32 - hsize;
471184610Salfred	/* Sign extend */
472184610Salfred	data = ((int32_t)data << hsize) >> hsize;
473184610Salfred	DPRINTFN(11, "hid_get_data: loc %d/%d = %lu\n",
474184610Salfred	    loc->pos, loc->size, (long)data);
475184610Salfred	return (data);
476184610Salfred}
477184610Salfred
478184610Salfred/*------------------------------------------------------------------------*
479184610Salfred *	hid_is_collection
480184610Salfred *------------------------------------------------------------------------*/
481184610Salfredint
482184610Salfredhid_is_collection(const void *desc, int size, uint32_t usage)
483184610Salfred{
484184610Salfred	struct hid_data *hd;
485184610Salfred	struct hid_item hi;
486184610Salfred	int err;
487184610Salfred
488184610Salfred	hd = hid_start_parse(desc, size, hid_input);
489184610Salfred	if (hd == NULL)
490184610Salfred		return (0);
491184610Salfred
492184610Salfred	err = hid_get_item(hd, &hi) &&
493184610Salfred	    hi.kind == hid_collection &&
494184610Salfred	    hi.usage == usage;
495184610Salfred	hid_end_parse(hd);
496184610Salfred	return (err);
497184610Salfred}
498184610Salfred
499184610Salfred/*------------------------------------------------------------------------*
500184610Salfred *	hid_get_descriptor_from_usb
501184610Salfred *
502184610Salfred * This function will search for a HID descriptor between two USB
503184610Salfred * interface descriptors.
504184610Salfred *
505184610Salfred * Return values:
506184610Salfred * NULL: No more HID descriptors.
507184610Salfred * Else: Pointer to HID descriptor.
508184610Salfred *------------------------------------------------------------------------*/
509184610Salfredstruct usb2_hid_descriptor *
510184610Salfredhid_get_descriptor_from_usb(struct usb2_config_descriptor *cd,
511184610Salfred    struct usb2_interface_descriptor *id)
512184610Salfred{
513184610Salfred	struct usb2_descriptor *desc = (void *)id;
514184610Salfred
515184610Salfred	if (desc == NULL) {
516184610Salfred		return (NULL);
517184610Salfred	}
518184610Salfred	while ((desc = usb2_desc_foreach(cd, desc))) {
519184610Salfred		if ((desc->bDescriptorType == UDESC_HID) &&
520184610Salfred		    (desc->bLength >= USB_HID_DESCRIPTOR_SIZE(0))) {
521184610Salfred			return (void *)desc;
522184610Salfred		}
523184610Salfred		if (desc->bDescriptorType == UDESC_INTERFACE) {
524184610Salfred			break;
525184610Salfred		}
526184610Salfred	}
527184610Salfred	return (NULL);
528184610Salfred}
529184610Salfred
530184610Salfred/*------------------------------------------------------------------------*
531184610Salfred *	usb2_req_get_hid_desc
532184610Salfred *
533184610Salfred * This function will read out an USB report descriptor from the USB
534184610Salfred * device.
535184610Salfred *
536184610Salfred * Return values:
537184610Salfred * NULL: Failure.
538184610Salfred * Else: Success. The pointer should eventually be passed to free().
539184610Salfred *------------------------------------------------------------------------*/
540184610Salfredusb2_error_t
541184610Salfredusb2_req_get_hid_desc(struct usb2_device *udev, struct mtx *mtx,
542184610Salfred    void **descp, uint16_t *sizep,
543184610Salfred    usb2_malloc_type mem, uint8_t iface_index)
544184610Salfred{
545184610Salfred	struct usb2_interface *iface = usb2_get_iface(udev, iface_index);
546184610Salfred	struct usb2_hid_descriptor *hid;
547184610Salfred	usb2_error_t err;
548184610Salfred
549184610Salfred	if ((iface == NULL) || (iface->idesc == NULL)) {
550184610Salfred		return (USB_ERR_INVAL);
551184610Salfred	}
552184610Salfred	hid = hid_get_descriptor_from_usb
553184610Salfred	    (usb2_get_config_descriptor(udev), iface->idesc);
554184610Salfred
555184610Salfred	if (hid == NULL) {
556184610Salfred		return (USB_ERR_IOERROR);
557184610Salfred	}
558184610Salfred	*sizep = UGETW(hid->descrs[0].wDescriptorLength);
559184610Salfred	if (*sizep == 0) {
560184610Salfred		return (USB_ERR_IOERROR);
561184610Salfred	}
562184610Salfred	if (mtx)
563184610Salfred		mtx_unlock(mtx);
564184610Salfred
565184610Salfred	*descp = malloc(*sizep, mem, M_ZERO | M_WAITOK);
566184610Salfred
567184610Salfred	if (mtx)
568184610Salfred		mtx_lock(mtx);
569184610Salfred
570184610Salfred	if (*descp == NULL) {
571184610Salfred		return (USB_ERR_NOMEM);
572184610Salfred	}
573184610Salfred	err = usb2_req_get_report_descriptor
574184610Salfred	    (udev, mtx, *descp, *sizep, iface_index);
575184610Salfred
576184610Salfred	if (err) {
577184610Salfred		free(*descp, mem);
578184610Salfred		*descp = NULL;
579184610Salfred		return (err);
580184610Salfred	}
581184610Salfred	return (USB_ERR_NORMAL_COMPLETION);
582184610Salfred}
583