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