xref: /openbsd-src/sys/dev/usb/uhidev.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
1 /*	$OpenBSD: uhidev.c,v 1.42 2011/07/03 15:47:17 matthew 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 	for (repid = 0; repid < nrepid; repid++) {
250 		repsz = hid_report_size(desc, size, hid_input, repid);
251 		DPRINTF(("uhidev_match: repid=%d, repsz=%d\n", repid, repsz));
252 		repsizes[repid] = repsz;
253 		if (repsz > 0) {
254 			if (repsz > sc->sc_isize)
255 				sc->sc_isize = repsz;
256 		}
257 	}
258 	sc->sc_isize += nrepid != 1;	/* space for report ID */
259 	DPRINTF(("uhidev_attach: isize=%d\n", sc->sc_isize));
260 
261 	uha.parent = sc;
262 	for (repid = 0; repid < nrepid; repid++) {
263 		DPRINTF(("uhidev_match: try repid=%d\n", repid));
264 		if (hid_report_size(desc, size, hid_input, repid) == 0 &&
265 		    hid_report_size(desc, size, hid_output, repid) == 0 &&
266 		    hid_report_size(desc, size, hid_feature, repid) == 0) {
267 			;	/* already NULL in sc->sc_subdevs[repid] */
268 		} else {
269 			uha.reportid = repid;
270 			dev = (struct uhidev *)config_found_sm(self, &uha,
271 			                           uhidevprint, uhidevsubmatch);
272 			sc->sc_subdevs[repid] = dev;
273 			if (dev != NULL) {
274 				dev->sc_in_rep_size = repsizes[repid];
275 #ifdef DIAGNOSTIC
276 				DPRINTF(("uhidev_match: repid=%d dev=%p\n",
277 					 repid, dev));
278 				if (dev->sc_intr == NULL) {
279 					DPRINTF(("%s: sc_intr == NULL\n",
280 					       sc->sc_dev.dv_xname));
281 					return;
282 				}
283 #endif
284 			}
285 		}
286 	}
287 }
288 
289 int
290 uhidev_maxrepid(void *buf, int len)
291 {
292 	struct hid_data *d;
293 	struct hid_item h;
294 	int maxid;
295 
296 	maxid = -1;
297 	h.report_ID = 0;
298 	for (d = hid_start_parse(buf, len, hid_none); hid_get_item(d, &h); )
299 		if (h.report_ID > maxid)
300 			maxid = h.report_ID;
301 	hid_end_parse(d);
302 	return (maxid);
303 }
304 
305 int
306 uhidevprint(void *aux, const char *pnp)
307 {
308 	struct uhidev_attach_arg *uha = aux;
309 
310 	if (pnp)
311 		printf("uhid at %s", pnp);
312 	if (uha->reportid != 0)
313 		printf(" reportid %d", uha->reportid);
314 	return (UNCONF);
315 }
316 
317 int uhidevsubmatch(struct device *parent, void *match, void *aux)
318 {
319 	struct uhidev_attach_arg *uha = aux;
320         struct cfdata *cf = match;
321 
322 	if (cf->uhidevcf_reportid != UHIDEV_UNK_REPORTID &&
323 	    cf->uhidevcf_reportid != uha->reportid)
324 		return (0);
325 	if (cf->uhidevcf_reportid == uha->reportid)
326 		uha->matchlvl = UMATCH_VENDOR_PRODUCT;
327 	else
328 		uha->matchlvl = 0;
329 	return ((*cf->cf_attach->ca_match)(parent, cf, aux));
330 }
331 
332 int
333 uhidev_activate(struct device *self, int act)
334 {
335 	struct uhidev_softc *sc = (struct uhidev_softc *)self;
336 	int i, rv = 0, r;
337 
338 	switch (act) {
339 	case DVACT_DEACTIVATE:
340 		for (i = 0; i < sc->sc_nrepid; i++)
341 			if (sc->sc_subdevs[i] != NULL) {
342 				r = config_deactivate(
343 				    &sc->sc_subdevs[i]->sc_dev);
344 				if (r)
345 					rv = r;
346 			}
347 		sc->sc_dying = 1;
348 		break;
349 	}
350 	return (rv);
351 }
352 
353 int
354 uhidev_detach(struct device *self, int flags)
355 {
356 	struct uhidev_softc *sc = (struct uhidev_softc *)self;
357 	int i, rv;
358 
359 	DPRINTF(("uhidev_detach: sc=%p flags=%d\n", sc, flags));
360 
361 	if (sc->sc_ipipe != NULL)
362 		usbd_abort_pipe(sc->sc_ipipe);
363 
364 	if (sc->sc_repdesc != NULL)
365 		free(sc->sc_repdesc, M_USBDEV);
366 
367 	rv = 0;
368 	for (i = 0; i < sc->sc_nrepid; i++) {
369 		if (sc->sc_subdevs[i] != NULL) {
370 			rv |= config_detach(&sc->sc_subdevs[i]->sc_dev, flags);
371 			sc->sc_subdevs[i] = NULL;
372 		}
373 	}
374 
375 	return (rv);
376 }
377 
378 void
379 uhidev_intr(usbd_xfer_handle xfer, usbd_private_handle addr, usbd_status status)
380 {
381 	struct uhidev_softc *sc = addr;
382 	struct uhidev *scd;
383 	u_char *p;
384 	u_int rep;
385 	u_int32_t cc;
386 
387 	usbd_get_xfer_status(xfer, NULL, NULL, &cc, NULL);
388 
389 #ifdef UHIDEV_DEBUG
390 	if (uhidevdebug > 5) {
391 		u_int32_t i;
392 
393 		DPRINTF(("uhidev_intr: status=%d cc=%d\n", status, cc));
394 		DPRINTF(("uhidev_intr: data ="));
395 		for (i = 0; i < cc; i++)
396 			DPRINTF((" %02x", sc->sc_ibuf[i]));
397 		DPRINTF(("\n"));
398 	}
399 #endif
400 
401 	if (status == USBD_CANCELLED)
402 		return;
403 
404 	if (status != USBD_NORMAL_COMPLETION) {
405 		DPRINTF(("%s: interrupt status=%d\n", sc->sc_dev.dv_xname,
406 			 status));
407 		usbd_clear_endpoint_stall_async(sc->sc_ipipe);
408 		return;
409 	}
410 
411 	p = sc->sc_ibuf;
412 	if (sc->sc_nrepid != 1)
413 		rep = *p++, cc--;
414 	else
415 		rep = 0;
416 	if (rep >= sc->sc_nrepid) {
417 		printf("uhidev_intr: bad repid %d\n", rep);
418 		return;
419 	}
420 	scd = sc->sc_subdevs[rep];
421 	DPRINTFN(5,("uhidev_intr: rep=%d, scd=%p state=0x%x\n",
422 		    rep, scd, scd ? scd->sc_state : 0));
423 	if (scd == NULL || !(scd->sc_state & UHIDEV_OPEN))
424 		return;
425 #ifdef UHIDEV_DEBUG
426 	if (scd->sc_in_rep_size != cc)
427 		printf("%s: bad input length %d != %d\n",sc->sc_dev.dv_xname,
428 		       scd->sc_in_rep_size, cc);
429 #endif
430 	scd->sc_intr(scd, p, cc);
431 }
432 
433 void
434 uhidev_get_report_desc(struct uhidev_softc *sc, void **desc, int *size)
435 {
436 	*desc = sc->sc_repdesc;
437 	*size = sc->sc_repdesc_size;
438 }
439 
440 int
441 uhidev_open(struct uhidev *scd)
442 {
443 	struct uhidev_softc *sc = scd->sc_parent;
444 	usbd_status err;
445 	int error;
446 
447 	DPRINTF(("uhidev_open: open pipe, state=%d refcnt=%d\n",
448 		 scd->sc_state, sc->sc_refcnt));
449 
450 	if (scd->sc_state & UHIDEV_OPEN)
451 		return (EBUSY);
452 	scd->sc_state |= UHIDEV_OPEN;
453 	if (sc->sc_refcnt++)
454 		return (0);
455 
456 	if (sc->sc_isize == 0)
457 		return (0);
458 
459 	sc->sc_ibuf = malloc(sc->sc_isize, M_USBDEV, M_WAITOK);
460 
461 	/* Set up input interrupt pipe. */
462 	DPRINTF(("uhidev_open: isize=%d, ep=0x%02x\n", sc->sc_isize,
463 	    sc->sc_iep_addr));
464 
465 	err = usbd_open_pipe_intr(sc->sc_iface, sc->sc_iep_addr,
466 		  USBD_SHORT_XFER_OK, &sc->sc_ipipe, sc, sc->sc_ibuf,
467 		  sc->sc_isize, uhidev_intr, USBD_DEFAULT_INTERVAL);
468 	if (err != USBD_NORMAL_COMPLETION) {
469 		DPRINTF(("uhidopen: usbd_open_pipe_intr failed, "
470 		    "error=%d\n", err));
471 		error = EIO;
472 		goto out1;
473 	}
474 
475 	DPRINTF(("uhidev_open: sc->sc_ipipe=%p\n", sc->sc_ipipe));
476 
477 	sc->sc_ixfer = usbd_alloc_xfer(sc->sc_udev);
478 	if (sc->sc_ixfer == NULL) {
479 		DPRINTF(("uhidev_open: couldn't allocate an xfer\n"));
480 		error = ENOMEM;
481 		goto out1; // xxxx
482 	}
483 
484 	/*
485 	 * Set up output interrupt pipe if an output interrupt endpoint
486 	 * exists.
487 	 */
488 	if (sc->sc_oep_addr != -1) {
489 		DPRINTF(("uhidev_open: oep=0x%02x\n", sc->sc_oep_addr));
490 
491 		err = usbd_open_pipe(sc->sc_iface, sc->sc_oep_addr,
492 		    0, &sc->sc_opipe);
493 
494 		if (err != USBD_NORMAL_COMPLETION) {
495 			DPRINTF(("uhidev_open: usbd_open_pipe failed, "
496 			    "error=%d\n", err));
497 			error = EIO;
498 			goto out2;
499 		}
500 		DPRINTF(("uhidev_open: sc->sc_opipe=%p\n", sc->sc_opipe));
501 
502 		sc->sc_oxfer = usbd_alloc_xfer(sc->sc_udev);
503 		if (sc->sc_oxfer == NULL) {
504 			DPRINTF(("uhidev_open: couldn't allocate an xfer\n"));
505 			error = ENOMEM;
506 			goto out3;
507 		}
508 
509 		sc->sc_owxfer = usbd_alloc_xfer(sc->sc_udev);
510 		if (sc->sc_owxfer == NULL) {
511 			DPRINTF(("uhidev_open: couldn't allocate owxfer\n"));
512 			error = ENOMEM;
513 			goto out3;
514 		}
515 	}
516 
517 	return (0);
518 
519 out3:
520 	/* Abort output pipe */
521 	usbd_close_pipe(sc->sc_opipe);
522 out2:
523 	/* Abort input pipe */
524 	usbd_close_pipe(sc->sc_ipipe);
525 out1:
526 	DPRINTF(("uhidev_open: failed in someway"));
527 	free(sc->sc_ibuf, M_USBDEV);
528 	scd->sc_state &= ~UHIDEV_OPEN;
529 	sc->sc_refcnt = 0;
530 	sc->sc_ipipe = NULL;
531 	sc->sc_opipe = NULL;
532 	if (sc->sc_oxfer != NULL) {
533 		usbd_free_xfer(sc->sc_oxfer);
534 		sc->sc_oxfer = NULL;
535 	}
536 	if (sc->sc_owxfer != NULL) {
537 		usbd_free_xfer(sc->sc_owxfer);
538 		sc->sc_owxfer = NULL;
539 	}
540 	return (error);
541 }
542 
543 void
544 uhidev_close(struct uhidev *scd)
545 {
546 	struct uhidev_softc *sc = scd->sc_parent;
547 
548 	if (!(scd->sc_state & UHIDEV_OPEN))
549 		return;
550 	scd->sc_state &= ~UHIDEV_OPEN;
551 	if (--sc->sc_refcnt)
552 		return;
553 	DPRINTF(("uhidev_close: close pipe\n"));
554 
555 	if (sc->sc_oxfer != NULL)
556 		usbd_free_xfer(sc->sc_oxfer);
557 
558 	if (sc->sc_owxfer != NULL)
559 		usbd_free_xfer(sc->sc_owxfer);
560 
561 	/* Disable interrupts. */
562 	if (sc->sc_opipe != NULL) {
563 		usbd_abort_pipe(sc->sc_opipe);
564 		usbd_close_pipe(sc->sc_opipe);
565 		sc->sc_opipe = NULL;
566 	}
567 
568 	if (sc->sc_ipipe != NULL) {
569 		usbd_abort_pipe(sc->sc_ipipe);
570 		usbd_close_pipe(sc->sc_ipipe);
571 		sc->sc_ipipe = NULL;
572 	}
573 
574 	if (sc->sc_ibuf != NULL) {
575 		free(sc->sc_ibuf, M_USBDEV);
576 		sc->sc_ibuf = NULL;
577 	}
578 }
579 
580 usbd_status
581 uhidev_set_report(struct uhidev *scd, int type, void *data, int len)
582 {
583 	char *buf;
584 	usbd_status retstat;
585 
586 	if (scd->sc_report_id == 0)
587 		return usbd_set_report(scd->sc_parent->sc_iface, type,
588 				       scd->sc_report_id, data, len);
589 
590 	buf = malloc(len + 1, M_TEMP, M_WAITOK);
591 	buf[0] = scd->sc_report_id;
592 	memcpy(buf+1, data, len);
593 
594 	retstat = usbd_set_report(scd->sc_parent->sc_iface, type,
595 				  scd->sc_report_id, buf, len + 1);
596 
597 	free(buf, M_TEMP);
598 
599 	return retstat;
600 }
601 
602 void
603 uhidev_set_report_async(struct uhidev *scd, int type, void *data, int len)
604 {
605 	/* XXX */
606 	char buf[100];
607 	if (scd->sc_report_id) {
608 		buf[0] = scd->sc_report_id;
609 		if ((uint)len > sizeof(buf) - 1) {
610 #ifdef DIAGNOSTIC
611 			printf("%s: report length too large (%d)\n",
612 			    scd->sc_dev.dv_xname, len);
613 #endif
614 			return;
615 		}
616 		memcpy(buf+1, data, len);
617 		len++;
618 		data = buf;
619 	}
620 
621 	usbd_set_report_async(scd->sc_parent->sc_iface, type,
622 			      scd->sc_report_id, data, len);
623 }
624 
625 usbd_status
626 uhidev_get_report(struct uhidev *scd, int type, void *data, int len)
627 {
628 	return usbd_get_report(scd->sc_parent->sc_iface, type,
629 			       scd->sc_report_id, data, len);
630 }
631 
632 usbd_status
633 uhidev_write(struct uhidev_softc *sc, void *data, int len)
634 {
635 
636 	DPRINTF(("uhidev_write: data=%p, len=%d\n", data, len));
637 
638 	if (sc->sc_opipe == NULL)
639 		return USBD_INVAL;
640 
641 #ifdef UHIDEV_DEBUG
642 	if (uhidevdebug > 50) {
643 
644 		u_int32_t i;
645 		u_int8_t *d = data;
646 
647 		DPRINTF(("uhidev_write: data ="));
648 		for (i = 0; i < len; i++)
649 			DPRINTF((" %02x", d[i]));
650 		DPRINTF(("\n"));
651 	}
652 #endif
653 	return usbd_intr_transfer(sc->sc_owxfer, sc->sc_opipe, 0,
654 	    USBD_NO_TIMEOUT, data, &len, "uhidevwi");
655 }
656 
657 int
658 uhidev_ioctl(struct uhidev *sc, u_long cmd, caddr_t addr, int flag,
659     struct proc *p)
660 {
661 	struct usb_ctl_report_desc *rd;
662 	struct usb_ctl_report *re;
663 	int size, extra;
664 	usbd_status err;
665 	void *desc;
666 
667 	switch (cmd) {
668 	case USB_GET_REPORT_DESC:
669 		uhidev_get_report_desc(sc->sc_parent, &desc, &size);
670 		rd = (struct usb_ctl_report_desc *)addr;
671 		size = min(size, sizeof rd->ucrd_data);
672 		rd->ucrd_size = size;
673 		memcpy(rd->ucrd_data, desc, size);
674 		break;
675 	case USB_GET_REPORT:
676 		re = (struct usb_ctl_report *)addr;
677 		switch (re->ucr_report) {
678 		case UHID_INPUT_REPORT:
679 			size = sc->sc_isize;
680 			break;
681 		case UHID_OUTPUT_REPORT:
682 			size = sc->sc_osize;
683 			break;
684 		case UHID_FEATURE_REPORT:
685 			size = sc->sc_fsize;
686 			break;
687 		default:
688 			return EINVAL;
689 		}
690 		extra = sc->sc_report_id != 0;
691 		err = uhidev_get_report(sc, re->ucr_report, re->ucr_data,
692 		    size + extra);
693 		if (extra)
694 			memcpy(re->ucr_data, re->ucr_data + 1, size);
695 		if (err)
696 			return EIO;
697 		break;
698 	case USB_SET_REPORT:
699 		re = (struct usb_ctl_report *)addr;
700 		switch (re->ucr_report) {
701 		case UHID_INPUT_REPORT:
702 			size = sc->sc_isize;
703 			break;
704 		case UHID_OUTPUT_REPORT:
705 			size = sc->sc_osize;
706 			break;
707 		case UHID_FEATURE_REPORT:
708 			size = sc->sc_fsize;
709 			break;
710 		default:
711 			return EINVAL;
712 		}
713 		err = uhidev_set_report(sc, re->ucr_report, re->ucr_data, size);
714 		if (err)
715 			return EIO;
716 		break;
717 	case USB_GET_REPORT_ID:
718 		*(int *)addr = sc->sc_report_id;
719 		break;
720 	default:
721 		return -1;
722 	}
723 	return 0;
724 }
725