| @@ -1,14 +1,14 @@ | | | @@ -1,14 +1,14 @@ |
1 | /* $NetBSD: uhidev.c,v 1.88 2022/03/28 12:43:58 riastradh Exp $ */ | | 1 | /* $NetBSD: uhidev.c,v 1.89 2022/03/28 12:44:06 riastradh Exp $ */ |
2 | | | 2 | |
3 | /* | | 3 | /* |
4 | * Copyright (c) 2001, 2012 The NetBSD Foundation, Inc. | | 4 | * Copyright (c) 2001, 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,44 +25,46 @@ | | | @@ -25,44 +25,46 @@ |
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: uhidev.c,v 1.88 2022/03/28 12:43:58 riastradh Exp $"); | | 38 | __KERNEL_RCSID(0, "$NetBSD: uhidev.c,v 1.89 2022/03/28 12:44:06 riastradh Exp $"); |
39 | | | 39 | |
40 | #ifdef _KERNEL_OPT | | 40 | #ifdef _KERNEL_OPT |
41 | #include "opt_usb.h" | | 41 | #include "opt_usb.h" |
42 | #endif | | 42 | #endif |
43 | | | 43 | |
44 | #include <sys/param.h> | | 44 | #include <sys/param.h> |
45 | #include <sys/types.h> | | 45 | #include <sys/types.h> |
46 | | | 46 | |
| | | 47 | #include <sys/atomic.h> |
47 | #include <sys/conf.h> | | 48 | #include <sys/conf.h> |
48 | #include <sys/device.h> | | 49 | #include <sys/device.h> |
49 | #include <sys/ioctl.h> | | 50 | #include <sys/ioctl.h> |
50 | #include <sys/kernel.h> | | 51 | #include <sys/kernel.h> |
51 | #include <sys/kmem.h> | | 52 | #include <sys/kmem.h> |
52 | #include <sys/lwp.h> | | 53 | #include <sys/lwp.h> |
53 | #include <sys/rndsource.h> | | 54 | #include <sys/rndsource.h> |
54 | #include <sys/signalvar.h> | | 55 | #include <sys/signalvar.h> |
55 | #include <sys/systm.h> | | 56 | #include <sys/systm.h> |
| | | 57 | #include <sys/xcall.h> |
56 | | | 58 | |
57 | #include <dev/usb/usb.h> | | 59 | #include <dev/usb/usb.h> |
58 | #include <dev/usb/usbhid.h> | | 60 | #include <dev/usb/usbhid.h> |
59 | | | 61 | |
60 | #include <dev/usb/usbdevs.h> | | 62 | #include <dev/usb/usbdevs.h> |
61 | #include <dev/usb/usbdi.h> | | 63 | #include <dev/usb/usbdi.h> |
62 | #include <dev/usb/usbdi_util.h> | | 64 | #include <dev/usb/usbdi_util.h> |
63 | #include <dev/usb/usb_quirks.h> | | 65 | #include <dev/usb/usb_quirks.h> |
64 | | | 66 | |
65 | #include <dev/usb/uhidev.h> | | 67 | #include <dev/usb/uhidev.h> |
66 | #include <dev/hid/hid.h> | | 68 | #include <dev/hid/hid.h> |
67 | | | 69 | |
68 | /* Report descriptor for broken Wacom Graphire */ | | 70 | /* Report descriptor for broken Wacom Graphire */ |
| @@ -612,27 +614,27 @@ uhidev_intr(struct usbd_xfer *xfer, void | | | @@ -612,27 +614,27 @@ uhidev_intr(struct usbd_xfer *xfer, void |
612 | rep = *p++, cc--; | | 614 | rep = *p++, cc--; |
613 | else | | 615 | else |
614 | rep = 0; | | 616 | rep = 0; |
615 | if (rep >= sc->sc_nrepid) { | | 617 | if (rep >= sc->sc_nrepid) { |
616 | printf("uhidev_intr: bad repid %d\n", rep); | | 618 | printf("uhidev_intr: bad repid %d\n", rep); |
617 | return; | | 619 | return; |
618 | } | | 620 | } |
619 | cdev = sc->sc_subdevs[rep]; | | 621 | cdev = sc->sc_subdevs[rep]; |
620 | if (!cdev) | | 622 | if (!cdev) |
621 | return; | | 623 | return; |
622 | scd = device_private(cdev); | | 624 | scd = device_private(cdev); |
623 | DPRINTFN(5,("uhidev_intr: rep=%d, scd=%p state=%#x\n", | | 625 | DPRINTFN(5,("uhidev_intr: rep=%d, scd=%p state=%#x\n", |
624 | rep, scd, scd ? scd->sc_state : 0)); | | 626 | rep, scd, scd ? scd->sc_state : 0)); |
625 | if (!(scd->sc_state & UHIDEV_OPEN)) | | 627 | if (!(atomic_load_acquire(&scd->sc_state) & UHIDEV_OPEN)) |
626 | return; | | 628 | return; |
627 | #ifdef UHIDEV_DEBUG | | 629 | #ifdef UHIDEV_DEBUG |
628 | if (scd->sc_in_rep_size != cc) { | | 630 | if (scd->sc_in_rep_size != cc) { |
629 | DPRINTF(("%s: expected %d bytes, got %d\n", | | 631 | DPRINTF(("%s: expected %d bytes, got %d\n", |
630 | device_xname(sc->sc_dev), scd->sc_in_rep_size, cc)); | | 632 | device_xname(sc->sc_dev), scd->sc_in_rep_size, cc)); |
631 | } | | 633 | } |
632 | #endif | | 634 | #endif |
633 | if (cc == 0) { | | 635 | if (cc == 0) { |
634 | DPRINTF(("%s: 0-length input ignored\n", | | 636 | DPRINTF(("%s: 0-length input ignored\n", |
635 | device_xname(sc->sc_dev))); | | 637 | device_xname(sc->sc_dev))); |
636 | return; | | 638 | return; |
637 | } | | 639 | } |
638 | rnd_add_uint32(&scd->rnd_source, (uintptr_t)(sc->sc_ibuf)); | | 640 | rnd_add_uint32(&scd->rnd_source, (uintptr_t)(sc->sc_ibuf)); |
| @@ -911,68 +913,69 @@ uhidev_open(struct uhidev *scd) | | | @@ -911,68 +913,69 @@ uhidev_open(struct uhidev *scd) |
911 | | | 913 | |
912 | DPRINTF(("uhidev_open(%s, report %d = %s): state=%x refcnt=%d\n", | | 914 | DPRINTF(("uhidev_open(%s, report %d = %s): state=%x refcnt=%d\n", |
913 | device_xname(sc->sc_dev), | | 915 | device_xname(sc->sc_dev), |
914 | scd->sc_report_id, | | 916 | scd->sc_report_id, |
915 | device_xname(scd->sc_dev), | | 917 | device_xname(scd->sc_dev), |
916 | scd->sc_state, | | 918 | scd->sc_state, |
917 | sc->sc_refcnt)); | | 919 | sc->sc_refcnt)); |
918 | | | 920 | |
919 | /* Mark the report id open. This is an exclusive lock. */ | | 921 | /* Mark the report id open. This is an exclusive lock. */ |
920 | if (scd->sc_state & UHIDEV_OPEN) { | | 922 | if (scd->sc_state & UHIDEV_OPEN) { |
921 | error = EBUSY; | | 923 | error = EBUSY; |
922 | goto out; | | 924 | goto out; |
923 | } | | 925 | } |
924 | scd->sc_state |= UHIDEV_OPEN; | | 926 | atomic_store_release(&scd->sc_state, scd->sc_state | UHIDEV_OPEN); |
925 | | | 927 | |
926 | /* Open the pipes which are shared by all report ids. */ | | 928 | /* Open the pipes which are shared by all report ids. */ |
927 | error = uhidev_open_pipes(sc); | | 929 | error = uhidev_open_pipes(sc); |
928 | if (error) | | 930 | if (error) |
929 | goto out; | | 931 | goto out; |
930 | | | 932 | |
931 | /* Success! */ | | 933 | /* Success! */ |
932 | error = 0; | | 934 | error = 0; |
933 | | | 935 | |
934 | out: if (error) { | | 936 | out: if (error) { |
935 | KASSERTMSG(scd->sc_state & UHIDEV_OPEN, | | 937 | KASSERTMSG(scd->sc_state & UHIDEV_OPEN, |
936 | "%s: report id %d: closed while opening", | | 938 | "%s: report id %d: closed while opening", |
937 | device_xname(sc->sc_dev), scd->sc_report_id); | | 939 | device_xname(sc->sc_dev), scd->sc_report_id); |
938 | scd->sc_state &= ~UHIDEV_OPEN; | | 940 | atomic_store_relaxed(&scd->sc_state, |
| | | 941 | scd->sc_state & ~UHIDEV_OPEN); |
939 | } | | 942 | } |
940 | mutex_exit(&sc->sc_lock); | | 943 | mutex_exit(&sc->sc_lock); |
941 | return error; | | 944 | return error; |
942 | } | | 945 | } |
943 | | | 946 | |
944 | /* | | 947 | /* |
945 | * uhidev_stop(scd) | | 948 | * uhidev_stop(scd) |
946 | * | | 949 | * |
947 | * Make all current and future output reports or xfers by scd to | | 950 | * Make all current and future output reports or xfers by scd to |
948 | * the output pipe to fail. Caller must then ensure no more will | | 951 | * the output pipe to fail. Caller must then ensure no more will |
949 | * be submitted and then call uhidev_close. | | 952 | * be submitted and then call uhidev_close. |
950 | * | | 953 | * |
951 | * Side effect: If uhidev_write was in progress for this scd, | | 954 | * Side effect: If uhidev_write was in progress for this scd, |
952 | * blocks all other uhidev_writes until uhidev_close on this scd. | | 955 | * blocks all other uhidev_writes until uhidev_close on this scd. |
953 | * | | 956 | * |
954 | * May sleep but only for a short duration to wait for USB | | 957 | * May sleep but only for a short duration to wait for USB |
955 | * transfer completion callbacks to run. | | 958 | * transfer completion callbacks to run. |
956 | */ | | 959 | */ |
957 | void | | 960 | void |
958 | uhidev_stop(struct uhidev *scd) | | 961 | uhidev_stop(struct uhidev *scd) |
959 | { | | 962 | { |
960 | struct uhidev_softc *sc = scd->sc_parent; | | 963 | struct uhidev_softc *sc = scd->sc_parent; |
961 | | | 964 | |
962 | mutex_enter(&sc->sc_lock); | | 965 | mutex_enter(&sc->sc_lock); |
963 | | | 966 | |
964 | /* Prevent further writes on this report from starting. */ | | 967 | /* Prevent further writes on this report from starting. */ |
965 | scd->sc_state |= UHIDEV_STOPPED; | | 968 | atomic_store_relaxed(&scd->sc_state, scd->sc_state | UHIDEV_STOPPED); |
966 | | | 969 | |
967 | /* If there's no output pipe at all, nothing to do. */ | | 970 | /* If there's no output pipe at all, nothing to do. */ |
968 | if (sc->sc_opipe == NULL) | | 971 | if (sc->sc_opipe == NULL) |
969 | goto out; | | 972 | goto out; |
970 | | | 973 | |
971 | /* | | 974 | /* |
972 | * If there's no write on this report in progress, nothing to | | 975 | * If there's no write on this report in progress, nothing to |
973 | * do -- any subsequent attempts will be prevented by | | 976 | * do -- any subsequent attempts will be prevented by |
974 | * UHIDEV_STOPPED. | | 977 | * UHIDEV_STOPPED. |
975 | */ | | 978 | */ |
976 | if (sc->sc_writereportid != scd->sc_report_id) | | 979 | if (sc->sc_writereportid != scd->sc_report_id) |
977 | goto out; | | 980 | goto out; |
978 | | | 981 | |
| @@ -1042,29 +1045,41 @@ uhidev_close(struct uhidev *scd) | | | @@ -1042,29 +1045,41 @@ uhidev_close(struct uhidev *scd) |
1042 | KASSERT(scd->sc_state & UHIDEV_STOPPED); | | 1045 | KASSERT(scd->sc_state & UHIDEV_STOPPED); |
1043 | sc->sc_stopreportid = -1; | | 1046 | sc->sc_stopreportid = -1; |
1044 | cv_broadcast(&sc->sc_cv); | | 1047 | cv_broadcast(&sc->sc_cv); |
1045 | } | | 1048 | } |
1046 | | | 1049 | |
1047 | /* | | 1050 | /* |
1048 | * Close our reference to the pipes, and mark our report as no | | 1051 | * Close our reference to the pipes, and mark our report as no |
1049 | * longer open. If it was stopped, clear that too -- drivers | | 1052 | * longer open. If it was stopped, clear that too -- drivers |
1050 | * are forbidden from issuing writes after uhidev_close anyway. | | 1053 | * are forbidden from issuing writes after uhidev_close anyway. |
1051 | */ | | 1054 | */ |
1052 | KASSERT(scd->sc_state & UHIDEV_OPEN); | | 1055 | KASSERT(scd->sc_state & UHIDEV_OPEN); |
1053 | uhidev_close_pipes(sc); | | 1056 | uhidev_close_pipes(sc); |
1054 | KASSERT(scd->sc_state & UHIDEV_OPEN); | | 1057 | KASSERT(scd->sc_state & UHIDEV_OPEN); |
1055 | scd->sc_state &= ~(UHIDEV_OPEN | UHIDEV_STOPPED); | | 1058 | atomic_store_relaxed(&scd->sc_state, |
| | | 1059 | scd->sc_state & ~(UHIDEV_OPEN | UHIDEV_STOPPED)); |
1056 | | | 1060 | |
| | | 1061 | /* |
| | | 1062 | * Make sure the next uhidev_intr (which runs in softint, like |
| | | 1063 | * XC_HIGHPRI) notices that UHIDEV_OPEN is cleared, and wait |
| | | 1064 | * for any current one to finish, in case the pipe is still |
| | | 1065 | * open for other report ids. |
| | | 1066 | * |
| | | 1067 | * We must drop the lock while doing this, because |
| | | 1068 | * uhidev_write_callback takes the lock in softint context and |
| | | 1069 | * it could deadlock with the xcall softint. |
| | | 1070 | */ |
1057 | mutex_exit(&sc->sc_lock); | | 1071 | mutex_exit(&sc->sc_lock); |
| | | 1072 | xc_barrier(XC_HIGHPRI); |
1058 | } | | 1073 | } |
1059 | | | 1074 | |
1060 | usbd_status | | 1075 | usbd_status |
1061 | uhidev_set_report(struct uhidev *scd, int type, void *data, int len) | | 1076 | uhidev_set_report(struct uhidev *scd, int type, void *data, int len) |
1062 | { | | 1077 | { |
1063 | char *buf; | | 1078 | char *buf; |
1064 | usbd_status retstat; | | 1079 | usbd_status retstat; |
1065 | | | 1080 | |
1066 | if (scd->sc_report_id == 0) | | 1081 | if (scd->sc_report_id == 0) |
1067 | return usbd_set_report(scd->sc_parent->sc_iface, type, | | 1082 | return usbd_set_report(scd->sc_parent->sc_iface, type, |
1068 | scd->sc_report_id, data, len); | | 1083 | scd->sc_report_id, data, len); |
1069 | | | 1084 | |
1070 | buf = kmem_alloc(len + 1, KM_SLEEP); | | 1085 | buf = kmem_alloc(len + 1, KM_SLEEP); |