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