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