xref: /openbsd-src/sys/dev/usb/uhidev.c (revision 0b149fa0fbcf646d5093849f9dcec55b341ce482)
1 /*	$OpenBSD: uhidev.c,v 1.29 2007/09/11 13:39:34 gilles 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 #ifdef __macppc__
117 	/*
118 	 * Some Apple laptops have USB phantom devices which match
119 	 * the ADB devices.  We want to ignore them to avoid
120 	 * confusing users, as the real hardware underneath is adb
121 	 * and has already attached.
122 	 */
123 	if (uaa->vendor == USB_VENDOR_APPLE &&
124 	    uaa->product == USB_PRODUCT_APPLE_ADB)
125 		return (UMATCH_NONE);
126 #endif
127 
128 	return (UMATCH_IFACECLASS_GENERIC);
129 }
130 
131 void
132 uhidev_attach(struct device *parent, struct device *self, void *aux)
133 {
134 	struct uhidev_softc *sc = (struct uhidev_softc *)self;
135 	struct usb_attach_arg *uaa = aux;
136 	usbd_interface_handle iface = uaa->iface;
137 	usb_interface_descriptor_t *id;
138 	usb_endpoint_descriptor_t *ed;
139 	struct uhidev_attach_arg uha;
140 	struct uhidev *dev;
141 	int size, nrepid, repid, repsz;
142 	int repsizes[256];
143 	int i;
144 	void *desc;
145 	const void *descptr;
146 	usbd_status err;
147 	char *devinfop;
148 
149 	sc->sc_udev = uaa->device;
150 	sc->sc_iface = iface;
151 	id = usbd_get_interface_descriptor(iface);
152 
153 	devinfop = usbd_devinfo_alloc(uaa->device, 0);
154 	printf("\n%s: %s, iclass %d/%d\n", sc->sc_dev.dv_xname,
155 	       devinfop, id->bInterfaceClass, id->bInterfaceSubClass);
156 	usbd_devinfo_free(devinfop);
157 
158 	(void)usbd_set_idle(iface, 0, 0);
159 #if 0
160 
161 	qflags = usbd_get_quirks(sc->sc_udev)->uq_flags;
162 	if ((qflags & UQ_NO_SET_PROTO) == 0 &&
163 	    id->bInterfaceSubClass != UISUBCLASS_BOOT)
164 		(void)usbd_set_protocol(iface, 1);
165 #endif
166 
167 	sc->sc_iep_addr = sc->sc_oep_addr = -1;
168 	for (i = 0; i < id->bNumEndpoints; i++) {
169 		ed = usbd_interface2endpoint_descriptor(iface, i);
170 		if (ed == NULL) {
171 			printf("%s: could not read endpoint descriptor\n",
172 			    sc->sc_dev.dv_xname);
173 			sc->sc_dying = 1;
174 			return;
175 		}
176 
177 		DPRINTFN(10,("uhidev_attach: bLength=%d bDescriptorType=%d "
178 		    "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d"
179 		    " bInterval=%d\n",
180 		    ed->bLength, ed->bDescriptorType,
181 		    ed->bEndpointAddress & UE_ADDR,
182 		    UE_GET_DIR(ed->bEndpointAddress)==UE_DIR_IN? "in" : "out",
183 		    ed->bmAttributes & UE_XFERTYPE,
184 		    UGETW(ed->wMaxPacketSize), ed->bInterval));
185 
186 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
187 		    (ed->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT) {
188 			sc->sc_iep_addr = ed->bEndpointAddress;
189 		} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
190 		    (ed->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT) {
191 			sc->sc_oep_addr = ed->bEndpointAddress;
192 		} else {
193 			printf("%s: unexpected endpoint\n", sc->sc_dev.dv_xname);
194 			sc->sc_dying = 1;
195 			return;
196 		}
197 	}
198 
199 	/*
200 	 * Check that we found an input interrupt endpoint. The output interrupt
201 	 * endpoint is optional
202 	 */
203 	if (sc->sc_iep_addr == -1) {
204 		printf("%s: no input interrupt endpoint\n", sc->sc_dev.dv_xname);
205 		sc->sc_dying = 1;
206 		return;
207 	}
208 
209 	/* XXX need to extend this */
210 	descptr = NULL;
211 	if (uaa->vendor == USB_VENDOR_WACOM) {
212 		static uByte reportbuf[] = {2, 2, 2};
213 
214 		/* The report descriptor for the Wacom Graphire is broken. */
215 		switch (uaa->product) {
216 		case USB_PRODUCT_WACOM_GRAPHIRE:
217 			size = sizeof uhid_graphire_report_descr;
218 			descptr = uhid_graphire_report_descr;
219 			break;
220 		case USB_PRODUCT_WACOM_GRAPHIRE3_4X5:
221 		case USB_PRODUCT_WACOM_GRAPHIRE4_4X5:
222 			usbd_set_report(uaa->iface, UHID_FEATURE_REPORT, 2,
223 			    &reportbuf, sizeof reportbuf);
224 			size = sizeof uhid_graphire3_4x5_report_descr;
225 			descptr = uhid_graphire3_4x5_report_descr;
226 			break;
227 		default:
228 			/* Keep descriptor */
229 			break;
230 		}
231 	}
232 
233 	if (descptr) {
234 		desc = malloc(size, M_USBDEV, M_NOWAIT);
235 		if (desc == NULL)
236 			err = USBD_NOMEM;
237 		else {
238 			err = USBD_NORMAL_COMPLETION;
239 			memcpy(desc, descptr, size);
240 		}
241 	} else {
242 		desc = NULL;
243 		err = usbd_read_report_desc(uaa->iface, &desc, &size, M_USBDEV);
244 	}
245 	if (err) {
246 		printf("%s: no report descriptor\n", sc->sc_dev.dv_xname);
247 		sc->sc_dying = 1;
248 		return;
249 	}
250 
251 	sc->sc_repdesc = desc;
252 	sc->sc_repdesc_size = size;
253 
254 	uha.uaa = uaa;
255 	nrepid = uhidev_maxrepid(desc, size);
256 	if (nrepid < 0)
257 		return;
258 	if (nrepid > 0)
259 		printf("%s: %d report ids\n", sc->sc_dev.dv_xname, nrepid);
260 	nrepid++;
261 	sc->sc_subdevs = malloc(nrepid * sizeof(struct device *),
262 	    M_USBDEV, M_NOWAIT | M_ZERO);
263 	if (sc->sc_subdevs == NULL) {
264 		printf("%s: no memory\n", sc->sc_dev.dv_xname);
265 		return;
266 	}
267 	sc->sc_nrepid = nrepid;
268 	sc->sc_isize = 0;
269 
270 	usbd_add_drv_event(USB_EVENT_DRIVER_ATTACH, sc->sc_udev,
271 			   &sc->sc_dev);
272 
273 	for (repid = 0; repid < nrepid; repid++) {
274 		repsz = hid_report_size(desc, size, hid_input, repid);
275 		DPRINTF(("uhidev_match: repid=%d, repsz=%d\n", repid, repsz));
276 		repsizes[repid] = repsz;
277 		if (repsz > 0) {
278 			if (repsz > sc->sc_isize)
279 				sc->sc_isize = repsz;
280 		}
281 	}
282 	sc->sc_isize += nrepid != 1;	/* space for report ID */
283 	DPRINTF(("uhidev_attach: isize=%d\n", sc->sc_isize));
284 
285 	uha.parent = sc;
286 	for (repid = 0; repid < nrepid; repid++) {
287 		DPRINTF(("uhidev_match: try repid=%d\n", repid));
288 		if (hid_report_size(desc, size, hid_input, repid) == 0 &&
289 		    hid_report_size(desc, size, hid_output, repid) == 0 &&
290 		    hid_report_size(desc, size, hid_feature, repid) == 0) {
291 			;	/* already NULL in sc->sc_subdevs[repid] */
292 		} else {
293 			uha.reportid = repid;
294 			dev = (struct uhidev *)config_found_sm(self, &uha,
295 			                           uhidevprint, uhidevsubmatch);
296 			sc->sc_subdevs[repid] = dev;
297 			if (dev != NULL) {
298 				dev->sc_in_rep_size = repsizes[repid];
299 #ifdef DIAGNOSTIC
300 				DPRINTF(("uhidev_match: repid=%d dev=%p\n",
301 					 repid, dev));
302 				if (dev->sc_intr == NULL) {
303 					printf("%s: sc_intr == NULL\n",
304 					       sc->sc_dev.dv_xname);
305 					return;
306 				}
307 #endif
308 			}
309 		}
310 	}
311 }
312 
313 int
314 uhidev_maxrepid(void *buf, int len)
315 {
316 	struct hid_data *d;
317 	struct hid_item h;
318 	int maxid;
319 
320 	maxid = -1;
321 	h.report_ID = 0;
322 	for (d = hid_start_parse(buf, len, hid_none); hid_get_item(d, &h); )
323 		if (h.report_ID > maxid)
324 			maxid = h.report_ID;
325 	hid_end_parse(d);
326 	return (maxid);
327 }
328 
329 int
330 uhidevprint(void *aux, const char *pnp)
331 {
332 	struct uhidev_attach_arg *uha = aux;
333 
334 	if (pnp)
335 		printf("uhid at %s", pnp);
336 	if (uha->reportid != 0)
337 		printf(" reportid %d", uha->reportid);
338 	return (UNCONF);
339 }
340 
341 int uhidevsubmatch(struct device *parent, void *match, void *aux)
342 {
343 	struct uhidev_attach_arg *uha = aux;
344         struct cfdata *cf = match;
345 
346 	if (cf->uhidevcf_reportid != UHIDEV_UNK_REPORTID &&
347 	    cf->uhidevcf_reportid != uha->reportid)
348 		return (0);
349 	if (cf->uhidevcf_reportid == uha->reportid)
350 		uha->matchlvl = UMATCH_VENDOR_PRODUCT;
351 	else
352 		uha->matchlvl = 0;
353 	return ((*cf->cf_attach->ca_match)(parent, cf, aux));
354 }
355 
356 int
357 uhidev_activate(struct device *self, enum devact act)
358 {
359 	struct uhidev_softc *sc = (struct uhidev_softc *)self;
360 	int i, rv = 0;
361 
362 	switch (act) {
363 	case DVACT_ACTIVATE:
364 		break;
365 
366 	case DVACT_DEACTIVATE:
367 		for (i = 0; i < sc->sc_nrepid; i++)
368 			if (sc->sc_subdevs[i] != NULL)
369 				rv |= config_deactivate(
370 					&sc->sc_subdevs[i]->sc_dev);
371 		sc->sc_dying = 1;
372 		break;
373 	}
374 	return (rv);
375 }
376 
377 int
378 uhidev_detach(struct device *self, int flags)
379 {
380 	struct uhidev_softc *sc = (struct uhidev_softc *)self;
381 	int i, rv;
382 
383 	DPRINTF(("uhidev_detach: sc=%p flags=%d\n", sc, flags));
384 
385 	sc->sc_dying = 1;
386 	if (sc->sc_ipipe != NULL)
387 		usbd_abort_pipe(sc->sc_ipipe);
388 
389 	if (sc->sc_repdesc != NULL)
390 		free(sc->sc_repdesc, M_USBDEV);
391 
392 	rv = 0;
393 	for (i = 0; i < sc->sc_nrepid; i++) {
394 		if (sc->sc_subdevs[i] != NULL) {
395 			rv |= config_detach(&sc->sc_subdevs[i]->sc_dev, flags);
396 			sc->sc_subdevs[i] = NULL;
397 		}
398 	}
399 
400 	usbd_add_drv_event(USB_EVENT_DRIVER_DETACH, sc->sc_udev,
401 			   &sc->sc_dev);
402 
403 	return (rv);
404 }
405 
406 void
407 uhidev_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status)
408 {
409 	struct uhidev_softc *sc = addr;
410 	struct uhidev *scd;
411 	u_char *p;
412 	u_int rep;
413 	u_int32_t cc;
414 
415 	usbd_get_xfer_status(xfer, NULL, NULL, &cc, NULL);
416 
417 #ifdef UHIDEV_DEBUG
418 	if (uhidevdebug > 5) {
419 		u_int32_t i;
420 
421 		DPRINTF(("uhidev_intr: status=%d cc=%d\n", status, cc));
422 		DPRINTF(("uhidev_intr: data ="));
423 		for (i = 0; i < cc; i++)
424 			DPRINTF((" %02x", sc->sc_ibuf[i]));
425 		DPRINTF(("\n"));
426 	}
427 #endif
428 
429 	if (status == USBD_CANCELLED)
430 		return;
431 
432 	if (status != USBD_NORMAL_COMPLETION) {
433 		DPRINTF(("%s: interrupt status=%d\n", sc->sc_dev.dv_xname,
434 			 status));
435 		usbd_clear_endpoint_stall_async(sc->sc_ipipe);
436 		return;
437 	}
438 
439 	p = sc->sc_ibuf;
440 	if (sc->sc_nrepid != 1)
441 		rep = *p++, cc--;
442 	else
443 		rep = 0;
444 	if (rep >= sc->sc_nrepid) {
445 		printf("uhidev_intr: bad repid %d\n", rep);
446 		return;
447 	}
448 	scd = sc->sc_subdevs[rep];
449 	DPRINTFN(5,("uhidev_intr: rep=%d, scd=%p state=0x%x\n",
450 		    rep, scd, scd ? scd->sc_state : 0));
451 	if (scd == NULL || !(scd->sc_state & UHIDEV_OPEN))
452 		return;
453 #ifdef UHIDEV_DEBUG
454 	if (scd->sc_in_rep_size != cc)
455 		printf("%s: bad input length %d != %d\n",sc->sc_dev.dv_xname,
456 		       scd->sc_in_rep_size, cc);
457 #endif
458 	scd->sc_intr(scd, p, cc);
459 }
460 
461 void
462 uhidev_get_report_desc(struct uhidev_softc *sc, void **desc, int *size)
463 {
464 	*desc = sc->sc_repdesc;
465 	*size = sc->sc_repdesc_size;
466 }
467 
468 int
469 uhidev_open(struct uhidev *scd)
470 {
471 	struct uhidev_softc *sc = scd->sc_parent;
472 	usbd_status err;
473 	int error;
474 
475 	DPRINTF(("uhidev_open: open pipe, state=%d refcnt=%d\n",
476 		 scd->sc_state, sc->sc_refcnt));
477 
478 	if (scd->sc_state & UHIDEV_OPEN)
479 		return (EBUSY);
480 	scd->sc_state |= UHIDEV_OPEN;
481 	if (sc->sc_refcnt++)
482 		return (0);
483 
484 	if (sc->sc_isize == 0)
485 		return (0);
486 
487 	sc->sc_ibuf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
488 
489 	/* Set up input interrupt pipe. */
490 	DPRINTF(("uhidev_open: isize=%d, ep=0x%02x\n", sc->sc_isize,
491 	    sc->sc_iep_addr));
492 
493 	err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_iep_addr,
494 		  USBD_SHORT_XFER_OK, &sc->sc_ipipe, sc, sc->sc_ibuf,
495 		  sc->sc_isize, uhidev_intr, USBD_DEFAULT_INTERVAL);
496 	if (err != USBD_NORMAL_COMPLETION) {
497 		DPRINTF(("uhidopen: usbd_open_pipe_intr failed, "
498 		    "error=%d\n", err));
499 		error = EIO;
500 		goto out1;
501 	}
502 
503 	DPRINTF(("uhidev_open: sc->sc_ipipe=%p\n", sc->sc_ipipe));
504 
505 	sc->sc_ixfer = usbd_alloc_xfer(sc->sc_udev);
506 	if (sc->sc_ixfer == NULL) {
507 		DPRINTF(("uhidev_open: couldn't allocate an xfer\n"));
508 		error = ENOMEM;
509 		goto out1; // xxxx
510 	}
511 
512 	/*
513 	 * Set up output interrupt pipe if an output interrupt endpoint
514 	 * exists.
515 	 */
516 	if (sc->sc_oep_addr != -1) {
517 		DPRINTF(("uhidev_open: oep=0x%02x\n", sc->sc_oep_addr));
518 
519 		err = usbd_open_pipe(sc->sc_iface, sc->sc_oep_addr,
520 		    0, &sc->sc_opipe);
521 
522 		if (err != USBD_NORMAL_COMPLETION) {
523 			DPRINTF(("uhidev_open: usbd_open_pipe failed, "
524 			    "error=%d\n", err));
525 			error = EIO;
526 			goto out2;
527 		}
528 		DPRINTF(("uhidev_open: sc->sc_opipe=%p\n", sc->sc_opipe));
529 
530 		sc->sc_oxfer = usbd_alloc_xfer(sc->sc_udev);
531 		if (sc->sc_oxfer == NULL) {
532 			DPRINTF(("uhidev_open: couldn't allocate an xfer\n"));
533 			error = ENOMEM;
534 			goto out3;
535 		}
536 
537 		sc->sc_owxfer = usbd_alloc_xfer(sc->sc_udev);
538 		if (sc->sc_owxfer == NULL) {
539 			DPRINTF(("uhidev_open: couldn't allocate owxfer\n"));
540 			error = ENOMEM;
541 			goto out3;
542 		}
543 	}
544 
545 	return (0);
546 
547 out3:
548 	/* Abort output pipe */
549 	usbd_close_pipe(sc->sc_opipe);
550 out2:
551 	/* Abort input pipe */
552 	usbd_close_pipe(sc->sc_ipipe);
553 out1:
554 	DPRINTF(("uhidev_open: failed in someway"));
555 	free(sc->sc_ibuf, M_USBDEV);
556 	scd->sc_state &= ~UHIDEV_OPEN;
557 	sc->sc_refcnt = 0;
558 	sc->sc_ipipe = NULL;
559 	sc->sc_opipe = NULL;
560 	if (sc->sc_oxfer != NULL) {
561 		usbd_free_xfer(sc->sc_oxfer);
562 		sc->sc_oxfer = NULL;
563 	}
564 	if (sc->sc_owxfer != NULL) {
565 		usbd_free_xfer(sc->sc_owxfer);
566 		sc->sc_owxfer = NULL;
567 	}
568 	return (error);
569 }
570 
571 void
572 uhidev_close(struct uhidev *scd)
573 {
574 	struct uhidev_softc *sc = scd->sc_parent;
575 
576 	if (!(scd->sc_state & UHIDEV_OPEN))
577 		return;
578 	scd->sc_state &= ~UHIDEV_OPEN;
579 	if (--sc->sc_refcnt)
580 		return;
581 	DPRINTF(("uhidev_close: close pipe\n"));
582 
583 	if (sc->sc_oxfer != NULL)
584 		usbd_free_xfer(sc->sc_oxfer);
585 
586 	if (sc->sc_owxfer != NULL)
587 		usbd_free_xfer(sc->sc_owxfer);
588 
589 	/* Disable interrupts. */
590 	if (sc->sc_opipe != NULL) {
591 		usbd_abort_pipe(sc->sc_opipe);
592 		usbd_close_pipe(sc->sc_opipe);
593 		sc->sc_opipe = NULL;
594 	}
595 
596 	if (sc->sc_ipipe != NULL) {
597 		usbd_abort_pipe(sc->sc_ipipe);
598 		usbd_close_pipe(sc->sc_ipipe);
599 		sc->sc_ipipe = NULL;
600 	}
601 
602 	if (sc->sc_ibuf != NULL) {
603 		free(sc->sc_ibuf, M_USBDEV);
604 		sc->sc_ibuf = NULL;
605 	}
606 }
607 
608 usbd_status
609 uhidev_set_report(struct uhidev *scd, int type, void *data, int len)
610 {
611 	char *buf;
612 	usbd_status retstat;
613 
614 	if (scd->sc_report_id == 0)
615 		return usbd_set_report(scd->sc_parent->sc_iface, type,
616 				       scd->sc_report_id, data, len);
617 
618 	buf = malloc(len + 1, M_TEMP, M_WAITOK);
619 	buf[0] = scd->sc_report_id;
620 	memcpy(buf+1, data, len);
621 
622 	retstat = usbd_set_report(scd->sc_parent->sc_iface, type,
623 				  scd->sc_report_id, data, len + 1);
624 
625 	free(buf, M_TEMP);
626 
627 	return retstat;
628 }
629 
630 void
631 uhidev_set_report_async(struct uhidev *scd, int type, void *data, int len)
632 {
633 	/* XXX */
634 	char buf[100];
635 	if (scd->sc_report_id) {
636 		buf[0] = scd->sc_report_id;
637 		memcpy(buf+1, data, len);
638 		len++;
639 		data = buf;
640 	}
641 
642 	usbd_set_report_async(scd->sc_parent->sc_iface, type,
643 			      scd->sc_report_id, data, len);
644 }
645 
646 usbd_status
647 uhidev_get_report(struct uhidev *scd, int type, void *data, int len)
648 {
649 	return usbd_get_report(scd->sc_parent->sc_iface, type,
650 			       scd->sc_report_id, data, len);
651 }
652 
653 usbd_status
654 uhidev_write(struct uhidev_softc *sc, void *data, int len)
655 {
656 
657 	DPRINTF(("uhidev_write: data=%p, len=%d\n", data, len));
658 
659 	if (sc->sc_opipe == NULL)
660 		return USBD_INVAL;
661 
662 #ifdef UHIDEV_DEBUG
663 	if (uhidevdebug > 50) {
664 
665 		u_int32_t i;
666 		u_int8_t *d = data;
667 
668 		DPRINTF(("uhidev_write: data ="));
669 		for (i = 0; i < len; i++)
670 			DPRINTF((" %02x", d[i]));
671 		DPRINTF(("\n"));
672 	}
673 #endif
674 	return usbd_intr_transfer(sc->sc_owxfer, sc->sc_opipe, 0,
675 	    USBD_NO_TIMEOUT, data, &len, "uhidevwi");
676 }
677