xref: /openbsd-src/sys/dev/usb/uhidev.c (revision 1a8dbaac879b9f3335ad7fb25429ce63ac1d6bac)
1 /*	$OpenBSD: uhidev.c,v 1.83 2020/08/31 12:26:49 patrick 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 <machine/bus.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/usbdivar.h>
56 #include <dev/usb/usb_mem.h>
57 #include <dev/usb/usb_quirks.h>
58 
59 #include <dev/usb/uhidev.h>
60 
61 #ifndef SMALL_KERNEL
62 /* Replacement report descriptors for devices shipped with broken ones */
63 #include <dev/usb/uhid_rdesc.h>
64 int uhidev_use_rdesc(struct uhidev_softc *, usb_interface_descriptor_t *,
65 		int, int, void **, int *);
66 #define UISUBCLASS_XBOX360_CONTROLLER 0x5d
67 #define UIPROTO_XBOX360_GAMEPAD 0x01
68 #endif /* !SMALL_KERNEL */
69 
70 #define DEVNAME(sc)		((sc)->sc_dev.dv_xname)
71 
72 #ifdef UHIDEV_DEBUG
73 #define DPRINTF(x)	do { if (uhidevdebug) printf x; } while (0)
74 #define DPRINTFN(n,x)	do { if (uhidevdebug>(n)) printf x; } while (0)
75 int	uhidevdebug = 0;
76 #else
77 #define DPRINTF(x)
78 #define DPRINTFN(n,x)
79 #endif
80 
81 struct uhidev_async_info {
82 	void (*callback)(void *priv, int id, void *data, int len);
83 	void *priv;
84 	void *data;
85 	int id;
86 };
87 
88 void uhidev_intr(struct usbd_xfer *, void *, usbd_status);
89 
90 int uhidev_maxrepid(void *buf, int len);
91 int uhidevprint(void *aux, const char *pnp);
92 int uhidevsubmatch(struct device *parent, void *cf, void *aux);
93 
94 int uhidev_match(struct device *, void *, void *);
95 void uhidev_attach(struct device *, struct device *, void *);
96 int uhidev_detach(struct device *, int);
97 int uhidev_activate(struct device *, int);
98 
99 void uhidev_get_report_async_cb(struct usbd_xfer *, void *, usbd_status);
100 void uhidev_set_report_async_cb(struct usbd_xfer *, void *, usbd_status);
101 
102 struct cfdriver uhidev_cd = {
103 	NULL, "uhidev", DV_DULL
104 };
105 
106 const struct cfattach uhidev_ca = {
107 	sizeof(struct uhidev_softc), uhidev_match, uhidev_attach,
108 	uhidev_detach, uhidev_activate,
109 };
110 
111 int
112 uhidev_match(struct device *parent, void *match, void *aux)
113 {
114 	struct usb_attach_arg *uaa = aux;
115 	usb_interface_descriptor_t *id;
116 
117 	if (uaa->iface == NULL)
118 		return (UMATCH_NONE);
119 	id = usbd_get_interface_descriptor(uaa->iface);
120 	if (id == NULL)
121 		return (UMATCH_NONE);
122 #ifndef SMALL_KERNEL
123 	if (id->bInterfaceClass == UICLASS_VENDOR &&
124 	    id->bInterfaceSubClass == UISUBCLASS_XBOX360_CONTROLLER &&
125 	    id->bInterfaceProtocol == UIPROTO_XBOX360_GAMEPAD)
126 		return (UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO);
127 #endif /* !SMALL_KERNEL */
128 	if (id->bInterfaceClass != UICLASS_HID)
129 		return (UMATCH_NONE);
130 	if (usbd_get_quirks(uaa->device)->uq_flags & UQ_BAD_HID)
131 		return (UMATCH_NONE);
132 
133 	return (UMATCH_IFACECLASS_GENERIC);
134 }
135 
136 void
137 uhidev_attach(struct device *parent, struct device *self, void *aux)
138 {
139 	struct uhidev_softc *sc = (struct uhidev_softc *)self;
140 	struct usb_attach_arg *uaa = aux;
141 	usb_interface_descriptor_t *id;
142 	usb_endpoint_descriptor_t *ed;
143 	struct uhidev_attach_arg uha;
144 	int size, nrepid, repid, repsz;
145 	int i, repsizes[256];
146 	void *desc = NULL;
147 	struct device *dev;
148 
149 	sc->sc_udev = uaa->device;
150 	sc->sc_iface = uaa->iface;
151 	sc->sc_ifaceno = uaa->ifaceno;
152 	id = usbd_get_interface_descriptor(sc->sc_iface);
153 
154 	sc->sc_iep_addr = sc->sc_oep_addr = -1;
155 	for (i = 0; i < id->bNumEndpoints; i++) {
156 		ed = usbd_interface2endpoint_descriptor(sc->sc_iface, i);
157 		if (ed == NULL) {
158 			printf("%s: could not read endpoint descriptor\n",
159 			    DEVNAME(sc));
160 			return;
161 		}
162 
163 		DPRINTFN(10,("uhidev_attach: bLength=%d bDescriptorType=%d "
164 		    "bEndpointAddress=%d-%s bmAttributes=%d wMaxPacketSize=%d"
165 		    " bInterval=%d\n",
166 		    ed->bLength, ed->bDescriptorType,
167 		    ed->bEndpointAddress & UE_ADDR,
168 		    UE_GET_DIR(ed->bEndpointAddress)==UE_DIR_IN? "in" : "out",
169 		    UE_GET_XFERTYPE(ed->bmAttributes),
170 		    UGETW(ed->wMaxPacketSize), ed->bInterval));
171 
172 		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
173 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
174 			sc->sc_iep_addr = ed->bEndpointAddress;
175 		} else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
176 		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_INTERRUPT) {
177 			sc->sc_oep_addr = ed->bEndpointAddress;
178 		} else {
179 			printf("%s: unexpected endpoint\n", DEVNAME(sc));
180 			return;
181 		}
182 	}
183 
184 	/*
185 	 * Check that we found an input interrupt endpoint.
186 	 * The output interrupt endpoint is optional
187 	 */
188 	if (sc->sc_iep_addr == -1) {
189 		printf("%s: no input interrupt endpoint\n", DEVNAME(sc));
190 		return;
191 	}
192 
193 #ifndef SMALL_KERNEL
194 	if (uhidev_use_rdesc(sc, id, uaa->vendor, uaa->product, &desc, &size))
195 		return;
196 #endif /* !SMALL_KERNEL */
197 
198 	if (desc == NULL) {
199 		struct usb_hid_descriptor *hid;
200 
201 		hid = usbd_get_hid_descriptor(sc->sc_udev, id);
202 		if (hid == NULL) {
203 			printf("%s: no HID descriptor\n", DEVNAME(sc));
204 			return;
205 		}
206 		size = UGETW(hid->descrs[0].wDescriptorLength);
207 		desc = malloc(size, M_USBDEV, M_NOWAIT);
208 		if (desc == NULL) {
209 			printf("%s: no memory\n", DEVNAME(sc));
210 			return;
211 		}
212 		if (usbd_get_report_descriptor(sc->sc_udev, sc->sc_ifaceno,
213 		    desc, size)) {
214 			printf("%s: no report descriptor\n", DEVNAME(sc));
215 			free(desc, M_USBDEV, size);
216 			return;
217 		}
218 	}
219 
220 	sc->sc_repdesc = desc;
221 	sc->sc_repdesc_size = size;
222 
223 	nrepid = uhidev_maxrepid(desc, size);
224 	if (nrepid < 0)
225 		return;
226 	printf("%s: iclass %d/%d", DEVNAME(sc), id->bInterfaceClass,
227 	    id->bInterfaceSubClass);
228 	if (nrepid > 0)
229 		printf(", %d report id%s", nrepid, nrepid > 1 ? "s" : "");
230 	printf("\n");
231 	nrepid++;
232 	sc->sc_subdevs = mallocarray(nrepid, sizeof(struct uhidev *),
233 	    M_USBDEV, M_NOWAIT | M_ZERO);
234 	if (sc->sc_subdevs == NULL) {
235 		printf("%s: no memory\n", DEVNAME(sc));
236 		return;
237 	}
238 	sc->sc_nrepid = nrepid;
239 	sc->sc_isize = 0;
240 
241 	for (repid = 0; repid < nrepid; repid++) {
242 		repsz = hid_report_size(desc, size, hid_input, repid);
243 		DPRINTF(("uhidev_match: repid=%d, repsz=%d\n", repid, repsz));
244 		repsizes[repid] = repsz;
245 		if (repsz > sc->sc_isize)
246 			sc->sc_isize = repsz;
247 	}
248 	sc->sc_isize += (nrepid != 1);	/* one byte for the report ID */
249 	DPRINTF(("uhidev_attach: isize=%d\n", sc->sc_isize));
250 
251 	uha.uaa = uaa;
252 	uha.parent = sc;
253 	uha.reportid = UHIDEV_CLAIM_ALLREPORTID;
254 
255 	/* Look for a driver claiming all report IDs first. */
256 	dev = config_found_sm(self, &uha, NULL, uhidevsubmatch);
257 	if (dev != NULL) {
258 		for (repid = 0; repid < nrepid; repid++)
259 			sc->sc_subdevs[repid] = (struct uhidev *)dev;
260 		return;
261 	}
262 
263 	for (repid = 0; repid < nrepid; repid++) {
264 		DPRINTF(("%s: try repid=%d\n", __func__, repid));
265 		if (hid_report_size(desc, size, hid_input, repid) == 0 &&
266 		    hid_report_size(desc, size, hid_output, repid) == 0 &&
267 		    hid_report_size(desc, size, hid_feature, repid) == 0)
268 			continue;
269 
270 		uha.reportid = repid;
271 		dev = config_found_sm(self, &uha, uhidevprint, uhidevsubmatch);
272 		sc->sc_subdevs[repid] = (struct uhidev *)dev;
273 	}
274 }
275 
276 #ifndef SMALL_KERNEL
277 int
278 uhidev_use_rdesc(struct uhidev_softc *sc, usb_interface_descriptor_t *id,
279 		int vendor, int product, void **descp, int *sizep)
280 {
281 	static uByte reportbuf[] = {2, 2};
282 	const void *descptr = NULL;
283 	void *desc;
284 	int size;
285 
286 	if (vendor == USB_VENDOR_WACOM) {
287 		/* The report descriptor for the Wacom Graphire is broken. */
288 		switch (product) {
289 		case USB_PRODUCT_WACOM_GRAPHIRE:
290 			size = sizeof(uhid_graphire_report_descr);
291 			descptr = uhid_graphire_report_descr;
292 			break;
293 		case USB_PRODUCT_WACOM_GRAPHIRE3_4X5:
294 		case USB_PRODUCT_WACOM_GRAPHIRE4_4X5:
295 			uhidev_set_report(sc, UHID_FEATURE_REPORT,
296 			    2, &reportbuf, sizeof(reportbuf));
297 			size = sizeof(uhid_graphire3_4x5_report_descr);
298 			descptr = uhid_graphire3_4x5_report_descr;
299 			break;
300 		default:
301 			break;
302 		}
303 	} else if ((id->bInterfaceClass == UICLASS_VENDOR &&
304 		   id->bInterfaceSubClass == UISUBCLASS_XBOX360_CONTROLLER &&
305 		   id->bInterfaceProtocol == UIPROTO_XBOX360_GAMEPAD)) {
306 		/* The Xbox 360 gamepad has no report descriptor. */
307 		size = sizeof(uhid_xb360gp_report_descr);
308 		descptr = uhid_xb360gp_report_descr;
309 	}
310 
311 	if (descptr) {
312 		desc = malloc(size, M_USBDEV, M_NOWAIT);
313 		if (desc == NULL)
314 			return (ENOMEM);
315 
316 		memcpy(desc, descptr, size);
317 
318 		*descp = desc;
319 		*sizep = size;
320 	}
321 
322 	return (0);
323 }
324 #endif /* !SMALL_KERNEL */
325 
326 int
327 uhidev_maxrepid(void *buf, int len)
328 {
329 	struct hid_data *d;
330 	struct hid_item h;
331 	int maxid;
332 
333 	maxid = -1;
334 	h.report_ID = 0;
335 	for (d = hid_start_parse(buf, len, hid_none); hid_get_item(d, &h); )
336 		if (h.report_ID > maxid)
337 			maxid = h.report_ID;
338 	hid_end_parse(d);
339 	return (maxid);
340 }
341 
342 int
343 uhidevprint(void *aux, const char *pnp)
344 {
345 	struct uhidev_attach_arg *uha = aux;
346 
347 	if (pnp)
348 		printf("uhid at %s", pnp);
349 	if (uha->reportid != 0 && uha->reportid != UHIDEV_CLAIM_ALLREPORTID)
350 		printf(" reportid %d", uha->reportid);
351 	return (UNCONF);
352 }
353 
354 int uhidevsubmatch(struct device *parent, void *match, void *aux)
355 {
356 	struct uhidev_attach_arg *uha = aux;
357         struct cfdata *cf = match;
358 
359 	if (cf->uhidevcf_reportid != UHIDEV_UNK_REPORTID &&
360 	    cf->uhidevcf_reportid != uha->reportid)
361 		return (0);
362 	return ((*cf->cf_attach->ca_match)(parent, cf, aux));
363 }
364 
365 int
366 uhidev_activate(struct device *self, int act)
367 {
368 	struct uhidev_softc *sc = (struct uhidev_softc *)self;
369 	int i, rv = 0, r;
370 
371 	switch (act) {
372 	case DVACT_DEACTIVATE:
373 		for (i = 0; i < sc->sc_nrepid; i++)
374 			if (sc->sc_subdevs[i] != NULL) {
375 				r = config_deactivate(
376 				    &sc->sc_subdevs[i]->sc_dev);
377 				if (r && r != EOPNOTSUPP)
378 					rv = r;
379 			}
380 		usbd_deactivate(sc->sc_udev);
381 		break;
382 	}
383 	return (rv);
384 }
385 
386 int
387 uhidev_detach(struct device *self, int flags)
388 {
389 	struct uhidev_softc *sc = (struct uhidev_softc *)self;
390 	int i, rv = 0;
391 
392 	DPRINTF(("uhidev_detach: sc=%p flags=%d\n", sc, flags));
393 
394 	if (sc->sc_opipe != NULL) {
395 		usbd_close_pipe(sc->sc_opipe);
396 		sc->sc_opipe = NULL;
397 	}
398 
399 	if (sc->sc_ipipe != NULL) {
400 		usbd_close_pipe(sc->sc_ipipe);
401 		sc->sc_ipipe = NULL;
402 	}
403 
404 	if (sc->sc_repdesc != NULL)
405 		free(sc->sc_repdesc, M_USBDEV, sc->sc_repdesc_size);
406 
407 	/*
408 	 * XXX Check if we have only one children claiming all the Report
409 	 * IDs, this is a hack since we need a dev -> Report ID mapping
410 	 * for uhidev_intr().
411 	 */
412 	if (sc->sc_nrepid > 1 && sc->sc_subdevs[0] != NULL &&
413 	    sc->sc_subdevs[0] == sc->sc_subdevs[1])
414 		return (config_detach(&sc->sc_subdevs[0]->sc_dev, flags));
415 
416 	for (i = 0; i < sc->sc_nrepid; i++) {
417 		if (sc->sc_subdevs[i] != NULL) {
418 			rv |= config_detach(&sc->sc_subdevs[i]->sc_dev, flags);
419 			sc->sc_subdevs[i] = NULL;
420 		}
421 	}
422 
423 	return (rv);
424 }
425 
426 void
427 uhidev_intr(struct usbd_xfer *xfer, void *addr, usbd_status status)
428 {
429 	struct uhidev_softc *sc = addr;
430 	struct uhidev *scd;
431 	u_char *p;
432 	u_int rep;
433 	u_int32_t cc;
434 
435 	if (usbd_is_dying(sc->sc_udev))
436 		return;
437 
438 	usbd_get_xfer_status(xfer, NULL, NULL, &cc, NULL);
439 
440 #ifdef UHIDEV_DEBUG
441 	if (uhidevdebug > 5) {
442 		u_int32_t i;
443 
444 		DPRINTF(("uhidev_intr: status=%d cc=%d\n", status, cc));
445 		DPRINTF(("uhidev_intr: data ="));
446 		for (i = 0; i < cc; i++)
447 			DPRINTF((" %02x", sc->sc_ibuf[i]));
448 		DPRINTF(("\n"));
449 	}
450 #endif
451 
452 	if (status == USBD_CANCELLED || status == USBD_IOERROR)
453 		return;
454 
455 	if (status != USBD_NORMAL_COMPLETION) {
456 		DPRINTF(("%s: interrupt status=%d\n", DEVNAME(sc), status));
457 		usbd_clear_endpoint_stall_async(sc->sc_ipipe);
458 		return;
459 	}
460 
461 	p = sc->sc_ibuf;
462 	if (sc->sc_nrepid != 1)
463 		rep = *p++, cc--;
464 	else
465 		rep = 0;
466 	if (rep >= sc->sc_nrepid) {
467 		printf("uhidev_intr: bad repid %d\n", rep);
468 		return;
469 	}
470 	scd = sc->sc_subdevs[rep];
471 	DPRINTFN(5,("uhidev_intr: rep=%d, scd=%p state=0x%x\n",
472 		    rep, scd, scd ? scd->sc_state : 0));
473 	if (scd == NULL || !(scd->sc_state & UHIDEV_OPEN))
474 		return;
475 
476 	scd->sc_intr(scd, p, cc);
477 }
478 
479 void
480 uhidev_get_report_desc(struct uhidev_softc *sc, void **desc, int *size)
481 {
482 	*desc = sc->sc_repdesc;
483 	*size = sc->sc_repdesc_size;
484 }
485 
486 int
487 uhidev_open(struct uhidev *scd)
488 {
489 	struct uhidev_softc *sc = scd->sc_parent;
490 	usbd_status err;
491 	int error;
492 
493 	DPRINTF(("uhidev_open: open pipe, state=%d refcnt=%d\n",
494 		 scd->sc_state, sc->sc_refcnt));
495 
496 	if (scd->sc_state & UHIDEV_OPEN)
497 		return (EBUSY);
498 	scd->sc_state |= UHIDEV_OPEN;
499 	if (sc->sc_refcnt++)
500 		return (0);
501 
502 	if (sc->sc_isize == 0)
503 		return (0);
504 
505 	sc->sc_ibuf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
506 
507 	/* Set up input interrupt pipe. */
508 	DPRINTF(("uhidev_open: isize=%d, ep=0x%02x\n", sc->sc_isize,
509 	    sc->sc_iep_addr));
510 
511 	err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_iep_addr,
512 		  USBD_SHORT_XFER_OK, &sc->sc_ipipe, sc, sc->sc_ibuf,
513 		  sc->sc_isize, uhidev_intr, USBD_DEFAULT_INTERVAL);
514 	if (err != USBD_NORMAL_COMPLETION) {
515 		DPRINTF(("uhidopen: usbd_open_pipe_intr failed, "
516 		    "error=%d\n", err));
517 		error = EIO;
518 		goto out1;
519 	}
520 
521 	DPRINTF(("uhidev_open: sc->sc_ipipe=%p\n", sc->sc_ipipe));
522 
523 	sc->sc_ixfer = usbd_alloc_xfer(sc->sc_udev);
524 	if (sc->sc_ixfer == NULL) {
525 		DPRINTF(("uhidev_open: couldn't allocate an xfer\n"));
526 		error = ENOMEM;
527 		goto out1; // xxxx
528 	}
529 
530 	/*
531 	 * Set up output interrupt pipe if an output interrupt endpoint
532 	 * exists.
533 	 */
534 	if (sc->sc_oep_addr != -1) {
535 		DPRINTF(("uhidev_open: oep=0x%02x\n", sc->sc_oep_addr));
536 
537 		err = usbd_open_pipe(sc->sc_iface, sc->sc_oep_addr,
538 		    0, &sc->sc_opipe);
539 		if (err != USBD_NORMAL_COMPLETION) {
540 			DPRINTF(("uhidev_open: usbd_open_pipe failed, "
541 			    "error=%d\n", err));
542 			error = EIO;
543 			goto out2;
544 		}
545 
546 		DPRINTF(("uhidev_open: sc->sc_opipe=%p\n", sc->sc_opipe));
547 
548 		sc->sc_oxfer = usbd_alloc_xfer(sc->sc_udev);
549 		if (sc->sc_oxfer == NULL) {
550 			DPRINTF(("uhidev_open: couldn't allocate an xfer\n"));
551 			error = ENOMEM;
552 			goto out3;
553 		}
554 
555 		sc->sc_owxfer = usbd_alloc_xfer(sc->sc_udev);
556 		if (sc->sc_owxfer == NULL) {
557 			DPRINTF(("uhidev_open: couldn't allocate owxfer\n"));
558 			error = ENOMEM;
559 			goto out3;
560 		}
561 	}
562 
563 	return (0);
564 
565 out3:
566 	/* Abort output pipe */
567 	usbd_close_pipe(sc->sc_opipe);
568 out2:
569 	/* Abort input pipe */
570 	usbd_close_pipe(sc->sc_ipipe);
571 out1:
572 	DPRINTF(("uhidev_open: failed in someway"));
573 	free(sc->sc_ibuf, M_USBDEV, sc->sc_isize);
574 	scd->sc_state &= ~UHIDEV_OPEN;
575 	sc->sc_refcnt = 0;
576 	sc->sc_ipipe = NULL;
577 	sc->sc_opipe = NULL;
578 	if (sc->sc_oxfer != NULL) {
579 		usbd_free_xfer(sc->sc_oxfer);
580 		sc->sc_oxfer = NULL;
581 	}
582 	if (sc->sc_owxfer != NULL) {
583 		usbd_free_xfer(sc->sc_owxfer);
584 		sc->sc_owxfer = NULL;
585 	}
586 	if (sc->sc_ixfer != NULL) {
587 		usbd_free_xfer(sc->sc_ixfer);
588 		sc->sc_ixfer = NULL;
589 	}
590 	return (error);
591 }
592 
593 void
594 uhidev_close(struct uhidev *scd)
595 {
596 	struct uhidev_softc *sc = scd->sc_parent;
597 
598 	if (!(scd->sc_state & UHIDEV_OPEN))
599 		return;
600 	scd->sc_state &= ~UHIDEV_OPEN;
601 	if (--sc->sc_refcnt)
602 		return;
603 	DPRINTF(("uhidev_close: close pipe\n"));
604 
605 	/* Disable interrupts. */
606 	if (sc->sc_opipe != NULL) {
607 		usbd_close_pipe(sc->sc_opipe);
608 		sc->sc_opipe = NULL;
609 	}
610 
611 	if (sc->sc_ipipe != NULL) {
612 		usbd_close_pipe(sc->sc_ipipe);
613 		sc->sc_ipipe = NULL;
614 	}
615 
616 	if (sc->sc_oxfer != NULL) {
617 		usbd_free_xfer(sc->sc_oxfer);
618 		sc->sc_oxfer = NULL;
619 	}
620 
621 	if (sc->sc_owxfer != NULL) {
622 		usbd_free_xfer(sc->sc_owxfer);
623 		sc->sc_owxfer = NULL;
624 	}
625 
626 	if (sc->sc_ixfer != NULL) {
627 		usbd_free_xfer(sc->sc_ixfer);
628 		sc->sc_ixfer = NULL;
629 	}
630 
631 	if (sc->sc_ibuf != NULL) {
632 		free(sc->sc_ibuf, M_USBDEV, sc->sc_isize);
633 		sc->sc_ibuf = NULL;
634 	}
635 }
636 
637 int
638 uhidev_report_type_conv(int hid_type_id)
639 {
640 	switch (hid_type_id) {
641 	case hid_input:
642 		return UHID_INPUT_REPORT;
643 	case hid_output:
644 		return UHID_OUTPUT_REPORT;
645 	case hid_feature:
646 		return UHID_FEATURE_REPORT;
647 	default:
648 		return -1;
649 	}
650 }
651 
652 int
653 uhidev_set_report(struct uhidev_softc *sc, int type, int id, void *data,
654     int len)
655 {
656 	usb_device_request_t req;
657 	char *buf = data;
658 	int actlen = len;
659 
660 	/* Prepend the reportID. */
661 	if (id > 0) {
662 		len++;
663 		buf = malloc(len, M_TEMP, M_WAITOK);
664 		buf[0] = id;
665 		memcpy(buf + 1, data, len - 1);
666 	}
667 
668 	if (sc->sc_opipe != NULL) {
669 		usbd_setup_xfer(sc->sc_owxfer, sc->sc_opipe, 0, buf, len,
670 		    USBD_SYNCHRONOUS | USBD_CATCH, 0, NULL);
671 		if (usbd_transfer(sc->sc_owxfer)) {
672 			usbd_clear_endpoint_stall(sc->sc_opipe);
673 			actlen = -1;
674 		}
675 	} else {
676 		req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
677 		req.bRequest = UR_SET_REPORT;
678 		USETW2(req.wValue, type, id);
679 		USETW(req.wIndex, sc->sc_ifaceno);
680 		USETW(req.wLength, len);
681 
682 		if (usbd_do_request(sc->sc_udev, &req, buf))
683 			actlen = -1;
684 	}
685 
686 	if (id > 0)
687 		free(buf, M_TEMP, len);
688 
689 	return (actlen);
690 }
691 
692 void
693 uhidev_set_report_async_cb(struct usbd_xfer *xfer, void *priv, usbd_status err)
694 {
695 	struct uhidev_softc *sc = priv;
696 
697 	if (err == USBD_STALLED)
698 		usbd_clear_endpoint_stall_async(sc->sc_opipe);
699 	usbd_free_xfer(xfer);
700 }
701 
702 int
703 uhidev_set_report_async(struct uhidev_softc *sc, int type, int id, void *data,
704     int len)
705 {
706 	struct usbd_xfer *xfer;
707 	usb_device_request_t req;
708 	int actlen = len;
709 	char *buf;
710 
711 	xfer = usbd_alloc_xfer(sc->sc_udev);
712 	if (xfer == NULL)
713 		return (-1);
714 
715 	if (id > 0)
716 		len++;
717 
718 	buf = usbd_alloc_buffer(xfer, len);
719 	if (buf == NULL) {
720 		usbd_free_xfer(xfer);
721 		return (-1);
722 	}
723 
724 	/* Prepend the reportID. */
725 	if (id > 0) {
726 		buf[0] = id;
727 		memcpy(buf + 1, data, len - 1);
728 	} else {
729 		memcpy(buf, data, len);
730 	}
731 
732 	if (sc->sc_opipe != NULL) {
733 		usbd_setup_xfer(xfer, sc->sc_opipe, sc, buf, len,
734 		    USBD_NO_COPY, USBD_DEFAULT_TIMEOUT,
735 		    uhidev_set_report_async_cb);
736 		if (usbd_transfer(xfer)) {
737 			usbd_clear_endpoint_stall_async(sc->sc_opipe);
738 			actlen = -1;
739 		}
740 	} else {
741 		req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
742 		req.bRequest = UR_SET_REPORT;
743 		USETW2(req.wValue, type, id);
744 		USETW(req.wIndex, sc->sc_ifaceno);
745 		USETW(req.wLength, len);
746 		if (usbd_request_async(xfer, &req, NULL, NULL))
747 			actlen = -1;
748 	}
749 
750 	return (actlen);
751 }
752 
753 int
754 uhidev_get_report(struct uhidev_softc *sc, int type, int id, void *data,
755     int len)
756 {
757 	usb_device_request_t req;
758 	char *buf = data;
759 	usbd_status err;
760 	int actlen;
761 
762 	if (id > 0) {
763 		len++;
764 		buf = malloc(len, M_TEMP, M_WAITOK|M_ZERO);
765 	}
766 
767 	req.bmRequestType = UT_READ_CLASS_INTERFACE;
768 	req.bRequest = UR_GET_REPORT;
769 	USETW2(req.wValue, type, id);
770 	USETW(req.wIndex, sc->sc_ifaceno);
771 	USETW(req.wLength, len);
772 
773 	err = usbd_do_request_flags(sc->sc_udev, &req, buf, 0, &actlen,
774 	    USBD_DEFAULT_TIMEOUT);
775 	if (err != USBD_NORMAL_COMPLETION && err != USBD_SHORT_XFER)
776 		actlen = -1;
777 
778 	/* Skip the reportID. */
779 	if (id > 0) {
780 		memcpy(data, buf + 1, len - 1);
781 		free(buf, M_TEMP, len);
782 	}
783 
784 	return (actlen);
785 }
786 
787 void
788 uhidev_get_report_async_cb(struct usbd_xfer *xfer, void *priv, usbd_status err)
789 {
790 	struct uhidev_async_info *info = priv;
791 	char *buf;
792 	int len = -1;
793 
794 	if (!usbd_is_dying(xfer->pipe->device)) {
795 		if (err == USBD_NORMAL_COMPLETION || err == USBD_SHORT_XFER) {
796 			len = xfer->actlen;
797 			buf = KERNADDR(&xfer->dmabuf, 0);
798 			if (info->id > 0) {
799 				len--;
800 				memcpy(info->data, buf + 1, len);
801 			} else {
802 				memcpy(info->data, buf, len);
803 			}
804 		}
805 		info->callback(info->priv, info->id, info->data, len);
806 	}
807 	free(info, M_TEMP, sizeof(*info));
808 	usbd_free_xfer(xfer);
809 }
810 
811 int
812 uhidev_get_report_async(struct uhidev_softc *sc, int type, int id, void *data,
813     int len, void *priv, void (*callback)(void *, int, void *, int))
814 {
815 	struct usbd_xfer *xfer;
816 	usb_device_request_t req;
817 	struct uhidev_async_info *info;
818 	int actlen = len;
819 	char *buf;
820 
821 	xfer = usbd_alloc_xfer(sc->sc_udev);
822 	if (xfer == NULL)
823 		return (-1);
824 
825 	if (id > 0)
826 		len++;
827 
828 	buf = usbd_alloc_buffer(xfer, len);
829 	if (buf == NULL) {
830 		usbd_free_xfer(xfer);
831 		return (-1);
832 	}
833 
834 	info = malloc(sizeof(*info), M_TEMP, M_NOWAIT);
835 	if (info == NULL) {
836 		usbd_free_xfer(xfer);
837 		return (-1);
838 	}
839 
840 	info->callback = callback;
841 	info->priv = priv;
842 	info->data = data;
843 	info->id = id;
844 
845 	req.bmRequestType = UT_READ_CLASS_INTERFACE;
846 	req.bRequest = UR_GET_REPORT;
847 	USETW2(req.wValue, type, id);
848 	USETW(req.wIndex, sc->sc_ifaceno);
849 	USETW(req.wLength, len);
850 
851 	if (usbd_request_async(xfer, &req, info, uhidev_get_report_async_cb)) {
852 		free(info, M_TEMP, sizeof(*info));
853 		actlen = -1;
854 	}
855 
856 	return (actlen);
857 }
858 
859 usbd_status
860 uhidev_write(struct uhidev_softc *sc, void *data, int len)
861 {
862 	usbd_status error;
863 
864 	DPRINTF(("uhidev_write: data=%p, len=%d\n", data, len));
865 
866 	if (sc->sc_opipe == NULL)
867 		return USBD_INVAL;
868 
869 #ifdef UHIDEV_DEBUG
870 	if (uhidevdebug > 50) {
871 
872 		u_int32_t i;
873 		u_int8_t *d = data;
874 
875 		DPRINTF(("uhidev_write: data ="));
876 		for (i = 0; i < len; i++)
877 			DPRINTF((" %02x", d[i]));
878 		DPRINTF(("\n"));
879 	}
880 #endif
881 	usbd_setup_xfer(sc->sc_owxfer, sc->sc_opipe, 0, data, len,
882 	    USBD_SYNCHRONOUS | USBD_CATCH, 0, NULL);
883 	error = usbd_transfer(sc->sc_owxfer);
884 	if (error)
885 		usbd_clear_endpoint_stall(sc->sc_opipe);
886 
887 	return (error);
888 }
889 
890 int
891 uhidev_ioctl(struct uhidev *sc, u_long cmd, caddr_t addr, int flag,
892     struct proc *p)
893 {
894 	struct usb_ctl_report_desc *rd;
895 	struct usb_ctl_report *re;
896 	int size;
897 	void *desc;
898 
899 	switch (cmd) {
900 	case USB_GET_REPORT_DESC:
901 		uhidev_get_report_desc(sc->sc_parent, &desc, &size);
902 		rd = (struct usb_ctl_report_desc *)addr;
903 		size = min(size, sizeof rd->ucrd_data);
904 		rd->ucrd_size = size;
905 		memcpy(rd->ucrd_data, desc, size);
906 		break;
907 	case USB_GET_REPORT:
908 		re = (struct usb_ctl_report *)addr;
909 		switch (re->ucr_report) {
910 		case UHID_INPUT_REPORT:
911 			size = sc->sc_isize;
912 			break;
913 		case UHID_OUTPUT_REPORT:
914 			size = sc->sc_osize;
915 			break;
916 		case UHID_FEATURE_REPORT:
917 			size = sc->sc_fsize;
918 			break;
919 		default:
920 			return EINVAL;
921 		}
922 		if (uhidev_get_report(sc->sc_parent, re->ucr_report,
923 		    sc->sc_report_id, re->ucr_data, size) != size)
924 			return EIO;
925 		break;
926 	case USB_SET_REPORT:
927 		re = (struct usb_ctl_report *)addr;
928 		switch (re->ucr_report) {
929 		case UHID_INPUT_REPORT:
930 			size = sc->sc_isize;
931 			break;
932 		case UHID_OUTPUT_REPORT:
933 			size = sc->sc_osize;
934 			break;
935 		case UHID_FEATURE_REPORT:
936 			size = sc->sc_fsize;
937 			break;
938 		default:
939 			return EINVAL;
940 		}
941 		if (uhidev_set_report(sc->sc_parent, re->ucr_report,
942 		    sc->sc_report_id, re->ucr_data, size) != size)
943 			return EIO;
944 		break;
945 	case USB_GET_REPORT_ID:
946 		*(int *)addr = sc->sc_report_id;
947 		break;
948 	default:
949 		return -1;
950 	}
951 	return 0;
952 }
953