| @@ -1,14 +1,14 @@ | | | @@ -1,14 +1,14 @@ |
1 | /* $NetBSD: uhid.c,v 1.108.2.1 2020/01/02 09:42:06 martin Exp $ */ | | 1 | /* $NetBSD: uhid.c,v 1.108.2.2 2020/07/15 14:09:04 martin Exp $ */ |
2 | | | 2 | |
3 | /* | | 3 | /* |
4 | * Copyright (c) 1998, 2004, 2008, 2012 The NetBSD Foundation, Inc. | | 4 | * Copyright (c) 1998, 2004, 2008, 2012 The NetBSD Foundation, Inc. |
5 | * All rights reserved. | | 5 | * All rights reserved. |
6 | * | | 6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation | | 7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Lennart Augustsson (lennart@augustsson.net) at | | 8 | * by Lennart Augustsson (lennart@augustsson.net) at |
9 | * Carlstedt Research & Technology and Matthew R. Green (mrg@eterna.com.au). | | 9 | * Carlstedt Research & Technology and Matthew R. Green (mrg@eterna.com.au). |
10 | * | | 10 | * |
11 | * Redistribution and use in source and binary forms, with or without | | 11 | * Redistribution and use in source and binary forms, with or without |
12 | * modification, are permitted provided that the following conditions | | 12 | * modification, are permitted provided that the following conditions |
13 | * are met: | | 13 | * are met: |
14 | * 1. Redistributions of source code must retain the above copyright | | 14 | * 1. Redistributions of source code must retain the above copyright |
| @@ -25,27 +25,27 @@ | | | @@ -25,27 +25,27 @@ |
25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | | 25 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | | 26 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | | 27 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | | 28 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | | 29 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
30 | * POSSIBILITY OF SUCH DAMAGE. | | 30 | * POSSIBILITY OF SUCH DAMAGE. |
31 | */ | | 31 | */ |
32 | | | 32 | |
33 | /* | | 33 | /* |
34 | * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf | | 34 | * HID spec: http://www.usb.org/developers/devclass_docs/HID1_11.pdf |
35 | */ | | 35 | */ |
36 | | | 36 | |
37 | #include <sys/cdefs.h> | | 37 | #include <sys/cdefs.h> |
38 | __KERNEL_RCSID(0, "$NetBSD: uhid.c,v 1.108.2.1 2020/01/02 09:42:06 martin Exp $"); | | 38 | __KERNEL_RCSID(0, "$NetBSD: uhid.c,v 1.108.2.2 2020/07/15 14:09:04 martin Exp $"); |
39 | | | 39 | |
40 | #ifdef _KERNEL_OPT | | 40 | #ifdef _KERNEL_OPT |
41 | #include "opt_compat_netbsd.h" | | 41 | #include "opt_compat_netbsd.h" |
42 | #include "opt_usb.h" | | 42 | #include "opt_usb.h" |
43 | #endif | | 43 | #endif |
44 | | | 44 | |
45 | #include <sys/param.h> | | 45 | #include <sys/param.h> |
46 | #include <sys/systm.h> | | 46 | #include <sys/systm.h> |
47 | #include <sys/kernel.h> | | 47 | #include <sys/kernel.h> |
48 | #include <sys/kmem.h> | | 48 | #include <sys/kmem.h> |
49 | #include <sys/signalvar.h> | | 49 | #include <sys/signalvar.h> |
50 | #include <sys/device.h> | | 50 | #include <sys/device.h> |
51 | #include <sys/ioctl.h> | | 51 | #include <sys/ioctl.h> |
| @@ -94,26 +94,27 @@ struct uhid_softc { | | | @@ -94,26 +94,27 @@ struct uhid_softc { |
94 | int sc_fsize; | | 94 | int sc_fsize; |
95 | | | 95 | |
96 | u_char *sc_obuf; | | 96 | u_char *sc_obuf; |
97 | | | 97 | |
98 | struct clist sc_q; /* protected by sc_lock */ | | 98 | struct clist sc_q; /* protected by sc_lock */ |
99 | struct selinfo sc_rsel; | | 99 | struct selinfo sc_rsel; |
100 | proc_t *sc_async; /* process that wants SIGIO */ | | 100 | proc_t *sc_async; /* process that wants SIGIO */ |
101 | void *sc_sih; | | 101 | void *sc_sih; |
102 | u_char sc_state; /* driver state */ | | 102 | u_char sc_state; /* driver state */ |
103 | #define UHID_ASLP 0x01 /* waiting for device data */ | | 103 | #define UHID_ASLP 0x01 /* waiting for device data */ |
104 | #define UHID_IMMED 0x02 /* return read data immediately */ | | 104 | #define UHID_IMMED 0x02 /* return read data immediately */ |
105 | | | 105 | |
106 | int sc_refcnt; | | 106 | int sc_refcnt; |
| | | 107 | int sc_raw; |
107 | u_char sc_dying; | | 108 | u_char sc_dying; |
108 | }; | | 109 | }; |
109 | | | 110 | |
110 | #define UHIDUNIT(dev) (minor(dev)) | | 111 | #define UHIDUNIT(dev) (minor(dev)) |
111 | #define UHID_CHUNK 128 /* chunk size for read */ | | 112 | #define UHID_CHUNK 128 /* chunk size for read */ |
112 | #define UHID_BSIZE 1020 /* buffer size */ | | 113 | #define UHID_BSIZE 1020 /* buffer size */ |
113 | | | 114 | |
114 | dev_type_open(uhidopen); | | 115 | dev_type_open(uhidopen); |
115 | dev_type_close(uhidclose); | | 116 | dev_type_close(uhidclose); |
116 | dev_type_read(uhidread); | | 117 | dev_type_read(uhidread); |
117 | dev_type_write(uhidwrite); | | 118 | dev_type_write(uhidwrite); |
118 | dev_type_ioctl(uhidioctl); | | 119 | dev_type_ioctl(uhidioctl); |
119 | dev_type_poll(uhidpoll); | | 120 | dev_type_poll(uhidpoll); |
| @@ -174,26 +175,28 @@ uhid_attach(device_t parent, device_t se | | | @@ -174,26 +175,28 @@ uhid_attach(device_t parent, device_t se |
174 | | | 175 | |
175 | sc->sc_hdev.sc_dev = self; | | 176 | sc->sc_hdev.sc_dev = self; |
176 | selinit(&sc->sc_rsel); | | 177 | selinit(&sc->sc_rsel); |
177 | sc->sc_hdev.sc_intr = uhid_intr; | | 178 | sc->sc_hdev.sc_intr = uhid_intr; |
178 | sc->sc_hdev.sc_parent = uha->parent; | | 179 | sc->sc_hdev.sc_parent = uha->parent; |
179 | sc->sc_hdev.sc_report_id = uha->reportid; | | 180 | sc->sc_hdev.sc_report_id = uha->reportid; |
180 | sc->sc_sih = softint_establish(SOFTINT_CLOCK, uhid_softintr, sc); | | 181 | sc->sc_sih = softint_establish(SOFTINT_CLOCK, uhid_softintr, sc); |
181 | | | 182 | |
182 | uhidev_get_report_desc(uha->parent, &desc, &size); | | 183 | uhidev_get_report_desc(uha->parent, &desc, &size); |
183 | repid = uha->reportid; | | 184 | repid = uha->reportid; |
184 | sc->sc_isize = hid_report_size(desc, size, hid_input, repid); | | 185 | sc->sc_isize = hid_report_size(desc, size, hid_input, repid); |
185 | sc->sc_osize = hid_report_size(desc, size, hid_output, repid); | | 186 | sc->sc_osize = hid_report_size(desc, size, hid_output, repid); |
186 | sc->sc_fsize = hid_report_size(desc, size, hid_feature, repid); | | 187 | sc->sc_fsize = hid_report_size(desc, size, hid_feature, repid); |
| | | 188 | sc->sc_raw = hid_is_collection(desc, size, uha->reportid, |
| | | 189 | HID_USAGE2(HUP_FIDO, HUF_U2FHID)); |
187 | | | 190 | |
188 | aprint_naive("\n"); | | 191 | aprint_naive("\n"); |
189 | aprint_normal(": input=%d, output=%d, feature=%d\n", | | 192 | aprint_normal(": input=%d, output=%d, feature=%d\n", |
190 | sc->sc_isize, sc->sc_osize, sc->sc_fsize); | | 193 | sc->sc_isize, sc->sc_osize, sc->sc_fsize); |
191 | | | 194 | |
192 | mutex_init(&sc->sc_access_lock, MUTEX_DEFAULT, IPL_NONE); | | 195 | mutex_init(&sc->sc_access_lock, MUTEX_DEFAULT, IPL_NONE); |
193 | mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SOFTUSB); | | 196 | mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_SOFTUSB); |
194 | cv_init(&sc->sc_cv, "uhidrea"); | | 197 | cv_init(&sc->sc_cv, "uhidrea"); |
195 | cv_init(&sc->sc_detach_cv, "uhiddet"); | | 198 | cv_init(&sc->sc_detach_cv, "uhiddet"); |
196 | | | 199 | |
197 | if (!pmf_device_register(self, NULL, NULL)) | | 200 | if (!pmf_device_register(self, NULL, NULL)) |
198 | aprint_error_dev(self, "couldn't establish power handler\n"); | | 201 | aprint_error_dev(self, "couldn't establish power handler\n"); |
199 | | | 202 | |
| @@ -472,35 +475,52 @@ uhidread(dev_t dev, struct uio *uio, int | | | @@ -472,35 +475,52 @@ uhidread(dev_t dev, struct uio *uio, int |
472 | int | | 475 | int |
473 | uhid_do_write(struct uhid_softc *sc, struct uio *uio, int flag) | | 476 | uhid_do_write(struct uhid_softc *sc, struct uio *uio, int flag) |
474 | { | | 477 | { |
475 | int error; | | 478 | int error; |
476 | int size; | | 479 | int size; |
477 | usbd_status err; | | 480 | usbd_status err; |
478 | | | 481 | |
479 | DPRINTFN(1, ("uhidwrite\n")); | | 482 | DPRINTFN(1, ("uhidwrite\n")); |
480 | | | 483 | |
481 | if (sc->sc_dying) | | 484 | if (sc->sc_dying) |
482 | return EIO; | | 485 | return EIO; |
483 | | | 486 | |
484 | size = sc->sc_osize; | | 487 | size = sc->sc_osize; |
485 | error = 0; | | | |
486 | if (uio->uio_resid != size || size == 0) | | 488 | if (uio->uio_resid != size || size == 0) |
487 | return EINVAL; | | 489 | return EINVAL; |
488 | error = uiomove(sc->sc_obuf, size, uio); | | 490 | error = uiomove(sc->sc_obuf, size, uio); |
| | | 491 | #ifdef UHID_DEBUG |
| | | 492 | if (uhiddebug > 5) { |
| | | 493 | uint32_t i; |
| | | 494 | |
| | | 495 | DPRINTF(("%s: outdata[%d] =", device_xname(sc->sc_hdev.sc_dev), |
| | | 496 | error)); |
| | | 497 | for (i = 0; i < size; i++) |
| | | 498 | DPRINTF((" %02x", sc->sc_obuf[i])); |
| | | 499 | DPRINTF(("\n")); |
| | | 500 | } |
| | | 501 | #endif |
489 | if (!error) { | | 502 | if (!error) { |
490 | err = uhidev_set_report(&sc->sc_hdev, UHID_OUTPUT_REPORT, | | 503 | if (sc->sc_raw) |
491 | sc->sc_obuf, size); | | 504 | err = uhidev_write(sc->sc_hdev.sc_parent, sc->sc_obuf, |
492 | if (err) | | 505 | size); |
| | | 506 | else |
| | | 507 | err = uhidev_set_report(&sc->sc_hdev, |
| | | 508 | UHID_OUTPUT_REPORT, sc->sc_obuf, size); |
| | | 509 | if (err) { |
| | | 510 | DPRINTF(("%s: err = %d\n", |
| | | 511 | device_xname(sc->sc_hdev.sc_dev), err)); |
493 | error = EIO; | | 512 | error = EIO; |
| | | 513 | } |
494 | } | | 514 | } |
495 | | | 515 | |
496 | return error; | | 516 | return error; |
497 | } | | 517 | } |
498 | | | 518 | |
499 | int | | 519 | int |
500 | uhidwrite(dev_t dev, struct uio *uio, int flag) | | 520 | uhidwrite(dev_t dev, struct uio *uio, int flag) |
501 | { | | 521 | { |
502 | struct uhid_softc *sc; | | 522 | struct uhid_softc *sc; |
503 | int error; | | 523 | int error; |
504 | | | 524 | |
505 | sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev)); | | 525 | sc = device_lookup_private(&uhid_cd, UHIDUNIT(dev)); |
506 | | | 526 | |
| @@ -572,26 +592,34 @@ uhid_do_ioctl(struct uhid_softc *sc, u_l | | | @@ -572,26 +592,34 @@ uhid_do_ioctl(struct uhid_softc *sc, u_l |
572 | mutex_enter(proc_lock); | | 592 | mutex_enter(proc_lock); |
573 | if (sc->sc_async == NULL) { | | 593 | if (sc->sc_async == NULL) { |
574 | mutex_exit(proc_lock); | | 594 | mutex_exit(proc_lock); |
575 | return EINVAL; | | 595 | return EINVAL; |
576 | } | | 596 | } |
577 | if (-*(int *)addr != sc->sc_async->p_pgid | | 597 | if (-*(int *)addr != sc->sc_async->p_pgid |
578 | && *(int *)addr != sc->sc_async->p_pid) { | | 598 | && *(int *)addr != sc->sc_async->p_pid) { |
579 | mutex_exit(proc_lock); | | 599 | mutex_exit(proc_lock); |
580 | return EPERM; | | 600 | return EPERM; |
581 | } | | 601 | } |
582 | mutex_exit(proc_lock); | | 602 | mutex_exit(proc_lock); |
583 | break; | | 603 | break; |
584 | | | 604 | |
| | | 605 | case USB_HID_GET_RAW: |
| | | 606 | *(int *)addr = sc->sc_raw; |
| | | 607 | break; |
| | | 608 | |
| | | 609 | case USB_HID_SET_RAW: |
| | | 610 | sc->sc_raw = *(int *)addr; |
| | | 611 | break; |
| | | 612 | |
585 | case USB_GET_REPORT_DESC: | | 613 | case USB_GET_REPORT_DESC: |
586 | uhidev_get_report_desc(sc->sc_hdev.sc_parent, &desc, &size); | | 614 | uhidev_get_report_desc(sc->sc_hdev.sc_parent, &desc, &size); |
587 | rd = (struct usb_ctl_report_desc *)addr; | | 615 | rd = (struct usb_ctl_report_desc *)addr; |
588 | size = uimin(size, sizeof(rd->ucrd_data)); | | 616 | size = uimin(size, sizeof(rd->ucrd_data)); |
589 | rd->ucrd_size = size; | | 617 | rd->ucrd_size = size; |
590 | memcpy(rd->ucrd_data, desc, size); | | 618 | memcpy(rd->ucrd_data, desc, size); |
591 | break; | | 619 | break; |
592 | | | 620 | |
593 | case USB_SET_IMMED: | | 621 | case USB_SET_IMMED: |
594 | if (*(int *)addr) { | | 622 | if (*(int *)addr) { |
595 | extra = sc->sc_hdev.sc_report_id != 0; | | 623 | extra = sc->sc_hdev.sc_report_id != 0; |
596 | if (sc->sc_isize + extra > sizeof(buffer)) | | 624 | if (sc->sc_isize + extra > sizeof(buffer)) |
597 | return ENOBUFS; | | 625 | return ENOBUFS; |