xref: /openbsd/sys/dev/usb/uhidev.c (revision 404b540a)
1 /*	$OpenBSD: uhidev.c,v 1.36 2009/10/13 19:33:19 pirofti Exp $	*/
2 /*	$NetBSD: uhidev.c,v 1.14 2003/03/11 16:44:00 augustss Exp $	*/
3 
4 /*
5  * Copyright (c) 2001 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by Lennart Augustsson (lennart@augustsson.net) at
10  * Carlstedt Research & Technology.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  *
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
23  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
25  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
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
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
31  * POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 /*
35  * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf
36  */
37 
38 #include <sys/param.h>
39 #include <sys/systm.h>
40 #include <sys/kernel.h>
41 #include <sys/malloc.h>
42 #include <sys/signalvar.h>
43 #include <sys/device.h>
44 #include <sys/ioctl.h>
45 #include <sys/conf.h>
46 
47 #include <dev/usb/usb.h>
48 #include <dev/usb/usbhid.h>
49 
50 #include <dev/usb/usbdevs.h>
51 #include <dev/usb/usbdi.h>
52 #include <dev/usb/usbdi_util.h>
53 #include <dev/usb/hid.h>
54 #include <dev/usb/usb_quirks.h>
55 
56 #include <dev/usb/uhidev.h>
57 
58 /* Report descriptor for broken Wacom Graphire */
59 #include <dev/usb/ugraphire_rdesc.h>
60 
61 #ifdef UHIDEV_DEBUG
62 #define DPRINTF(x)	do { if (uhidevdebug) printf x; } while (0)
63 #define DPRINTFN(n,x)	do { if (uhidevdebug>(n)) printf x; } while (0)
64 int	uhidevdebug = 0;
65 #else
66 #define DPRINTF(x)
67 #define DPRINTFN(n,x)
68 #endif
69 
70 void uhidev_intr(usbd_xfer_handle, usbd_private_handle, usbd_status);
71 
72 int uhidev_maxrepid(void *buf, int len);
73 int uhidevprint(void *aux, const char *pnp);
74 int uhidevsubmatch(struct device *parent, void *cf, void *aux);
75 
76 int uhidev_match(struct device *, void *, void *);
77 void uhidev_attach(struct device *, struct device *, void *);
78 int uhidev_detach(struct device *, int);
79 int uhidev_activate(struct device *, int);
80 
81 struct cfdriver uhidev_cd = {
82 	NULL, "uhidev", DV_DULL
83 };
84 
85 const struct cfattach uhidev_ca = {
86 	sizeof(struct uhidev_softc),
87 	uhidev_match,
88 	uhidev_attach,
89 	uhidev_detach,
90 	uhidev_activate,
91 };
92 
93 int
94 uhidev_match(struct device *parent, void *match, void *aux)
95 {
96 	struct usb_attach_arg *uaa = aux;
97 	usb_interface_descriptor_t *id;
98 
99 	if (uaa->iface == NULL)
100 		return (UMATCH_NONE);
101 	id = usbd_get_interface_descriptor(uaa->iface);
102 	if (id == NULL || id->bInterfaceClass != UICLASS_HID)
103 		return (UMATCH_NONE);
104 	if (usbd_get_quirks(uaa->device)->uq_flags & UQ_BAD_HID)
105 		return (UMATCH_NONE);
106 	if (uaa->matchlvl)
107 		return (uaa->matchlvl);
108 
109 	return (UMATCH_IFACECLASS_GENERIC);
110 }
111 
112 void
113 uhidev_attach(struct device *parent, struct device *self, void *aux)
114 {
115 	struct uhidev_softc *sc = (struct uhidev_softc *)self;
116 	struct usb_attach_arg *uaa = aux;
117 	usbd_interface_handle iface = uaa->iface;
118 	usb_interface_descriptor_t *id;
119 	usb_endpoint_descriptor_t *ed;
120 	struct uhidev_attach_arg uha;
121 	struct uhidev *dev;
122 	int size, nrepid, repid, repsz;
123 	int repsizes[256];
124 	int i;
125 	void *desc;
126 	const void *descptr;
127 	usbd_status err;
128 
129 	sc->sc_udev = uaa->device;
130 	sc->sc_iface = iface;
131 	id = usbd_get_interface_descriptor(iface);
132 
133 	(void)usbd_set_idle(iface, 0, 0);
134 #if 0
135 
136 	qflags = usbd_get_quirks(sc->sc_udev)->uq_flags;
137 	if ((qflags & UQ_NO_SET_PROTO) == 0 &&
138 	    id->bInterfaceSubClass != UISUBCLASS_BOOT)
139 		(void)usbd_set_protocol(iface, 1);
140 #endif
141 
142 	sc->sc_iep_addr = sc->sc_oep_addr = -1;
143 	for (i = 0; i < id->bNumEndpoints; i++) {
144 		ed = usbd_interface2endpoint_descriptor(iface, i);
145 		if (ed == NULL) {
146 			printf("%s: could not read endpoint descriptor\n",
147 			    sc->sc_dev.dv_xname);
148 			sc->sc_dying = 1;
149 			return;
150 		}
151 
152 		DPRINTFN(10,("uhidev_attach: bLength=%d bDescriptorType=%d "
153 		    "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d"
154 		    " bInterval=%d\n",
155 		    ed->bLength, ed->bDescriptorType,
156 		    ed->bEndpointAddress & UE_ADDR,
157 		    UE_GET_DIR(ed->bEndpointAddress)==UE_DIR_IN? "in" : "out",
158 		    ed->bmAttributes & UE_XFERTYPE,
159 		    UGETW(ed->wMaxPacketSize), ed->bInterval));
160 
161 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
162 		    (ed->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT) {
163 			sc->sc_iep_addr = ed->bEndpointAddress;
164 		} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
165 		    (ed->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT) {
166 			sc->sc_oep_addr = ed->bEndpointAddress;
167 		} else {
168 			printf("%s: unexpected endpoint\n", sc->sc_dev.dv_xname);
169 			sc->sc_dying = 1;
170 			return;
171 		}
172 	}
173 
174 	/*
175 	 * Check that we found an input interrupt endpoint. The output interrupt
176 	 * endpoint is optional
177 	 */
178 	if (sc->sc_iep_addr == -1) {
179 		printf("%s: no input interrupt endpoint\n", sc->sc_dev.dv_xname);
180 		sc->sc_dying = 1;
181 		return;
182 	}
183 
184 	/* XXX need to extend this */
185 	descptr = NULL;
186 	if (uaa->vendor == USB_VENDOR_WACOM) {
187 		static uByte reportbuf[] = {2, 2, 2};
188 
189 		/* The report descriptor for the Wacom Graphire is broken. */
190 		switch (uaa->product) {
191 		case USB_PRODUCT_WACOM_GRAPHIRE:
192 			size = sizeof uhid_graphire_report_descr;
193 			descptr = uhid_graphire_report_descr;
194 			break;
195 		case USB_PRODUCT_WACOM_GRAPHIRE3_4X5:
196 		case USB_PRODUCT_WACOM_GRAPHIRE4_4X5:
197 			usbd_set_report(uaa->iface, UHID_FEATURE_REPORT, 2,
198 			    &reportbuf, sizeof reportbuf);
199 			size = sizeof uhid_graphire3_4x5_report_descr;
200 			descptr = uhid_graphire3_4x5_report_descr;
201 			break;
202 		default:
203 			/* Keep descriptor */
204 			break;
205 		}
206 	}
207 
208 	if (descptr) {
209 		desc = malloc(size, M_USBDEV, M_NOWAIT);
210 		if (desc == NULL)
211 			err = USBD_NOMEM;
212 		else {
213 			err = USBD_NORMAL_COMPLETION;
214 			memcpy(desc, descptr, size);
215 		}
216 	} else {
217 		desc = NULL;
218 		err = usbd_read_report_desc(uaa->iface, &desc, &size, M_USBDEV);
219 	}
220 	if (err) {
221 		printf("%s: no report descriptor\n", sc->sc_dev.dv_xname);
222 		sc->sc_dying = 1;
223 		return;
224 	}
225 
226 	sc->sc_repdesc = desc;
227 	sc->sc_repdesc_size = size;
228 
229 	uha.uaa = uaa;
230 	nrepid = uhidev_maxrepid(desc, size);
231 	if (nrepid < 0)
232 		return;
233 	printf("%s: iclass %d/%d", sc->sc_dev.dv_xname,
234 	    id->bInterfaceClass, id->bInterfaceSubClass);
235 	if (nrepid > 0)
236 		printf(", %d report id%s", nrepid,
237 		    nrepid > 1 ? "s" : "");
238 	printf("\n");
239 	nrepid++;
240 	sc->sc_subdevs = malloc(nrepid * sizeof(struct device *),
241 	    M_USBDEV, M_NOWAIT | M_ZERO);
242 	if (sc->sc_subdevs == NULL) {
243 		printf("%s: no memory\n", sc->sc_dev.dv_xname);
244 		return;
245 	}
246 	sc->sc_nrepid = nrepid;
247 	sc->sc_isize = 0;
248 
249 	usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
250 			   &sc->sc_dev);
251 
252 	for (repid = 0; repid < nrepid; repid++) {
253 		repsz = hid_report_size(desc, size, hid_input, repid);
254 		DPRINTF(("uhidev_match: repid=%d, repsz=%d\n", repid, repsz));
255 		repsizes[repid] = repsz;
256 		if (repsz > 0) {
257 			if (repsz > sc->sc_isize)
258 				sc->sc_isize = repsz;
259 		}
260 	}
261 	sc->sc_isize += nrepid != 1;	/* space for report ID */
262 	DPRINTF(("uhidev_attach: isize=%d\n", sc->sc_isize));
263 
264 	uha.parent = sc;
265 	for (repid = 0; repid < nrepid; repid++) {
266 		DPRINTF(("uhidev_match: try repid=%d\n", repid));
267 		if (hid_report_size(desc, size, hid_input, repid) == 0 &&
268 		    hid_report_size(desc, size, hid_output, repid) == 0 &&
269 		    hid_report_size(desc, size, hid_feature, repid) == 0) {
270 			;	/* already NULL in sc->sc_subdevs[repid] */
271 		} else {
272 			uha.reportid = repid;
273 			dev = (struct uhidev *)config_found_sm(self, &uha,
274 			                           uhidevprint, uhidevsubmatch);
275 			sc->sc_subdevs[repid] = dev;
276 			if (dev != NULL) {
277 				dev->sc_in_rep_size = repsizes[repid];
278 #ifdef DIAGNOSTIC
279 				DPRINTF(("uhidev_match: repid=%d dev=%p\n",
280 					 repid, dev));
281 				if (dev->sc_intr == NULL) {
282 					printf("%s: sc_intr == NULL\n",
283 					       sc->sc_dev.dv_xname);
284 					return;
285 				}
286 #endif
287 			}
288 		}
289 	}
290 }
291 
292 int
293 uhidev_maxrepid(void *buf, int len)
294 {
295 	struct hid_data *d;
296 	struct hid_item h;
297 	int maxid;
298 
299 	maxid = -1;
300 	h.report_ID = 0;
301 	for (d = hid_start_parse(buf, len, hid_none); hid_get_item(d, &h); )
302 		if (h.report_ID > maxid)
303 			maxid = h.report_ID;
304 	hid_end_parse(d);
305 	return (maxid);
306 }
307 
308 int
309 uhidevprint(void *aux, const char *pnp)
310 {
311 	struct uhidev_attach_arg *uha = aux;
312 
313 	if (pnp)
314 		printf("uhid at %s", pnp);
315 	if (uha->reportid != 0)
316 		printf(" reportid %d", uha->reportid);
317 	return (UNCONF);
318 }
319 
320 int uhidevsubmatch(struct device *parent, void *match, void *aux)
321 {
322 	struct uhidev_attach_arg *uha = aux;
323         struct cfdata *cf = match;
324 
325 	if (cf->uhidevcf_reportid != UHIDEV_UNK_REPORTID &&
326 	    cf->uhidevcf_reportid != uha->reportid)
327 		return (0);
328 	if (cf->uhidevcf_reportid == uha->reportid)
329 		uha->matchlvl = UMATCH_VENDOR_PRODUCT;
330 	else
331 		uha->matchlvl = 0;
332 	return ((*cf->cf_attach->ca_match)(parent, cf, aux));
333 }
334 
335 int
336 uhidev_activate(struct device *self, int act)
337 {
338 	struct uhidev_softc *sc = (struct uhidev_softc *)self;
339 	int i, rv = 0;
340 
341 	switch (act) {
342 	case DVACT_ACTIVATE:
343 		break;
344 
345 	case DVACT_DEACTIVATE:
346 		for (i = 0; i < sc->sc_nrepid; i++)
347 			if (sc->sc_subdevs[i] != NULL)
348 				rv |= config_deactivate(
349 					&sc->sc_subdevs[i]->sc_dev);
350 		sc->sc_dying = 1;
351 		break;
352 	}
353 	return (rv);
354 }
355 
356 int
357 uhidev_detach(struct device *self, int flags)
358 {
359 	struct uhidev_softc *sc = (struct uhidev_softc *)self;
360 	int i, rv;
361 
362 	DPRINTF(("uhidev_detach: sc=%p flags=%d\n", sc, flags));
363 
364 	sc->sc_dying = 1;
365 	if (sc->sc_ipipe != NULL)
366 		usbd_abort_pipe(sc->sc_ipipe);
367 
368 	if (sc->sc_repdesc != NULL)
369 		free(sc->sc_repdesc, M_USBDEV);
370 
371 	rv = 0;
372 	for (i = 0; i < sc->sc_nrepid; i++) {
373 		if (sc->sc_subdevs[i] != NULL) {
374 			rv |= config_detach(&sc->sc_subdevs[i]->sc_dev, flags);
375 			sc->sc_subdevs[i] = NULL;
376 		}
377 	}
378 
379 	usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
380 			   &sc->sc_dev);
381 
382 	return (rv);
383 }
384 
385 void
386 uhidev_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status)
387 {
388 	struct uhidev_softc *sc = addr;
389 	struct uhidev *scd;
390 	u_char *p;
391 	u_int rep;
392 	u_int32_t cc;
393 
394 	usbd_get_xfer_status(xfer, NULL, NULL, &cc, NULL);
395 
396 #ifdef UHIDEV_DEBUG
397 	if (uhidevdebug > 5) {
398 		u_int32_t i;
399 
400 		DPRINTF(("uhidev_intr: status=%d cc=%d\n", status, cc));
401 		DPRINTF(("uhidev_intr: data ="));
402 		for (i = 0; i < cc; i++)
403 			DPRINTF((" %02x", sc->sc_ibuf[i]));
404 		DPRINTF(("\n"));
405 	}
406 #endif
407 
408 	if (status == USBD_CANCELLED)
409 		return;
410 
411 	if (status != USBD_NORMAL_COMPLETION) {
412 		DPRINTF(("%s: interrupt status=%d\n", sc->sc_dev.dv_xname,
413 			 status));
414 		usbd_clear_endpoint_stall_async(sc->sc_ipipe);
415 		return;
416 	}
417 
418 	p = sc->sc_ibuf;
419 	if (sc->sc_nrepid != 1)
420 		rep = *p++, cc--;
421 	else
422 		rep = 0;
423 	if (rep >= sc->sc_nrepid) {
424 		printf("uhidev_intr: bad repid %d\n", rep);
425 		return;
426 	}
427 	scd = sc->sc_subdevs[rep];
428 	DPRINTFN(5,("uhidev_intr: rep=%d, scd=%p state=0x%x\n",
429 		    rep, scd, scd ? scd->sc_state : 0));
430 	if (scd == NULL || !(scd->sc_state & UHIDEV_OPEN))
431 		return;
432 #ifdef UHIDEV_DEBUG
433 	if (scd->sc_in_rep_size != cc)
434 		printf("%s: bad input length %d != %d\n",sc->sc_dev.dv_xname,
435 		       scd->sc_in_rep_size, cc);
436 #endif
437 	scd->sc_intr(scd, p, cc);
438 }
439 
440 void
441 uhidev_get_report_desc(struct uhidev_softc *sc, void **desc, int *size)
442 {
443 	*desc = sc->sc_repdesc;
444 	*size = sc->sc_repdesc_size;
445 }
446 
447 int
448 uhidev_open(struct uhidev *scd)
449 {
450 	struct uhidev_softc *sc = scd->sc_parent;
451 	usbd_status err;
452 	int error;
453 
454 	DPRINTF(("uhidev_open: open pipe, state=%d refcnt=%d\n",
455 		 scd->sc_state, sc->sc_refcnt));
456 
457 	if (scd->sc_state & UHIDEV_OPEN)
458 		return (EBUSY);
459 	scd->sc_state |= UHIDEV_OPEN;
460 	if (sc->sc_refcnt++)
461 		return (0);
462 
463 	if (sc->sc_isize == 0)
464 		return (0);
465 
466 	sc->sc_ibuf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
467 
468 	/* Set up input interrupt pipe. */
469 	DPRINTF(("uhidev_open: isize=%d, ep=0x%02x\n", sc->sc_isize,
470 	    sc->sc_iep_addr));
471 
472 	err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_iep_addr,
473 		  USBD_SHORT_XFER_OK, &sc->sc_ipipe, sc, sc->sc_ibuf,
474 		  sc->sc_isize, uhidev_intr, USBD_DEFAULT_INTERVAL);
475 	if (err != USBD_NORMAL_COMPLETION) {
476 		DPRINTF(("uhidopen: usbd_open_pipe_intr failed, "
477 		    "error=%d\n", err));
478 		error = EIO;
479 		goto out1;
480 	}
481 
482 	DPRINTF(("uhidev_open: sc->sc_ipipe=%p\n", sc->sc_ipipe));
483 
484 	sc->sc_ixfer = usbd_alloc_xfer(sc->sc_udev);
485 	if (sc->sc_ixfer == NULL) {
486 		DPRINTF(("uhidev_open: couldn't allocate an xfer\n"));
487 		error = ENOMEM;
488 		goto out1; // xxxx
489 	}
490 
491 	/*
492 	 * Set up output interrupt pipe if an output interrupt endpoint
493 	 * exists.
494 	 */
495 	if (sc->sc_oep_addr != -1) {
496 		DPRINTF(("uhidev_open: oep=0x%02x\n", sc->sc_oep_addr));
497 
498 		err = usbd_open_pipe(sc->sc_iface, sc->sc_oep_addr,
499 		    0, &sc->sc_opipe);
500 
501 		if (err != USBD_NORMAL_COMPLETION) {
502 			DPRINTF(("uhidev_open: usbd_open_pipe failed, "
503 			    "error=%d\n", err));
504 			error = EIO;
505 			goto out2;
506 		}
507 		DPRINTF(("uhidev_open: sc->sc_opipe=%p\n", sc->sc_opipe));
508 
509 		sc->sc_oxfer = usbd_alloc_xfer(sc->sc_udev);
510 		if (sc->sc_oxfer == NULL) {
511 			DPRINTF(("uhidev_open: couldn't allocate an xfer\n"));
512 			error = ENOMEM;
513 			goto out3;
514 		}
515 
516 		sc->sc_owxfer = usbd_alloc_xfer(sc->sc_udev);
517 		if (sc->sc_owxfer == NULL) {
518 			DPRINTF(("uhidev_open: couldn't allocate owxfer\n"));
519 			error = ENOMEM;
520 			goto out3;
521 		}
522 	}
523 
524 	return (0);
525 
526 out3:
527 	/* Abort output pipe */
528 	usbd_close_pipe(sc->sc_opipe);
529 out2:
530 	/* Abort input pipe */
531 	usbd_close_pipe(sc->sc_ipipe);
532 out1:
533 	DPRINTF(("uhidev_open: failed in someway"));
534 	free(sc->sc_ibuf, M_USBDEV);
535 	scd->sc_state &= ~UHIDEV_OPEN;
536 	sc->sc_refcnt = 0;
537 	sc->sc_ipipe = NULL;
538 	sc->sc_opipe = NULL;
539 	if (sc->sc_oxfer != NULL) {
540 		usbd_free_xfer(sc->sc_oxfer);
541 		sc->sc_oxfer = NULL;
542 	}
543 	if (sc->sc_owxfer != NULL) {
544 		usbd_free_xfer(sc->sc_owxfer);
545 		sc->sc_owxfer = NULL;
546 	}
547 	return (error);
548 }
549 
550 void
551 uhidev_close(struct uhidev *scd)
552 {
553 	struct uhidev_softc *sc = scd->sc_parent;
554 
555 	if (!(scd->sc_state & UHIDEV_OPEN))
556 		return;
557 	scd->sc_state &= ~UHIDEV_OPEN;
558 	if (--sc->sc_refcnt)
559 		return;
560 	DPRINTF(("uhidev_close: close pipe\n"));
561 
562 	if (sc->sc_oxfer != NULL)
563 		usbd_free_xfer(sc->sc_oxfer);
564 
565 	if (sc->sc_owxfer != NULL)
566 		usbd_free_xfer(sc->sc_owxfer);
567 
568 	/* Disable interrupts. */
569 	if (sc->sc_opipe != NULL) {
570 		usbd_abort_pipe(sc->sc_opipe);
571 		usbd_close_pipe(sc->sc_opipe);
572 		sc->sc_opipe = NULL;
573 	}
574 
575 	if (sc->sc_ipipe != NULL) {
576 		usbd_abort_pipe(sc->sc_ipipe);
577 		usbd_close_pipe(sc->sc_ipipe);
578 		sc->sc_ipipe = NULL;
579 	}
580 
581 	if (sc->sc_ibuf != NULL) {
582 		free(sc->sc_ibuf, M_USBDEV);
583 		sc->sc_ibuf = NULL;
584 	}
585 }
586 
587 usbd_status
588 uhidev_set_report(struct uhidev *scd, int type, void *data, int len)
589 {
590 	char *buf;
591 	usbd_status retstat;
592 
593 	if (scd->sc_report_id == 0)
594 		return usbd_set_report(scd->sc_parent->sc_iface, type,
595 				       scd->sc_report_id, data, len);
596 
597 	buf = malloc(len + 1, M_TEMP, M_WAITOK);
598 	buf[0] = scd->sc_report_id;
599 	memcpy(buf+1, data, len);
600 
601 	retstat = usbd_set_report(scd->sc_parent->sc_iface, type,
602 				  scd->sc_report_id, buf, len + 1);
603 
604 	free(buf, M_TEMP);
605 
606 	return retstat;
607 }
608 
609 void
610 uhidev_set_report_async(struct uhidev *scd, int type, void *data, int len)
611 {
612 	/* XXX */
613 	char buf[100];
614 	if (scd->sc_report_id) {
615 		buf[0] = scd->sc_report_id;
616 		if ((uint)len > sizeof(buf) - 1) {
617 #ifdef DIAGNOSTIC
618 			printf("%s: report length too large (%d)\n",
619 			    scd->sc_dev.dv_xname, len);
620 #endif
621 			return;
622 		}
623 		memcpy(buf+1, data, len);
624 		len++;
625 		data = buf;
626 	}
627 
628 	usbd_set_report_async(scd->sc_parent->sc_iface, type,
629 			      scd->sc_report_id, data, len);
630 }
631 
632 usbd_status
633 uhidev_get_report(struct uhidev *scd, int type, void *data, int len)
634 {
635 	return usbd_get_report(scd->sc_parent->sc_iface, type,
636 			       scd->sc_report_id, data, len);
637 }
638 
639 usbd_status
640 uhidev_write(struct uhidev_softc *sc, void *data, int len)
641 {
642 
643 	DPRINTF(("uhidev_write: data=%p, len=%d\n", data, len));
644 
645 	if (sc->sc_opipe == NULL)
646 		return USBD_INVAL;
647 
648 #ifdef UHIDEV_DEBUG
649 	if (uhidevdebug > 50) {
650 
651 		u_int32_t i;
652 		u_int8_t *d = data;
653 
654 		DPRINTF(("uhidev_write: data ="));
655 		for (i = 0; i < len; i++)
656 			DPRINTF((" %02x", d[i]));
657 		DPRINTF(("\n"));
658 	}
659 #endif
660 	return usbd_intr_transfer(sc->sc_owxfer, sc->sc_opipe, 0,
661 	    USBD_NO_TIMEOUT, data, &len, "uhidevwi");
662 }
663