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